【问题标题】:How to draw on QGraphicScene in real time?如何在 QGraphicScene 上实时绘图?
【发布时间】:2021-02-15 11:28:16
【问题描述】:

我在制作 Windows Paint 之类的应用时遇到了问题。我想在按住鼠标按钮的同时在我的窗口上绘图。我知道有 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 框架中,所以我希望(我希望)我正在做一些愚蠢的事情。如果有任何帮助,我将不胜感激。

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

【问题讨论】:

  • 我知道有 MouseMoveEvent,但它的刷新率对我来说真的很差你有没有在发布模式下测试过你的代码。
  • 不要自己生成事件。相反,对系统生成的事件做出反应。
  • @drescherjm 开玩笑,我已经在发布模式下测试过了。

标签: c++ qt qgraphicsview


【解决方案1】:

原因

我在做傻事

要详细说明这一点,您的任务方法是错误的。 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();
}

结果

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

【讨论】:

  • 感谢您的回答,它看起来真的很有帮助。下班后我会尝试实施你的方法。
  • 我已经编辑了我以前的帖子,所以你可以看到我目前有什么。我已经设法用 moveEvent 做到了这一点,但我想让它变得稳固(当我移动鼠标太快时,每个点之间没有空格)。我认为用我自己的事件和计时器可以实现这一点,但我现在不确定。也许使用 QGraphicPathItem 会更好,并由我自己在这些点之间建立连接?
  • @Tojmak,我已经为您提供了解决问题的正确方法。运行代码,研究它是如何工作的,玩弄它,最后让它适应你自己的需要。如果您正在寻找一个简单的东西,我怀疑有人会为您提供这样的东西。
  • 我不是。但是看着你的想法让我觉得我做了一些非常相似的东西。也许我没有抓住什么,如果那是真的,比对不起我的不耐烦,我会更仔细地研究。但就目前而言,我真的很喜欢使用 mouseMoveEvent 的基本想法。我之前写过我试过了。您提到的 3 个指针正是我首先实现的。我只是在寻找想法。好吧......如果你确定它应该对我有所帮助 - 很抱歉,我会尝试更多地了解你写的内容
  • @Tojmak,您正在自己生成事件。这是非常不同的。
【解决方案2】:

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

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() 方法是必备的。如果没有这种性能,一开始会很好,但在绘制过程中它会快速下降,因为你会不断添加新的路径,这是性能杀手。

【讨论】:

  • 您是否设法使用此代码连续绘制了两个形状?
  • @schopchanov 是的......但感谢您指出这一点,因为我不知道为什么。更有趣的是,如果我在 mousePressEvent 中删除具有 mEllipse 属性的部分,它就会开始工作错误。第二次鼠标点击后应用程序关闭。任何想法为什么?
  • 我不知道为什么 -&gt; 因为this-&gt;clear();
  • @scopchanov 我认为不是。如果我将其注释掉,那么它的工作原理是一样的。 3 行 mEllipse 未注释它工作得很好,如果我删除那些行(仍然没有 this->clear() 方法)它不是。
  • @scopchanov 好吧,我认为这是有道理的......更准确地说,它适用于mEllipse = new QGraphicsEllipseItem(...) 行。它必须是因为 mouseMoveEvent 需要它。我已经检查过了,如果 mEllipse 属性用布尔标志切换(如果按下鼠标按钮则为真)它工作得很好。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-01-10
  • 1970-01-01
  • 2017-04-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多