Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/150.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 从Qt应用程序内部模拟TAB键或空格键_C++_Windows_Qt - Fatal编程技术网

C++ 从Qt应用程序内部模拟TAB键或空格键

C++ 从Qt应用程序内部模拟TAB键或空格键,c++,windows,qt,C++,Windows,Qt,其目的是从第二个应用程序中选择并单击Qt应用程序的小部件(就像用户可以通过制表符和空格键进行操作一样)。这两个应用程序都在同一个网络上,通过QTcpSocket进行“对话”,但这不是问题所在。 因此,2nb应用程序可以看作是一个3个按钮面板(TAB、shift TAB和SPACE),应该控制第一个应用程序。此第一个应用程序也可以像任何其他Qt应用程序一样直接使用。 从第一个应用程序的内部,在收到adequat消息时,我试图发送一个按键事件,但什么也没有发生: void Appli1::onApp

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

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
  • QKeyEvent
    中提供OP遗漏的
    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
    :将焦点移动到焦点链中的下一个项目(如选项卡所示)

  • PREVIOUS\u FOCUS
    :将焦点移动到焦点链中的上一个项目(如Shift选项卡所示)

  • CLICK
    :调用聚焦小部件的
    CLICK()
    方法-所有Qt的按钮都支持它,因为它在它们的基本
    QAbstractButton
    类中

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

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
   }
}