Touchscreen QPushButton does not emit released()
-
I have a dialog running on a touchscreen that has a number of "jog" buttons on it. Each button has it's pressed and released signal connected to start and stop jogging in the specified direction. Only one button at a time is expected to get pressed.
connect(ui->buttonXp, &QPushButton::pressed, this, [this](){ //start X+ jogging }); connect(ui->buttonXp, &QPushButton::released, this, [this](){ //stop X+ jogging }); // same for X-, Y+, and Y- jogging
-
If I press a button and release it everything works fine.
-
But if I press a button and another button gets pressed before the first button is released, I don't get the release for the first button. I also don't get a press or release for the second button.
-
If I press a button then press and release a second button I do get the release for the first button. But again, no press or release for the second button.
1 is the intended operation so that's good. 3 isn't great, since i miss the second button, but it's fine since it's not the intended operation.
2 is a problem. It appears my touchscreen is a multi-touch, I'm not sure if that is part of the problem.
Any ideas how I can fix this?
Thanks
-
-
@bigguiness I can confirm your problem with my customized buttons on a touch screen. I guess it is not encouraged to press two buttons with two fingers at the same time. Like you can not press two buttons at the same time with a mouse. Also it is better to press buttons quickly. Slow press and release are not good with Qt on touch screen.
Use an event filter to find out which events are sent out.
I tried to use my two fingers to type words in my cell phone(Android) and two letters are displayed. No issue. That says Qt has problems to handle this case.
-
@bigguiness Hi
If you are using widgets I don't have a clue how to fix this, if using qml is a option, this can be fixed with MultiPointTouchArea and TouchPoint but with some limitations. TouchPoint does not have press and release signals, you would have to use onPressedChanged handler. Another limitation would be the number of jog buttons pressed / released at the same time would be limited to the maximum avaible touch points in the screen (usually is 10, so probably this is not a problem, I dont see anyone using more than 10 fingers at the same time). You would have to "dinamically" "assing" each TouchPoint to a jog button, based on the order of the touchs being pressed and their screen position, which is a bit of work, but doable.Some pseudo code:
MultiPointTouchArea { touchPoints: [ TouchPoint { id: point1 onPressedChanged: { if (pressed) { //pressed if ( x, y inside jog button1 screen position) jogBtn1.press() if ( x, y inside jog button2) jogBtn2.press() //add more buttons } else { //released if ( x, y inside jog button1) jogBtn1.release() if ( x, y inside jog button2) jogBtn2.release() //add more buttons } } }, TouchPoint { id: point2 onPressedChanged: { if (pressed) { //pressed if ( x, y inside jog button1 screen position) jogBtn1.press() if ( x, y inside jog button2) jogBtn2.press() //add more buttons } else { //released if ( x, y inside jog button1) jogBtn1.release() if ( x, y inside jog button2) jogBtn2.release() //add more buttons } } }, //repeat same stuff for TouchPoint 3}
-
@johngod said in Touchscreen QPushButton does not emit released():
MultiPointTouchArea
Do the similar thing with touch events?
protected: bool event(QEvent *event) override { if (event->type() == QEvent::TouchBegin || event->type() == QEvent::TouchUpdate || event->type() == QEvent::TouchEnd) { QTouchEvent* touchEvent = static_cast<QTouchEvent*>(event); foreach (const QTouchEvent::TouchPoint &touchPoint, touchEvent->touchPoints()) { // Handle touch events here qDebug() << "Touch event ID: " << touchPoint.id() << "Pos: " << touchPoint.pos() << "Pressure: " << touchPoint.pressure(); } return true; // Mark the event as handled } return QWidget::event(event); }
-
@JoeCFD
I tried adding the event() override to see if I could do something in it to handle the multi-touch event. Right now it's just a big switch() that outputs a qDebug() message for every event->type() so I can see what is happening. Once I verified it worked I started disabling the message for various events (Enter, Move, Leave, Paint, etc.) to filter out only the ones I am interested in.The output I am seeing now is not quite what I expected.
My "jog" buttons are actually handled in a widget that I put on the dialog.
namespace Ui { class Jogger; } class Jogger : public QWidget { Q_OBJECT public: // Constructor/Destructor // public methods protected: bool event(QEvent *event) override; .... };
All the buttons are connected to the QPushButton::pressed() and QPushButton::released() signals using lambdas. I have added some qDebug() in each to debug the press/release.
When I run code on a Windows machine (non-touchscreen) and use a mouse, I see debug like this when I press/release a button:
Jogger : buttonXp pressed Jogger : buttonXp released
I always get a press/release for a single button. If I click on a button and move the mouse off the button then back on the button then release, I see something like this:
Jogger : buttonXp pressed Jogger : buttonXp released QEvent::MouseMove QEvent::MouseMove QEvent::MouseMove QEvent::MouseMove Jogger : buttonXp pressed Jogger : buttonXp released
So moving off the button results in a released() and moving back on results in a pressed(). The move onto another button does not result in a pressed() for the new button.
If I click somewhere that is not a button I see this:
QEvent::MouseButtonPress QEvent::MouseButtonRelease
I was surprised that I did not get the MouseButtonPress/MouseButtonRelease events for the buttons. I guess the pressed()/released() is handled first so the event() never occurs.
I need to rebuild the code to run on my BeagleBone platform with the touchscreen. Hopefully it will output the TouchBegin/TouchUpdate/TouchEnd events. Not sure what I am going to be able to do with them but at least I have a path to look at...
-
OK this is confusing.
If I touch and release a non-button location on the touchscreen I get:
QEvent::MouseButtonPress QEvent::MouseButtonRelease
If I touch and release two fingers to a non-button location I get:
QEvent::MouseButtonPress QEvent::TouchUpdate Touch ID: 0 Pos: QPointF(xxx,xxx) Pressure: 0 Touch ID: 1 Pos: QPointF(xxx,xxx) Pressure: 0 QEvent::TouchUpdate Touch ID: 0 Pos: QPointF(xxx,xxx) Pressure: 0 Touch ID: 1 Pos: QPointF(xxx,xxx) Pressure: 0 QEvent::TouchUpdate Touch ID: 0 Pos: QPointF(xxx,xxx) Pressure: 0 QEvent::TouchEnd Touch ID: 0 Pos: QPointF(xxx,xxx) Pressure: 0
The last TouchUpdate and Touch may be Touch ID 0 or 1 depending on which finger is lifted first.
If I touch and release one button I get:
Jogger : buttonXX pressed Jogger : buttonXX released
If I touch one button then another then release the second then the first I get:
Jogger : buttonXX pressed Jogger : buttonXX released
And if I touch one button then another then release the first then the second I get:
Jogger : buttonXX pressed
So, I do get the multi-touch events as long as I don't touch a button. But touching a button does not give me any events. And it they are not released in the correct order, I don't get the final release.
Any other ideas?
-
@bigguiness It could be the case that touch events are converted to pressed and released events only when a button is clicked.
Can you try one more thing:
To receive touch events, widgets have to have the Qt::WA_AcceptTouchEvents attribute set and graphics items need to have the acceptTouchEvents attribute set to true.
to see if you are able to get more. -
@JoeCFD setting the Qt::WA_AcceptTouchEvents attribute on all the buttons did not change anything.
Do i also need to set it on the Jogger widget itself? This is my Jogger widget:
Also, I just noticed that the second touch does not have to be on a button. If any location in the widget is touched while a button is touched, if the button is released before the second touch I don't get the release. But, if the second location is released first I do get the release of the button.
-
I tried setting the Qt::WA_AcceptTouchEvents attribute on the Jogger widget itself.
Now ALL button touches show up as TouchBegin/TouchUpdate/TouchEnd events. I never get a pressed() or released() signal.
-
OK. The Qt::WA_AcceptTouchEvents attribute on the Jogger widget might work. I'm just not sure how to do the following.
- I'm only interested in the first touch, so ignore all touchPoint.id() != 0.
- A QEvent::TouchBegin event is basically a QEvent::MouseButtonPress.
- A QEvent::TouchEnd is basically a QEvent::MouseButtonRelease.
So:
protected: bool event(QEvent *event) override { if (event->type() == QEvent::TouchBegin) { QTouchEvent *touchEvent = static_cast<QTouchEvent*>(event); foreach (const QTouchEvent::TouchPoint &touchPoint, touchEvent->touchPoints()) { if (touchPoint.id() == 0) { // figure out what button is associated with the touchPoint.pos() // emit pressed() for that button } } return true; // handled as a QEvent::MouseButtonPress } else if (event->type() == QEvent::TouchUpdate) { return true; // ignore these } else if (event->type() == QEvent::TouchEnd) { QTouchEvent *touchEvent = static_cast<QTouchEvent*>(event); foreach (const QTouchEvent::TouchPoint &touchPoint, touchEvent->touchPoints()) { if (touchPoint.id() == 0) { // figure out what button is associated with the touchPoint.pos() // emit released() for that button } } return true; // handled as a QEvent::MouseButtonRelease } return QWidget::event(event); }
This may work I just don't know how to work out the button based on the touchPoint.pos(). I assume after that I can just do a "emit button->pressed()/released()".
What do you think?
-
@bigguiness if touch events come, you can get touch points from
https://doc.qt.io/qt-5.15/qtouchevent.html#touchPoints
const QListQTouchEvent::TouchPoint &QTouchEvent::touchPoints() const
Then follow the code of @johngod. -
@bigguiness check if these points are inside rect of the buttons.
https://doc.qt.io/qt-5.15/qrect.html#contains -
@JoeCFD thanks for the leads.
I have it working now using event() to handle the touch events instead of using the normal mouse events.
A couple notes about this.
- Setting the Qt::WA_AcceptTouchEvents attribute disables all the normal moue events.
Instead of the mousePressEvent() -> emit pressed() happening, you get an event() with either a QEvent::TouchBegin or QEvent::TouchUpdate. You then have to figure out what button the touch points are associated with. Then you need to handle the "pressed" and setDown(true) the button.
As long as any touch is still present you will periodically get an event() with QEvent::TouchUpdate. This also happens instead of a mouseMoveEvent(). Only the touches still active will be in the touch points. If a touch you are interested in is not in the points, that associated button will need to be "released" and setDown(false).
Instead of a mouseReleaseEvent() -> emit released(), when all touches are released you will get an event() with QEvent::TouchEnd with the touch points of the final released touches. Again these need to be associated to buttons that need to be "released" and setDown(false).
In my case I'm only interested in the first touch so I ignore all QTouchEvent::TouchPoint id() != 0.
This seems to be something that Qt should be able to handle internally. But.... Oh well...