You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 03:37:35