使用QKeyEvent keyPress(QEvent::KeyPress, key模拟键盘发送事件,会导致主程序卡死

原因:只有按下,没有弹起事件,导致系统以为,该按键一直是按下的,就比如,实体键盘一样,长按,会一直打字下去,qt我发现并没有cliked事件,只有自己手动拼接一个按下抬起操作,不然会导致内存遗漏,主线程运行内存满了,导致卡死,并非死循环,而且内存满了

本人遇到的问题及,解决问题,原本以为qt按键事件会自动处理 自动弹起 自动销毁信号,后面发现,发送事件,是往底层,硬件区内存写事件

用 `QKeyEvent` 发送键盘事件时,如果处理不当确实可能导致程序卡死或事件循环问题。以下是几种解决方案:

方法1:使用 `postEvent` 替代 `sendEvent`

```cpp

#include <QCoreApplication>

#include <QKeyEvent>

#include <QQuickWindow>

#include <QQuickItem>

class SafeKeyEventSender : public QObject

{

Q_OBJECT

public:

explicit SafeKeyEventSender(QObject *parent = nullptr) : QObject(parent) {}

// 安全发送Tab键事件 - 使用postEvent异步发送

void sendTabKeySafely(QQuickWindow *window)

{

if (!window) return;

// 异步发送按键事件,避免阻塞

QKeyEvent *pressEvent = new QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::NoModifier);

QKeyEvent *releaseEvent = new QKeyEvent(QEvent::KeyRelease, Qt::Key_Tab, Qt::NoModifier);

QCoreApplication::postEvent(window, pressEvent);

// 添加小延迟再发送释放事件,模拟真实按键

QTimer::singleShot(10, window, releaseEvent() {

QCoreApplication::postEvent(window, releaseEvent);

});

}

// 发送到特定焦点项

void sendTabToFocusItem(QQuickWindow *window)

{

if (!window) return;

QQuickItem *focusItem = window->activeFocusItem();

if (!focusItem) {

// 如果没有焦点项,发送到窗口

sendTabKeySafely(window);

return;

}

// 异步发送到焦点项

QKeyEvent *pressEvent = new QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::NoModifier);

QKeyEvent *releaseEvent = new QKeyEvent(QEvent::KeyRelease, Qt::Key_Tab, Qt::NoModifier);

QCoreApplication::postEvent(focusItem, pressEvent);

QTimer::singleShot(10, focusItem, releaseEvent() {

QCoreApplication::postEvent(focusItem, releaseEvent);

});

}

};

```

方法2:使用QMetaObject::invokeMethod异步调用

```cpp

class AsyncKeySender : public QObject

{

Q_OBJECT

public:

explicit AsyncKeySender(QObject *parent = nullptr) : QObject(parent) {}

Q_INVOKABLE void sendTabAsync()

{

// 在QML线程中安全执行

QQuickWindow *window = findMainWindow();

if (window) {

sendTabKeySafely(window);

}

}

Q_INVOKABLE void sendKeyAsync(int key, int modifiers = 0)

{

QMetaObject::invokeMethod(this, "delayedKeySend",

Qt::QueuedConnection,

Q_ARG(int, key),

Q_ARG(int, modifiers));

}

private slots:

void delayedKeySend(int key, int modifiers)

{

QQuickWindow *window = findMainWindow();

if (!window) return;

Qt::KeyboardModifiers qtModifiers = static_cast<Qt::KeyboardModifiers>(modifiers);

QKeyEvent *pressEvent = new QKeyEvent(QEvent::KeyPress, key, qtModifiers);

QKeyEvent *releaseEvent = new QKeyEvent(QEvent::KeyRelease, key, qtModifiers);

QCoreApplication::postEvent(window, pressEvent);

QTimer::singleShot(20, window, releaseEvent() {

QCoreApplication::postEvent(window, releaseEvent);

});

}

private:

QQuickWindow* findMainWindow()

{

foreach (QWindow *window, QGuiApplication::allWindows()) {

if (QQuickWindow *quickWindow = qobject_cast<QQuickWindow*>(window)) {

return quickWindow;

}

}

return nullptr;

}

void sendTabKeySafely(QQuickWindow *window)

{

// 同方法1的实现

QKeyEvent *pressEvent = new QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::NoModifier);

QKeyEvent *releaseEvent = new QKeyEvent(QEvent::KeyRelease, Qt::Key_Tab, Qt::NoModifier);

QCoreApplication::postEvent(window, pressEvent);

QTimer::singleShot(10, window, releaseEvent() {

QCoreApplication::postEvent(window, releaseEvent);

});

}

};

```

方法3:使用QML Shortcut(推荐 - 避免原生事件)

```qml

// 在QML中直接使用Shortcut,避免C++事件问题

Item {

id: keySimulator

// 模拟Tab键的快捷键

Shortcut {

sequence: "Ctrl+Alt+T" // 使用不冲突的组合键来模拟

onActivated: {

console.log("Simulating Tab key press")

simulateTabNavigation()

}

context: Qt.ApplicationShortcut

}

function simulateTabNavigation() {

// 直接操作焦点,而不是发送键盘事件

if (gridView.focusedIndex !== undefined) {

var nextIndex = gridView.focusedIndex + 1

if (nextIndex >= gridView.count) nextIndex = 0

gridView.focusedIndex = nextIndex

}

}

// 暴露给C++调用

function triggerTabSimulation() {

simulateTabNavigation()

}

}

```

在C++中调用:

```cpp

// 在C++中通过调用QML函数来模拟,而不是发送键盘事件

QObject *qmlRoot = engine.rootObjects().first();

QMetaObject::invokeMethod(qmlRoot, "triggerTabSimulation");

```

方法4:使用事件过滤器避免递归

```cpp

class NonBlockingKeySender : public QObject

{

Q_OBJECT

public:

explicit NonBlockingKeySender(QObject *parent = nullptr) : QObject(parent), m_isSending(false) {}

void sendKeyWithoutBlock(int key, Qt::KeyboardModifiers modifiers = Qt::NoModifier)

{

if (m_isSending) {

qDebug() << "Key sending in progress, skipping";

return; // 防止递归

}

m_isSending = true;

QQuickWindow *window = findMainWindow();

if (!window) {

m_isSending = false;

return;

}

// 使用单次定时器确保不会阻塞

QTimer::singleShot(0, this, window, key, modifiers() {

QKeyEvent *pressEvent = new QKeyEvent(QEvent::KeyPress, key, modifiers);

QKeyEvent *releaseEvent = new QKeyEvent(QEvent::KeyRelease, key, modifiers);

QCoreApplication::postEvent(window, pressEvent);

QTimer::singleShot(20, this, window, releaseEvent() {

QCoreApplication::postEvent(window, releaseEvent);

m_isSending = false; // 重置状态

});

});

}

private:

bool m_isSending;

QQuickWindow* findMainWindow()

{

foreach (QWindow *window, QGuiApplication::allWindows()) {

if (QQuickWindow *quickWindow = qobject_cast<QQuickWindow*>(window)) {

return quickWindow;

}

}

return nullptr;

}

};

```

方法5:完整的焦点管理解决方案(推荐)

与其发送键盘事件,不如直接管理焦点:

```cpp

class FocusManager : public QObject

{

Q_OBJECT

public:

explicit FocusManager(QQmlApplicationEngine *engine, QObject *parent = nullptr)

: QObject(parent), m_engine(engine) {}

Q_INVOKABLE void moveFocusNext()

{

QObject *root = m_engine->rootObjects().first();

if (!root) return;

// 调用QML中的焦点移动函数

QMetaObject::invokeMethod(root, "moveFocusToNextItem",

Qt::QueuedConnection);

}

Q_INVOKABLE void moveFocusPrevious()

{

QObject *root = m_engine->rootObjects().first();

if (!root) return;

QMetaObject::invokeMethod(root, "moveFocusToPreviousItem",

Qt::QueuedConnection);

}

private:

QQmlApplicationEngine *m_engine;

};

```

在QML中实现焦点管理:

```qml

// Main.qml

ApplicationWindow {

id: mainWindow

// ... 其他属性 ...

property int currentFocusIndex: 0

property var focusableItems: \[\]

function moveFocusToNextItem() {

if (focusableItems.length === 0) return;

currentFocusIndex = (currentFocusIndex + 1) % focusableItems.length

var nextItem = focusableItemscurrentFocusIndex

if (nextItem) {

nextItem.forceActiveFocus()

}

}

function moveFocusToPreviousItem() {

if (focusableItems.length === 0) return;

currentFocusIndex = currentFocusIndex - 1

if (currentFocusIndex < 0) currentFocusIndex = focusableItems.length - 1

var prevItem = focusableItemscurrentFocusIndex

if (prevItem) {

prevItem.forceActiveFocus()

}

}

Component.onCompleted: {

// 收集所有可聚焦的项目

collectFocusableItems()

}

function collectFocusableItems() {

focusableItems = \[\]

// 遍历查找所有可聚焦的项目

// 这里需要根据你的实际UI结构来调整

findFocusableItems(mainWindow.contentItem)

if (focusableItems.length > 0) {

focusableItems0.forceActiveFocus()

}

}

function findFocusableItems(item) {

if (!item) return

// 检查这个项目是否可聚焦

if (item.activeFocusOnTab !== undefined && item.activeFocusOnTab) {

focusableItems.push(item)

}

// 递归检查子项目

if (item.children) {

for (var i = 0; i < item.children.length; i++) {

findFocusableItems(item.childreni)

}

}

}

}

```

总结与建议

  1. **首选方法3或方法5**:避免使用原生键盘事件,而是直接在QML层面管理焦点

  2. **如果必须使用键盘事件**:

  • 使用 `postEvent` 而不是 `sendEvent`

  • 添加适当的延迟

  • 使用标志位防止递归

  1. **调试技巧**:

```cpp

// 在发送事件前检查事件循环状态

if (QCoreApplication::hasPendingEvents()) {

qDebug() << "有未处理的事件,可能发生阻塞";

}

```

  1. **性能考虑**:频繁发送键盘事件会影响性能,建议使用直接焦点管理

使用这些方法应该能解决您遇到的程序卡死问题。推荐使用QML层面的焦点管理方案,这样更稳定且性能更好。

相关推荐
用户805533698032 天前
不止三件套:QObject 属性系统全关键字与运行时反射!
c++·qt
xcyxiner2 天前
DicomViewer (vcpkg Windows和ubuntu编译)7
qt
Quz7 天前
QML Hello World 入门示例
qt
xcyxiner10 天前
DicomViewer (dcmtk读取dcm文件)5
qt
xcyxiner11 天前
DicomViewer (后台线程处理文件)4
qt
xcyxiner11 天前
DicomViewer (添加模型类)3
qt
xcyxiner12 天前
DicomViewer (目录调整) 2
qt
xcyxiner12 天前
dcmtk vtk vtk-dicom(gdcm) 编译(debug) v2
qt
u1521096484914 天前
S.S.Audio PRO A2音频隔离器
嵌入式硬件·音视频·实时音视频·视频编解码·视频
桥田智能14 天前
桥田智能 QT-650S:面向白车身焊装的 800kg 重载快换解决方案
开发语言·qt·系统架构