当前位置: 首页 > news >正文

在 QCustomPlot中自定义绘图元素

QCustomPlot 提供了多种方式来创建和自定义绘图元素,从简单的图形到复杂的交互式组件。以下是几种主要的自定义方法:

1. 继承 QCPAbstractItem 创建自定义项

这是最灵活的方式,适合创建全新的交互式绘图元素。

cpp

class CustomArrow : public QCPAbstractItem
{Q_OBJECT
public:CustomArrow(QCustomPlot *parentPlot);~CustomArrow();// 必须实现的纯虚函数virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const;// 自定义属性void setStart(const QPointF &start);void setEnd(const QPointF &end);void setHeadWidth(double width);void setHeadLength(double length);void setColor(const QColor &color);protected:// 必须实现的纯虚函数virtual void draw(QCPPainter *painter);private:// 定义项的位置锚点QCPItemPosition *mStart;QCPItemPosition *mEnd;// 自定义属性double mHeadWidth;double mHeadLength;QColor mColor;// 计算箭头路径的辅助函数QPainterPath arrowPath() const;
};// 实现
CustomArrow::CustomArrow(QCustomPlot *parentPlot) :QCPAbstractItem(parentPlot),mHeadWidth(10),mHeadLength(15),mColor(Qt::black)
{mStart = new QCPItemPosition(this, "start");mEnd = new QCPItemPosition(this, "end");
}double CustomArrow::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
{if (onlySelectable && !mSelectable)return -1;QPainterPath path = arrowPath();if (path.contains(pos))return mParentPlot->selectionTolerance()*0.99;return -1;
}void CustomArrow::draw(QCPPainter *painter)
{QPainterPath path = arrowPath();painter->setPen(QPen(mColor, 2));painter->setBrush(mColor);painter->drawPath(path);
}QPainterPath CustomArrow::arrowPath() const
{QPointF start = mStart->pixelPosition();QPointF end = mEnd->pixelPosition();QLineF line(end, start);QLineF normal = line.normalVector();normal.setLength(mHeadWidth/2.0);QPointF p1 = normal.p2();normal.setLength(-mHeadWidth/2.0);QPointF p2 = normal.p2();QLineF headLine1(end, p1);headLine1.setLength(mHeadLength);QLineF headLine2(end, p2);headLine2.setLength(mHeadLength);QPainterPath path;path.moveTo(start);path.lineTo(end);path.moveTo(p1);path.lineTo(end);path.lineTo(p2);return path;
}

2. 继承 QCPLayerable 创建底层绘图元素

这种方式适合需要完全控制绘制过程的元素。

cpp

class CustomWaveform : public QCPLayerable
{Q_OBJECT
public:CustomWaveform(QCustomPlot *parentPlot, const QVector<double> &data);// 自定义方法void setData(const QVector<double> &data);void setColor(const QColor &color);void setLineWidth(double width);protected:virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const;virtual void draw(QCPPainter *painter);private:QVector<double> mData;QColor mColor;double mLineWidth;
};// 使用示例
CustomWaveform *wave = new CustomWaveform(customPlot, data);
wave->setColor(Qt::blue);
wave->setLineWidth(2);
customPlot->replot();

3. 使用 QCPItem* 类组合创建复杂元素

可以通过组合现有的 QCPItem 类来创建复杂元素。

cpp

// 创建一个带文本标注的箭头
void createAnnotatedArrow(QCustomPlot *plot, const QPointF &start, const QPointF &end, const QString &text)
{// 创建箭头QCPItemLine *arrowLine = new QCPItemLine(plot);arrowLine->start->setCoords(start);arrowLine->end->setCoords(end);arrowLine->setHead(QCPLineEnding::esSpikeArrow);// 创建文本标注QCPItemText *arrowText = new QCPItemText(plot);arrowText->position->setParentAnchor(arrowLine->end);arrowText->position->setCoords(10, -10); // 偏移arrowText->setText(text);arrowText->setPositionAlignment(Qt::AlignLeft|Qt::AlignVCenter);// 创建连接线QCPItemCurve *connector = new QCPItemCurve(plot);connector->start->setParentAnchor(arrowText->left);connector->startDir->setParentAnchor(arrowText->left);connector->end->setParentAnchor(arrowLine->end);connector->endDir->setParentAnchor(arrowLine->end);connector->setPen(QPen(Qt::gray, 1, Qt::DashLine));
}

4. 自定义绘图函数

直接在 QCustomPlot 的绘制事件中添加自定义绘图。

cpp

// 在自定义类中
connect(customPlot, &QCustomPlot::afterReplot, this, &MyClass::drawCustomElements);void MyClass::drawCustomElements()
{QCPPainter painter(customPlot);// 绘制自定义背景网格painter.setPen(QPen(QColor(220, 220, 220), 0, Qt::DotLine));for (int i=0; i<10; ++i) {double x = customPlot->xAxis->range().lower + i*customPlot->xAxis->range().size()/10.0;painter.drawLine(customPlot->xAxis->coordToPixel(x),customPlot->yAxis->coordToPixel(customPlot->yAxis->range().lower),customPlot->xAxis->coordToPixel(x),customPlot->yAxis->coordToPixel(customPlot->yAxis->range().upper));}// 绘制自定义标记painter.setPen(Qt::NoPen);painter.setBrush(QColor(255, 100, 100, 150));painter.drawEllipse(QPointF(customPlot->xAxis->coordToPixel(5.0),customPlot->yAxis->coordToPixel(0.5)), 20, 20);
}

5. 自定义图例项

cpp

class CustomLegendItem : public QCPAbstractLegendItem {
public:CustomLegendItem(QCPLegend *parent, const QString &text, const QColor &color) : QCPAbstractLegendItem(parent), mText(text), mColor(color) {setText(text);}protected:virtual void draw(QCPPainter *painter) override {QRectF rect = mOuterRect.adjusted(2, 2, -2, -2);// 绘制背景painter->setBrush(QBrush(mColor.lighter(120)));painter->setPen(Qt::NoPen);painter->drawRoundedRect(rect, 3, 3);// 绘制图标painter->setBrush(QBrush(mColor));painter->drawRect(rect.left() + 3, rect.center().y() - 5, 10, 10);// 绘制文本painter->setPen(Qt::black);painter->setFont(QFont("Arial", 9));painter->drawText(rect.left() + 18, rect.top(), rect.width() - 18, rect.height(),Qt::AlignLeft | Qt::AlignVCenter, mText);}virtual QSize minimumSizeHint() const override {QFontMetrics metrics(QFont("Arial", 9));return QSize(metrics.horizontalAdvance(mText) + 25, 20);}private:QString mText;QColor mColor;
};//使用图例
// 创建自定义图例项并添加到图例
CustomLegendItem *customItem1 = new CustomLegendItem(customPlot->legend, "数据系列1", Qt::blue);
CustomLegendItem *customItem2 = new CustomLegendItem(customPlot->legend, "数据系列2", Qt::red);
customPlot->legend->addItem(customItem1);
customPlot->legend->addItem(customItem2);// 设置图例位置和样式
customPlot->legend->setFillOrder(QCPLegend::foColumnsFirst);
customPlot->legend->setWrap(3); // 每行最多3个项
customPlot->legend->setBorderPen(Qt::NoPen);

6. 自定义坐标轴标签

cpp

// 自定义坐标轴标签绘制
customPlot->xAxis->setTickLabelRotation(45);
customPlot->xAxis->setSubTickLength(0, 5);// 或者完全自定义标签
customPlot->xAxis->setTickLabelType(QCPAxis::ltDateTime);
customPlot->xAxis->setDateTimeFormat("hh:mm:ss\nyyyy-MM-dd");
customPlot->xAxis->setDateTimeSpec(Qt::UTC);// 更复杂的自定义可以通过子类化QCPAxisTicker
class CustomTicker : public QCPAxisTicker
{
protected:virtual QString getTickLabel(double tick, const QLocale &locale, QChar formatChar, int precision){// 自定义标签文本if (tick == 0) return "零点";else if (tick > 0) return QString("+%1").arg(tick);else return QString("-%1").arg(-tick);}
};// 使用自定义ticker
CustomTicker *ticker = new CustomTicker;
customPlot->yAxis->setTicker(QSharedPointer<CustomTicker>(ticker));

性能优化技巧

  1. 缓存绘制结果

    cpp

    // 在自定义项中
    QPixmap mCache;
    bool mCacheInvalidated;void invalidateCache() { mCacheInvalidated = true; }void draw(QCPPainter *painter) {if (mCacheInvalidated || mCache.isNull()) {mCache = QPixmap(size());QCPPainter cachePainter(&mCache);// 绘制到缓存...mCacheInvalidated = false;}painter->drawPixmap(0, 0, mCache);
    }
  2. 选择性重绘

    cpp

    // 只重绘必要的部分
    connect(customPlot, &QCustomPlot::beforeReplot, [=](){customPlot->setViewport(customPlot->axisRect()->rect()); // 限制重绘区域
    });
  3. 使用OpenGL加速

    cpp

    customPlot->setOpenGl(true);

相关文章:

  • 【CentOs】构建云服务器部署环境
  • ClickHouse核心架构设计
  • day47——平方数之和(LeetCode-633)
  • STM32(M4)入门:GPIO与位带操作(价值 3w + 的嵌入式开发指南)
  • FFmpeg:M3U8的AES加密
  • 《Android 应用开发基础教程》——第三章:布局管理与 UI 组件详解
  • 多模态大语言模型arxiv论文略读(三十一)
  • 机器学习 Day12 集成学习简单介绍
  • [Windows]_[VS2017]_[如何进行远程调试程序]
  • POSIX标准系统调用详解:从概念到实践
  • 破解吞咽困境!进行性核上性麻痹患者的科学饮食方案
  • 62页华为IPD-MM流程:市场调研理论与实践方案精读【附全文阅读】
  • Linux 网络基础(二) (传输协议层:UDP、TCP)
  • 【算法提高】单源最短路的建图方式
  • Linux系统编程---孤儿进程与僵尸进程
  • UML统一建模
  • Vue常用指令入门
  • 【项目实训个人博客】数据集搜集
  • 【python】尾部多写个逗号会把表达式变成 tuple
  • 使用virtualbox的HostOnly建立共享网络-实现虚拟机上网
  • 湖南平江发生一起意外翻船事件,6人不幸溺亡
  • 上海又现昆虫新物种:体长仅1.5毫米,却是凶猛的捕食者
  • 全国类脑智能产业创新发展推进会在上海召开
  • 能上天入海的“鲲龙”毕业了,AG600取得型号合格证
  • 人民日报头版:各地扎实开展学习教育,一体推进学查改
  • 黑龙江双鸭山市委常委、秘书长王长泰因病去世