在QGraphicsScene中选择项目

时间:2014-03-26 16:03:58

标签: qt selection qgraphicsitem

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

我创建了一个QGraphicsView的孩子,并开始重载其mousePressEvent,但似乎QGraphicsItem捕获了选择和移动操作。我如何覆盖它,因为它们受到保护而且QGraphicsView的孩子无法看到?

我可以想象我需要在QGraphicsItem::mousePressEvent中重载myGraphicsItem,但这意味着我还必须重载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顶点中? 感谢

2 个答案:

答案 0 :(得分:7)

这完全取决于您重新定义&#34;选择和转换项目的方式的确切含义&#34;

让我们以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继承的最低要求,因为在QGraphicsItem中boundingRect和paint函数是纯虚拟的。

所以,现在我们可以在课程中添加起点和终点: -

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);
}

虽然这个类在功能上是完整的,但是当行是水平时你会发现边界矩形非常大,这在选择对象时是个问题,所以我们可以覆盖形状函数来解决这个问题

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

现在我们有了自己的行类,我们可以添加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的形状并在鼠标事件中进行操作。

答案 1 :(得分:0)

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

#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