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