Перехват событий QGraphicView

Все с праздником! Итак, задача в следующем:

  1. Расположить на QGraphicsScene предмет QGraphicsItem

  2. Предмет QGraphicsItem должен уметь: вращаться вокруг своей оси, перемещаться мышкой, удаляться при нажатии на него ПКМ, должен иметь рандомный цвет.

  3. QGraphicsScene должна уметь приближать и отдалять предметы, изображенные на ней

Собственно, первым делом я создаю обычный шаблонный QWidget (не буду выкладывать его код).

Далее, я создаю класс с предметом QGraphicsItem, в котором реализовываю все условия задачи по работе с предметом - перемещение предмета, рандомный цвет предмета, удаление предмета.

MoveItem.h

#ifndef MOVEITEM_H
#define MOVEITEM_H

#include <QObject>
#include <QGraphicsItem>
#include <QPainter>
#include <QGraphicsSceneMouseEvent>
#include <QCursor>
#include <QRandomGenerator>
#include <QColor>

class MoveItem : public QObject, public QGraphicsItem
{
    Q_OBJECT
public:
    explicit MoveItem(short typeItem, QObject *parent = 0);
    ~MoveItem();
private:
    QPainterPath path;
    QPen pen;
    QBrush brush;
    QRectF boundingRect() const override;
    void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override;
    void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override;
    void mousePressEvent(QGraphicsSceneMouseEvent *event) override;
    void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override;
    bool moving;
    int getRandomColorValue();
    int degree;
    short typeItem;
};

#endif // MOVEITEM_H

MoveItem.cpp

#include "moveitem.h"

MoveItem::MoveItem(short typeItem, QObject *parent) :
    QObject(parent), QGraphicsItem()
{
    this->typeItem = typeItem;
    pen = QPen(QColor(getRandomColorValue(), getRandomColorValue(), getRandomColorValue()));
    brush = QBrush(QColor(getRandomColorValue(), getRandomColorValue(), getRandomColorValue()));

    path.moveTo(0, -30);
    path.lineTo(-20, 30);
    path.lineTo(30, 0);
    path.lineTo(-30,0);
    path.lineTo(20,30);
    path.lineTo(0,-30);
    path.closeSubpath();

    degree = 0;
}

MoveItem::~MoveItem()
{

}

QRectF MoveItem::boundingRect() const
{
    return QRectF (-30,-30,60,60);
}

void MoveItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
    switch (typeItem)
    {
    case 0:
        painter->setPen(pen);
        painter->setBrush(brush);
        painter->drawRect(-30,-30,60,60);
        break;
    case 1:
        painter->setPen(pen);
        painter->setBrush(brush);
        painter->drawEllipse(-30,-30,60,60);
        break;
    case 2:
        painter->setPen(pen);
        painter->setBrush(brush);
        painter->drawPath(path);
        break;
    default:
        typeItem = 0;
        break;
    }

    update();
    Q_UNUSED(option);
    Q_UNUSED(widget);
}

void MoveItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
    if (moving)
    {
        this->setPos(mapToScene(event->pos()));
    }
}

void MoveItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
    if (event->button() == Qt::LeftButton)
    {
        this->setCursor(QCursor(Qt::ClosedHandCursor));
        moving = true;
    }

    if (event->button() == Qt::MiddleButton)
    {
        this->topLevelItem()->setRotation(degree);
        degree < 360 ? degree += 10 : degree = 0;
    }
}

void MoveItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
    if (event->button() == Qt::LeftButton)
    {
        this->setCursor(QCursor(Qt::ArrowCursor));
        moving = false;
    }

    if (event->button() == Qt::RightButton)
    {
        this->topLevelItem()->~QGraphicsItem();
    }

    Q_UNUSED(event);
}

int MoveItem::getRandomColorValue()
{
    std::uniform_int_distribution <int> dist(0, 255);
    return dist(*QRandomGenerator::global());
}

Вызываю в QWidget (главном окне) самую простую инициализацию QGraphicsScene и добавляю на нее предмет QGraphicItem с помощью класса выше.

QWidget.cpp

#include "widget.h"
#include "ui_widget.h"

Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);
    this->resize(800,600);
    
    QGraphicsView* view = new QGraphicsView (this);
    view->setGeometry(0, 0, width(), height());
    QGraphicsScene* gs = new QGraphicsScene(view);
    gs->setSceneRect(0,0,width(),height());
    gs->update();
    view->setScene(gs);
    MoveItem* it = new MoveItem(0);
    gs->addItem(it);
    it->setPos(width()/2,height()/2);
}

Widget::~Widget()
{
    delete ui;
}

Все отлично работает! Осталось только добавить управление QGraphicsScene, а именно приближение/отдаление предметов на сцене. Для этого выносим в отдельный класс QGraphicsView в нем инициализируем сцену и перехватываем события QGraphicsView:

Scene.h

#ifndef SCENE_H
#define SCENE_H

#include "moveitem.h"
#include <QGraphicsView>
#include <QMouseEvent>
#include <QList>

class Scene : public QGraphicsView
{
    Q_OBJECT
public:
    explicit Scene(QWidget  *parent = 0);
    ~Scene();
protected:
    QGraphicsScene* scene;
    void mouseMoveEvent(QMouseEvent *event) override;
    void mousePressEvent(QMouseEvent *event) override;
    void mouseReleaseEvent(QMouseEvent *event) override;
    void wheelEvent(QWheelEvent *event) override;
    void keyReleaseEvent(QKeyEvent *event) override;
    short typeItem;
private:
QList<MoveItem*> listItems;
};

#endif // SCENE_H

Scene.cpp

#include "scene.h"

Scene::Scene(QWidget *parent): QGraphicsView(parent)
{
    typeItem = 0;

    scene = new QGraphicsScene(this);

    this->setScene(scene);
}

Scene::~Scene()
{

}

void Scene::mouseMoveEvent(QMouseEvent *event)
{

}

void Scene::mousePressEvent(QMouseEvent *event)
{

}

void Scene::mouseReleaseEvent(QMouseEvent *event)
{
    if (event->button() == Qt::LeftButton)
    {
        if(scene)
        {
            listItems.append(new MoveItem(typeItem));
            scene->addItem(listItems.last());
            QPointF p = mapToScene(event->pos());
            listItems.last()->setPos(p);
            typeItem < 2 ? typeItem++ : typeItem = 0;
        }
    }
}

void Scene::wheelEvent(QWheelEvent *event)
{
    if(scene)
    {
        if(event->delta() < 0)
        {
            this->scale(1/1.05, 1/1.05);
        }
        else
        {
            this->scale(1.05, 1.05);
        }
    }
}

void Scene::keyReleaseEvent(QKeyEvent *event)
{
    if(event->key() == Qt::Key_Plus)
    {
        this->scale(1.05, 1.05);
    }
    else if(event->key() == Qt::Key_Minus)
    {
        this->scale(1/1.05, 1/1.05);
    }
}

Изменяем инициализацию в головном виджете QWidget:

QWidget.cpp

#include "widget.h"
#include "ui_widget.h"

Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);
    this->resize(800,600);

    scene = new Scene(this);
}

Widget::~Widget()
{
    delete ui;
}

О чудо! не работают все перехваченные события из класса MoveItem:

QRectF MoveItem::boundingRect() const
{
    return QRectF (-30,-30,60,60);
}

void MoveItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
    switch (typeItem)
    {
    case 0:
        painter->setPen(pen);
        painter->setBrush(brush);
        painter->drawRect(-30,-30,60,60);
        break;
    case 1:
        painter->setPen(pen);
        painter->setBrush(brush);
        painter->drawEllipse(-30,-30,60,60);
        break;
    case 2:
        painter->setPen(pen);
        painter->setBrush(brush);
        painter->drawPath(path);
        break;
    default:
        typeItem = 0;
        break;
    }

    update();
    Q_UNUSED(option);
    Q_UNUSED(widget);
}

void MoveItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
    if (moving)
    {
        this->setPos(mapToScene(event->pos()));
    }
}

void MoveItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
    if (event->button() == Qt::LeftButton)
    {
        this->setCursor(QCursor(Qt::ClosedHandCursor));
        moving = true;
    }

    if (event->button() == Qt::MiddleButton)
    {
        this->topLevelItem()->setRotation(degree);
        degree < 360 ? degree += 10 : degree = 0;
    }
}

void MoveItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
    if (event->button() == Qt::LeftButton)
    {
        this->setCursor(QCursor(Qt::ArrowCursor));
        moving = false;
    }

    if (event->button() == Qt::RightButton)
    {
        this->topLevelItem()->~QGraphicsItem();
    }

    Q_UNUSED(event);
}

В чем моя ошибка? Заранее спасибо!


Ответы (1 шт):

Автор решения: Alexander Chernin

Из документации:

You can interact with the items on the scene by using the mouse and keyboard. QGraphicsView translates the mouse and key events into scene events, (events that inherit QGraphicsSceneEvent,), and forward them to the visualized scene. In the end, it's the individual item that handles the events and reacts to them. For example...

Перевод:

Вы можете взаимодействовать с итемами на сцене при помощи мыши и клавиатуры. QGraphicsView преобразует соответствующие события в события сцены (которые наследуют QGraphicsSceneEvent) и передают их видимой сцене. В конце, события передаются соответствующему итему

Таким образом, чтобы это преобразование произошло, я думаю вам надо вызвать методы базового класса QGraphcisView при наследовании, то есть:

void Scene::mousePressEvent(QMouseEvent *event)
{
    QGraphicsView::mousePressEvent(event);
}

и так далее

→ Ссылка