Дело №1
Мы хотим изменять размер сцены и объектов в ней согласно размеру отображаемого окна. В доке сказано:«QGraphicsView takes ownership of the viewport widget». Ну что-ж, создадим простейшим проект и напишем следующее:
MainWindow.h:
#ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QMainWindow> #include <QGraphicsScene> #include <QGraphicsEllipseItem> #include <QGraphicsView> class MainWindow : public QWidget { Q_OBJECT public: explicit MainWindow(QWidget *parent = 0); ~MainWindow(); private: bool eventFilter(QObject *, QEvent *); private: QGraphicsScene *m_scene; QGraphicsEllipseItem *m_elipse; QGraphicsView * graphicsView; }; #endif // MAINWINDOW_H
MainWindow.cpp:
#include "MainWindow.h" #include <QGridLayout> #include <QEvent> #include <QResizeEvent> MainWindow::MainWindow(QWidget *parent) : QWidget(parent) { setLayout(new QGridLayout()); graphicsView = new QGraphicsView(); layout()->addWidget(graphicsView); graphicsView->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); graphicsView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); m_scene = new QGraphicsScene(); m_elipse = new QGraphicsEllipseItem(); m_scene->addItem(m_elipse); m_scene->setSceneRect(m_elipse->boundingRect()); graphicsView->setScene(m_scene); graphicsView->installEventFilter(this); } MainWindow::~MainWindow() { } bool MainWindow::eventFilter(QObject *, QEvent *event) { if(event->type() == QEvent::Resize ) { QResizeEvent *res = reinterpret_cast<QResizeEvent*>(event); m_elipse->setRect(0, 0, res->size().width(), res->size().height()); return true; } return false; }
Ожидается, что меняя размера виджета, мы будем менять размер эллипса, но он по-прежнему будет виден целиком, т.к. изменится размер и отображаемой области. Компилируем, запускаем и убеждаемся, что как надо не работает: можно уменьшить первоначальный размер, но вот при превышении эллипс обрезается.
Тогда меняем
graphicsView->installEventFilter(this);
на
graphicsView->viewport()->installEventFilter(this);
Компилируем, запускаем — все работает. Теперь попробуем установить фильтр в сцену. Компилируем, запускаем и ничего не видим, вообще.
Дело №2
Хотим отслеживать положение мыши, при ее перемещении по сцене. Для это модифицируем наш тестовый и класс, следующим образом:
bool MainWindow::eventFilter(QObject *, QEvent *event) { if(event->type() ==QEvent::MouseMove) { qDebug()<<event; return true; } return false; }
В конструктор дописываем:
graphicsView->setMouseTracking(true);
Ну и фильтр инсталлируем в graphicsView.
Компилируем, запускаем, убеждаемся что не работает. Инсталлируем фильтр в viewport и все снова работает.
Теперь немного модифицируем:
if(event->type() ==QEvent::MouseMove)
заменим на
if(event->type() ==QEvent::GraphicsSceneMouseMove)
И инсталлируем фильтр в сцену. И в отличии от прошлого раза мы обнаружим, что перемещение мыши отслеживается.
Выводы следствия
Как это было бы не странно с точки зрения новичка, но Graphics Framework действительно следует логике: отображение отдельно, а композиция отдельно. И потому событие об изменении размера не передается в сцену. И graphicsView действительно берет шефство над viewport. В этом можно убедится если в деле номер №1 заменить фильтр на:
if(event->type() == QEvent::Resize ) { QResizeEvent *res = reinterpret_cast<QResizeEvent*>(event); m_elipse->setRect(0, 0, res->size().width(), res->size().height()); } return false;
Т.е. убрать фильтрацию события, то мы увидим ожидаемое поведение. Т.е. событие отфильтровалось и дальше не было передано в viewport. А вот адекватное поведение при уменьшении вызвано внутренней логикой Qt или спонтанными событиями, как их именуют в коде.
Вернемся к передачи событий в сцену. Этот процесс вообще не описан, а здесь есть важный момент: перед тем как отправится в сцену graphicsView преобразуют событие в соответствующий QGRaphisSceneEvent, но происходит это уже в специализированных методах, собственно поэтому в доке и рекомендует переопределять их, а не точку входа (event или viewportEvent).
Еще раз обращу внимания на последний момент: логика фреймворка такова, что все внешние события подготавливаются для работы со сценой. Т.е. если у вас есть ваш QEvent, который должен быть передан сцене, то лучший способ, не нарушающий логику Qt — это создать аналог кастомного события, отнаследованный от QGRaphicsSceneEvent, написать метод преобразование и посылки преобразованного в сцену, который будет вызываться в классе, отнаследованном от QGraphicsView, ну и посылать QEvent в этот-самый модифицированный QGraphicsView.
Осталось рассмотреть последний этап: доставка события к Item-ам. Здесь картина аналогичная с доставкой до сцены, а именно: после того как событие пришло определяется какую специализированную функцию надо задействовать; в этой специализированной функции определяются координаты события, с помощью методов itemAt() определяется каким Item-а доставлять событие, затем событие подготавливается к доставке в Item и только после этого отправляется в него.
Кстати, здесь тоже грабли лежат. В доке сказано, что перед изменением геометрических размеров нужно вызывать geometryChange, в противном случае наткнемся на проблему с отрисовкой. Но не только, проблемы возникнут и с доставкой событий.
ссылка на оригинал статьи http://habrahabr.ru/post/182614/
Добавить комментарий