Выбор элементов в QGraphicsScene

я пытаюсь понять, как можно переопределить способ выбора и преобразования элементов (После выбора) в 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 ответов


это все зависит от того, что вы имеете в виду, переопределяя "способ выбора и преобразования элементов"

давайте возьмем 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);
}

в то время как этот класс функционально завершен, вы обнаружите, что ограничивающая прямая очень велика, когда линия горизонтальна, что является проблемой при выборе объекта, поэтому мы можем переопределить функцию 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) вы можете перемещать и обновление событий mousemove, пока mouseReleaseEvent называется.

если исходный mouseEvent в mousePressEvent ближе к центру, а не к одной из точек, то просто перенаправьте событие в родительский класс для его перемещения вся линия.

конечно, вы можете использовать это как шаблон для полигона со списком точек, которые вы затем рисуете краской, добавляете в painterPath в форме и манипулируете событиями мыши.


As ответ упоминает, это может быть проще сделать в 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.

Если вы делаете только общие манипуляции с элементами, которые могут применяться к любому QGraphicsItem подкласс, то взгляните на!--12-->Эластичные Узлы Пример.