如何在QGraphicSscene上实时绘图?

How to draw on QGraphicScene in real time?

我在制作像 Windows 画图这样的应用程序时遇到了问题。我想在按住鼠标按钮的同时在 window 上画画。我知道有 MouseMoveEvent,但它的刷新率对我来说真的很差。因此,我想创建自己的活动。我尝试了一些选项,至于现在我有类似的东西:

DrawArea::DrawArea(QObject *parent) : QGraphicsScene(parent), buttonPressed(false)
{
    mouseEv = new QGraphicsSceneMouseEvent(QEvent::MouseMove);
    timer = new QTimer(this);
    timer->setInterval(60);
    connect(timer, &QTimer::timeout, [=](){ mousePressEvent(mouseEv); });
}

void DrawArea::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
        QPointF pos = event->scenePos();
        qDebug() << pos;
        auto radius = qreal(2);

        auto* ellipse = this->addEllipse(QRect(pos.x()-radius, pos.y()-radius, 2*radius, 2*radius));
        ellipse->setBrush(QBrush(QColor(Qt::black),Qt::SolidPattern));
        buttonPressed = true;

        //connect(timer, &QTimer::timeout, [=](){ updateDraw(event->scenePos()); });
        timer->start();

        event->accept();
}

但问题是,当我按住鼠标按钮时,“pos”变量等于 (0,0)。 Pos 是正确的,只要我只用我的鼠标按钮点击。

之前我也试过这样的:

DrawArea::DrawArea(QObject *parent) : QGraphicsScene(parent), buttonPressed(false)
{
    mouseEv = new QGraphicsSceneMouseEvent(QEvent::MouseMove);
    timer = new QTimer(this);
    timer->setInterval(60);
}

void DrawArea::mousePressEvent(QGraphicsSceneMouseEvent *event)
{

    if(event->button() & Qt::LeftButton)
    {
        QPointF pos = event->scenePos();
        qDebug() << pos;
        auto radius = qreal(2);

        auto* ellipse = this->addEllipse(QRect(pos.x()-radius, pos.y()-radius, 2*radius, 2*radius));
        ellipse->setBrush(QBrush(QColor(Qt::black),Qt::SolidPattern));
        buttonPressed = true;

        connect(timer, &QTimer::timeout, [=](){ updateDraw(event->scenePos()); });
        timer->start();
    }
    else event->ignore();
}

void DrawArea::updateDraw(QPointF mousePos)
{
    mouseEv = new QMouseEvent(QEvent::MouseMove, mousePos, Qt::NoButton, Qt::LeftButton, Qt::NoModifier);
    mouseMoveEv(mouseEv);
}

void DrawArea::mouseMoveEv(QMouseEvent *event)
{
    QPointF pos = event->pos();
    qDebug() << pos;
    auto radius = qreal(20);

    auto* ellipse = this->addEllipse(QRect(pos.x()-radius, pos.y()-radius, 2*radius, 2*radius));
    ellipse->setBrush(QBrush(QColor(Qt::black),Qt::SolidPattern));

    event->accept();
}

但仍然无法正常工作。我对 Qt 很陌生,特别是在 Qt Graphics View 框架中,所以很可能(我希望)我在做一些愚蠢的事情。非常感谢您的帮助。

加粗编辑 我正在发布我的应用程序的图片。

原因

i'm doing something stupid

要详细说明这一点,您对任务的处理方法有误。 QGraphicsSceneMouseEventmousePressEvent 均不应由用户生成。

解决方案

不要自己生成事件。相反,对系统生成的事件做出反应:

  1. 重新实现 QGraphicsView::mousePressEvent 以创建新的图形项
  2. 重新实现QGraphicsView::mouseMoveEvent修改新创建的图形项
  3. 重新实现QGraphicsView::mouseReleaseEvent以确认创建,即让项目保留在场景中,或删除项目,例如单击鼠标右键

例子

这是我为您编写的示例,用于演示解决该问题的可能方法:

#include <QApplication>
#include <QGraphicsView>
#include <QGraphicsEllipseItem>
#include <QMouseEvent>
#include <QBoxLayout>

class GraphicsView : public QGraphicsView
{
    QGraphicsEllipseItem *m_ellipse;
public:
    explicit GraphicsView(QWidget *parent = nullptr) :
        QGraphicsView(parent),
        m_ellipse(nullptr) {
        setScene(new QGraphicsScene(this));
        setSceneRect(0, 0, 1000, 800);
        setAlignment(Qt::AlignLeft | Qt::AlignTop);
    }

protected:
    void mousePressEvent(QMouseEvent *event) override {
        QGraphicsView::mousePressEvent(event);

        if (!(event->button() == Qt::LeftButton))
            return;

        m_ellipse = new QGraphicsEllipseItem(-5, -5, 10, 10);
        m_ellipse->setPos(event->pos());
        m_ellipse->setPen(QPen(Qt::red));

        scene()->addItem(m_ellipse);
    }

    void mouseReleaseEvent(QMouseEvent *event) override {
        QGraphicsView::mouseReleaseEvent(event);

        if (event->button() == Qt::LeftButton) {
            if (m_ellipse)
                m_ellipse->setPen(QPen(Qt::black));
        } else {
            delete m_ellipse;
        }

        m_ellipse = nullptr;
    }

    void mouseMoveEvent(QMouseEvent *event) override {
        QGraphicsView::mouseMoveEvent(event);

        if (!m_ellipse)
            return;

        QPointF d = 2*(event->pos() - m_ellipse->pos());

        m_ellipse->setRect(-0.5*d.x(), -0.5*d.y(), d.x(), d.y());
    }
};

class MainWindow : public QWidget
{
public:
    MainWindow(QWidget *parent = nullptr) :
        QWidget(parent) {
        (new QVBoxLayout(this))->addWidget(new GraphicsView(this));

        resize(800, 600);
    }
};

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

    w.show();

    return a.exec();
}

结果

提供的示例(带有一点创意)产生以下结果:

如果有人遇到类似问题,这是我的实现:

DrawArea::DrawArea(QObject *parent) : QGraphicsScene(parent), mRadius(2), mDrawPath{}
{
    drawing = false; // drawing is boolean type attribute
}

void DrawArea::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
    if(event->button() & Qt::LeftButton)
    {
       mDrawPath.moveTo(event->scenePos());
       event->accept();
       drawing = true;
    }
    else event->ignore();
}

void DrawArea::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
   if(event->button() & Qt::LeftButton)
   {
     drawing = false;
   }
}

void DrawArea::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{ 
    if(drawing == false){}
    else
    {
        mDrawPath.lineTo(event->scenePos());

        this->clear();
        addPath(mDrawPath);
    }
}

值得一提的是mouseMoveEvent方法中的this->clear()方法是一个must-have。如果没有这种性能,开始时会很好,但在绘制过程中它会快速下降,因为您将不断添加新路径,这是性能杀手。