4

我想对我的 Qt 应用程序进行基本的自动化测试。它记录鼠标事件并将它们写入文件 (fe mousepress(300, 400))。启动自动化时,它从文件中读取坐标,发送适当的鼠标事件,并与之前保存的屏幕截图进行像素比较。

目前,我有一个覆盖应用程序并具有透明鼠标事件的覆盖小部件。它所做的只是跟踪坐标。当重新读取数据时,该覆盖在鼠标按下位置绘制一个矩形。在将 mousePressEvents 发送到 Qt 的事件系统时,我需要帮助。它在正确的位置绘制点,但实际上从不进行物理点击。有没有办法用 Qt 做到这一点,还是我必须使用 Window 的 SendInput()?

有没有办法暂停并等待鼠标事件完成?我需要知道事件何时完成才能开始逐像素比较。

Widget::Widget( QWidget *parent )
: QFrame( parent )
, mPoint(QPoint(0,0))
{
   setWindowFlags(Qt::WindowStaysOnTopHint);
   setStyleSheet("background-color: rgba(0, 0,255, 2%);");
   setAttribute(Qt::WA_TransparentForMouseEvents, true);
   setGeometry(parent->geometry());
   ...
}

void Widget::run()
{
   QFile file( "coordinates.txt", NULL );
   if(!file.open( QIODevice::ReadOnly ))
       return;

   QTextStream in(&file);
   int i = 0;
   while (!in.atEnd())
   {
       QString line = in.readLine();
       if(line.startsWith("mousepress"))
       {
          int startIndex = line.indexOf('(');
          int endIndex = line.indexOf(')');

          QString coord = line.mid(startIndex+1, line.size() - startIndex - 2);
          QStringList nbr = coord.split(',');
          mPoint = QPoint(nbr[0].toInt(), nbr[1].toInt());
          QWidget *receiver  = QApplication::widgetAt(mPoint);
          QMouseEvent *event = new QMouseEvent(QEvent::MouseButtonPress, mPoint, Qt::LeftButton, Qt::LeftButton,  Qt::NoModifier);
          QCoreApplication::postEvent(receiver, event); // same result with sendEvent() 
          QCoreApplication::processEvents();
          update();
          // wait till the event finished, then continue with next point
      }
   }
}


void Widget::paintEvent(QPaintEvent *event)
{
  QPainter p( this );
  QPen pen;
  pen.setBrush(Qt::NoBrush);

  if(!mPoint.isNull())
  {
    pen.setColor(Qt::red);
    pen.setWidth( 2 );
    p.setPen(pen);

    p.drawRoundRect(mPoint.x(), mPoint.y(), 10, 10, 25, 25);
    p.drawText(mPoint, QString::number(mPoint.x()) + ", " + QString::number(mPoint.y()));
  }
}

[已编辑]

我遵循了 ddriver 的建议,在进行了一些更改后它可以工作:我将全局和本地位置保存在文件中,以发送到 QMouseEvent。

在进行屏幕截图并将其与保存的图像进行比较之前,我如何确定鼠标单击已完成?

void Widget::DoStep()
{
  if(!mInStream.atEnd())
  {
      QString line = mInStream.readLine();
      if(line.startsWith("MouseButtonPress"))
      {
          QPoint pos = parseGlobalPos();
          QPoint localPos = parseLocalPos();
          QWidget *receiver  = QApplication::widgetAt(pos);

          QMouseEvent *event = new QMouseEvent(QEvent::MouseButtonPress,localPos, pos, Qt::LeftButton, Qt::LeftButton,  Qt::NoModifier);
          QApplication::postEvent(receiver, event);
          QMouseEvent *eventRelease = new QMouseEvent(QEvent::MouseButtonRelease, localPos, pos, Qt::LeftButton, Qt::LeftButton,  Qt::NoModifier);
          QApplication::postEvent(receiver, eventRelease);

          // after successful click, take screenshot and compare them
      } 
   }

  if (!mInStream.atEnd())
      QMetaObject::invokeMethod(this, "DoStep", Qt::QueuedConnection);
  else
      file.close();
}
4

2 回答 2

2

如果我正确理解了这个问题,它的来源是阻塞while循环,它阻塞线程并且不允许事件循环旋转和处理事件。没有办法“暂停”,因为这也会阻塞事件循环,并且它也无法完成工作,但是有一种方法可以将工作拆分为一次完成一个步骤,一个事件循环一次循环。

解决方案是不使用阻塞while循环,而是实现事件系统驱动循环。然后你在每个事件循环周期处理一行,直到你用完工作。

将文件、流和所有这些东西移到函数之外,使它们成为类成员,以便它们持久存在。然后,run()您只需设置输入以进行读取,并将所有事件发布内容移动到新功能,例如doStep(),在run()您设置后,您将拥有:

QMetaObject::invokeMethod(this, "doStep", Qt::QueuedConnection);

doStep(),除了事件的东西,最后你还做调度:

if (!in.atEnd()) QMetaObject::invokeMethod(this, "doStep", Qt::QueuedConnection);
else // we are done, close file, cleanup, whatever

这样,每个事件循环周期只会执行一个步骤,从而允许事件循环旋转并处理事件。虽然有工作,但将为下一个事件循环周期安排一个步骤。您还可以使用单次计时器来执行此操作,甚至可以使用排队连接,您可以通过发出重载事件来触发完成,从而在完成时发出信号。您不会也不应该强制处理事件,使用这种方法就没有必要了。

总的来说,这个概念是关于一个非阻塞异步事件驱动的自调度工作者,我在这里发布了一个完全实现的。好处是您可以跟踪进度、暂停、取消和所有这些好东西。

于 2016-03-09T22:27:58.127 回答
-1

尝试将事件发送到QGraphicsView的视口:

qApp->sendEvent(view->viewport(), &mousePressEvent);
于 2016-03-03T17:37:57.003 回答