From widgets to quick2
-
I have converted my old widget-based code to a new shiny QML UI running on touch-enabled windows desktop and android tablets in Qt5.2. I have done this with mixed results so here is an overview of the journey - I would love to hear from others who are doing this, i.e. not bugs and features, more a 'product' viewpoint.
My App has two main UI components,
- a viewport for images with pinch/zoom/pan.
- a dashboard with several animated controls.
The starting point was a version using C++ widgets. The graphicview provided all the functionality for multi-touch and keyboard and I was quite pleased with the performance of the image zoom and pan. I also used a declarative view for an animated busy spinner written in QML. The UI appearance was predictably boring (except for the spinner!). I used Qt5.2 and the App runs well on Windows8 laptop and Android Nexus 7.
The next step was to replace the Declarative views with a QuickView. I also wanted to add some additional QML sophistication to the controls. This went well until I tried to deploy to android. Using widgets and Quick together meant that I had to use createWindowsContainer() which works fine on Windows desktop but does not (and I believe cannot) work on Android.
Not discouraged, I stretched for the 'true path', i.e. a fully implemented QML UI with Quick2. This went smoothly until I tried to port the pinch/zoom functionality. I thought it would be easy to connect the touch events from a QML MouseArea to my old GraphicsView code which was easily updated to a QuickItem. Everything builds and runs but I could not get the mousePressEvents to work.
Clearly this is not quite the 'pure' Quick2 implementation but to my surprise I found no comments from anyone else trying to do this. Is the best way to put all the pinch/zoom code into the QML? I would rather use my existing C++ code than start a jungle of javascript. There are not many Qt5.2 examples that I could find of touch / gesture UIs.
So what shall I do?
vote A: revert to widgets and declarative code.
vote B: tackle head-on a new QML touch front-end.
vote C: split responsibility for pinch/zoom between QML and C++ code.Happy New Year!
-
Hi,
Sounds like you want a PinchArea, not a MouseArea: http://qt-project.org/doc/qt-5/qml-qtquick-pincharea.html
[quote]This went smoothly until I tried to port the pinch/zoom functionality. I thought it would be easy to connect the touch events from a QML MouseArea to my old GraphicsView code which was easily updated to a QuickItem. Everything builds and runs but I could not get the mousePressEvents to work.[/quote]If you still have difficulties, please provide some code.
-
That would be a good future step. But right now I am blocked because I cannot get simple mouse events to propagate to my handling class (below).
Keyboard events work fine from a QML TextInput (but I would like to be able to propagate individual characters rather than a terminated string)
I am missing something!
@
#pragma once
#include <QKeyEvent>
#include <QtQuick>class ViewportWidget : public QQuickPaintedItem
{
Q_OBJECTpublic:
ViewportWidget(QQuickItem *parent = 0);
virtual ~ViewportWidget(void);public slots:
void zoomIn();
void zoomOut();
void displayUpdate();protected:
void keyPressEvent(QKeyEvent* event);
void mousePressEvent(QMouseEvent* event);
void timerEvent(QTimerEvent* event);
#ifndef QT_NO_WHEELEVENT
void wheelEvent(QWheelEvent* event);
#endif
void paint(QPainter *painter);
void scaleView(qreal scaleFactor);private:
QGraphicsScene* _scene;
};
@ -
I don't have experience in combining Qt Quick with the old Graphics View technology or in implementing a custom QQuickPaintedItem, sorry.
What does your view port need to do? If it's only to show images with pinch/zoom/pan, it might be far simpler to reimplement everything in Qt Quick, instead of trying to glue old graphics view code to a new Qt Quick UI.
See the "Photo Surface example":http://qt-project.org/doc/qt-5/qtquick-demos-photosurface-example.html, which does pinch/zoom/drag+drop/rotate. (you can build and run it by opening Qt Creator and searching for "Photo Surface" in Welcome -> Examples
But anyway, if you'd still like to try your current approach but don't get any more replies here, try subscribing to the "Interest mailing list":http://lists.qt-project.org/mailman/listinfo/interest and asking there.
-
Thanks for the example, and indeed this is a great demo of the power of QML for the UI-side of the problem.
But I need to feed mouse and touch events to my C++ image analysis code, for example to interact and provide location cues to the analysis.
For example, suppose I wish to send a mousePressed event. ViewportWidget is a registered type and I would expect mouse events to be automatically connected to the mousePressEvent(...) override method in the C++ class of the same name.
I have tried explicit connections too, but nothing works. The events in QML are type MouseEvent, but in C++ they are QMouseEvent. It does not seem to be designed to be used this way. As I said, I must be missing something!
@
ViewportWidget {
id: viewportwidget
width: 600; height: 600MouseArea { anchors.fill: parent acceptedButtons: Qt.AllButtons onPressed: { console.debug("hello")
// viewportwidget.mousePressEvent(mouse)
}
}
}
@If I uncomment line 10, I get the runtime error
TypeError: Object [object Object] has no method 'mousePressEvent' -
mousePressEvent() is not an "invokable method":http://qt-project.org/doc/qt-5/qobject.html#Q_INVOKABLE, so it is not accessible from QML.
The MouseArea is a separate object from the ViewportWidget, so it's not connected to ViewportWidget::mousePressEvent().
@
MouseArea { anchors.fill: parent }
@
The code above says: "Create a new (invisible) MouseArea object, and stretch it to cover the parent object". Mouse events are received by the MouseArea object, NOT the parent object.I'm purely guessing here, but try this:
Remove the MouseArea completely
Use qDebug() to print a message in your C++ implementation of ViewportWidget::mousePressEvent()
Build your application, and click on the QML-rendered ViewportWidget
-
I think I follow your line of thought but it didn't work. I also tried declaring,
@
protected:
void keyPressEvent(QKeyEvent* event);
Q_INVOKABLE void mousePressEvent(QMouseEvent* event);
@What puzzles me is that if I add at the same level as the MouseArea,
@
TextInput {
id: kbdinput
anchors.fill: parent
focus: true
}
@a text string entered anywhere in the viewport does get passed to keyPressEvent(...)
-
Does your ViewportWidget call setAcceptedMouseButtons()? It is needed for mousePressEvent to be called.
For keypressEvent your item needs to have active focus, or a child item must have active focus (and not accept the event).
See also "keyboard focus in QtQuick":http://qt-project.org/doc/qt-5/qtquick-input-focus.html
-
Hi Torgier
In the QML MouseArea I have
@
acceptedButtons: Qt.AllButtons
@And the problem remains,
keyPressEvents work; mousePressEvents do not. -
[quote author="zing0000" date="1388768258"]
In the QML MouseArea I have
@
acceptedButtons: Qt.AllButtons
@
[/quote]Please try adding that to the ViewportWidget instead of the MouseArea.
-
Just tried it, but same result.
Do you have any thoughts about the experimental code line,
@viewportwidget.mousePressEvent(mouse)
@My understanding is that this should call the method directly, and it seems to try, but with the error message,
@
hello
qrc:/qml/MainPanelLandscape.qml:45: Error: Unknown method parameter type: QMouseEvent*
@
But the QML property "mouse" is of type MouseEvent, and this does not match the method argument QMouseEvent*. -
Sorry for the confusion. My response was to this problem:
[quote author="zing0000" date="1388688947"]That would be a good future step. But right now I am blocked because I cannot get simple mouse events to propagate to my handling class (below).
[/quote]If you call setAcceptedMouseButtons() in the constructor of ViewportWidget the signal handler in ViewportWidget should be called. That worked for me.
Then you don't need any MouseArea nor do you need to declare Q_INVOKABLE for the mousePressEvent().
All you need then in QML is:
@ViewportWidget {
id: viewportwidget
width: 600; height: 600
@On the other hand if you want to handle it with a MouseArea and forward the events, that is possible too. However you can't forward the 'mouse' parameter as it is of type QQuickMouseEvent* and your mousePressEvent() handler needs QMouseEvent*.
You could forward mouse.x and mouse.y to a Q_INVOKABLE that takes two int parameters for example.
-
Thank you!
The problem was that I misunderstood Qt::AllButtons in the MouseArea - which was correctly consuming all mouse events instead of my intention of passing them on.
With Qt::NoButtons it now works.But in any case, you are right, the MouseArea is not needed.
However, I am also trying to pass keystroke events, and I am still looking for a way of passing events for individual characters.
Thanks again for nudging me in the right direction.
-
[quote author="zing0000" date="1388885058"]
However, I am also trying to pass keystroke events, and I am still looking for a way of passing events for individual characters.
[/quote]To receive key press and release events your ViewportWidget item needs to have active focus. It may be enough to set
@focus: true@
on it, or you may have to call forceActiveFocus() on it.
When the item has active focus, the event handlers keyPressEvent(QKeyEvent *) and keyReleaseEvent(QKeyEvent *) will be called for individual key presses.
-
Yes, it needed both.
my ViewportWidget is part of a ScrollView, so I had to set focus on that.
and it did need forceActiveFocus() in the ViewportWidget constructor.
Clearly I need to read up a bit more about all this....