C++ 从Qt应用程序内部模拟TAB键或空格键
其目的是从第二个应用程序中选择并单击Qt应用程序的小部件(就像用户可以通过制表符和空格键进行操作一样)。这两个应用程序都在同一个网络上,通过QTcpSocket进行“对话”,但这不是问题所在。 因此,2nb应用程序可以看作是一个3个按钮面板(TAB、shift TAB和SPACE),应该控制第一个应用程序。此第一个应用程序也可以像任何其他Qt应用程序一样直接使用。 从第一个应用程序的内部,在收到adequat消息时,我试图发送一个按键事件,但什么也没有发生:C++ 从Qt应用程序内部模拟TAB键或空格键,c++,windows,qt,C++,Windows,Qt,其目的是从第二个应用程序中选择并单击Qt应用程序的小部件(就像用户可以通过制表符和空格键进行操作一样)。这两个应用程序都在同一个网络上,通过QTcpSocket进行“对话”,但这不是问题所在。 因此,2nb应用程序可以看作是一个3个按钮面板(TAB、shift TAB和SPACE),应该控制第一个应用程序。此第一个应用程序也可以像任何其他Qt应用程序一样直接使用。 从第一个应用程序的内部,在收到adequat消息时,我试图发送一个按键事件,但什么也没有发生: void Appli1::onApp
void Appli1::onAppli2_TabClickedMessage()
{
QKeyEvent keyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::NoModifier);
QCoreApplication::sendEvent((QObject*)appli1.MainWindow(), &keyEvent);
}
我想这不是正确的做法。我验证了我在调试器中执行了这个插槽,但是什么都没有发生,就像事件进入无限空间一样
任何受到赞赏的帮助(方法或想法)IInspectable提到OP想要的正确方法是使用。(当然,这是假设OP是在MS Windows上开发的,但其他OS/Windows系统肯定也有类似的解决方案。)
出于好奇,我玩了一会儿 我的接收器是一个
QLineEdit
并且是唯一的小部件–它在启动后获得焦点
然而,我没有让它工作。我还有其他想法:
- 对称发送
和QKeyPress
QKeyRelease
- 在
中提供OP遗漏的QKeyEvent
参数QString
sendEvent()
替换为postEvent()
,因为我有点担心直接发送事件(应用程序事件循环除外)。令人惊讶的是,这是有效的
testQSendEvent.cc
:
#include <QtWidgets>
int main(int argc, char **argv)
{
qDebug() << "Qt Version:" << QT_VERSION_STR;
QApplication app(argc, argv);
QLineEdit qEdt;
qEdt.show();
QTimer qTimer;
qTimer.setInterval(1000/*ms*/);
qTimer.start();
int i = 0;
QObject::connect(&qTimer, &QTimer::timeout,
[&]() {
qDebug() << "About to send key event for" << i;
#if 0 // 1st attempt
{ QKeyEvent keyEvent(QEvent::KeyPress, Qt::Key_0 + i, Qt::NoModifier,
QString(QChar('0' + i)));
QApplication::sendEvent(&qEdt, &keyEvent);
}
{ QKeyEvent keyEvent(QEvent::KeyRelease, Qt::Key_0 + i, Qt::NoModifier,
QString(QChar('0' + i)));
QApplication::sendEvent(&qEdt, &keyEvent);
}
#else // 2nd attempt
QApplication::postEvent(&qEdt,
new QKeyEvent(QEvent::KeyPress, Qt::Key_0 + i, Qt::NoModifier,
QString(QChar('0' + i))));
QApplication::postEvent(&qEdt,
new QKeyEvent(QEvent::KeyRelease, Qt::Key_0 + i, Qt::NoModifier,
QString(QChar('0' + i))));
#endif // 0
if (++i >= 10) i = 0;
});
return app.exec();
}
编译和测试于:
$qmake-qt5 testQSendEvent.pro
$make
$./testQSendEvent
Qt版本:5.9.4
即将发送0的密钥事件
即将发送1的密钥事件
即将发送2的密钥事件
即将发送3的关键事件
即将发送4的关键事件
即将发送5的关键事件
即将发送6的关键事件
即将发送7的关键事件
即将发送8的关键事件
即将发送9的关键事件
即将发送0的密钥事件
即将发送1的密钥事件
即将发送2的密钥事件
即将发送3的关键事件
即将发送4的关键事件
即将发送5的关键事件
带有两个
QLineEdit
s的修改样本(其中至少有一个没有焦点):
甚至发出了QPushButton::clicked
信号:
$。/testQSendEvent
Qt版本:5.9.4
单击按钮1
点击按钮2
单击按钮1
点击按钮2
单击按钮1
点击按钮2
单击按钮1
我试过有焦点改变和没有焦点改变。(在我排除选项卡事件之前,我可以看到焦点发生了变化。)但是,在QLineEdit
中,空格键事件在QPushButton
中独立于焦点进行处理
最后一个示例代码版本,我使用Qt5.9.2(用于本机WinAPI)在VS2013中再次编译。它的行为与我在cygwin中使用g++
和针对X11的Qt5.9.4编译的完全相同。
即,将空格键事件发送到QPushButton
改变了其视觉外观,并且QPushButton::clicked
信号正确发出
我“偶然”观察到
QKeyEvent
的第四个QString
参数应该正确设置。我在发帖时发现了这一点
QKeyEvent(QEvent::KeyPress, Qt::Key_A + i, Qt::NoModifier,
QString(QChar('0' + i)));
接收
QLineEdit
插入的数字显然忽略了Qt::Key_A+i
模拟按键似乎没有必要。接收器处理三个命令:
- 如果主窗口处于非活动状态,它将被激活,如果没有小部件具有焦点,则第一个获得它的小部件将显式聚焦,并且忽略该命令
:将焦点移动到焦点链中的下一个项目(如选项卡所示)NEXT\u FOCUS
:将焦点移动到焦点链中的上一个项目(如Shift选项卡所示)PREVIOUS\u FOCUS
:调用聚焦小部件的CLICK
方法-所有Qt的按钮都支持它,因为它在它们的基本CLICK()
类中QAbstractButton
enum Command {
NEXT_FOCUS, // focus next clickable widget
PREVIOUS_FOCUS, // focus previous clickable widget
FOCUS, // focus current clickable widget, or next one if none
CLICK // like FOCUS, but also clicks the widget
};
bool executeCommand(QWidget *window, Command command) {
Q_ASSERT(window && window->isWindow());
using Method = bool (QWidget::*)(bool);
struct Helper : QWidget {
static Method get_focusNextPrevChild() { return &Helper::focusNextPrevChild; }
};
static Method const focusNextPrevChild = Helper::get_focusNextPrevChild();
if (!window->isActiveWindow()) {
window->activateWindow();
command = FOCUS;
}
for (QWidget *focused = nullptr;;) {
if (command == NEXT_FOCUS) {
if (!(window->*focusNextPrevChild)(true)) return false;
} else if (command == PREVIOUS_FOCUS) {
if (!(window->*focusNextPrevChild)(false)) return false;
}
auto *newFocused = window->focusWidget();
if (!newFocused) return false;
if (focused && focused == newFocused)
return false; // no eligible widgets found
else if (!focused)
focused = newFocused;
auto *newMetaObject = newFocused->metaObject();
int const clickIdx = newMetaObject->indexOfMethod("click()");
if (clickIdx >= 0)
return command == FOCUS || newMetaObject->method(clickIdx).invoke(focused);
if (command != NEXT_FOCUS && command != PREVIOUS_FOCUS)
command = NEXT_FOCUS; // iterate over widgets till a clickable one is found
}
}
您拥有的代码应该可以工作。唯一看起来奇怪的是这个cast
(QObject*)appli1.MainWindow()
,应该不需要它。此外,一般不应该在C++中使用C风格的CAST。我只是看了一下。你的样品看起来是对的。您认为MainWindow()
将如何处理此关键事件?尝试将“可打印”键发送到,例如QLineEdit
。但是,如果没有焦点,它会对按键做出什么反应?(我从未尝试过这个。)可能是,使用重载的keyPressEvent()
从QWidget
派生一个小部件,将其用作接收器。因此,您可以很容易地确认发送的密钥事件已经到达。您不能像那样伪造输入,特别是对于以输入处理不可靠著称的Qt应用程序。尝试使用UI自动化,毕竟这就是它的用途。@Jaa-c谢谢。。。你是对的,它工作:-)我构建了一个小应用程序,它工作了。。。我只需插入一个appli->processEvents()即可使其可见。主题已关闭为什么不将此作为自答发送?顺便说一句,我通过切换到postEvent
解决了您的问题。我有几个小部件,我使用TAB按键事件从一个小部件移动到下一个小部件(shift-TAB按相反顺序),但是。。。空格键不启动按钮,但
QKeyEvent(QEvent::KeyPress, Qt::Key_A + i, Qt::NoModifier,
QString(QChar('0' + i)));
enum Command {
NEXT_FOCUS, // focus next clickable widget
PREVIOUS_FOCUS, // focus previous clickable widget
FOCUS, // focus current clickable widget, or next one if none
CLICK // like FOCUS, but also clicks the widget
};
bool executeCommand(QWidget *window, Command command) {
Q_ASSERT(window && window->isWindow());
using Method = bool (QWidget::*)(bool);
struct Helper : QWidget {
static Method get_focusNextPrevChild() { return &Helper::focusNextPrevChild; }
};
static Method const focusNextPrevChild = Helper::get_focusNextPrevChild();
if (!window->isActiveWindow()) {
window->activateWindow();
command = FOCUS;
}
for (QWidget *focused = nullptr;;) {
if (command == NEXT_FOCUS) {
if (!(window->*focusNextPrevChild)(true)) return false;
} else if (command == PREVIOUS_FOCUS) {
if (!(window->*focusNextPrevChild)(false)) return false;
}
auto *newFocused = window->focusWidget();
if (!newFocused) return false;
if (focused && focused == newFocused)
return false; // no eligible widgets found
else if (!focused)
focused = newFocused;
auto *newMetaObject = newFocused->metaObject();
int const clickIdx = newMetaObject->indexOfMethod("click()");
if (clickIdx >= 0)
return command == FOCUS || newMetaObject->method(clickIdx).invoke(focused);
if (command != NEXT_FOCUS && command != PREVIOUS_FOCUS)
command = NEXT_FOCUS; // iterate over widgets till a clickable one is found
}
}