2

I'm trying to solve an issue in a Qt5 application that's caused by a touchscreen driver I wrote a couple years ago. The problem being that my touchscreen driver generates mouse press events to simulate touch behavior. This was because at the time I wrote it, my team was using an outdated Linux OS that didn't have a native touch driver.

The problem is that when I touch the +/- buttons (the scroll buttons that increase/decrease the slider by a single tick, or cause it to rapidly scroll if held), the slider rapidly slides up/down as if I had clicked and held the button. This is because my driver uses QApplication::postEvent() to dispatch the mouse event from a separate thread, and the delay in between touch and release is just long enough for it to register a click-and-hold type event, though the release event doesn't stop the sliding.

I would rewrite the application to use the now available native touch driver, but it doesn't offer enough control to be able to do what my driver does (my driver can generate left, right, or middle mouse events, whereas the native driver only provides left).

I tried rewriting the driver to use QApplication::sendEvent() instead, but that was causing a segmentation fault that I couldn't figure out the cause of.

So is there a way I can disable the "click-and-hold" type behavior on the QSlider itself? So that I can still tap the buttons, but they'll only increase/decrease by a single tick at a time?

EDIT: Here's an example of how I'm generating the "touch" events via my driver.

void InputHandler::touchPress(TouchPoint * tp, QWidget * widget) {
    QPoint screenPos(tp->cx(), tp->cy());
    QWidget * target = widget;
    if(target == NULL) target = QApplication::widgetAt(screenPos);

    if(target != NULL) {
        QPoint local = target->mapFromGlobal(screenPos);
        QMouseEvent * press = new QMouseEvent(QEvent::MouseButtonPress, local, screenPos, Qt::LeftButton, Qt::LeftButton, Qt::NoModifier);
        QApplication::postEvent(target, press);

        DBG(" -- touch press on: " << typeid(*widget).name());

        // Check to see if the target is a QLineEdit or QComboBox, and if so, set focus on it.
        QLineEdit * lineEdit = dynamic_cast<QLineEdit*>(target);
        QComboBox * comboBox = dynamic_cast<QComboBox*>(target);
        if(lineEdit) lineEdit->setFocus(Qt::MouseFocusReason);
        if(comboBox) comboBox->setFocus(Qt::MouseFocusReason);
    }
}

EDIT 2: So a coworker played around with the slider and pointed out that he pressed the +/- buttons, causing it to slide, and then he clicks the "reset" button we have to reset all controls on the form, the slider would reset and then continue sliding, indicating that the touch press was never released. He then would click the +/- buttons normally with the mouse and reset again and it would stop. So even though the logs indicate that the mousePressEvent and mouseReleaseEvent methods are being triggered by my events, they don't seem to have the same behavior as using the mouse.

Any ideas?

EDIT 3: If it helps, I added an eventFilter and printed out every event type that the QSlider receives, from when I first touch the screen to when I release. The events received are:

Mouse Press (2)
Tooltip Change (184)
Paint (12)
Mouse Move (5)
Mouse Move (5)
Mouse Release (3)

But then after the release event, I get spammed in the logs with QEvent::Timer events, which does not happen when I simply click with the mouse, as opposed to touching the slider with the touchscreen. If I then tap somewhere else, drag the slider, or click the slider with the actual mouse, these timer events stop.

Why is my generated QMouseEvent causing these QEvent::Timer events to be generated when I tap the slider with the touchscreen, but not with the mouse?

Darin Beaudreau
  • 375
  • 7
  • 30
  • Is this related to QtWidgets or QtQuick? – m7913d Mar 07 '22 at 14:11
  • Increasing the [`QStyleHints::mousePressAndHoldInterval`](https://doc.qt.io/qt-5/qstylehints.html#mousePressAndHoldInterval-prop) to a larger/very large value may work. – m7913d Mar 07 '22 at 14:14
  • @m7913d QtWidgets – Darin Beaudreau Mar 07 '22 at 14:17
  • @m7913d Thanks! I'll look into that. – Darin Beaudreau Mar 07 '22 at 14:17
  • I'm not sure if this property is relevant in QtWidgets. – m7913d Mar 07 '22 at 14:23
  • @m7913d How would I even set the style hint? Is there any way that doesn't involve creating an entire new QProxyStyle class just for this one hint? – Darin Beaudreau Mar 07 '22 at 14:38
  • `QStyleHints::setMousePressAndHoldInterval` exists, but is not documented: `QGuiApplication::styleHints()->setMousePressAndHoldInterval(5000 /*milliseconds*/);` – m7913d Mar 07 '22 at 15:14
  • @m7913d Probably doesn't matter since I'm almost certain this is the only slider in the application, but just to make sure... does this set that property globally, or just for the widget calling the method? – Darin Beaudreau Mar 07 '22 at 15:16
  • This sets the option globally, but I'm not sure if this property is used by QtWidgets. How do you even detect/respond to a 'press and hold' event in QtWidgets? As far as I know, QtWidgets only provide `QMouseEvent`. – m7913d Mar 07 '22 at 15:26
  • @m7913d Well, setting the interval seems to have had no effect, so I'm going to guess you're right about it not applying in this case. – Darin Beaudreau Mar 07 '22 at 15:54
  • But which event do you use to capture a 'press and hold' event in QtWidgets? – m7913d Mar 07 '22 at 16:03
  • @m7913d What do you mean? There is no "press and hold" event. Just press, move, and release. As far as I can tell, the press and hold behavior isn't configurable, hence my question. – Darin Beaudreau Mar 07 '22 at 20:14
  • QtWidget generates distinct `QMouseEvent`'s events for those 3 actions (mousePressEvent, mouseMoveEvent and mouseReleaseEvent). In QtQuick those basic events may be interpreted into a [`pressAndHold`](https://doc.qt.io/qt-5/qml-qtquick-mousearea.html#pressAndHold-signal) event, if you press long enough without moving your mouse. So, I do not understand what you mean with _"click-and-hold" type behavior on the QSlider_. – m7913d Mar 07 '22 at 20:26
  • @m7913d Ok, but I'm not using QtQuick. This is C++, not QML. Qt does not have a "press and hold event". But the slider itself responds by repeatedly scrolling if you hold down your mouse on it. – Darin Beaudreau Mar 07 '22 at 20:34
  • Could you share a [mcve] (of your GUI)? If possible, include some hardcoded events, resulting in the observed issue. Is this related to the +/- buttons (QPushButton's) or to the QSlider itself? – m7913d Mar 07 '22 at 20:46
  • @m7913d The application is too large to dumb down like that. The issue is literally just a QSlider, defined in a UI file, with an associated C++ class. There's nothing special about the slider other than the styling. No example I can give you is going to give you more information than I've already given. But if you're talking about the way in which I generate the events, I'll add an example to the main post. – Darin Beaudreau Mar 08 '22 at 13:17
  • It may be better to send the mouse event to the topLevelWindowAt(screenPos), so Qt will handle the translation from screen to local pos itself in the main thread. – m7913d Mar 08 '22 at 13:48
  • @m7913d Ok, but the position isn't the problem. The problem is that postEvent causes a delay just long enough for the slider to interpret the press and subsequent release as a press and hold action. But if sendEvent is used, as mentioned in my original post, it causes a segmentation fault I haven't been able to debug. The driver itself is on a separate thread, so it doesn't matter whether the translation to local position is in the main thread. – Darin Beaudreau Mar 08 '22 at 14:14
  • @m7913d I added new information to the bottom of the post. Definitely seems significant. – Darin Beaudreau Mar 08 '22 at 16:33
  • Could you add a screenshot of your GUI and try if the slider behaves normally if you disable the zoom related to the slider. – m7913d Mar 08 '22 at 19:39
  • @m7913d New information added to my post. – Darin Beaudreau Mar 10 '22 at 14:46
  • Could you also share the mouse release event generation code? – m7913d Mar 10 '22 at 16:44

1 Answers1

3

A minimal example to generate mouse press/release events that are correctly handled by QSlider is:

#include <QApplication>
#include <QMainWindow>
#include <QMouseEvent>
#include <QSlider>
#include <QTimer>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    QMainWindow m;
    QSlider *slider = new QSlider();
    m.setCentralWidget(slider);
    m.resize(100, 100);
    m.show();

    QTimer::singleShot(1000, [&](){
        a.postEvent(slider, new QMouseEvent(QEvent::MouseButtonPress, QPoint(50,50), m.mapToGlobal(QPoint(50,50)), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier));
        a.postEvent(slider, new QMouseEvent(QEvent::MouseButtonRelease, QPoint(50,50), m.mapToGlobal(QPoint(50,50)), Qt::LeftButton, Qt::NoButton, Qt::NoModifier));
    });

    return a.exec();
}

This example decreases the slider value with 1 tick after 1 second.

Note that it is important to pass Qt::NoButton as buttons parameters to the QEvent::MouseButtonRelease event, otherwise the slider will continue moving to the current mouse position (see QAbstractSlider::setRepeatAction), as QSlider thinks the button is still pressed. I assume this behaviour is what you call 'press-and-hold'.

m7913d
  • 10,244
  • 7
  • 28
  • 56