PDA

View Full Version : Retrofitting a Qt App based on QStacikedWidgets to support form transition animation



bob2oneil
17th January 2011, 18:07
INTRO
-----

I am developing an industrial application for Linux embedded which will run on an 800 x 400 LCD touch screen display. There are 4 external pushbuttons that will be used to drive the application in addition to the touch screen similar to modern smart phones (Home, Back, Help, and Menu). The embedded platform is not yet finalized, but we are thinking about using an Atom based processor for embedded Linux running at maybe 1 GHz. The application is a conventional desktop application as opposed to a graphics based application, although we will likely need 2D graphing support.

The application software is currently a 4 tiered sequence, beginning with a home icon screen, where selections make visible a secondary level of icon screens. Upon selection of an icon from the secondary screen, a list view is displayed. When an item is selected for the list view, an editor specific to the list view type is displayed. The editors are dynamic and include content for editing text, numeric, boolean, conditional lists, and running a method.

I am currently using QCreator/Qt/C++ for the development. The architecture of the current application is based on QStackedWidgets. The main window contains a header and footer which are present for all views. The client area of the main window has swappable content that is programmatically controlled based on the user selections from the icon screens.

This client area inscribed by the persistent header and footer consists of QStackedWidgets, which can also bring up a widget that contains QStackedWidgets, as is the case for the variable editors. Edit stack widget content is created as a separate form in
QtDesigner and are all based on QWidget.

The list view shown requires multiline content as shown in my current implementation. It is currently driven by the mouse to implement scrolling for a touch screen. It would be desirable to implement kinetic scrolling, over scroll indications, and perhaps bouncing (not a hard requirement). I have looked at the "Flick Charm" project for a possible enhancement to my current implementation.


QUESTION #1: Sliding Window Architecture and Animations.
-----------

I am reviewing the existing application architecture relative to the requirement of having animated transitions between screens. We have a requirement to show screen transitions in a slide manner of a left and right scroll (similar to the IPhone or Android platforms).

Eventually, I need to have a home screen, and swipes on the touch screen in one of four directions (up, down, right, left) need to slide to reveal the other screens. Navigation back to the home screen would be the reverse of the action taken to navigate out of the home screen. This is similar to modern smart phones.

How would one architect such an application, presumably in QTCreator and what Qt classes would be most applicable. For example, to achieve a home screen which can be slide in one of four directions, would it be advisable to create one larger monster screen, and the action of sliding would reveal other content? It would be preferable to develop the individual screen content separately in modular fashion and then perhaps knit them together in the application code.

I have reviewed the QPropertyAnimation class, and was wondering if it would be applicable for animating the position of entire forms
created within QtCreator with child widgets.

How would one revise my current QStackWidget based framework to support the animation requirements cognizant of the desire for high
performance under embedded Linux?

I am also looking for fade animation based on opaque levels for a pixmap based image (currenty QLabel, which has no alpha modification property, but I believe QGraphicsWidget does).


QUESTIONS #2: QGraphicsView/OpenGL benefits/embedded performance
------------

I am NOT currently not using QGraphicsView or graphics based class derivatives. The components that are currently being used include QListWidget, QStackedWidgets, QLabel, QFrame, and other basic components available within QtCreator. The animation requirements that we currently have are for
screen transitions as described, and perhaps 2D graphing longer term.

I am pondering the benefits of using QGraphicsView and OpenGL for the user interface, and what that might mean to embedded performance.
It is not yet know whether the target Atom board will provide hardware graphics acceleration (GPU), and how that might influence the decision on the Qt application base classes and implementation.

I would welcome you input on this question, i.e. create a UI without using QGraphicsView/OpenGL or not from the perspective of application performance.

I welcome any general direction.

wysota
17th January 2011, 19:29
As for the first question... QtQuick/QML seems perfect for you but it would require a rewrite of almost all your code and since you are probably not used to declarative programming languages it would need a bit of learning and experiments first. Alternatively you can use a state machine to drive your stack. Here is a short demo, a bit crude but shows what I mean.


#include <QtGui>

int main(int argc, char **argv){
QApplication app(argc, argv);
QWidget panel;
QVBoxLayout *l = new QVBoxLayout(&panel);
QFrame *viewport = new QFrame;
viewport->setFrameShape(QFrame::Box);
viewport->setFixedSize(400,600);

l->addWidget(viewport);
QPushButton *b = new QPushButton("Swap");
l->addWidget(b);
QStateMachine machine;
QState *s1 = new QState;
QState *s2 = new QState;

QWidget *w1 = new QCalendarWidget(viewport);
w1->setFixedSize(300,500);
QWidget *w2 = new QListView(viewport);
w2->setFixedSize(300,500);

QGraphicsBlurEffect *e1 = new QGraphicsBlurEffect(w1);
QGraphicsBlurEffect *e2 = new QGraphicsBlurEffect(w2);
w1->setGraphicsEffect(e1);
w2->setGraphicsEffect(e2);

s1->assignProperty(w1, "pos", QPoint(50,50));
s1->assignProperty(w2, "pos", QPoint(450,50));
s1->assignProperty(e1, "blurRadius", 0);
s1->assignProperty(e2, "blurRadius", 15);
s2->assignProperty(w1, "pos", QPoint(-350, 50));
s2->assignProperty(w2, "pos", QPoint(50,50));
s2->assignProperty(e1, "blurRadius", 15);
s2->assignProperty(e2, "blurRadius", 0);

s1->addTransition(b, SIGNAL(clicked()), s2);
s2->addTransition(b, SIGNAL(clicked()), s1);

machine.addState(s1);
machine.addState(s2);

QPropertyAnimation *anim1 = new QPropertyAnimation(w1, "pos");
QPropertyAnimation *anim2 = new QPropertyAnimation(w2, "pos");
anim1->setEasingCurve(QEasingCurve::InOutCubic);
anim2->setEasingCurve(anim1->easingCurve());
anim1->setDuration(2000);
anim2->setDuration(anim1->duration());
machine.addDefaultAnimation(anim1);
machine.addDefaultAnimation(anim2);

anim1 = new QPropertyAnimation(e1, "blurRadius");
anim2 = new QPropertyAnimation(e2, "blurRadius");
anim1->setDuration(1000);
anim2->setDuration(anim1->duration());
machine.addDefaultAnimation(anim1);
machine.addDefaultAnimation(anim2);
machine.setInitialState(s1);
machine.start();
panel.show();
return app.exec();
}

So essentially substitute your stacks with a simple parent and children driven by a state machine.

As for question two - if your GUI is based on widgets and has to be based on widgets then I wouldn't recommend using Graphics View (if you use QML then you use GV but since QML is not widget based my recommandation still holds) because it will be much slower although of course you'll have more control over your items. As for OpenGL - it depends. You might get a better performance with the raster graphics engine for some cases than with OpenGL or the default engine.

bob2oneil
17th January 2011, 20:18
Thanks for your input, I will study your sample code and try and apply it towards my application. I am not familiar with QML/QtQuick, but it is certainly not too far down the road for us to change gears if this is a better way to go. What is the downside of QML vs. Qt/C++ implementations? We will need XML support, threading support in addition to the UI. Would this disqualify QML? Is QML slow and interpreted? I will do some QML research to see if I can understand this technology better.

wysota
17th January 2011, 20:59
QML is only about the UI, you can implement the logic in C++. But QML is a declarative language - you define how things should "be" ("this has to be red when this is clicked") and not what should be "happening" ("when this is clicked, the palette needs to be queried, its foreground component changed and reapplied back to the widget"). The downside of QML is that it works in a concept of rectangles and not in a concept of widgets.

bob2oneil
19th January 2011, 20:36
I built the code and tested it, and it is very cool.

Is there any reason why this technique should not work on a complete form and corresponding C++/h files composed in QtCreator derived from QWidget rather than on single GUI elements?

Are there constraints that need to be imposed on the form or form elements to allow this to occur (such as fixed sizing, fixed sizing policies, etc?)

W.r.t the technique of hiding content by giving GUI elements X value that are outside the viewport, is there any known potential problem with conditionally hiding a "page" altogether, or stacking them atop each other and
programmatically displaying a single page of the stack?

My state behavior will require at any time that only a single screen is visible, and either a scroll forward or scroll backwards (left or right) similar to Andriod or IPhone.

wysota
19th January 2011, 21:01
Is there any reason why this technique should not work on a complete form and corresponding C++/h files composed in QtCreator derived from QWidget rather than on single GUI elements?
It will work. It will just be much slower.


W.r.t the technique of hiding content by giving GUI elements X value that are outside the viewport, is there any known potential problem with conditionally hiding a "page" altogether, or stacking them atop each other and
programmatically displaying a single page of the stack?
I wouldn't stack them on top of each other. QStackedWidget doesn't do that, it replaces widgets with other widgets. Widgets are clipped to their parent so moving them outside their parent's area is ok.

bob2oneil
25th January 2011, 20:14
Hi wysota. To take this discussion further, I could use your general advice on how to construct this state machine based animated navigation for say 10 to 20 screens. In general, for any given screen (current screen), I would need to have a previous screen and next screen. Navigation from the control button BACK would need to move back to the previous screen. Navigation "forward" would be based on a user selection on a given screen. There is also a home button that would cause immediate display of one of selected group of "home" screens. To this end, I would need to have a left screen available as the navigation destination for the current screen. This leftmost screen could be the previous screen for BACK navigation, or perhaps the HOME screen for the navigation from a HOME button. The rightmost screen to scroll to would be the conditional destination based on the user selection from the current screen. The leftmost and rightmost nomenclature accomplishes my goal of animated scrolling down the hierarchy and back.

So from this perspective, somewhat like your example, I could have a current visible screen, a left screen available for a scroll back, and a right screen as the destination for a user selection from the current screen.

Once the current screen changes, say from current to rightmost, rightmost becomes current, current becomes leftmost, and rightmost is undefined but defined at the time of user selection on the current screen.

Where I am having a disjoint in understanding is how best to arrange this method using the QStateMachine concept and using off screen offsets for the pre-created content.
For example, I do not want to have the various screens (left, current, right) at increasing negative (leftmost) or positive (offsets) to make them appear off screen to facilitate the scrolling process. I am not sure apriori that I could determine which screens should be staged to be either leftmost or rightmost.

In concept, I was pondering the stacked widget concept, where all available screens are available at any given time, and a simple index makes one of them visible hiding all the others. I was wondering is a similar concept could be used to implement my right and left animated scrolling. However, a screen could be either a leftmost or rightmost screen to another screen.

In concept, I was thinking of a left screen placeholder, where a created single instance of a screen could be loaded for a back scroll, the current screen is loaded with current content, and a right placeholder screen, where content could be placed based on user navigation.

Let me know what your thoughts are on implementing this type of solution efficiently, and whether or not the QT State Machine class could be used for such. I need to update the application title based on the currently active screen, and it will contain breadcrumb content based upon screen navigation.

Any given screen in the state machine would need to have a back and forward screen pointer much like a doubly linked list.

wysota
25th January 2011, 20:25
Let's make it clear, using a stack machine only to animate panels doesn't make sense. You can have the exact same functionality with a standalone animation object and only changing the subject of animation. If you are to use the stack machine make it useful for you. In my opinion you are approaching the problem from the wrong end. Don't think how to do animation but rather what is the general architecture you want to have. Then think how to implement this architecture. It always pays off in the long run.

bob2oneil
10th February 2011, 16:31
Thanks wysota for your general direction. I was successful in converting the application from a stack based approach, to one where each screen is animated into view (much like the modern cellphone GUIs) using your suggestions.