从Qt应用程序内部模拟TAB或SPACE按键

问题描述 投票:0回答:2

目的是从第二个应用程序中选择并单击Qt应用程序的小部件(就像用户可以使用TAB和SPACE键击一样)。这两个应用程序都在同一个网络上并通过QTcpSocket的方式进行“对话”,但这不是问题所在。因此,2nb应用程序可以看作应该控制第一个应用程序的3个按钮面板(TAB,shift TAB和SPACE)。第一个应用程序也可以像任何其他Qt应用程序一样直接使用。从第一个应用程序的内部,并收到一个充分的消息,我试图发送一个按键事件,但没有任何反应:

void Appli1::onAppli2_TabClickedMessage()
{
    QKeyEvent keyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::NoModifier);
    QCoreApplication::sendEvent((QObject*)appli1.MainWindow(), &keyEvent);
}

我想这不是正确的做法。我验证了我在调试器中执行这个插槽但没有任何反应,就像事件进入无限空洞一样......

任何帮助(方法或想法)赞赏

c++ windows qt
2个回答
1
投票

不可思议地提到,OP打算使用UIAutomation的正确方法。 (当然,这是假设OP在MS Windows上开发,但对于其他操作系统/ Windows系统肯定有类似的解决方案。)


出于好奇,我玩了一下。

我的接收器是一个QLineEdit和唯一的小部件 - 它在开始后获得焦点。

但是,我没有让它发挥作用。我有其他想法:

  • 对称地发送QKeyPressQKeyRelease
  • QString中提供QKeyEvent论证,其中OP被排除在外

但这没有帮助。

最后,我用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();
}

testQSendEvent.pro

SOURCES = testQSendEvent.cc

QT = widgets

cygwin64编译和测试:

$ qmake-qt5 testQSendEvent.pro

$ make

$ ./testQSendEvent
Qt Version: 5.9.4
About to send key event for 0
About to send key event for 1
About to send key event for 2
About to send key event for 3
About to send key event for 4
About to send key event for 5
About to send key event for 6
About to send key event for 7
About to send key event for 8
About to send key event for 9
About to send key event for 0
About to send key event for 1
About to send key event for 2
About to send key event for 3
About to send key event for 4
About to send key event for 5

snapshot of testQSendEvent


修改过的样本有两个QLineEdits(其中至少有一个肯定不是焦点):

#include <QtWidgets>

int main(int argc, char **argv)
{
  qDebug() << "Qt Version:" << QT_VERSION_STR;
  QApplication app(argc, argv);
  QWidget qWin;
  QVBoxLayout qVBox;
  QLineEdit qEdt1, qEdt2;
  qVBox.addWidget(&qEdt1);
  qVBox.addWidget(&qEdt2);
  qWin.setLayout(&qVBox);
  qWin.show();
  QTimer qTimer;
  qTimer.setInterval(1000/*ms*/);
  qTimer.start();
  int i = 0;
  QObject::connect(&qTimer, &QTimer::timeout,
    [&]() {
      QApplication::postEvent(&qEdt1,
        new QKeyEvent(QEvent::KeyPress, Qt::Key_0 + i, Qt::NoModifier,
        QString(QChar('0' + i))));
      QApplication::postEvent(&qEdt1,
        new QKeyEvent(QEvent::KeyRelease, Qt::Key_0 + i, Qt::NoModifier,
        QString(QChar('0' + i))));
      QApplication::postEvent(&qEdt2,
        new QKeyEvent(QEvent::KeyPress, Qt::Key_A + i, Qt::NoModifier,
        QString(QChar('A' + i))));
      QApplication::postEvent(&qEdt2,
        new QKeyEvent(QEvent::KeyRelease, Qt::Key_A + i, Qt::NoModifier,
        QString(QChar('A' + i))));
      if (++i >= 10) i = 0;
    });
  return app.exec();
}

snapshot of testQSendEvent (2nd version)

即使是没有焦点的QLineEdits,如果直接发布给他们,似乎也能正确处理关键事件。 (我不确定这种行为是否取决于Windows系统。在我的情况下,它是我在cygwin中测试的X11。)


当OP明确提到QPushButtons时,我再次调整了我的样本:

#include <QtWidgets>

int main(int argc, char **argv)
{
  qDebug() << "Qt Version:" << QT_VERSION_STR;
  QApplication app(argc, argv);
  QWidget qWin;
  QVBoxLayout qVBox;
  QPushButton qBtn1("Button 1");
  QPushButton qBtn2("Button 2");
  qVBox.addWidget(&qBtn1);
  qVBox.addWidget(&qBtn2);
  qWin.setLayout(&qVBox);
  qWin.show();
  QTimer qTimer;
  qTimer.setInterval(1000/*ms*/);
  qTimer.start();
  int i = 0;
  QObject::connect(&qTimer, &QTimer::timeout,
    [&]() {
      switch (i) {
        case 0:
          QApplication::postEvent(&qBtn1,
            new QKeyEvent(QEvent::KeyPress, Qt::Key_Space, Qt::NoModifier,
              QString(QChar(' '))));
          break;
        case 1:
          QApplication::postEvent(&qBtn1,
            new QKeyEvent(QEvent::KeyRelease, Qt::Key_Space, Qt::NoModifier,
              QString(QChar(' '))));
          break;
        case 2:
#if 0 // EXCLUDED
          QApplication::postEvent(&qBtn1,
            new QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::NoModifier,
              QString(QChar('\t'))));
          QApplication::postEvent(&qBtn1,
            new QKeyEvent(QEvent::KeyRelease, Qt::Key_Tab, Qt::NoModifier,
              QString(QChar('\t'))));
#endif // 0
          break;
        case 3:
          QApplication::postEvent(&qBtn2,
            new QKeyEvent(QEvent::KeyPress, Qt::Key_Space, Qt::NoModifier,
              QString(QChar(' '))));
          break;
        case 4:
          QApplication::postEvent(&qBtn2,
            new QKeyEvent(QEvent::KeyRelease, Qt::Key_Space, Qt::NoModifier,
              QString(QChar(' '))));
          break;
        case 5:
#if 0 // EXCLUDED
          QApplication::postEvent(&qBtn2,
            new QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::NoModifier,
              QString(QChar('\t'))));
          QApplication::postEvent(&qBtn2,
            new QKeyEvent(QEvent::KeyRelease, Qt::Key_Tab, Qt::NoModifier,
              QString(QChar('\t'))));
#endif // 0
          break;
      }
      if (++i > 5) i = 0;
    });
  QObject::connect(&qBtn1, &QPushButton::clicked,
    [&](bool) { qDebug() << "Button 1 clicked"; });
  QObject::connect(&qBtn2, &QPushButton::clicked,
    [&](bool) { qDebug() << "Button 2 clicked"; });
  return app.exec();
}

snapshot of testQSendEvent (3rd version)

甚至发出了QPushButton::clicked信号:

$ ./testQSendEvent
Qt Version: 5.9.4
Button 1 clicked
Button 2 clicked
Button 1 clicked
Button 2 clicked
Button 1 clicked
Button 2 clicked
Button 1 clicked

我尝试了无焦点改变。 (在我发布Tab事件之前,我可以看到焦点发生变化。)然而,与EXCLUDED一样,空间键事件在QLineEdit中独立于焦点处理。

最后一个示例代码版本,我在VS2013中使用Qt 5.9.2再次编译(对于本机WinAPI)。它的行为与我在cygwin中使用QPushButton和Qt 5.9.4编译的X11完全相同。即将空间键事件发送到g++改变了它的视觉外观,并且正确发射了QPushButton信号。

QPushButton::clicked


我“意外地”认为应该正确设置snapshot of testQEvent (3rd version) compiled with Qt/WinAPI的第四个QString论点。我发布时发现了这个

QKeyEvent

接收 QKeyEvent(QEvent::KeyPress, Qt::Key_A + i, Qt::NoModifier, QString(QChar('0' + i))); 插入数字显然忽略QLineEdit


0
投票

模拟按键似乎是不必要的。接收器处理三个命令:

  • 如果主窗口处于非活动状态,则会激活它,如果没有窗口小部件具有焦点,则会明确关注获取它的第一个窗口小部件,并忽略该命令。
  • Qt::Key_A + i:将焦点移动到焦点链中的下一个项目(如Tab所示)
  • NEXT_FOCUS:将焦点移动到焦点链中的上一个项目(如Shift-Tab所示)
  • PREVIOUS_FOCUS:调用焦点小部件的CLICK方法 - 所有Qt的按钮都支持它,因为它在它们的基础click()类中。

因此,像下面这样的东西应该可以做到这一点,你不需要模仿键盘 - 特别是你不能保证你提到的特定键具有你期望的效果 - 行为是特定于平台的,并且可能甚至是主题特定的。例如。在OS X上,原生行为是因为空格键没有效果,因为按钮不可聚焦。

QAbstractButton
© www.soinside.com 2019 - 2024. All rights reserved.