如何用触摸数据模拟鼠标单击?

时间:2019-05-29 17:58:26

标签: c++ linux qt qt5

我正在开发一个Qt5应用程序,试图利用触摸屏上的原始输入数据,因为Linux内核(2.6.32)并不完全支持触摸。我为来自 / dev / input / eventX 的原始数据编写了一个解析器,因为即使X不支持触摸,事件仍然存在。

我能够很好地读取事件,并编写了一个名为“ TouchPoint”的包装,其中包含每个触摸点的ID和(x,y)坐标,以及一个布尔值,指示该触摸点是否当前处于“活动状态”(即:用户当前手指在屏幕上)。 值得注意的是,我还输出了当前屏幕上的触摸点数,并且该值是准确的。

我的问题是,我似乎无法弄清楚如何用它来准确模拟鼠标单击。对于Linux中的多点触摸事件,当用户将手指按到屏幕上时,为每个触摸点分配一个“跟踪ID”,并且当该点抬起时,会生成将插槽的跟踪ID设为-1的事件。我将ID的值从某个值更改为-1,以表明接触点是否“向下”:

void TouchPoint::setID(int nid) {
    if((id == -1) && (nid >= 0)) down = true;
    else if(nid == -1) down = false;
    id = nid;
}

因此,为了模拟鼠标单击,我会在读取跟踪ID事件时执行此操作:

else if(event.code == ABS_MT_TRACKING_ID) {
    bool before = touchPoints[cSlot].isDown(); // where cSlot is the current slot the events are referring to
    touchPoints[cSlot].setID(event.value);
    bool after = touchPoints[cSlot].isDown();

    if(!before && after) touch(touchPoints[cSlot]);
}

然后触摸方法执行此操作:

void MainWindow::touch(TouchPoint tp) {
    if(ui->touchMe->rect().contains(QPoint(tp.getX(), tp.getY()))) {
        on_touchMe_clicked();
    }
}

该按钮不会直接响应我,但有时如果我在屏幕上疯狂挥动手指,将显示在按下时应显示的消息框,但是当它按下时,我的手指始终在另一个位置屏幕区域。

有人能想到我在这里做错了什么吗?我检查触摸点是否在按钮范围内的方法是否错误?还是我跟踪触点的“下降”状态的逻辑是错误的?还是完全其他?

更新:通过输出触摸位置和按钮位置的屏幕坐标,我注意到了两件事。

  1. 数字化仪的分辨率大于屏幕的分辨率,所以我需要缩放来自原始事件的X和Y坐标。
  2. 我的坐标是偏移的,因为它们是屏幕坐标。

更新2:现在,我已经处理了上次更新中提到的两个问题,但是我仍然无法弄清楚如何准确模拟触摸/鼠标事件,以便能够与界面进行交互。我还修改了我的TouchPoint类,使其具有一个布尔标志,指示是否刚刚更新了接触点,当更新跟踪ID时将其设置为true,而在引发EV_SYN事件时将其重置。这样一来,我可以在为应用程序创建事件之前获取X和Y位置事件。我尝试了以下方法:

  1. 使用QApplication类将鼠标事件发布到具有触摸点位置的QApplication :: desktop()-> screen()小部件。
  2. 使用QTest类将触摸事件引发到QApplication :: desktop()-> screen()小部件,该方法使用指定位置和触点位置的按下和释放方法,然后使用bool事件(QEvent * )尝试捕获事件的方法。我还在主窗口上启用了WA_AcceptTouchEvents属性。

由于某种原因,当我具有“ bool event(QEvent *)” 方法时,这些方法都不起作用,线程发出的信号读取 / dev / input / eventX 不会触发主窗口类中的插槽。而且我似乎找不到其他方法来完成事件的模拟。有人有什么想法吗?

1 个答案:

答案 0 :(得分:0)

您写道,有时当手指处于“错误位置”时,您的消息框会被“单击”,因此听起来好像您混合了全局屏幕坐标和小部件局部坐标。看来,您在MainWindow::touch中尝试检查全局屏幕坐标中的某个点,例如(530,815)在小部件的几何内部(以其本地坐标)。 QWidget::rect()返回按钮(小部件)的内部几何形状,即小部件的宽度和高度的矩形,例如(0,0,60,100)。

您必须将rect在全局屏幕坐标中移动到正确的位置。 QWidget::pos()将小部件的本地位置返回到其父级。您可以使用QWidget::mapToGlobal将小部件坐标位置转换为全局屏幕坐标。然后您的rect就像(500,850,60,100)一样,您应该受到打击并获得您的广告位。

但是,更好的方法是使用QApplication::widgetAt将小部件放置在特定的屏幕位置并为其产生鼠标单击。

您可以通过以下操作来为小部件产生鼠标点击:按下并释放它,如下所示:

QPoint screenPos = QPoint(tp.getX(), tp.getY());
QWidget *targetWidget = QApplication::widgetAt(screenPos);

QPoint localPos = targetWidget->mapFromGlobal(screenPos); 
QMouseEvent *eventPress = new QMouseEvent(QEvent::MouseButtonPress, localPos, screenPos, Qt::LeftButton, Qt::LeftButton,  Qt::NoModifier);
QApplication::postEvent(targetWidget, eventPress);
QMouseEvent *eventRelease = new QMouseEvent(QEvent::MouseButtonRelease, localPos, screenPos, Qt::LeftButton, Qt::LeftButton,  Qt::NoModifier);
QApplication::postEvent(targetWidget, eventRelease);