自定义的Qt直方图控件
就是显示1维数据的控件,输入的是大于零的整形数组,对应着y轴的值,不需要x轴数据,默认是1,2,3,4……它是只读控件。目前做的不够智能,不过没关系,对显示简单的统计数据还是可用的。比如:显示1张灰度图的灰度值像素的统计直方图。该控件在VS2015和Qt5.9上测试通过。显示效果如下:
下面上代码,头文件:
class MHistogram : public QWidget { Q_OBJECT public: MHistogram(QWidget *parernt = nullptr); void setValue(const QVector<int>& ibins); private: void paintEvent(QPaintEvent *event) override; void mouseMoveEvent(QMouseEvent* event) override; void enterEvent(QEvent *event) override; void leaveEvent(QEvent *event) override; int calcNearInteger(int value, bool upper0rLower); private: QVector<int> bins; QVector<QRectF> rects; int hitBin; };
CPP文件:
MHistogram::MHistogram(QWidget* parent) : QWidget(parent), hitBin(-1) { setMouseTracking(true); } void MHistogram::setValue(const QVector<int>& ibins) { bins = ibins; hitBin = -1; update(); } //--------------------------------------------------------------------------------------- // 计算数值value附近最近的可整除10或100或1000…的数值 // value:输入数值 // upperorLower:指示向上取值还是向下取值 // 返回值:返回附近可整除10或100或1000…的数值 //--------------------------------------------------------------------------------------- int MHistogram::calcNearInteger(int value, bool upperOrLower) { int i = 10; while (value / i != 0) { i *= 10; } i = qMax(i / 10, 10); int x = value / i; if (upperOrLower) { return i * (x + 1); } return i * x; } void MHistogram::paintEvent(QPaintEvent *) { QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); QFontMetrics fm = painter.fontMetrics(); painter.setBrush(Qt::NoBrush); painter.setPen(Qt::black); const QMargins margin(40, 20, 20, 20); /* 绘制原点 */ 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(); /* 绘制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(); const qreal upRatio = 0.9; const qreal lowRatio = 0.1; auto maxv = bins.empty() ? 0 : *std::max_element(bins.begin(), bins.end()); maxv = calcNearInteger(maxv, true); auto minv = bins.empty() ? 0 : *std::min_element(bins.begin(), bins.end()); minv = calcNearInteger(minv, false); /* 绘制Y轴上的刻度和对应的数字,只画5个刻度 */ qreal yMaxLength = qAbs(axisy.y() - axiso.y()) * upRatio; qreal yMinLength = qAbs(axisy.y() - axiso.y()) * lowRatio; int loopCount = 5; if (minv == 0) /* 如数据最小是0,则扩展到原点分为5份 */ { yMinLength = 0; loopCount = 4; } int ystart = axiso.y() - (int)yMaxLength; int yend = axiso.y() - (int)yMinLength; for (int i = 0; i <= loopCount; i++) { int y = ystart + (yend - ystart) / 5 * i; QPoint keDuY1(axiso.x(), y); QPoint keDuY2(axiso.x() + 5, 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); } rects.clear(); const qreal xRatio = 0.9; /* 绘制每个条形 */ qreal xLength = (axisx.x() - axiso.x()) * xRatio; int count = (int)bins.size(); qreal binWidth = xLength / count; for (int i = 0; i < count; i++) { if (i == hitBin) { painter.setBrush(QColor(32, 199, 255)); painter.setPen(QColor(32, 147, 193)); } else { painter.setBrush(QColor(32, 173, 243)); painter.setPen(QColor(32, 127, 173)); } qreal binLen = yMinLength + (yMaxLength - yMinLength) * (bins[i] - minv) / (maxv - minv); QRectF binRect; binRect.setX(axiso.x() + i * binWidth + 1); binRect.setY(axiso.y() - binLen); binRect.setWidth(binWidth); binRect.setHeight(binLen); painter.drawRect(binRect); rects.push_back(binRect); } } void MHistogram::mouseMoveEvent(QMouseEvent *event) { int entered = -1; int count = (int)bins.size(); for (int i = 0; i < count; i++) { if (rects[i].contains(event->pos())) { entered = i; break; } } if (entered != hitBin) { setToolTip((entered >= 0) ? QString(u8"值:%1").arg(bins[entered]) : QString()); hitBin = entered; update(); } } void MHistogram::enterEvent(QEvent *) { // do mothing. } void MHistogram::leaveEvent(QEvent *) { hitBin = -1; update(); }