Главная
Блог разработчиков phpBB
 
+ 17 предустановленных модов
+ SEO-оптимизация форума
+ авторизация через соц. сети
+ защита от спама

Qt Graphics View Framework — темная сторона. Часть 2

Anna | 25.06.2014 | нет комментариев
Начав свой путь, мы не останавливаемся и продолжаем постигать темные стороны документации. Где-то они могут быть характерны для каждого Qt, а где-то присущи только Graphics View. Но так либо напротив встреча с ними не неизменно проходит безболезненно.

Дело №3

Мы хотим написать свой item, в качестве примера пишем дальнейший код

MainWindow.h

class MainWindow : public QWidget
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();
private:
    QGraphicsScene *m_scene;
    QGraphicsRectItem *m_rect;
    QGraphicsItem *m_cross;
    QGraphicsView * graphicsView;
};

MainWindow.cpp

class CrossItem: public QGraphicsItem
{

public:
    QRectF boundingRect() const
    {
        return QRectF(0, 0, 30*scale(), 30*scale());
    }
    void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
    {
        Q_UNUSED(option) Q_UNUSED(widget);
        painter->setPen(QColor(Qt::red));
        painter->drawLine(10, 0, 20, 0);
            painter->drawLine(20*scale(), 0*scale(), 20*scale(), 10*scale());
            painter->drawLine(20, 10, 30, 10);
            painter->drawLine(30, 10, 30, 20);
            painter->drawLine(30, 20, 20, 20);
            painter->drawLine(20, 20, 20, 30);
        painter->drawLine(20, 30, 10, 30);
            painter->drawLine(10, 30, 10, 20);
            painter->drawLine(10, 20,  0, 20);
            painter->drawLine( 0, 20,  0, 10);
            painter->drawLine( 0, 10, 10, 10);
            painter->drawLine(10, 10, 10, 0);
    }
};

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_cross = new CrossItem();
        m_scene->addItem(m_cross);
//	  m_cross->setScale(2);
    m_rect = new QGraphicsRectItem(m_cross->boundingRect(), m_cross);
        m_scene->addItem(m_rect);
    m_scene->setSceneRect(m_rect->boundingRect());
    graphicsView->setScene(m_scene);
}

MainWindow::~MainWindow()
{
}

Комплируем, запускаем, видим вписанный в крест квадрат. Раскоментируем строчку и пересоберем план, Дабы позже запуска увидеть все свои ошибки.
Дело в том, что:

painter->drawLine(20*scale(), 0*scale(), 20*scale(), 10*scale());

и

return QRectF(0, 0, 30*scale(), 30*scale());

— бедствие от ума.

Давайте разглядим как хранит QGraphicsItem данные о трансформациях:

struct QGraphicsItemPrivate::TransformData
{
    QTransform transform;
    qreal scale;
    qreal rotation;
    qreal xOrigin;
    qreal yOrigin;
    QList<QGraphicsTransform *> graphicsTransforms;
    bool onlyTransform;

    TransformData() :
        scale(1.0), rotation(0.0),
        xOrigin(0.0), yOrigin(0.0),
        onlyTransform(true)
    { }

    QTransform computedFullTransform(QTransform *postmultiplyTransform = 0) const
    {
        if (onlyTransform) {
            if (!postmultiplyTransform || postmultiplyTransform->isIdentity())
                return transform;
            if (transform.isIdentity())
                return *postmultiplyTransform;
            return transform * *postmultiplyTransform;
        }

        QTransform x(transform);
        if (!graphicsTransforms.isEmpty()) {
            QMatrix4x4 m;
            for (int i = 0; i < graphicsTransforms.size();   i)
                graphicsTransforms.at(i)->applyTo(&m);
            x *= m.toTransform();
        }
        x.translate(xOrigin, yOrigin);
        x.rotate(rotation);
        x.scale(scale, scale);
        x.translate(-xOrigin, -yOrigin);
        if (postmultiplyTransform)
            x *= *postmultiplyTransform;
        return x;
    }
};

Все дюже легко и незатейливо, как мычание коровы на лугу.И собственно перед тем как рисовать сцена дергает способ computedFullTransform и, совершив еще пару шаманских действий, подсовывает это QPainter-у. Здесь можем поставить себе на заметку, что если мы хотим делать что-то хитроумное с формой нашего item-a, то нам следует применять способы setScalesetPossetRotation и setTransforms и больше нам ничего не нужно.

Дело №4

Хотим отслеживать перемещение мыши по item-у. Сразу предупреждаю — грабли тут разложены дословно на всяком шагу. И били они меня дюже больно идюже настойчиво. Но полный список мытарств описывать не буду. Остановлюсь только на ключевых моментах

Перепишем Mainwindow.cpp дальнейшим образом:

#include "MainWindow.h"
#include <QGridLayout>
#include <QGraphicsSceneHoverEvent>
#include <QDebug>
class MyRect: public QGraphicsRectItem
{
public:
    MyRect(const QRectF &rect, QGraphicsItem *parent=0):QGraphicsRectItem(rect, parent){}
protected:
    void hoverMoveEvent(QGraphicsSceneHoverEvent *event)
    {
        qDebug()<<"rect"<<event->pos();
        QGraphicsRectItem::hoverMoveEvent(event);
    }
};

class CrossItem: public QGraphicsItem
{

public:
    QRectF boundingRect() const
    {
        return QRectF(0, 0, 30, 30);
    }
    void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
    {
        Q_UNUSED(option) Q_UNUSED(widget);
        painter->setPen(QColor(Qt::red));
        painter->drawLine(10, 0, 20, 0);
            painter->drawLine(20, 0, 20, 10);
            painter->drawLine(20, 10, 30, 10);
            painter->drawLine(30, 10, 30, 20);
            painter->drawLine(30, 20, 20, 20);
            painter->drawLine(20, 20, 20, 30);
        painter->drawLine(20, 30, 10, 30);
            painter->drawLine(10, 30, 10, 20);
            painter->drawLine(10, 20,  0, 20);
            painter->drawLine( 0, 20,  0, 10);
            painter->drawLine( 0, 10, 10, 10);
            painter->drawLine(10, 10, 10, 0);
    }
protected:
    void hoverMoveEvent(QGraphicsSceneHoverEvent *event)
    {
        qDebug()<<"cross"<<event->pos();
        QGraphicsItem::hoverMoveEvent(event);
    }
};

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_cross = new CrossItem();
        m_rect = new MyRect(m_cross->boundingRect(), m_cross);
    m_cross->setAcceptHoverEvents(true);
        m_rect->setAcceptHoverEvents(true);
    m_scene->addItem(m_cross);
    m_scene->setSceneRect(m_rect->boundingRect());
    graphicsView->setScene(m_scene);
}

MainWindow::~MainWindow()
{
}

Мы следуем всеобщей логике, что события сцены — полные аналоги обыкновенных событий. О чем нам счастливо уведомляет дока по QGraphicsSceneHoverEvent.

Компилируем, запускаем и видим, что при перемещении мыши событие доходит только до прямоугольника. Лезем в доку, и где-то на задворках при изложении зачухонного способа setAcceptHoverEvents читаем:

Parent items receive hover enter events before their children, and leave events after their children. The parent does not receive a hover leave event if the cursor enters a child, though; the parent stays «hovered» until the cursor leaves its area, including its children’s areas.

If a parent item handles child events, it will receive hover move, drag move, and drop events as the cursor passes through its children, but it does not receive hover enter and hover leave, nor drag enter and drag leave events on behalf of its children.

A QGraphicsWidget with window decorations will accept hover events regardless of the value of acceptHoverEvents().

Выходит, нам нужно отслеживать события потомка. Поняв это и приняв во внимание другое, ищем способ, тот, что нам поможет. И такой находится, это setFiltersChildEvents(не путать схожим setHandlesChildEvents, на том пути нам славы не обнаружить). Добавим его в конструктор:

m_cross->setFiltersChildEvents(true);

А также добавим фильтр в класс CrossItem:

    bool sceneEventFilter(QGraphicsItem *watched, QEvent *event)
    {
        if(event->type() == QEvent::GraphicsSceneHoverMove)
            qDebug()<<"cross"<<event;
        return false;
    }

Компилируем, запускаем. Ура! Заработало!!!

Результат

К сожалению подвести конструктивный результат, как в прошлой статье теперь у меня не получится. Скорее это недоумение — потратив существенные усилия на автономность item-а от любых геометрических реформирований, разработчики Qt предложили нам дюже необычную систему доставки событий к item-ам. Правда приведенный выше пример является скорее исключительным случаем, но одного взора на флаги вQGraphicsItem довольно, чтоб крепко задуматься.

Источник: programmingmaster.ru

Оставить комментарий
Форум phpBB, русская поддержка форума phpBB
Рейтинг@Mail.ru 2008 - 2017 © BB3x.ru - русская поддержка форума phpBB