2016-03-03 22 views
1

我想对我的Qt应用程序做一个基本的自动化测试。它记录鼠标事件并将它们写入文件(f.e. mousepress(300,400))。当开始自动化时,它从文件读取坐标,发送适当的鼠标事件,并与先前保存的屏幕截图进行像素比较。如何通过将事件发布到Qt事件系统来模拟鼠标点击?

目前,我有一个覆盖小部件,跨越应用程序,并有透明的鼠标事件。它所做的就是跟踪坐标。 重新读取数据时,该覆盖图会在鼠标按下位置绘制一个矩形。 发送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(); 
} 
+0

这可能无法完全解决这个问题,但是,您提交MouseButtonPress的事件,但你缺少一个相应的MouseButtonRelease。另外,我建议不要循环并等待事件完成,而应该为每个事件使用单个QTimer,并让主循环继续运行,而不要求助于调用processEvents。 – TheDarkKnight

+0

谢谢,我试图发布一个额外的MouseButtonRelease事件,但没有奏效。 – user2246120

+0

我不太清楚我的理解你的QTimer的想法。 QTimer :: singleShot(0,receiver,SlotSendEvent());无法将坐标传递给插槽。你是什​​么意思? – user2246120

回答

1

如果我理解正确的问题,它的来源是阻挡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 

这样只有一步将每个事件循环周期内执行,从而使事件循环旋转并处理事件。在工作的同时,将为下一个事件循环周期安排一个步骤。您也可以使用单次计时器来执行此操作,或者甚至可以通过排队连接,通过发送过载事件发出完成信号来触发。你不这样做,也不应该强迫事件进行处理,使用这种方法将不需要。

的概念一般是约非阻塞异步事件驱动的自我调度工人,我已经发布了一个完全实现的一个here。好处是你可以跟踪进度,暂停,取消和所有的好东西。

+0

工作:但我怎么能确保在做截图并将其与保存的图像进行比较之前单击鼠标完成?看到我编辑的问题 – user2246120

+0

@ user2246120 - 你可以做另一个排队的调用 - 'doStep()'将安排'takeScreenshot()'和'takeScreenshot()'将安排在下一个'doStep()'。这样所有的事件将在每个预定的调用之前处理。 – dtech

+0

另一种替代方法是从接收器的点击事件处理程序计划屏幕截图。目前尚不清楚“点击完成”的含义。我认为这是点击位置的绘制。所以你可以把它放在绘图代码之后。 – dtech

-1

尝试发送事件QGraphicsView的视口:

qApp->sendEvent(view->viewport(), &mousePressEvent); 
+2

QWidget中没有一个视... – user2246120

+1

这并没有回答,甚至试图回答这个问题。 – dtech