簡述
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()。