讓QGraphicsItemGroup中的item處理自己的事件

簡述

QGraphicsItem 分組比較簡單,但在分組之後 group 中的 QGraphicsItem 無法捕獲自己的相關事件(例如:鼠標事件、鍵盤事件),實際接受消息對象爲 QGraphicsItemGroup。那麼,如何處理呢?

 

處理方式

處理方式有兩種:

  • 方式一,也是最簡單的一種:

void QGraphicsItem::setHandlesChildEvents(bool enabled)

如果 enabled 爲 true,QGraphicsItemGroup 將處理其所有子 item 的所有事件(即,其任何子 item 的所有事件都發送到此 item),例如:鼠標點擊子 item 的事件不會被子 item 自身處理;否則,如果 enabled 爲 false,QGraphicsItemGroup 將只處理自己的事件,不會阻止子 item 的事件,並讓子 item 處理自己的事件。

根據官方文檔描述,該函數參數的默認值爲 false。經過實驗,重寫鼠標事件、鍵盤事件之後,會發現依然會阻止子 item 的事件,究竟爲何?難道是文檔有誤?

當然不會,打開 QGraphicsItemGroup 的源碼,可以發現:

QGraphicsItemGroup::QGraphicsItemGroup(QGraphicsItem *parent)
    : QGraphicsItem(*new QGraphicsItemGroupPrivate, parent)
{
    setHandlesChildEvents(true);
}

在 QGraphicsItemGroup 的構造函數中就這一行代碼,也正是我們要找的答案!

所以,要讓 QGraphicsItemGroup 中的 item 處理自己的事件,還需要在構造 group 後,再手動調用:

QGraphicsItemGroup::setHandlesChildEvents(false);

這一行代碼即可。

  • 方式二,

bool QGraphicsItem::sceneEvent(QEvent *event)

該虛函數接收到此 item 的事件。重新實現這個函數,在事件被分派到專門的事件處理程序之前攔截事件 contextMenuEvent()、focusInEvent()、focusOutEvent()、hoverEnterEvent()、hoverMoveEvent()、hoverLeaveEvent()、keyPressEvent()、keyReleaseEvent()、mousePressEvent()、mouseReleaseEvent()、mouseMoveEvent()、和 mouseDoubleClickEvent()。

如果事件被識別和處理,則返回 true;否則(例如,如果事件類型未被識別),則返回 false。

event 是攔截的事件。

這樣看來,sceneEvent() 接收一個 item 的所有事件,非常類似於 QWidget::event()。

既然如此,重寫此函數也可以讓 QGraphicsItemGroup 中的 item 處理自己的事件。

#include <QGraphicsEllipseItem>
#include <QEvent>
#include <QGraphicsSceneMouseEvent>
#include <QKeyEvent>
#include <qDebug>

class CustomItem : public QGraphicsEllipseItem
{
public:
    CustomItem(QGraphicsItem *parent = 0) {
        setFlag(QGraphicsItem::ItemIsFocusable);
    }
protected:
    // 按鍵按下事件
    void keyPressEvent(QKeyEvent *event) Q_DECL_OVERRIDE {
        Q_UNUSED(event);
        qDebug() << "keyPressEvent";
    }
    // 按鍵釋放事件
    void keyReleaseEvent(QKeyEvent *event) Q_DECL_OVERRIDE {
        Q_UNUSED(event);
        qDebug() << "keyReleaseEvent";
    }
    // 鼠標按下事件
    void mousePressEvent(QGraphicsSceneMouseEvent *event) Q_DECL_OVERRIDE {
        Q_UNUSED(event);
        qDebug() << "mousePressEvent";
    }
    // 鼠標按下事件
    void mouseMoveEvent(QGraphicsSceneMouseEvent *event) Q_DECL_OVERRIDE {
        Q_UNUSED(event);
        qDebug() << "mouseMoveEvent";
    }
    // 鼠標釋放事件
    void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) Q_DECL_OVERRIDE {
        Q_UNUSED(event);
        qDebug() << "mouseReleaseEvent";
    }
    // 處理上述事件
    bool sceneEvent(QEvent *event) Q_DECL_OVERRIDE {
        switch (event->type()) {
        case QEvent::GraphicsSceneMousePress:
            mousePressEvent(static_cast<QGraphicsSceneMouseEvent *>(event));
            break;
        case QEvent::GraphicsSceneMouseRelease:
            mouseReleaseEvent(static_cast<QGraphicsSceneMouseEvent *>(event));
            break;
        case QEvent::GraphicsSceneMouseMove:
            mouseMoveEvent(static_cast<QGraphicsSceneMouseEvent *>(event));
            break;
        case QEvent::KeyPress:
            keyPressEvent(static_cast<QKeyEvent *>(event));
            break;
        case QEvent::KeyRelease:
            keyReleaseEvent(static_cast<QKeyEvent *>(event));
            break;
        default:
            break;
        }
        event->accept();
        return true;
    }
};

顯然,大多數情況下,正確的姿勢應該選擇方式一,因爲對我們來說更簡單,方式二則需要爲每一個自定義 item 都去實現 sceneEvent()。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章