使用鼠标事件传播同步绘制矩形

时间:2018-06-11 10:00:07

标签: c++ qt

我有一个继承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

我不确定如何实现这一点。

1 个答案:

答案 0 :(得分:0)

我不确定我100%理解你在这里问的是什么(主要是因为你没有提供一个最小的可编辑的例子),这就是我所理解的:

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

如果这就是你所拥有的,那么这些是你的选择:

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

    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个矩形并将其传递给其他每个小部件以显示它(具有适当的几何体),因为它将使我移动和调整大小将反映在所有小部件上。

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

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

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

    因为实施QAbstractItemModel对此有点矫枉过正,所以有以下几种选择:

    • 使用Qt's Graphics View Framework:在两个QGraphicsScene中显示相同的QGraphicsView,并将一些QGraphicsRectItem放入场景中。
    • 使用Qt的参数支持半自动数据绑定。
    • 滚动你自己,不是那么难:
      • 基本上,您必须将视图逻辑与模型逻辑分离为例如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();
    }