【问题标题】:Synchronize drawing a rectangle using mouse event propagation使用鼠标事件传播同步绘制矩形
【发布时间】:2018-06-22 15:49:11
【问题描述】:

我有一个基类MainWidget,它继承了QOpenGLWidgetMainWidget 有 2 个子类 ColorWidgetBWWidget。我希望在两个子小部件之间同步绘制一个矩形,即当我开始在ColorWidget 上绘制一个形状时,该形状应该显示在BWWidget 上。我已经为基类覆盖了mouseMoveEvent 来绘制矩形。

void MainWidget::mouseMoveEvent(QMouseEvent* event)
{
    if(m_mousePressed && event->type() == QEvent::MouseMove)
    {
        // check some conditions
        if (/*conditions*/)
        {
            m_activeItem.reset(new Rectangle(this));

            m_activeItem->show();
        }
    }
}

我可以单独在子类上绘制矩形。但我希望它们同时绘制。我试图为两个子类覆盖mouseMoveEvent,但似乎从子类调用MainWidget::mouseMoveEvent() 会导致无限循环和崩溃。

就我而言,ColorWidgetBWWidget 的父级是 QDockWidget,而我的 Rectangle 类继承 QLabel

不知道如何实现。

【问题讨论】:

  • 正如您提到的QOpenGLWidget,您可以使用不同的片段着色器(其中黑白图像的着色器将颜色与灰度混合)渲染相同的数据。
  • 如果您使用QGraphicsScene 来保存形状并使用QGraphicsView 来显示它,那么所有这些都将变得非常简单。 QLabel 不是一些通用的“矩形”,当然除了可能是它们的孩子之外,它与其他小部件无关。你能解释一下你想要的功能吗? 用户期望的行为是什么,其目的是什么?很可能有一个非常简单的解决方案,不涉及您的问题所暗示的任何内容 - 您如此专注于错误的解决方案,以至于您忘记了真正的问题。参考文献XY 问题。
  • @KubaOber 您对我执行解决方案的看法可能是正确的。我将尝试简要解释一下,我有一个 QMainWindow 和 2 个 QDockWidgets。 Dockwidgets 包含我正在使用 QPainterpaintEvent() 绘制的 openglwidgets(ColorWidget 和 BWWidget)。现在,我想通过在小部件上绘制矩形来选择图像中的对象。我选择它是为了在小部件上绘制叠加层时获得 OpenGL 加速。我可能错了,可能有更好的解决方案。
  • 请记住,我会将其扩展到使用鼠标绘制圆形和多边形。
  • 在正常工作之前不要加速。朴素的老画家的表现应该绰绰有余。您可以将图像和瞬态矩形都放在场景中,视图都会为您显示它。还要记住,仅仅加速覆盖是没有意义的:覆盖几乎不需要绘制任何东西。昂贵的部分是渲染图像。

标签: c++ qt


【解决方案1】:

我不确定我是否 100% 理解您在这里的要求(主要是因为您没有提供最小的可编译示例),这就是我的理解:

  • 您有两个小部件,
    • class ColorWidget : public MainWidget 类型之一
    • class BWWidget : public MainWidget 之一
    • class MainWidget : public QOpenGLWidget
  • 这两个小部件是并排可见的。
  • 当您的两个小部件中的任何一个收到 mouseMoveEvent() 时,都应该显示一些内容。

如果你有这些,这些是你的选择:

  1. 首先,您应该移动功能以从鼠标移动事件处理程序中创建一个矩形(例如,移动到函数 MainWidget::DrawMouseMoveRectangle(QPoint pos).
    • 这样做已经降低了意外递归调用的可能性。
  2. (可能:)让两个小部件相互了解(例如,通过QPointer<MainWidget> pOther)。
    • 覆盖mouseMoveEvent() 并为thispOther 调用DrawMouseMoveRectangle(QPoint pos)
  3. (更好:)既然您已经在使用 Qt,请创建一个信号 MainWidget::SignalMouseMove(QPoint p) 和插槽 MainWidget::MouseMovedSlot(QPoint p)
    • 在公共父窗口小部件的初始化中,您将两个窗口小部件的插槽连接到每个窗口小部件的信号(见下文)。
    • 在MainWidget的mouseMoveEvent()emit SignalMouseMove(evt->pos)*
      • (*) 可能您需要传递一些上下文来正确解释几何。
    • 现在这两个函数的调用独立于接收事件的小部件。

如何建立连接:

bool ok = true;
ok = ok && connect(w1, &MainWidget::SignalMouseMove, w1, &MainWidget::MouseMovedSlot);
ok = ok && connect(w1, &MainWidget::SignalMouseMove, w2, &MainWidget::MouseMovedSlot);
ok = ok && connect(w2, &MainWidget::SignalMouseMove, w1, &MainWidget::MouseMovedSlot);
ok = ok && connect(w2, &MainWidget::SignalMouseMove, w2, &MainWidget::MouseMovedSlot);
Q_ASSERT(ok);

编辑

回答您的评论:

我想要的是我应该能够绘制 1 个矩形并将其传递给每个其他小部件以显示它(使用适当的几何图形),因为它使我能够移动和调整大小将反映在所有小部件上。

在两个不同的父窗口小部件中显示同一个窗口小部件实例可能是可能的,但绝对感觉像一个杂物,而不是 Qt 的设计方式。

你应该看看Qt's model-view-programming,因为这本质上是你想要实现的:

  • 在两个视图中显示一个模型。
  • 根据模型的任何更改更新两个视图。

因为实现QAbstractItemModel 有点矫枉过正,这里有一些替代方案:

  • 使用Qt's Graphics View Framework:在两个QGraphicsViews 中显示相同的QGraphicsScene,并将一些QGraphicsRectItem 放入场景中。
  • 使用 Qt 的参数支持进行半自动数据绑定。
  • 自己动手,并不难:
    • 基本上,您必须将 view 逻辑与 model 逻辑分开,例如RectangleViewRectangleModel
    • 然后使两者可连接,例如通过模型中的信号和视图中的插槽。
    • 在您的两个小部件中创建一个视图。
    • 为每个小部件提供一个指向模型的共享指针。
    • 将模型与两个视图连接起来。
    • 结果:无论何时更改模型(从哪个小部件都无关紧要),都会更新两个视图。

编辑 2:非常使用共享 QGraphicsRectItem 的简单示例

// this is a minimal compilable example of two views sharing the same model, with automatic update on mouse events
#include <QApplication>
#include <QGraphicsView>
#include <QGraphicsScene>
#include <QGraphicsRectItem>
#include <QMouseEvent>
#include <QHBoxLayout>

class TestWidget : public QWidget
{
    //Q_OBJECT // Not neccessary here because we don't use and don't need moc for this example. -> Don't try this at home!
public:
    TestWidget() : QWidget(Q_NULLPTR),
        lt(new QHBoxLayout(this)),
        v1(new QGraphicsView(this)),
        v2(new QGraphicsView(this)),
        s(new QGraphicsScene(this)),
        r(Q_NULLPTR)
    {
        // initialization
        r = s->addRect(0, 0, 10, 10, QPen(Qt::red), Qt::yellow);

        lt->addWidget(v1);
        lt->addWidget(v2);

        // sharing the same model:
        v1->setScene(s);
        v2->setScene(s);
        // in reality, you need to share QGraphicsRectItem *r, too.
        // (it would actually be sufficient to share *r only)

        // very simple mouse interaction:
        v1->viewport()->installEventFilter(this);
        v1->viewport()->setMouseTracking(true);
        v2->viewport()->installEventFilter(this);
        v2->viewport()->setMouseTracking(true);
    }

    bool eventFilter(QObject *o, QEvent *evt) Q_DECL_OVERRIDE
    {
        QGraphicsView *gv = Q_NULLPTR;
        if(o == v1->viewport())
        {
            gv = v1;
        }
        else if(o == v2->viewport())
        {
            gv = v2;
        }

        if(gv != Q_NULLPTR)
        {
            QPointF p;
            switch(evt->type())
            {
            case QEvent::MouseMove:
                p = static_cast<QMouseEvent*>(evt)->pos();
                evt->accept();
                break;
            default:
                break;
            }
            if(p != QPoint())
            {
                // do something different depending on interacting view - just for demonstration
                if(gv == v1)
                {
                    r->setRect(QRectF(p, r->rect().bottomRight()));
                }
                else
                {
                    r->setRect(QRectF(r->rect().topLeft(), p));
                }
            }
        }
        return QWidget::eventFilter(o, evt);
    }

private:
    QHBoxLayout *lt;
    QGraphicsView *v1;
    QGraphicsView *v2;
    QGraphicsScene *s;
    QGraphicsRectItem *r;
};

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    TestWidget w;
    w.show();
    return a.exec();
}

【讨论】:

  • 选择选项 3。我相信 w1w2 是子类对象。但是我已经在我的QMainWindow 中实例化了它们,QDockWidget 作为它们的父级。我不确定创建多个实例是否是个好主意。所以,有点困惑我如何在这里使用信号/插槽?
  • 我只是将 'w1' 和 'w2' 用作占位符,因为我不知道您的小部件的名称 - 例如 ColorWidget *myWidgetui.colorWidget 或其他。
  • 我试过你的方法它有效。这里有一个问题。将每个小部件连接到插槽会创建 Rectangle 的实例。我想要的是我应该能够绘制 1 Rectangle 并将其传递给所有其他小部件以显示它(使用适当的几何图形),因为它将使我能够使用 movingresizing 将反映在所有小部件上。我尝试通过在mouseReleaseEvent 上发出信号并使用show() 来传递创建的Rectangle,但我看不到Rectangle
  • 这看起来很有希望。所以在这种情况下,RectangleView 应该继承自 QGraphicsViewRectangleModel 继承自 QGraphicsScene?
  • 这是一个非常好的想法,但我正在努力实现它。您能否发布一个小sn-p作为我如何实现这一目标?我可以更好地理解它。
猜你喜欢
  • 1970-01-01
  • 2014-07-30
  • 2021-08-18
  • 2015-06-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-02-16
相关资源
最近更新 更多