QGraphicsView and Qt::WA_AcceptTouchEvents
-
I have implemented a class
MyView
derived fromQGraphicsView
. It is supposed to- handle touch events like a "pinch" for zooming
- and its screen should grow when now items are added of dragged to the outside of the current bounding rectangle.
With my current implementation I have the following problem: When an item is dragged beyond the viewport the
MyView
object no longer receives any touch events.I have reproduced the issue with a simpler program shown below. On my MacBook I can reproduce the following effect:
- After the program was started I can do a "pinch" and get the debug message "2 finger touch event"
- Then I move the rectangle item to the bottom and beyond the viewport.
- Now I no longer receive any event in
viewportEvent
.
Where is my error in reasoning?
#include <QApplication> #include <QGraphicsRectItem> #include <QGraphicsScene> #include <QGraphicsView> #include <QMainWindow> #include <QTouchEvent> class MyView : public QGraphicsView { public: MyView(QWidget *parent = nullptr) : QGraphicsView{parent} { setScene(new QGraphicsScene); viewport()->setAttribute(Qt::WA_AcceptTouchEvents); } bool viewportEvent(QEvent *event) override { switch (event->type()) { case QEvent::TouchBegin: case QEvent::TouchUpdate: case QEvent::TouchEnd: { auto *touchEvent = static_cast<QTouchEvent *>(event); const auto touchPoints = touchEvent->points(); if (touchPoints.count() == 2) { qDebug() << "2 finger touch event"; } } return true; default: return QGraphicsView::viewportEvent(event); } } }; class MainWindow : public QMainWindow { public: MainWindow(QWidget *parent = nullptr) : QMainWindow{parent} , view{new MyView} { setCentralWidget(view); auto item = new QGraphicsRectItem{0, 0, 50, 50}; view->scene()->addItem(item); item->setFlag(QGraphicsItem::ItemIsMovable); } private: QGraphicsView *view = nullptr; }; int main(int argc, char *argv[]) { QApplication app(argc, argv); MainWindow mainWindow; mainWindow.setGeometry(100, 100, 800, 500); mainWindow.show(); return app.exec(); }
-
Hi,
Which version of Qt are you using ?
On which version of macOS ? -
@SGaist I am using Qt 6.5.2 on macOS Ventura 13.5.2
Edit: ... and a MacBook Pro 2019, 2,3 GHz 8-Core Intel Core i9
I used the installer qt-unified-macOS-x64-4.6.1-online.dmg from https://download.qt.io/archive/online_installers/4.6/
-
@Michael-Lehn said in QGraphicsView and Qt::WA_AcceptTouchEvents:
MyView(QWidget *parent = nullptr)
: QGraphicsView{parent}
{
setScene(new QGraphicsScene);
viewport()->setAttribute(Qt::WA_AcceptTouchEvents);
}Does anything change if you set a static
sceneRect
(e.g.(0, 0, w, h)
) here?
Can you track the touchEvents and check where exactly they stop? -
Is anyone able to reproduce the effect? I would like to rule out installation/platform issues.
@Pl45m4 I made the following change:
MainWindow(QWidget *parent = nullptr) : QMainWindow{parent} , view{new MyView} { setCentralWidget(view); view->scene()->setSceneRect(0, 0, 100, 100); view->scene()->addItem(new QGraphicsRectItem{0, 0, 100, 100}); auto item = new QGraphicsRectItem{0, 0, 50, 50}; view->scene()->addItem(item); item->setFlag(QGraphicsItem::ItemIsMovable); }
So now there is a rectangle indicating the
sceneRect()
area.Now I can observe tfor example he following effect:
- When I move the
item
to the right and outside thescreenRect()
at some point I no longer receive touch events. This does not happen when it is outside but still "close enough". - If the
item
has not left theviewport()
area and I move it back, i.e. close enough to thesceneRect()
area I can again receive touch events - Once the
item
has left theviewport()
area I can no longer recover. That means after resizing the main widget so that I can grab the rectangle and moving it back has no observable effect.
- When I move the
-
For better keeping track where the effect stats I made the following changes:
- Class
MyRectItem
that prints the position after it has changed:
class MyRectItem : public QGraphicsRectItem { public: MyRectItem(qreal x, qreal y, qreal width, qreal height, QGraphicsItem *parent = nullptr) : QGraphicsRectItem{x, y, width, height, parent} { setFlag(QGraphicsItem::ItemSendsGeometryChanges); } QVariant itemChange(GraphicsItemChange change, const QVariant &value) override { if (change == ItemPositionHasChanged) { qDebug() << "item pos:" << pos(); } return QGraphicsRectItem::itemChange(change, value); } };
- ... and this class is now used in the constructor of
MainWindow
:
auto item = new MyRectItem{0, 0, 50, 50}; view->scene()->addItem(item);
Strangely I now never loose the touch events as long as the item is within the
viewport()
area. Even if it is outside thesceneRect()
area.However, as before the touch evens are lost once the item was outside the
viewport()
area. - Class
-
@Michael-Lehn said in QGraphicsView and Qt::WA_AcceptTouchEvents:
However, as before the touch evens are lost once the item was outside the
viewport()
area.To be more precise: I have to move the item outside the
viewport()
area. Drop it there. Only then I loose the touch events.I do not loose the events if I keep holding the item while moving it outside the viewport and back.
-
@Michael-Lehn
The GraphicsView can only receive those touch event that are delivered to the parent widget of the viewport. In order to continue receiving events even when they happen outside of the widget area, you typically would grab the event (see e.g. QWidget::grabMouse or QGraphicsItem::grabMouse).Unfortunately, I am not aware of a way to grab low-level touch events in QWidgets/QGraphicsView directly. You might need to go via QWidget::grabGesture or QGraphicsObject::grabGesture, which would require defining your gesture in terms of QGestureManager.
Alternatively, you could consider installing an application-level event filter, and handle the touch events on this low level, using your own logic to decide which widget (and thereby viewport) is supposed to receive which event.
Neither solution sounds very simple or satisfying, I'm afraid.
-
@Asperamanca I am not sure if I described my problem accurately. I am not interested in touch events that happen outside of the viewport. The problem is that the viewport suddenly does not receive any touch events once an item has left its area. It still receives all other kind of events (mouse and key events).
IMHO this is a bug (either in my code or qt).
-
@Michael-Lehn
Do you receive touch events again after releasing and again touching into the viewport, after that mentioned item was moved out? -
Ok, I tested your code on Windows (MSVC 2022 with Qt 6.5.2):
I can drag the item out and back into the window, all the while receiving touch events. This could be a Mac issue. Maybe take a look if you find something here: https://bugreports.qt.io/issues -
@Asperamanca No, I do not. Once the item was dropped outside the viewport area I no longer receive any viewport event (but still mouse and key events).
Thanks for testing! When you drag it out and back: Did you drop it in-between? I mean drag it out, drop it, resize the window so that you can see it again, and then drag it back?
-
@Michael-Lehn
No, I tested with one long drag that goes beyond the window boundaries and back into the window. Is the other case something I should test, too? -
@Asperamanca Yes, that would be great. Sorry for not stating this clear enough. Only when I drop the item outside the viewport I loose the viewport events. Without the drop everything is fine.
-
@Michael-Lehn Not sure the test setup is correct. I can move the item just as well with one finger (default move behavior, I guess), although I do not get into your viewport event handler then, of course.
I also added a timestamp to the qDebug() to see when I actually got events.So....
- I can drag the item out of the window, and receive touch events until I release
- I can enlarge the window and see the item again
- I can start another drag operation on the item, and the viewport event recognizes it, and the item is dragged
-
Thanks, this helped me to understand better what actually goes wrong. I changed the code of
MyView
as follows:class MyView : public QGraphicsView { public: MyView(QWidget *parent = nullptr) : QGraphicsView{parent} { setScene(new QGraphicsScene); viewport()->setAttribute(Qt::WA_AcceptTouchEvents); } bool viewportEvent(QEvent *event) override { qDebug() << "MyView::viewportEvent(QEvent *event)"; switch (event->type()) { case QEvent::TouchBegin: case QEvent::TouchUpdate: case QEvent::TouchEnd: qDebug() << "Some touch event"; return true; default: return QGraphicsView::viewportEvent(event); } } };
So now I can distinguish between
- getting any
viewportEvent
at all - and getting touch events in particular (here I no longer distinguish between 1 or 2 finger events)
Now I did the what you described:
@Asperamanca said in QGraphicsView and Qt::WA_AcceptTouchEvents:
- I can drag the item out of the window, and receive touch events until I release
- I can enlarge the window and see the item again
- I can start another drag operation on the item, and the viewport event recognizes it, and the item is dragged
First I get messages like
MyView::viewportEvent(QEvent *event) Some touch event MyView::viewportEvent(QEvent *event) Some touch event ...
when I move the item around. Once the item was dropped outside the viewport I just get
MyView::viewportEvent(QEvent *event) MyView::viewportEvent(QEvent *event) ...
So I actually still get viewport events. Except for touch events. It seems that touch events are now filtered out or no longer sent.
Moving the item around was never the problem. The problem is that I use touch events gestures for zooming.
- getting any