QLabel子类PacMan的keyPressEvent仅按Tab后触发问题排查与解决
问题描述
我编写了一个继承自QLabel的PacMan类,发现该类的keyPressEvent仅在按下Tab键后才会被触发,直接按下按键时无任何反应(keyPressEvent完全未被调用)。以下是相关代码实现:
头文件代码
enum ElementType { tBall, tWall, tGate, tPacman, tGhost, tWeakGhost, tPowerBall }; enum Dir { Left, Right, Up, Down, Stop }; class PacMan : public QLabel { Q_OBJECT public: explicit PacMan(); ~PacMan() = default; private: QMovie anim; Dir dir; ElementType type; int x, y; private: void keyPressEvent(QKeyEvent* event); };
cpp文件代码
PacMan::PacMan() : QLabel(), type(type), dir(Stop) { setFocus(); anim.setFileName("../Texture/startPacman.png"); anim.start(); setMovie(&anim); setFixedSize(20, 20); x = pos().x(); y = pos().y(); } void PacMan::keyPressEvent(QKeyEvent* event) { switch (event->nativeVirtualKey()) { case Qt::Key_A: dir = Left; anim.stop(); anim.setFileName("../Texture/lPacman.gif"); anim.start(); move(--x, y); break; case Qt::Key_D: dir = Right; anim.stop(); anim.setFileName("../Texture/rPacman.gif"); anim.start(); move(++x, y); break; } }
main.cpp代码
int main(int argc, char* argv[]) { QApplication app(argc, argv); QGraphicsView view(new QGraphicsScene); PacMan* move = new PacMan(); move->setFocusPolicy(Qt::StrongFocus); view.scene()->addWidget(move); view.show(); return app.exec(); }
原因分析
- 焦点归属问题:通过
addWidget将QLabel子类嵌入QGraphicsScene后,界面焦点默认落在QGraphicsView上,而非嵌入的Widget。Tab键能触发事件是因为Tab会在界面元素间切换焦点,此时PacMan才会获得焦点,进而响应按键。 - 构造函数初始化错误:
type(type)属于自初始化,是未定义行为,会导致成员变量值异常。 - 按键事件判断的跨平台问题:使用
event->nativeVirtualKey()不如event->key()稳定,部分平台可能无法正确识别按键。
解决方法
方法1:让QGraphicsView转发按键事件
通过自定义QGraphicsView,将按键事件直接转发给PacMan,无需手动切换焦点:
修改main.cpp:
#include <QKeyEvent> class CustomView : public QGraphicsView { public: CustomView(QGraphicsScene* scene, PacMan* pacman, QWidget* parent = nullptr) : QGraphicsView(scene, parent), m_pacman(pacman) {} protected: void keyPressEvent(QKeyEvent* event) override { if (m_pacman) { QCoreApplication::sendEvent(m_pacman, event); } QGraphicsView::keyPressEvent(event); } private: PacMan* m_pacman; }; int main(int argc, char* argv[]) { QApplication app(argc, argv); QGraphicsScene* scene = new QGraphicsScene; PacMan* move = new PacMan(); move->setFocusPolicy(Qt::StrongFocus); scene->addWidget(move); CustomView view(scene, move); view.show(); return app.exec(); }
同时修复PacMan构造函数的初始化错误:
PacMan::PacMan() : QLabel(), dir(Stop), type(tPacman) // 正确初始化type { setFocus(); // ... 其余代码不变 }
方法2:改用QGraphicsWidget(更贴合Qt图形框架)
放弃QLabel,继承QGraphicsWidget直接作为场景中的图形项,原生支持焦点和事件处理:
头文件修改
enum ElementType { tBall, tWall, tGate, tPacman, tGhost, tWeakGhost, tPowerBall }; enum Dir { Left, Right, Up, Down, Stop }; class PacMan : public QGraphicsWidget { Q_OBJECT public: explicit PacMan(QGraphicsItem* parent = nullptr); ~PacMan() = default; protected: void keyPressEvent(QKeyEvent* event) override; void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget = nullptr) override; private: QMovie anim; Dir dir; ElementType type; };
cpp文件修改
#include <QPainter> PacMan::PacMan(QGraphicsItem* parent) : QGraphicsWidget(parent), dir(Stop), type(tPacman) { setFlag(QGraphicsItem::ItemIsFocusable); setFocus(); anim.setFileName("../Texture/startPacman.png"); anim.start(); setFixedSize(20, 20); } void PacMan::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) { if (!anim.currentFrame().isNull()) { painter->drawPixmap(rect(), anim.currentFrame()); } } void PacMan::keyPressEvent(QKeyEvent* event) { switch (event->key()) { case Qt::Key_A: dir = Left; anim.stop(); anim.setFileName("../Texture/lPacman.gif"); anim.start(); moveBy(-1, 0); break; case Qt::Key_D: dir = Right; anim.stop(); anim.setFileName("../Texture/rPacman.gif"); anim.start(); moveBy(1, 0); break; } update(); }
main.cpp修改
int main(int argc, char* argv[]) { QApplication app(argc, argv); QGraphicsView view(new QGraphicsScene); PacMan* move = new PacMan(); view.scene()->addItem(move); view.show(); return app.exec(); }
内容的提问来源于stack exchange,提问作者Денис Трофимов
相关产品推荐
相关产品推荐

