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

时间:2018-09-13 12:18:34

标签: c++ windows qt

目的是从另一个应用程序中选择并单击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);
}

我认为这不是正确的方法。我确认我在调试器中执行了这个插槽,但是什么也没发生,就像事件进入了无限的虚无...

感谢任何帮助(方法或想法)

1 个答案:

答案 0 :(得分:1)

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


出于好奇,我玩了一点。

我的接收器是一个QLineEdit,并且是唯一的一个小部件–启动后将获得焦点。

但是,我没有使它工作。我有其他想法:

  • 对称地发送QKeyPressQKeyRelease
  • QString中提供OP忽略的QKeyEvent参数

但这没有帮助。

最后,由于有点担心直接发送事件(除了应用程序事件循环),我用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


修改后的示例包含两个QLineEdit(其中至少一个绝对不是焦点):

#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)

即使没有重点关注的QLineEdit,如果直接发布到关键事件上,也似乎可以正确处理。 (我不确定这种行为是否取决于Windows系统。就我而言,这是我在cygwin中测试的X11。)


正如OP明确提到的QPushButton一样,我再次修改了示例:

#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

我尝试了有无焦点切换。 (在我EXCLUDED Tab 事件之前,我可以看到焦点发生了变化。)但是,与QLineEdit中的 Space 键事件一样,在QPushButton中处理,与焦点无关。

最后一个示例代码版本,我在VS2013中使用Qt 5.9.2再次编译(对于本机WinAPI)。它的表现完全像我在cygwin中用g++和Qt 5.9.4 for X11编译的那样。 即将 Space 键事件发送到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插入的数字显然忽略了Qt::Key_A + i