【问题标题】:Selection of items in QGraphicsSceneQGraphicsScene 中的项目选择
【发布时间】:2014-12-12 11:25:19
【问题描述】:

我试图了解如何重新定义在QGraphicsScene 中选择和转换(一旦选择)项目的方式。例如改变一条线的长度,移动一条线,通过移动它的一个点来改变一个多边形。

我创建了QGraphicsView 的一个子级,并开始重载它的mousePressEvent,但似乎选择和移动动作被QGraphicsItem 捕获。我如何覆盖它,因为它们受到保护并且对QGraphicsView 的孩子不可见?

我可以想象我需要在myGraphicsItem 中重载QGraphicsItem::mousePressEvent,但这意味着我还必须重载QGraphicsScene 来处理myGraphicsItem?当它被移动时,我如何处理场景中的选定项目位置?

有什么可以看的例子吗?

我(显然)有点失落。

更新:根据反馈,我创建了一个QGraphicsItems 的孩子,如下所示:

class baseGraphicItem : public QGraphicsItem
{
public:
    explicit baseGraphicItem(QVector<QPoint> data, operationType shape, QObject * parent = 0);

signals:

public slots:

public:
    virtual QRectF boundingRect() const;
    virtual void paint(QPainter * painter, const QStyleOptionGraphicsItem * option, QWidget * widget = 0);
    QPainterPath shape() const;
    virtual void hoverEnterEvent ( QGraphicsSceneHoverEvent * event );
    virtual void hoverLeaveEvent ( QGraphicsSceneHoverEvent * event );


private:
    QPolygon vertex;
    operationType shapeType;

};

baseGraphicItem::baseGraphicItem(QVector<QPoint> data, operationType shape, QObject *parent) :
    QGraphicsItem(), vertex(data), shapeType(shape)
{
    qDebug() << vertex;
    this->setAcceptHoverEvents(false);
}

这些用于绘画、boundingRect 和形状。

void baseGraphicItem::paint(QPainter * painter, const QStyleOptionGraphicsItem*, QWidget*)
{
    int i=0;

    // Following needs better code for polygons
    do painter->drawLine(vertex.at(i), vertex.at(i+1));
    while (i++<vertex.size()-2);
}

QRectF baseGraphicItem::boundingRect() const
{
    return vertex.boundingRect();
}

QPainterPath baseGraphicItem::shape() const
{
    QPainterPath path;
    path.addPolygon(vertex);
    return path;
}

不幸的是,选择适用于一条线或多边形。但是当一条线在多边形内时,几乎总是选择多边形而不是线。这是因为boundingRect 还是形状?另外,如何获取存储在QPolygon 顶点中的新坐标? 谢谢

【问题讨论】:

  • 如果我没看错的话,QGraphicsView 被子类化并用于捕获事件。我正在这样做,我无法捕获选择,因为它似乎是由 QGraphicsItem::mousepressevent 而不是由 QGraphicsView::mousepressevent 完成的。

标签: qt selection qgraphicsitem


【解决方案1】:

这完全取决于您重新定义“选择和转换项目的方式”的意思

我们以 QGraphicsLineItem 为例。

如果我希望这个项目是可移动的,我可以调用它的函数 setFlag(QGraphicsItem::ItemIsMovable)。现在可以单击该项目并在场景中移动。当然,这假设项目是可选择的,这可以通过设置标志 QGraphicsItem::ItemIsSelectable 来实现。

现在,如果我想改变一条线的点,我可能会使用它的 setLine 函数并继续重新定义这条线。但是,最好直接从 QGraphicsItem 继承并创建自己的。

class MyLine : public QGraphicsItem
{
    Q_OBJECT

    public:
        virtual QRectF boundingRect();
        virtual void paint(QPainter * painter, const QStyleOptionGraphicsItem * option, QWidget * widget = 0);
};

所以这是我从 QGraphicsItem 继承的最低要求,因为 boundingRect 和 paint 函数在 QGraphicsItem 中是纯虚拟的。

所以,现在我们可以为类添加起点和终点了:-

class MyLine : public QGraphicsItem
{
    Q_OBJECT

    public:
        virtual QRectF boundingRect();
        virtual void paint(QPainter * painter, const QStyleOptionGraphicsItem * option, QWidget * widget = 0);

    private:
        QPointF m_pointA;
        QPointF m_pointB;
};

接下来,类需要在paint函数中绘制自己:-

void MyLine::paint(QPainter * painter, const QStyleOptionGraphicsItem* , QWidget*)
{
    // The painter's pen and brush could be set here first

    // Draw the line
    painter->drawLine(m_pointA, m_pointB);
}

最后完成这个类的是boundingRect函数,它代表类的可见区域:-

QRectF MyLine::boundingRect()
{
    return QRectF(m_pointA, m_pointB);
}

虽然这个类在功能上是完整的,但是你会发现在直线水平的时候bounding rect很大,这是选择物体时的问题,所以我们可以重写shape函数来解决这个问题

QPainterPath MyLine::shape() const
{
    QPainterPath path;
    path.moveTo(m_pointA);
    path.lineTo(m_pointB);
    return path;
}

现在我们有了自己的 line 类,我们可以添加 mouseEvent 处理程序:-

class MyLine : public QGraphicsItem
{
    Q_OBJECT

    public:
        virtual QRectF boundingRect();
        QPainterPath shape() const
        virtual void paint(QPainter * painter, const QStyleOptionGraphicsItem * option, QWidget * widget = 0);


        virtual void mouseMoveEvent(QGraphicsSceneMouseEvent * event);
        virtual void mousePressEvent(QGraphicsSceneMouseEvent * event);
        virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent * event);

    private:
        QPointF m_pointA;
        QPointF m_pointB;
};

使用事件处理程序,您需要将鼠标单击的时间存储在 mouseMoveEvent 中。鼠标事件最接近的点(m_pointA 或 m_PointB)是您可以在 mouseMoveEvent 中移动和更新的点,直到调用 mouseReleaseEvent。

如果 mousePressEvent 中的原始 mouseEvent 更靠近中心,而不是其中一个点,则只需将事件转发给父类,以便它移动整条线。

当然,您可以将其用作带有点列表的多边形的模板,然后您可以在绘画中绘制它,将形状添加到painterPath 并在鼠标事件中进行操作。

【讨论】:

  • 谢谢。非常详细。基本上我需要从中制作 myGraphicsItem 和 myGraphicsPolygon ,依此类推。一个问题,将项目添加到 QGraphicsScene 怎么样?
  • 继承自 QGraphicsItem 或 QGraphicsObject(如果您需要信号和插槽)以创建 MyGraphicsLine 和 MyGraphicsPolygon
  • 我尝试了上述方法并用于一个可以接受多边形的类,但边界框是这样的,如果我在另一个多边形内绘制一些东西,那么选择里面的项目是一场噩梦。有什么建议吗?
  • 你重载了形状函数吗?
  • 我想我明白了。最好的可能是为提出一个正方形的点制作一个新项目。多边形中包含它们的行项目。以及一个包含线条的新多边形项目。通过这种方式,我可以通过继承来区分多边形的每个元素。
【解决方案2】:

正如this answer 所提到的,在QGraphicItems 本身中执行此操作可能更容易,特别是如果操作特定于某些项目。在这种情况下,以下代码显示了它是如何完成的:

#include <QtWidgets>

class Item : public QGraphicsRectItem
{
public:
    Item() {
        setRect(0, 0, 100, 100);
        setFlag(QGraphicsItem::ItemIsMovable, true);
    }

    void mousePressEvent(QGraphicsSceneMouseEvent *event) {
        qDebug() << "Item";
        QGraphicsRectItem::mousePressEvent(event);
    }
};

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);

    QGraphicsView view;
    QGraphicsScene *scene = new QGraphicsScene;
    scene->addItem(new Item());
    view.setScene(scene);
    view.resize(400, 400);
    view.show();

    return app.exec();
}

#include "main.moc"

有关详细信息,请参阅QGraphicsItem documentation

如果您只是对可应用于任何QGraphicsItem 子类的项目进行通用操作,请查看Elastic Nodes Example

【讨论】:

  • 梅林,米奇,谢谢。看来我无法避开这条路线。对于信息,我只是为了绘制平面图来操作多边形。在这里,我需要通过拖动线或点来拉伸多边形。最重要的是,我正在为只能沿着多边形移动的对象添加图层(因为它们代表墙壁)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-05-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-06-23
相关资源
最近更新 更多