如何让QQuickPaintedItem与QwtPlotPicker协同工作?
解决QmlQwtPlot与QwtPlotPicker的协同工作问题
我之前在做QML嵌入QwtPlot的项目时,也遇到过QwtPlotPicker完全没反应的问题——毕竟QmlQwtPlot是继承自QQuickPaintedItem的,QML的事件体系和QWidget的事件体系不直接打通,Picker依附在QwtPlot的canvas上,根本拿不到QML层面传过来的鼠标事件。折腾了一阵终于找到靠谱的解决办法,分享给你:
核心思路
我们需要手动把QmlQwtPlot接收到的QML鼠标/滚轮事件,转换为QWidget兼容的事件,转发给内部QwtPlot的canvas控件,这样QwtPlotPicker就能正常捕获事件并工作了。
具体实现步骤
1. 扩展类成员,添加QwtPlotPicker指针
首先在QmlQwtPlot的私有成员里加入Picker的指针:
class QmlQwtPlot : public QQuickPaintedItem { Q_OBJECT public: QmlQwtPlot(QQuickItem* parent = 0); ~QmlQwtPlot(); void paint(QPainter *painter) override; void mousePressEvent(QMouseEvent *event) override; void mouseMoveEvent(QMouseEvent* event) override; void mouseReleaseEvent(QMouseEvent* event) override; void wheelEvent(QWheelEvent *event) override; void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) override; signals: void plotSelected(const QRectF& selectedRect); private slots: void onPickerSelected(const QwtDoubleRect& rect); private: QwtPlot* qwtPlot; QwtPlotPanner* panner; QwtPlotPicker* plotPicker; // 新增Picker指针 };
2. 初始化QwtPlotPicker
在构造函数里初始化Picker,绑定到QwtPlot的canvas上,同时要确保QmlQwtPlot能接收所有鼠标事件:
QmlQwtPlot::QmlQwtPlot(QQuickItem* parent) : QQuickPaintedItem(parent) { qwtPlot = new QwtPlot(this); panner = new QwtPlotPanner(qwtPlot->canvas()); panner->setMouseButton(Qt::LeftButton); // 初始化Picker,这里用矩形选择为例,你可以根据需求修改类型 plotPicker = new QwtPlotPicker(qwtPlot->canvas()); plotPicker->setRubberBand(QwtPlotPicker::RectRubberBand); // 显示选择框 plotPicker->setTrackerMode(QwtPlotPicker::ActiveOnly); // 仅在选择时显示跟踪文本 plotPicker->setSelectionFlags(QwtPicker::DragSelection | QwtPicker::CornerToCorner); // 支持拖拽选择 // 关键:让QML item接收所有鼠标事件 setAcceptedMouseButtons(Qt::AllButtons); setAcceptHoverEvents(true); setFlag(QQuickItem::ItemHasContents, true); // 可选:连接Picker的选中信号到自定义槽,转发给QML connect(plotPicker, SIGNAL(selected(QwtDoubleRect)), this, SLOT(onPickerSelected(QwtDoubleRect))); }
3. 事件转发:把QML事件传给QwtPlot的canvas
重写所有鼠标和滚轮事件处理函数,把QML事件转换为QWidget事件后发送给canvas:
void QmlQwtPlot::mousePressEvent(QMouseEvent *event) { // 转换QML事件为QWidget事件 QMouseEvent widgetEvent(event->type(), event->pos(), event->globalPos(), event->button(), event->buttons(), event->modifiers()); // 发送给canvas,让Picker捕获 QCoreApplication::sendEvent(qwtPlot->canvas(), &widgetEvent); // 别忘了调用父类的事件处理,避免QML层面的事件丢失 QQuickPaintedItem::mousePressEvent(event); } void QmlQwtPlot::mouseMoveEvent(QMouseEvent* event) { QMouseEvent widgetEvent(event->type(), event->pos(), event->globalPos(), event->button(), event->buttons(), event->modifiers()); QCoreApplication::sendEvent(qwtPlot->canvas(), &widgetEvent); QQuickPaintedItem::mouseMoveEvent(event); } void QmlQwtPlot::mouseReleaseEvent(QMouseEvent* event) { QMouseEvent widgetEvent(event->type(), event->pos(), event->globalPos(), event->button(), event->buttons(), event->modifiers()); QCoreApplication::sendEvent(qwtPlot->canvas(), &widgetEvent); QQuickPaintedItem::mouseReleaseEvent(event); } void QmlQwtPlot::wheelEvent(QWheelEvent *event) { QWheelEvent widgetEvent(event->pos(), event->globalPos(), event->pixelDelta(), event->angleDelta(), event->delta(), event->orientation(), event->buttons(), event->modifiers(), event->phase(), event->inverted()); QCoreApplication::sendEvent(qwtPlot->canvas(), &widgetEvent); QQuickPaintedItem::wheelEvent(event); }
4. 同步大小与绘制逻辑
确保QmlQwtPlot的大小变化时,QwtPlot也同步更新,同时正确绘制QwtPlot的内容:
void QmlQwtPlot::paint(QPainter *painter) { if (!qwtPlot) return; // 同步QwtPlot的大小到QML item的尺寸 qwtPlot->resize(size().toSize()); // 重新绘制QwtPlot内容 qwtPlot->replot(); // 把QwtPlot的内容绘制到QML的画布上 painter->drawImage(QPoint(0,0), qwtPlot->grab()); } void QmlQwtPlot::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) { QQuickPaintedItem::geometryChanged(newGeometry, oldGeometry); if (qwtPlot) { qwtPlot->resize(newGeometry.size().toSize()); update(); // 触发重绘 } }
5. 可选:把Picker的选中信号暴露给QML
如果需要在QML中响应Picker的选中事件,实现对应的槽函数转发信号:
void QmlQwtPlot::onPickerSelected(const QwtDoubleRect& rect) { // 转换Qwt的矩形为QML的QRectF emit plotSelected(QRectF(rect.left(), rect.top(), rect.width(), rect.height())); }
这样一来,QwtPlotPicker就能和QmlQwtPlot完美协同工作了——不管是拖拽选择还是跟踪坐标,都能正常触发。
内容的提问来源于stack exchange,提问作者Nikita
相关产品推荐
相关产品推荐

