如何在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
要详细说明这一点,您对任务的处理方法有误。 QGraphicsSceneMouseEvent
和 mousePressEvent
均不应由用户生成。
解决方案
不要自己生成事件。相反,对系统生成的事件做出反应:
- 重新实现
QGraphicsView::mousePressEvent
以创建新的图形项
- 重新实现
QGraphicsView::mouseMoveEvent
修改新创建的图形项
- 重新实现
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。如果没有这种性能,开始时会很好,但在绘制过程中它会快速下降,因为您将不断添加新路径,这是性能杀手。
我在制作像 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
要详细说明这一点,您对任务的处理方法有误。 QGraphicsSceneMouseEvent
和 mousePressEvent
均不应由用户生成。
解决方案
不要自己生成事件。相反,对系统生成的事件做出反应:
- 重新实现
QGraphicsView::mousePressEvent
以创建新的图形项 - 重新实现
QGraphicsView::mouseMoveEvent
修改新创建的图形项 - 重新实现
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。如果没有这种性能,开始时会很好,但在绘制过程中它会快速下降,因为您将不断添加新路径,这是性能杀手。