#include "VWavechart.h" QPainterPath SmoothCurveCreator::createSmoothCurve(const QVector& points) { QPainterPath path; int len = points.size(); if (len < 2) { return path; } QVector firstControlPoints; QVector secondControlPoints; calculateControlPoints(points, &firstControlPoints, &secondControlPoints); path.moveTo(points[0].x(), points[0].y()); for (int i = 0; i < len - 1; ++i) { path.cubicTo(firstControlPoints[i], secondControlPoints[i], points[i + 1]); } return path; } void SmoothCurveCreator::calculateFirstControlPoints(double*& result, const double* rhs, int n) { result = new double[n]; double* tmp = new double[n]; double b = 2.0; result[0] = rhs[0] / b; for (int i = 1; i < n; i++) { tmp[i] = 1 / b; b = (i < n - 1 ? 4.0 : 3.5) - tmp[i]; result[i] = (rhs[i] - result[i - 1]) / b; } for (int i = 1; i < n; i++) { result[n - i - 1] -= tmp[n - i] * result[n - i]; } delete tmp; } void SmoothCurveCreator::calculateControlPoints(const QVector& knots, QVector* firstControlPoints, QVector* secondControlPoints) { int n = knots.size() - 1; for (int i = 0; i < n; ++i) { firstControlPoints->append(QPointF()); secondControlPoints->append(QPointF()); } if (n == 1) { (*firstControlPoints)[0].rx() = (2 * knots[0].x() + knots[1].x()) / 3; (*firstControlPoints)[0].ry() = (2 * knots[0].y() + knots[1].y()) / 3; (*secondControlPoints)[0].rx() = 2 * (*firstControlPoints)[0].x() - knots[0].x(); (*secondControlPoints)[0].ry() = 2 * (*firstControlPoints)[0].y() - knots[0].y(); return; } double* xs = 0; double* ys = 0; double* rhsx = new double[n]; double* rhsy = new double[n]; for (int i = 1; i < n - 1; ++i) { rhsx[i] = 4 * knots[i].x() + 2 * knots[i + 1].x(); rhsy[i] = 4 * knots[i].y() + 2 * knots[i + 1].y(); } rhsx[0] = knots[0].x() + 2 * knots[1].x(); rhsx[n - 1] = (8 * knots[n - 1].x() + knots[n].x()) / 2.0; rhsy[0] = knots[0].y() + 2 * knots[1].y(); rhsy[n - 1] = (8 * knots[n - 1].y() + knots[n].y()) / 2.0; calculateFirstControlPoints(xs, rhsx, n); calculateFirstControlPoints(ys, rhsy, n); for (int i = 0; i < n; ++i) { (*firstControlPoints)[i].rx() = xs[i]; (*firstControlPoints)[i].ry() = ys[i]; if (i < n - 1) { (*secondControlPoints)[i].rx() = 2 * knots[i + 1].x() - xs[i + 1]; (*secondControlPoints)[i].ry() = 2 * knots[i + 1].y() - ys[i + 1]; } else { (*secondControlPoints)[i].rx() = (knots[n].x() + xs[n - 1]) / 2; (*secondControlPoints)[i].ry() = (knots[n].y() + ys[n - 1]) / 2; } } delete xs; delete ys; delete rhsx; delete rhsy; } VWaveChart::VWaveChart( QWidget* parent, const QPoint& pos, const QSize& size, CONTROL_PROPERTY* pProperty ) : QWidget(parent) , VControlObject(pProperty) { minValue = 0; maxValue = 100; xStep = 10; yStep = 10; space = 40; title = "曲线图"; smooth = false; showHLine = true; showPoint = true; showPointBg = true; bgColorStart = QColor(79, 79, 79); bgColorEnd = QColor(51, 51, 51); textColor = QColor(250, 250, 250); pointColor = QColor(38, 114, 179); // 设置尺寸 if (size == DEFAULT_CONTROL_SIZE) { this->resize(DEFAULT_WAVE_SIZE); } else { this->resize(size); } // 设置位置 this->move(pos); this->m_pWidget = this; m_Type = VALUE_TYPE::Control_Wavechart; } void VWaveChart::paintEvent(QPaintEvent *) { //绘制准备工作,启用反锯齿 QPainter painter(this); painter.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing); //绘制背景 drawBg(&painter); //绘制盒子 drawBox(&painter); //绘制文字 drawText(&painter); //绘制标题 drawTitle(&painter); //绘制数据点 drawPoint(&painter); } void VWaveChart::drawBg(QPainter *painter) { painter->save(); painter->setPen(Qt::NoPen); QLinearGradient topGradient(rect().topLeft(), rect().bottomLeft()); topGradient.setColorAt(0.0, bgColorStart); topGradient.setColorAt(1.0, bgColorEnd); painter->setBrush(topGradient); painter->drawRect(rect()); painter->restore(); } void VWaveChart::drawBox(QPainter *painter) { painter->save(); QPointF topLeftPot(space, space); QPointF bottomRightPot(width() - space / 2, height() - space / 2); painter->setPen(textColor); painter->setBrush(Qt::NoBrush); pointRect = QRectF(topLeftPot, bottomRightPot); painter->drawRect(pointRect); //绘制横线 if (showHLine) { QPen pen(textColor, 1, Qt::DotLine); painter->setPen(pen); int step = (maxValue - minValue) / xStep; double increment = (double)pointRect.height() / step; double startY = pointRect.topLeft().y(); for (int i = 0; i < step - 1; i++) { startY += increment; QPointF leftPot(pointRect.topLeft().x(), startY); QPointF rightPot(pointRect.topRight().x(), startY); painter->drawLine(leftPot, rightPot); } } painter->restore(); } void VWaveChart::drawText(QPainter *painter) { painter->save(); painter->setPen(textColor); painter->setBrush(Qt::NoBrush); int step = (maxValue - minValue) / xStep; int value = maxValue; double increment = (double)pointRect.height() / step; double startY = pointRect.topLeft().y(); QString strValue; for (int i = 0; i <= step; i++) { strValue = QString("%1").arg(value); double textWidth = fontMetrics().width(strValue); double textHeight = fontMetrics().height(); QPointF textPot(pointRect.topLeft().x() - 5 - textWidth, startY + textHeight / 2); painter->drawText(textPot, strValue); value -= xStep; startY += increment; } painter->restore(); } void VWaveChart::drawTitle(QPainter *painter) { painter->save(); painter->setPen(textColor); painter->setBrush(Qt::NoBrush); double titleX = (double)width() / 2; double titleY = space; double textWidth = fontMetrics().width(title); double textHeight = fontMetrics().height(); //标题加粗显示 QFont titleFont; titleFont.setBold(true); titleFont.setPixelSize(13); painter->setFont(titleFont); QPointF textPot(titleX - textWidth / 2, titleY / 2 + textHeight / 2); painter->drawText(textPot, title); painter->restore(); } void VWaveChart::drawPoint(QPainter *painter) { painter->save(); double startX = pointRect.topRight().x(); QVector points; if (showPointBg) { points.push_back(QPointF(startX, pointRect.bottomRight().y())); } for (int i = 0; i < listData.count(); i++) { QPointF dataPot(startX, pointRect.bottomRight().y() - (double)listData.at(i) * (pointRect.height() / (maxValue - minValue))); points.push_back(dataPot); startX -= yStep; } if (showPointBg) { points.push_back(QPointF(startX, pointRect.bottomRight().y())); } //如果只有两个数据点不需要处理 if (showPointBg && points.count() <= 2) { painter->restore(); return; } painter->setPen(pointColor); if (showPointBg) { painter->setBrush(QColor(pointColor.red(), pointColor.green(), pointColor.blue(), 150)); if (!smooth) { painter->drawPolygon(QPolygonF(points)); } } else { painter->setBrush(Qt::NoBrush); if (!smooth) { painter->drawPolyline(QPolygonF(points)); } } //绘制平滑曲线 if (smooth) { QPainterPath path = SmoothCurveCreator::createSmoothCurve(points); painter->drawPath(path); } //绘制坐标点 if (showPoint) { for (int i = 0; i < points.count(); i++) { QPointF dataPot = points.at(i); painter->setBrush(pointColor); painter->drawEllipse(dataPot, 3, 3); } } painter->restore(); } void VWaveChart::updateData() { int count = pointRect.width() / yStep; int i = listData.count() - count; if (i > 0) { listData.remove(count, i); } update(); } double VWaveChart::getMinValue() const { return this->minValue; } double VWaveChart::getMaxValue() const { return this->maxValue; } double VWaveChart::getXStep() const { return this->xStep; } double VWaveChart::getYStep() const { return this->yStep; } double VWaveChart::getSpace() const { return this->space; } QString VWaveChart::getTitle() const { return this->title; } bool VWaveChart::getSmooth() const { return this->smooth; } bool VWaveChart::getShowHLine() const { return this->showHLine; } bool VWaveChart::getShowPoint() const { return this->showPoint; } bool VWaveChart::getShowPointBg() const { return this->showPointBg; } QColor VWaveChart::getBgColorStart() const { return this->bgColorStart; } QColor VWaveChart::getBgColorEnd() const { return this->bgColorEnd; } QColor VWaveChart::getTextColor() const { return this->textColor; } QColor VWaveChart::getPointColor() const { return this->pointColor; } QSize VWaveChart::sizeHint() const { return QSize(500, 300); } QSize VWaveChart::minimumSizeHint() const { return QSize(200, 70); } void VWaveChart::addData(double data) { listData.push_front(data); updateData(); } void VWaveChart::setData(QVector data) { listData = data; updateData(); } void VWaveChart::clearData() { listData.clear(); update(); } void VWaveChart::setMinValue(double minValue) { if (this->minValue != minValue) { this->minValue = minValue; update(); } } void VWaveChart::setMaxValue(double maxValue) { if (this->maxValue != maxValue) { this->maxValue = maxValue; update(); } } void VWaveChart::setXStep(double xStep) { if (this->xStep != xStep) { this->xStep = xStep; update(); } } void VWaveChart::setYStep(double yStep) { if (this->yStep != yStep) { this->yStep = yStep; update(); } } void VWaveChart::setSpace(double space) { if (this->space != space) { this->space = space; update(); } } void VWaveChart::setTitle(const QString &title) { if (this->title != title) { this->title = title; update(); } } void VWaveChart::setSmooth(bool smooth) { if (this->smooth != smooth) { this->smooth = smooth; update(); } } void VWaveChart::setShowHLine(bool showHLine) { if (this->showHLine != showHLine) { this->showHLine = showHLine; update(); } } void VWaveChart::setShowPoint(bool showPoint) { if (this->showPoint != showPoint) { this->showPoint = showPoint; update(); } } void VWaveChart::setShowPointBg(bool showPointBg) { if (this->showPointBg != showPointBg) { this->showPointBg = showPointBg; update(); } } void VWaveChart::setBgColorStart(const QColor &bgColorStart) { if (this->bgColorStart != bgColorStart) { this->bgColorStart = bgColorStart; update(); } } void VWaveChart::setBgColorEnd(const QColor &bgColorEnd) { if (this->bgColorEnd != bgColorEnd) { this->bgColorEnd = bgColorEnd; update(); } } void VWaveChart::setTextColor(const QColor &textColor) { if (this->textColor != textColor) { this->textColor = textColor; update(); } } void VWaveChart::setPointColor(const QColor &pointColor) { if (this->pointColor != pointColor) { this->pointColor = pointColor; update(); } }