此控件和统计折线图控件相似,代码也大部分相同。参见“自定义的Qt统计折线图控件”,这里不多叙述。效果图如下:

上代码,头文件:

class MBarChart : public QWidget
{
    Q_OBJECT

public:
    MBarChart(QWidget *parent);
    QRgb append(const QVector<qreal>& idata);
    void setDataTag(int i, const QString& tagName);

private:
    struct XInfo;
    struct YInfo;
    void paintEvent(QPaintEvent *event) override;
    QRgb obtainColor();
    qreal calcBaseNumber(qreal v);
    qreal calcNearNumber(qreal value, bool upperOrLower);
    void correctApex(qreal& min, qreal& max);
    void huaKeDuX(QPainter* painter, const QFontMetrics& fm, const QPoint& axiso, const QPoint& axisx, XInfo& xinfo);
    void huaKeDuY(QPainter* painter, const QFontMetrics& fm, const QPoint& axiso, const QPoint& axisy, YInfo& yInfo);
    std::pair<qreal, qreal> minMaxValue(const QVector<QVector<qreal>>& datas);

    struct XInfo
    {
        QVector<qreal> starts;
        qreal binWidth;
    };

    struct YInfo
    {
        qreal minv;
        qreal maxv;
        qreal scrStart; /* 对应于minv的屏幕坐标 */
        qreal scrEnd; /* 对应于maxv的屏幕坐标 */
    };

private:
    const static QMargins margin;
    const static qreal maxRatio;
    const static qreal minRatio;
    QVector<QVector<qreal>> datas;
    QVector<QRgb> colors;
    QStringList tags;
};

CPP文件:

const QMargins MBarChart::margin(40, 20, 20, 25);
const qreal MBarChart::maxRatio = 0.9;
const qreal MBarChart::minRatio = 0.1;

MBarChart::MBarChart(QWidget *parent)
    : QWidget(parent)
{
}

QRgb MBarChart::append(const QVector<qreal>& idata)
{
    if (datas.empty())
    {
        datas.push_back(idata);
        int count = (int)idata.size();
        for (int i = 0; i < count; i++)
        {
            tags.append(QString(u8"第%1组").arg(i + 1));
        }
    }
    else
    {
        int dcount = (int)datas.first().size();
        int icount = (int)idata.size();
        if (dcount > icount)
        {
            QVector<qreal> temp = idata;
            icount = dcount - icount;
            for (int i = 0; i < icount; i++)
            {
                temp.push_back(0);
            }
            datas.push_back(qMove(temp));
        }
        else if (dcount < icount)
        {
            QVector<qreal> temp = idata;
            temp.erase(temp.begin() + dcount, temp.end());
            datas.push_back(qMove(temp));
        }
        else
        {
            datas.push_back(idata);
        }
    }
    QRgb rgb = obtainColor();
    colors.append(rgb);
    update();
    return rgb;
}

void MBarChart::setDataTag(int i, const QString& tagName)
{
    tags[i] = tagName;
    update();
}

QRgb MBarChart::obtainColor()
{
    int r = qrand() % 223 + 32;
    int b = qrand() % 223 + 32;
    int g = 255 - qMin(r, b);
    return qRgb(r, g, b);
}

std::pair<qreal, qreal> MBarChart::minMaxValue(const QVector<QVector<qreal>>& datas)
{
    std::pair<qreal, qreal> mmValue(0, 0);
    if (datas.empty())
    {
        return mmValue;
    }
    QVector<qreal> minmaxs;
    for (auto& it : datas)
    {
        auto apex = std::minmax_element(it.begin(), it.end());
        minmaxs.push_back(*apex.first);
        minmaxs.push_back(*apex.second);
    }
    auto apex = std::minmax_element(minmaxs.begin(), minmaxs.end());
    mmValue.first = *apex.first;
    mmValue.second = *apex.second;
    return mmValue;
}

void MBarChart::correctApex(qreal& min, qreal& max)
{
    if (min == 0 && max == 0)
    {
        max = 1;
    }
    else if (min > 0 && max / min > 10)
    {
        min = 0;
    }
    else if (max < 0 && min / max > 10)
    {
        max = 0;
    }
    else if (min * max < 0 && max / min < -10)
    {
        min = max / -10;
    }
    else if (min * max < 0 && min / max < -10)
    {
        max = min / -10;
    }
}

qreal MBarChart::calcBaseNumber(qreal v)
{
    qreal i = 1e-34;
    while (qAbs(v / i) >= 1)
    {
        i *= 10;
    }
    i /= 10;
    return i;
}

//---------------------------------------------------------------------------------------
// 计算数值value附近最近的可整除…0.1或1或10或100或1000…的数值
// value:输入数值
// upperorLower : 指示向上取值还是向下取值
// 返回值:返回附近可整除10或100或1000…的数值
//---------------------------------------------------------------------------------------
qreal MBarChart::calcNearNumber(qreal value, bool upperOrLower)
{
    if (value == 0)
    {
        return 0;
    }
    qreal i = calcBaseNumber(value);
    int x = int(value / i);
    qreal y = i * x;
    if (upperOrLower)
    {
        return (value > 0) ? y + i : y;
    }
    else
    {
        return (value > 0) ? y : y - i;
    }
}

void MBarChart::huaKeDuX(QPainter* painter, const QFontMetrics& fm, const QPoint& axiso, const QPoint& axisx, XInfo& xInfo)
{
    const int binPad = 20; /* 柱状图每组柱之间的间隔 */
    int cols = tags.size();
    qreal xMinLength = qAbs(axisx.x() - axiso.x()) * minRatio;
    qreal xMaxLength = qAbs(axisx.x() - axiso.x()) * maxRatio;
    qreal step = ((xMaxLength - xMinLength) - (cols - 1) * binPad) / cols;
    int rows = colors.size();
    xInfo.binWidth = step / rows;
    /* 计算X轴柱的坐标 */
    for (int i = 0; i < cols; i++)
    {
        qreal xv = axiso.x() + xMinLength + (step + binPad) * i;
        xInfo.starts.push_back(xv);
    }
    /* 画X轴信息字符串 */
    const int tagExtend = 14;
    for (int i = 0; i < cols; i++)
    {
        QPoint xcenter((int)(xInfo.starts[i] + step / 2), axiso.y() + tagExtend);
        QSize xsz = fm.size(0, tags[i]);
        painter->drawText(QRect(QPoint(xcenter.x() - xsz.width() / 2, xcenter.y() - xsz.height() / 2), xsz), tags[i]);
    }
}

void MBarChart::huaKeDuY(QPainter* painter, const QFontMetrics& fm, const QPoint& axiso, const QPoint& axisy, YInfo& yInfo)
{
    auto minmax = minMaxValue(datas);
    qreal minv = calcNearNumber(minmax.first, false);
    qreal maxv = calcNearNumber(minmax.second, true);
    correctApex(minv, maxv);
    /* 绘制Y轴上的刻度和对应的数字,只画5个刻度 */
    qreal yMaxLength = qAbs(axisy.y() - axiso.y()) * maxRatio;
    qreal yMinLength = qAbs(axisy.y() - axiso.y()) * minRatio;
    int loopCount = 5;
    if (minv == 0) /* 如数据最小是0,则扩展到原点分为5份 */
    {
        yMinLength = 0;
        loopCount = 4;
    }
    qreal ymax = axiso.y() - yMaxLength;
    qreal ymin = axiso.y() - yMinLength;
    for (int i = 0; i <= loopCount; i++)
    {
        qreal y = ymax + (ymin - ymax) / 5.0 * i;
        QPointF keDuY1(axiso.x(), y);
        QPointF keDuY2(axiso.x() + 5.0, y);
        painter->drawLine(keDuY1, keDuY2);
        QString zhi = QString::number(maxv + (minv - maxv) / 5 * i);
        QPoint zcenter(axiso.x() - margin.left() / 2, y);
        QSize zsz = fm.size(0, zhi);
        painter->drawText(QRect(QPoint(zcenter.x() - zsz.width() / 2, zcenter.y() - zsz.height() / 2), zsz), zhi);
    }
    yInfo.minv = minv;
    yInfo.maxv = maxv;
    yInfo.scrStart = ymin;
    yInfo.scrEnd = ymax;
}

void MBarChart::paintEvent(QPaintEvent *event)
{
    QPainter painter(this);
    painter.setRenderHint(QPainter::Antialiasing);
    QFontMetrics fm = painter.fontMetrics();
    painter.setBrush(Qt::NoBrush);
    painter.setPen(Qt::black);

    /* 绘制原点 */
    QPoint axiso(margin.left(), height() - margin.bottom());
    QPoint ocenter(axiso.x() - 7, axiso.y() + 7);
    QSize osz = fm.size(0, u8"O");
    painter.drawText(QRect(QPoint(ocenter.x() - osz.width() / 2, ocenter.y() - osz.height() / 2), osz), u8"O");

    /* 绘制X轴 */
    QPoint axisx(width() - margin.right(), height() - margin.bottom());
    painter.drawLine(axiso, axisx);
    QPoint jianTouX[] = /* 向右箭头 */
    {
        { -5, 3 },
        { 0, 0 },
        { -5, -3 },
    };
    painter.save();
    painter.translate(axisx);
    painter.drawPolyline(jianTouX, 3);
    painter.restore();
    /* 计算X坐标刻度位置 */
    XInfo xInfo;
    huaKeDuX(&painter, fm, axiso, axisx, xInfo);

    /* 绘制Y轴 */
    QPoint axisy(margin.left(), margin.top());
    painter.drawLine(axiso, axisy);
    QPoint jianTouY[] = /* 向上箭头 */
    {
        { 3, 5 },
        { 0, 0 },
        { -3, 5 },
    };
    painter.save();
    painter.translate(axisy);;
    painter.drawPolyline(jianTouY, 3);
    painter.restore();
    /* 计算Y坐标刻度位置 */
    YInfo yInfo;
    huaKeDuY(&painter, fm, axiso, axisy, yInfo);

    /* 画数据 */
    int dcount = datas.size();
    for (int i = 0; i < dcount; i++)
    {
        const QVector<qreal>& pts = datas[i];
        int pcount = pts.size();
        painter.setPen(Qt::NoPen);
        for (int j = 0; j < pcount; j++)
        {
            qreal x = xInfo.starts[j] + xInfo.binWidth * i;
            qreal y = yInfo.scrStart + (yInfo.scrEnd - yInfo.scrStart) * (pts[j] - yInfo.minv) / (yInfo.maxv - yInfo.minv);
            QRectF rect;
            rect.setX(x);
            rect.setY(y);
            rect.setWidth(xInfo.binWidth);
            rect.setHeight(axiso.y() - y);
            painter.setBrush(QColor(colors[i]));
            painter.drawRect(rect);
        }
    }
}

 

原文地址:http://www.cnblogs.com/mengxiangdu/p/16897316.html

1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长! 2. 分享目的仅供大家学习和交流,请务用于商业用途! 3. 如果你也有好源码或者教程,可以到用户中心发布,分享有积分奖励和额外收入! 4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解! 5. 如有链接无法下载、失效或广告,请联系管理员处理! 6. 本站资源售价只是赞助,收取费用仅维持本站的日常运营所需! 7. 如遇到加密压缩包,默认解压密码为"gltf",如遇到无法解压的请联系管理员! 8. 因为资源和程序源码均为可复制品,所以不支持任何理由的退款兑现,请斟酌后支付下载 声明:如果标题没有注明"已测试"或者"测试可用"等字样的资源源码均未经过站长测试.特别注意没有标注的源码不保证任何可用性