【问题标题】:Qt: Line connects 2 separate drawingsQt:线连接2个单独的图纸
【发布时间】:2013-12-17 10:52:17
【问题描述】:

我想画不同的任意图形。在图形视图中单击鼠标时开始绘制,并在停止单击鼠标时结束。但是,当从图形视图中的不同点开始绘制新绘图或继续上一个绘图时,会从第一个绘图的最后一个鼠标坐标绘制一条线,到第二个绘图的第一个坐标。图纸不一定是不同的图纸,也可以是对图纸的调整。这是我的代码。

#include "mousedraw.h"
#include <QDebug>

MouseDraw::MouseDraw()
{
    setFlag(ItemIsMovable);
}

QRectF MouseDraw::boundingRect() const
{
    return QRectF(0,0,300,300);
}

void MouseDraw::paint(QPainter *painter, const QStyleOptionGraphicsItem *option,      QWidget *widget)
{
    QPainterPath path;
    path.addPolygon(polyPoints2);
    painter->setPen(QPen(QColor(qrand() % 256, qrand() % 256, qrand() % 256),3));
    painter->drawPath(path);
}

void MouseDraw::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
    QPointF point = event->pos();
    if (boundingRect().contains(point)) {
         array1 = point.x();
        array2 = point.y();
        polyPoints2 << QPoint(array1,array2);
        update();
    }
}

【问题讨论】:

  • 您必须重新定义 QGraphicsSceneQGraphicsView 鼠标事件,而不是父窗口小部件的事件
  • 另外,setMouseTracking(true) 应该在构造函数中调用一次。只有当您想在不按鼠标按钮的情况下跟踪鼠标移动时才需要它。
  • 不,我不想要连接,连接是我的问题,但我不知道如何解决这个问题.. 所有绘图都需要一次性完成,问题是例如绘制正方形时,先从左到右,再从右到上,然后松开鼠标,在左下角再次点击,画一条线,比如,一条线也是从右上到左下画的

标签: qt mouseevent qgraphicsview qgraphicsscene


【解决方案1】:

没有必要从QGraphicsItem 派生自定义MouseDraw 类。有一个QGraphicsPathItem 可以正确地为您处理这一切。

在正在创建的项目中处理鼠标交互在概念上是不正确的 - 您的大边界矩形是它工作所需要的 hack,但这是错误的方法。您不希望 item 与鼠标交互。您希望 场景 与鼠标交互并动态创建项目。仅当编辑现有项目时您希望进行鼠标交互,但即便如此,最好创建一个单独的编辑器项目覆盖在正在编辑的项目上以处理编辑交互。

用鼠标右键单击窗口以获取上下文菜单,其中包含清除图片的操作。您还可以切换后续路径是否连接到前一个路径。此代码已使用 Qt 4.8.5 和 5.2 进行了测试。

当场景小于视图时,视图坚持将其居中(使用alignment 属性)。 EmptyItem 用作此“功能”的解决方法,以在场景小于视图时(包括场景为“空”时)限制场景在视图内的位置。

代码为每个不相交的路径创建一个单独的项目 - QGraphicsScene 通常会在多个不重叠的项目中表现最佳。您当然可以只保留一个项目并在每次按下鼠标时继续为其添加新的片段,但这最终会对性能产生影响——尤其是当您放大场景时,您希望性能越少越好显示出来了。

// https://github.com/KubaO/stackoverflown/tree/master/questions/gscene-paint-20632209
#include <QtGui>
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
#include <QtWidgets>
#endif

class EmptyItem : public QGraphicsItem
{
public:
    EmptyItem(QGraphicsItem * parent = nullptr) : QGraphicsItem{parent} {}
    QRectF boundingRect() const override { return {0, 0, 1, 1}; }
    void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget *) override {}
};

class Scene : public QGraphicsScene
{
    Q_OBJECT
    Q_PROPERTY(bool joinFigures READ joinFigures WRITE setJoinFigures)
    bool m_joinFigures = false;
    QGraphicsPathItem * m_item = nullptr;
    QPainterPath m_path;

    void newItem() {
        addItem(m_item = new QGraphicsPathItem);
        m_item->setPen(QPen{{qrand() % 256, qrand() % 256, qrand() % 256}});
        m_path = QPainterPath{}; // using std::swap; swap(m_path, QPainterPath());
    }
    void newPoint(const QPointF& pt) {
        if (! m_item) {
            newItem();
            m_path.moveTo(pt);
        } else {
            m_path.lineTo(pt);
            m_item->setPath(m_path);
        }
    }
    void mousePressEvent(QGraphicsSceneMouseEvent * ev) override {
        if (ev->buttons() != Qt::LeftButton) return;
        if (! m_joinFigures) m_item = nullptr;
        newPoint(ev->scenePos());
    }
    void mouseMoveEvent(QGraphicsSceneMouseEvent *ev) override {
        if (ev->buttons() != Qt::LeftButton) return;
        newPoint(ev->scenePos());
    }
    void mouseReleaseEvent(QGraphicsSceneMouseEvent *) override {
        if (! m_path.isEmpty()) return;
        delete m_item; // Empty items are useless
        m_item = nullptr;
    }
public:
    Scene(QObject *parent = nullptr) : QGraphicsScene{parent}
    {
        addItem(new EmptyItem{});
    }
    Q_SLOT void setJoinFigures(bool j) { m_joinFigures = j; }
    bool joinFigures() const { return m_joinFigures; }
};

class Window : public QWidget
{
    Q_OBJECT
    QGridLayout m_layout{this};
    QGraphicsView m_view;
    QCheckBox m_join{"Join Figures (toggle with Spacebar)"};
    QAction m_toggleJoin{this};
public:
    Window(QWidget * parent = 0) : QWidget{parent}
    {
        m_layout.addWidget(&m_view);
        m_layout.addWidget(&m_join);
        m_view.setAlignment(Qt::AlignLeft | Qt::AlignTop);

        m_toggleJoin.setShortcut(QKeySequence(Qt::Key_Space));
        connect(&m_toggleJoin, SIGNAL(triggered()), &m_join, SLOT(toggle()));
        addAction(&m_toggleJoin);

        m_view.addAction(new QAction{"Clear", this});
        m_view.setContextMenuPolicy(Qt::ActionsContextMenu);
        connect(m_view.actions().at(0), SIGNAL(triggered()), SLOT(newScene()));

        // Create a new scene instead of clear()-ing it, since scenes can only grow their
        // sceneRect().
        newScene();
    }
    Q_SLOT void newScene() {
        if (m_view.scene()) m_view.scene()->deleteLater();
        m_view.setScene(new Scene);
        m_view.scene()->connect(&m_join, SIGNAL(toggled(bool)), SLOT(setJoinFigures(bool)));
    }
};

int main(int argc, char *argv[])
{
    QApplication a{argc, argv};
    Window w;
    w.show();
    return a.exec();
}

#include "main.moc"

【讨论】:

  • 我使用了另一种方法,它也有效,但这不允许在不连接它们的情况下绘制单独的图形。我也尝试了您的方法,但我无法使其正常工作,看起来您可以在不连接它们的情况下绘制多个图形。我也无法更改笔的颜色。
  • 对此我还有一个问题,按钮如何工作?通常我去设计,只需拖放按钮,然后连接它们。但是这个设置似乎不起作用?
  • @user3110781:​​我不明白你的意思。您的意思是如何将Window 集成到设计为 .ui 文件的小部件/对话框中?
【解决方案2】:

创建一个继承自 QGraphicsItem 的类,如果需要信号和槽,则创建 QGraphicsObject。

将 QPainterPath 存储为类的成员。当您检测到按下鼠标按钮时,调用painter path moveTo() 函数,提供坐标。在接收到鼠标移动事件时,使用坐标调用painter path lineTo()函数。

重载 boundingRect 函数以返回画家路径的矩形,并重载 shape() 函数以返回正确的碰撞形状。

最后在类的paint函数中,绘制painter路径。这是您可以使用的骨架类。

class MouseDraw : public QGraphicsItem
{
    public:
        QRectF boundingRect() const
        {
            return m_painterpath.boundingRect();
        }

        QPainterPath shape() const
        {
            return m_painterpath;
        }

        void QGraphicsItem::paint(QPainter * painter, const QStyleOptionGraphicsItem * option, QWidget * widget)
        {
            // setup pen and brush
            // ....
            // draw the path

            painter->drawPath(m_painterpath);
        }

    private:
        QPainterPath m_painterpath;
};

处理 mousePress、mouseMove 和 mouseRelease 事件以将所需的点添加到画家路径,然后实例化该类的对象并将其添加到场景中:-

MouseDraw* mouseDraw = new MouseDraw;
scene->addItem(mouseDraw);

请注意,对象只添加到场景中一次,并且是动态创建的。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多