我想绘制不同的任意数字。在图形视图中单击鼠标时开始绘制,在停止单击鼠标时结束绘图。但是,当从图形视图中的不同点开始制作新图形或在上一个图形上继续时,将从第一个图形的最后一个鼠标坐标绘制一条线到第二个图形的第一个坐标。附图不一定需要是不同的附图,而是也可以仅对附图进行调整。这是我的代码。
#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();
}
}
答案 0 :(得分:1)
从MouseDraw
派生自定义QGraphicsItem
类是不必要的。有一个QGraphicsPathItem
可以正确处理这一切。
在创建的项目中处理鼠标交互在概念上是不正确的 - 您的大型边界矩形是一个工作所需的黑客,但这是错误的方法。您不希望项与鼠标交互。您希望场景与鼠标交互并动态创建项目。只有在编辑现有项目时,您才希望进行鼠标交互,但即使这样,最好还是在正在编辑的项目上创建一个单独的编辑器项目来处理编辑交互。
用鼠标右键单击窗口以获取上下文菜单,其中包含清除图片的操作。您还可以切换后续路径是否已连接到上一个路径。此代码使用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"
答案 1 :(得分:0)
如果需要信号和插槽,请创建一个继承自QGraphicsItem或QGraphicsObject的类。
将QPainterPath存储为该类的成员。当您检测到按下的鼠标按钮时,调用画家路径moveTo()函数,提供坐标。在接收鼠标移动事件时,使用坐标调用画家路径lineTo()函数。
重载boundingRect函数以返回画家路径的rect,并重载shape()函数以返回正确的碰撞形状。
最后在类的绘画功能中,绘制画家路径。这是你可以使用的骨架类。
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事件,将所需的点添加到painter路径,然后实例化该类的对象并将其添加到场景中: -
MouseDraw* mouseDraw = new MouseDraw;
scene->addItem(mouseDraw);
请注意,该对象仅添加一次到场景中并动态创建。