You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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();
}

原因分析
  1. 焦点归属问题:通过addWidget将QLabel子类嵌入QGraphicsScene后,界面焦点默认落在QGraphicsView上,而非嵌入的Widget。Tab键能触发事件是因为Tab会在界面元素间切换焦点,此时PacMan才会获得焦点,进而响应按键。
  2. 构造函数初始化错误:type(type)属于自初始化,是未定义行为,会导致成员变量值异常。
  3. 按键事件判断的跨平台问题:使用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,提问作者Денис Трофимов

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.16 08:45:29