1 | /****************************************************************************
|
---|
2 | **
|
---|
3 | ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
|
---|
4 | ** All rights reserved.
|
---|
5 | ** Contact: Nokia Corporation ([email protected])
|
---|
6 | **
|
---|
7 | ** This file is part of the documentation of the Qt Toolkit.
|
---|
8 | **
|
---|
9 | ** $QT_BEGIN_LICENSE:FDL$
|
---|
10 | ** Commercial Usage
|
---|
11 | ** Licensees holding valid Qt Commercial licenses may use this file in
|
---|
12 | ** accordance with the Qt Commercial License Agreement provided with the
|
---|
13 | ** Software or, alternatively, in accordance with the terms contained in a
|
---|
14 | ** written agreement between you and Nokia.
|
---|
15 | **
|
---|
16 | ** GNU Free Documentation License
|
---|
17 | ** Alternatively, this file may be used under the terms of the GNU Free
|
---|
18 | ** Documentation License version 1.3 as published by the Free Software
|
---|
19 | ** Foundation and appearing in the file included in the packaging of this
|
---|
20 | ** file.
|
---|
21 | **
|
---|
22 | ** If you have questions regarding the use of this file, please contact
|
---|
23 | ** Nokia at [email protected].
|
---|
24 | ** $QT_END_LICENSE$
|
---|
25 | **
|
---|
26 | ****************************************************************************/
|
---|
27 |
|
---|
28 | /*!
|
---|
29 | \example graphicsview/padnavigator
|
---|
30 | \title Pad Navigator Example
|
---|
31 |
|
---|
32 | The Pad Navigator Example shows how you can use Graphics View together with
|
---|
33 | embedded widgets and Qt's \l{State Machine Framework} to create a simple
|
---|
34 | but useful, dynamic, animated user interface.
|
---|
35 |
|
---|
36 | \image padnavigator-example.png
|
---|
37 |
|
---|
38 | The interface consists of a flippable, rotating pad with icons that can be
|
---|
39 | selected using the arrow keys on your keyboard or keypad. Pressing enter
|
---|
40 | will flip the pad around and reveal its back side, which has a form
|
---|
41 | embedded into a QGraphicsProxyWidget. You can interact with the form, and
|
---|
42 | press the enter key to flip back to the front side of the pad at any time.
|
---|
43 |
|
---|
44 | Graphics View provides the QGraphicsScene class for managing and
|
---|
45 | interacting with a large number of custom-made 2D graphical items derived
|
---|
46 | from the QGraphicsItem class, and a QGraphicsView widget for visualizing
|
---|
47 | the items, with support for zooming and rotation.
|
---|
48 |
|
---|
49 | This example consists of a \c RoundRectItem class, a \c FlippablePad class,
|
---|
50 | a \c PadNavigator class, a \c SplashItem class, and a \c main() function.
|
---|
51 |
|
---|
52 | \section1 RoundRectItem Class Definition
|
---|
53 |
|
---|
54 | The \c RoundRectItem class is used by itself to diplay the icons on the
|
---|
55 | pad, and as a base class for \c FlippablePad, the class for the pad itself.
|
---|
56 | The role of the class is to paint a round rectangle of a specified size and
|
---|
57 | gradient color, and optionally to paint a pixmap icon on top. To support \c
|
---|
58 | FlippablePad it also allows filling its contents with a plain window
|
---|
59 | background color.
|
---|
60 |
|
---|
61 | Let's start by reviewing the \c RoundRectItem class declaration.
|
---|
62 |
|
---|
63 | \snippet examples/graphicsview/padnavigator/roundrectitem.h 0
|
---|
64 |
|
---|
65 | \c RoundRectItem inherits QGraphicsObject, which makes it easy to control
|
---|
66 | its properties using QPropertyAnimation. Its constructor takes a rectangle
|
---|
67 | to determine its bounds, and a color.
|
---|
68 |
|
---|
69 | Besides implementing the mandatory \l{QGraphicsItem::paint()}{paint()} and
|
---|
70 | \l{QGraphicsItem::boundingRect()}{boundingRect()} pure virtual functions,
|
---|
71 | it also provides the \c pixmap and \c fill properties.
|
---|
72 |
|
---|
73 | The \c pixmap property sets an optional pixmap that is drawn on top of the
|
---|
74 | round rectangle. The \c fill property will, when true, fill the round
|
---|
75 | rectangle contents with a fixed QPalette::Window background color.
|
---|
76 | Otherwise the contents are filled using a gradient based on the color
|
---|
77 | passed to \c RoundRectItem's constructor.
|
---|
78 |
|
---|
79 | \snippet examples/graphicsview/padnavigator/roundrectitem.h 1
|
---|
80 |
|
---|
81 | The private data members are:
|
---|
82 |
|
---|
83 | \list
|
---|
84 | \o \c pix: The optional pixmap that is drawn on top of the rectangle.
|
---|
85 | \o \c fillRect: Corresponds to the \c fill property.
|
---|
86 | \o \c color: The configurable gradient color fill of the rectangle.
|
---|
87 | \o \c bounds: The bounds of the rectangle.
|
---|
88 | \o \c gradient: A precalculated gradient used to fill the rectangle.
|
---|
89 | \endlist
|
---|
90 |
|
---|
91 | We will now review the \c RoundRectItem implementation. Let's start by
|
---|
92 | looking at its constructor:
|
---|
93 |
|
---|
94 | \snippet examples/graphicsview/padnavigator/roundrectitem.cpp 0
|
---|
95 |
|
---|
96 | The constructor initializes its member variables and forwards the \c parent
|
---|
97 | argument to QGraphicsObject's constructor. It then constructs the linear
|
---|
98 | gradient that is used in \l{QGraphicsItem::paint()}{paint()} to draw the
|
---|
99 | round rectangle's gradient background. The linear gradient's starting point
|
---|
100 | is at the top-left corner of the bounds, and the end is at the bottom-left
|
---|
101 | corner. The start color is identical to the color passed as an argument,
|
---|
102 | and a slightly darker color is chosen for the final stop.
|
---|
103 |
|
---|
104 | We store this gradient as a member variable to avoid having to recreate the
|
---|
105 | gradient every time the item is repainted.
|
---|
106 |
|
---|
107 | Finally we set the cache mode
|
---|
108 | \l{QGraphicsItem::ItemCoordinateCache}{ItemCoordinateCache}. This mode
|
---|
109 | causes the item's rendering to be cached into an off-screen pixmap that
|
---|
110 | remains persistent as we move and transform the item. This mode is ideal
|
---|
111 | for this example, and works particularily well with OpenGL and OpenGL ES.
|
---|
112 |
|
---|
113 | \snippet examples/graphicsview/padnavigator/roundrectitem.cpp 1
|
---|
114 |
|
---|
115 | The \c pixmap property implementation simple returns the member pixmap, or
|
---|
116 | sets it and then calls \l{QGraphicsItem::update()}{update()}.
|
---|
117 |
|
---|
118 | \snippet examples/graphicsview/padnavigator/roundrectitem.cpp 2
|
---|
119 |
|
---|
120 | As the \l{QGraphicsItem::paint()}{paint()} implementation below draws a
|
---|
121 | simple drop shadow down and to the right of the item, we return a slightly
|
---|
122 | adjusted rectangle from \l{QGraphicsItem::boundingRect()}{boundingRect()}.
|
---|
123 |
|
---|
124 | \snippet examples/graphicsview/padnavigator/roundrectitem.cpp 3
|
---|
125 |
|
---|
126 | The \l{QGraphicsItem::paint()}{paint()} implementation starts by rendering
|
---|
127 | a semi transparent black round rectangle drop shadow, two units down and to
|
---|
128 | the right of the main item.
|
---|
129 |
|
---|
130 | \snippet examples/graphicsview/padnavigator/roundrectitem.cpp 4
|
---|
131 |
|
---|
132 | We then draw the "foreground" round rectangle itself. The fill depends on
|
---|
133 | the \c fill property; if true, we will with a plain QPalette::Window color.
|
---|
134 | We get the corrent brush from QApplication::palette(). We assign a single
|
---|
135 | unit wide pen for the stroke, assign the brush, and then draw the
|
---|
136 | rectangle.
|
---|
137 |
|
---|
138 | \snippet examples/graphicsview/padnavigator/roundrectitem.cpp 5
|
---|
139 |
|
---|
140 | If a pixmap has been assigned to the \e pixmap property, we draw this
|
---|
141 | pixmap in the center of the rectangle item. The pixmaps are scaled to match
|
---|
142 | the size of the icons; in arguably a better approach would have been to
|
---|
143 | store the icons with the right size in the first places.
|
---|
144 |
|
---|
145 | \snippet examples/graphicsview/padnavigator/roundrectitem.cpp 6
|
---|
146 |
|
---|
147 | Finally, for completeness we include the \c fill property implementation.
|
---|
148 | It returns the \c fill member variable's value, and when assigned to, it
|
---|
149 | calls \l{QGraphicsItem::update()}{update()}.
|
---|
150 |
|
---|
151 | As mentioned already, \c RoundRectItem is the base class for \c
|
---|
152 | FlippablePad, which is the class representing the tilting pad itself. We
|
---|
153 | will proceed to reviewing \c FlippablePad.
|
---|
154 |
|
---|
155 | \section1 FlippablePad Class Definition
|
---|
156 |
|
---|
157 | \c FlippablePad is, in addition to its inherited \c RoundRectItem
|
---|
158 | responsibilities, responsible for creating and managing a grid of icons.
|
---|
159 |
|
---|
160 | \snippet examples/graphicsview/padnavigator/flippablepad.h 0
|
---|
161 |
|
---|
162 | Its declaration is very simple: It inherits \c RoundRectItem and does not
|
---|
163 | need any special polymorphic behavior. It's suitable to declare its own
|
---|
164 | constructor, and a getter-function that allows \c PadNavigator to access
|
---|
165 | the icons in the grid by (row, column).
|
---|
166 |
|
---|
167 | The example has no "real" behavior or logic of any kind, and because of
|
---|
168 | that, the icons do not need to provide any \e behavior or special
|
---|
169 | interactions management. In a real application, however, it would be
|
---|
170 | natural for the \c FlippablePad and its icons to handle more of the
|
---|
171 | navigation logic. In this example, we have chosen to leave this to
|
---|
172 | the \c PadNavigator class, which we will get back to below.
|
---|
173 |
|
---|
174 | We will now review the \c FlippablePad implementation. This implementation
|
---|
175 | starts with two helper functions: \c boundsFromSize() and \c
|
---|
176 | posForLocation():
|
---|
177 |
|
---|
178 | \snippet examples/graphicsview/padnavigator/flippablepad.cpp 0
|
---|
179 |
|
---|
180 | \c boundsForSize() takes a QSize argument, and returns the bounding
|
---|
181 | rectangle of the flippable pad item. The QSize determines how many rows and
|
---|
182 | columns the icon grid should have. Each icon is given 150x150 units of
|
---|
183 | space, and this determines the bounds.
|
---|
184 |
|
---|
185 | \snippet examples/graphicsview/padnavigator/flippablepad.cpp 1
|
---|
186 |
|
---|
187 | \c posForLocation() returns the position of an icon given its row and
|
---|
188 | column position. Like \c boundsForSize(), the function assumes each icon is
|
---|
189 | given 150x150 units of space, and that all icons are centered around the
|
---|
190 | flippable pad item's origin (0, 0).
|
---|
191 |
|
---|
192 | \snippet examples/graphicsview/padnavigator/flippablepad.cpp 2
|
---|
193 |
|
---|
194 | The \c FlippablePad constructor passes suitable bounds (using \c
|
---|
195 | boundsForSize()) and specific color to \c RoundRectItem's constructor.
|
---|
196 |
|
---|
197 | \snippet examples/graphicsview/padnavigator/flippablepad.cpp 3
|
---|
198 |
|
---|
199 | It then loads pixmaps from compiled-in resources to use for its icons.
|
---|
200 | QDirIterator is very useful in this context, as it allows us to fetch all
|
---|
201 | resource "*.png" files inside the \c :/images directory without explicitly
|
---|
202 | naming the files.
|
---|
203 |
|
---|
204 | We also make sure not to load more pixmaps than we need.
|
---|
205 |
|
---|
206 | \snippet examples/graphicsview/padnavigator/flippablepad.cpp 4
|
---|
207 |
|
---|
208 | Now that we have the pixmaps, we can create icons, position then and assign
|
---|
209 | pixmaps. We start by finding a suitable size and color for the icons, and
|
---|
210 | initializing a convenient grid structure for storing the icons. This \c
|
---|
211 | iconGrid is also used later to find the icon for a specific (column, row)
|
---|
212 | location.
|
---|
213 |
|
---|
214 | For each row and column in our grid, we proceed to constructing each icon
|
---|
215 | as an instance of \c RoundRectItem. The item is placed by using the \c
|
---|
216 | posForLocation() helper function. To make room for the slip-behind
|
---|
217 | selection item, we give each icon a \l{QGraphicsItem::zValue()}{Z-value} of
|
---|
218 | 1. The pixmaps are distributed to the icons in round-robin fasion.
|
---|
219 |
|
---|
220 | Again, this approach is only suitable for example purposes. In a real-life
|
---|
221 | application where each icon represents a specific action, it would be more
|
---|
222 | natural to assign the pixmaps directly, or that the icons themselves
|
---|
223 | provide suitable pixmaps.
|
---|
224 |
|
---|
225 | \snippet examples/graphicsview/padnavigator/flippablepad.cpp 5
|
---|
226 |
|
---|
227 | Finally, the \c iconAt() function returns a pointer to the icon at a
|
---|
228 | specific row and column. It makes a somewhat bold assumption that the input
|
---|
229 | is valid, which is fair because the \c PadNavigator class only calls this
|
---|
230 | function with correct input.
|
---|
231 |
|
---|
232 | We will now review the \c SplashItem class.
|
---|
233 |
|
---|
234 | \section1 SplashItem Class Definition
|
---|
235 |
|
---|
236 | The \c SplashItem class represents the "splash window", a semitransparent
|
---|
237 | white overlay with text that appears immediately after the application has
|
---|
238 | started, and disappears after pressing any key. The animation is controlled
|
---|
239 | by \c PadNavigator; this class is very simple by itself.
|
---|
240 |
|
---|
241 | \snippet examples/graphicsview/padnavigator/splashitem.h 0
|
---|
242 |
|
---|
243 | The class declaration shows that \c SplashItem inherits QGraphicsObject to
|
---|
244 | allow it to be controlled by QPropertyAnimation. It reimplements the
|
---|
245 | mandatory \l{QGraphicsItem::paint()}{paint()} and
|
---|
246 | \l{QGraphicsItem::boundingRect()}{boundingRect()} pure virtual functions,
|
---|
247 | and keeps a \c text member variable which will contain the information text
|
---|
248 | displayed on this splash item.
|
---|
249 |
|
---|
250 | Let's look at its implementation.
|
---|
251 |
|
---|
252 | \snippet examples/graphicsview/padnavigator/splashitem.cpp 0
|
---|
253 |
|
---|
254 | The constructor forwards to QGraphicsObject as expected, assigns a text
|
---|
255 | message to the \c text member variable, and enables
|
---|
256 | \l{QGraphicsItem::DeviceCoordinateCache}{DeviceCoordinateCache}. This cache
|
---|
257 | mode is suitable because the splash item only moves and is never
|
---|
258 | transformed, and because it contains text, it's important that it has a
|
---|
259 | pixel perfect visual appearance (in constrast to
|
---|
260 | \l{QGraphicsItem::ItemCoordinateCache}{ItemCoordinateCache}, where the
|
---|
261 | visual appearance is not as good).
|
---|
262 |
|
---|
263 | We use caching to avoid having to relayout and rerender the text for each
|
---|
264 | frame. An alterative approach would be to use the new QStaticText class.
|
---|
265 |
|
---|
266 | \snippet examples/graphicsview/padnavigator/splashitem.cpp 1
|
---|
267 |
|
---|
268 | \c SplashItem's bounding rectangle is fixed at (400x175).
|
---|
269 |
|
---|
270 | \snippet examples/graphicsview/padnavigator/splashitem.cpp 2
|
---|
271 |
|
---|
272 | The \l{QGraphicsItem::paint()}{paint()} implementation draws a clipped
|
---|
273 | round rectangle with a thick 2-unit border and a semi-transparent white
|
---|
274 | background. It proceeds to finding a suitable text area by adjusting the
|
---|
275 | splash item's bounding rectangle with 10 units in each side. The text is
|
---|
276 | rendered inside this rectangle, with top-left alignment, and with word
|
---|
277 | wrapping enabled.
|
---|
278 |
|
---|
279 | The main class now remains. We will proceed to reviewing \c PadNavigator.
|
---|
280 |
|
---|
281 | \section1 PadNavigator Class Definition
|
---|
282 |
|
---|
283 | \c PadNavigator represents the main window of our Pad Navigator Example
|
---|
284 | application. It creates and controls a somewhat complex state machine, and
|
---|
285 | several animations. Its class declaration is very simple:
|
---|
286 |
|
---|
287 | \snippet examples/graphicsview/padnavigator/padnavigator.h 0
|
---|
288 |
|
---|
289 | It inherits QGraphicsView and reimplements only one function:
|
---|
290 | \l{QGraphicsView::resizeEvent()}{resizeEvent()}, to ensure the scene is
|
---|
291 | scaled to fit inside the view when resizing the main window.
|
---|
292 |
|
---|
293 | The \c PadNavigator constructor takes a QSize argument that determines the
|
---|
294 | number or rows and columns in the grid.
|
---|
295 |
|
---|
296 | It also keeps a private member instance, \c form, which is the generated
|
---|
297 | code for the pad's back side item's QGraphicsProxyWidget-embedded form.
|
---|
298 |
|
---|
299 | \snippet examples/graphicsview/padnavigator/padnavigator.cpp 0
|
---|
300 |
|
---|
301 | \c PadNavigator's constructor is a bit long. In short, its job is to create
|
---|
302 | all items, including the \c FlippablePad, the \c SplashItem and the
|
---|
303 | QGraphicsProxyWidget \c backItem, and then to set up all animations, states
|
---|
304 | and transitions that control the behavior of the application.
|
---|
305 |
|
---|
306 | It starts out simple, by forwarding to QGraphicsView's constructor.
|
---|
307 |
|
---|
308 | \snippet examples/graphicsview/padnavigator/padnavigator.cpp 1
|
---|
309 |
|
---|
310 | The first item to be created is \c SplashItem. This is going to be a top-level
|
---|
311 | item in the scene, next to \c FlippablePad, and stacked on top of it, so we
|
---|
312 | assign it a \l{QGraphicsItem::zValue()}{Z-value} of 1.
|
---|
313 |
|
---|
314 | \snippet examples/graphicsview/padnavigator/padnavigator.cpp 2
|
---|
315 |
|
---|
316 | Now we construct the \c FlippablePad item, passing its column-row count to
|
---|
317 | its constructor.
|
---|
318 |
|
---|
319 | The pad is constrolled by three transformations, and we create one
|
---|
320 | QGraphicsRotation object for each of these.
|
---|
321 |
|
---|
322 | \list
|
---|
323 | \o \c flipRotation: Rotates the grid around its Qt::YAxis. This rotation is
|
---|
324 | animated from 0 to 180, and eventually back, when enter is pressed on the
|
---|
325 | keyboard, flipping the pad around.
|
---|
326 | \o \c xRotation: Rotates the grid around its Qt::XAxis. This is used to
|
---|
327 | tilt the pad vertically corresponding to which item is currently selected.
|
---|
328 | This way, the selected item is always kept in front.
|
---|
329 | \o \c yRotation: Rotates the grid around its Qt::YAxis. This is used to
|
---|
330 | tilt the pad horizontally corresponding to which item is selected. This
|
---|
331 | way, the selected item is always kept in front.
|
---|
332 | \endlist
|
---|
333 |
|
---|
334 | The combination of all three rotations is assigned via
|
---|
335 | QGraphicsItem::setTransformations().
|
---|
336 |
|
---|
337 | \snippet examples/graphicsview/padnavigator/padnavigator.cpp 3
|
---|
338 |
|
---|
339 | Now we construct the QGraphicsProxyWidget-embedded \c backItem. The proxy
|
---|
340 | widget is created as a child of the pad. We create a new QWidget and
|
---|
341 | populate it with the \c form member. To ensure the \c hostName line edit is
|
---|
342 | the first to receive input focus when this item is shown, we call
|
---|
343 | \l{QWidget::setFocus()}{setFocus()} immediately. This will not give the
|
---|
344 | widget focus right away; it will only prepare the item to automatically
|
---|
345 | receive focus once it is shown.
|
---|
346 |
|
---|
347 | The QWidget based form is embedded into the proxy widget. The proxy is
|
---|
348 | hidden initially; we only want to show it when the pad is rotated at least
|
---|
349 | 90 degrees, and we also rotate the proxy itself by 180 degrees. This way we
|
---|
350 | give the impression that the proxy widget is "behind" the flipped pad, when
|
---|
351 | in fact, it's actually \e{on top of it}.
|
---|
352 |
|
---|
353 | We enable \l{QGraphicsItem::ItemCoordinateCache}{ItemCoordinateCache} to
|
---|
354 | ensure the flip animation can run smoothly.
|
---|
355 |
|
---|
356 | \snippet examples/graphicsview/padnavigator/padnavigator.cpp 4
|
---|
357 |
|
---|
358 | We now create the selection item. This is simply another instance of \c
|
---|
359 | RoundRectItem that is slightly larger than the icons on the pad. We create
|
---|
360 | it as an immediate child of the \c FlippablePad, so the selection item is a
|
---|
361 | sibling to all the icons. By giving it a
|
---|
362 | \l{QGraphicsItem::zValue()}{Z-value} of 0.5 we ensure it will slide beteen
|
---|
363 | the pad and its icons.
|
---|
364 |
|
---|
365 | What follows now is a series of animation initializations.
|
---|
366 |
|
---|
367 | \snippet examples/graphicsview/padnavigator/padnavigator.cpp 5
|
---|
368 |
|
---|
369 | We begin with the animations that apply to the splash item. The first
|
---|
370 | animation, \c smoothSplashMove, ensures that the "y" property of \c splash
|
---|
371 | will be animated with a 250-millisecond duration
|
---|
372 | \l{QEasingCurve::InQuad}{InQuad} easing function. \c smoothSplashOpacity
|
---|
373 | ensures the opacity of \c splash eases in and out in 250 milliseconds.
|
---|
374 |
|
---|
375 | The values are assigned by \c PadNavigator's state machine, which is
|
---|
376 | created later.
|
---|
377 |
|
---|
378 | \snippet examples/graphicsview/padnavigator/padnavigator.cpp 6
|
---|
379 |
|
---|
380 | These are the animations that control the selection item's movement and the
|
---|
381 | \c xRotation and \c yRotation QGraphicsRotation objects that tilt the pad.
|
---|
382 | All animations have a duration of 125 milliseconds, and they all use the
|
---|
383 | \l{QEasingCurve::InOutQuad}{InOutQuad} easing function.
|
---|
384 |
|
---|
385 | \snippet examples/graphicsview/padnavigator/padnavigator.cpp 7
|
---|
386 |
|
---|
387 | We now create the animations that control the flip-effect when you press
|
---|
388 | the enter key. The main goal is to rotate the pad by 180 degrees or back,
|
---|
389 | but we also need to make sure the selection item's tilt rotations are reset
|
---|
390 | back to 0 when the pad is flipped, and restored back to their original
|
---|
391 | values when flipped back:
|
---|
392 |
|
---|
393 | \list
|
---|
394 | \o \c smoothFlipRotation: Animates the main 180 degree rotation of the pad.
|
---|
395 | \o \c smoothFlipScale: Scales the pad out and then in again while the pad is rotating.
|
---|
396 | \o \c smoothFlipXRotation: Animates the selection item's X-tilt to 0 and back.
|
---|
397 | \o \c smoothFlipYRotation: Animates the selection item's Y-tilt to 0 and back.
|
---|
398 | \o \c flipAnimation: A parallel animation group that ensures all the above animations are run in parallel.
|
---|
399 | \endlist
|
---|
400 |
|
---|
401 | All animations are given a 500 millisecond duration and an
|
---|
402 | \l{QEasingCurve::InOutQuad}{InOutQuad} easing function.
|
---|
403 |
|
---|
404 | It's worth taking a close look at \c smoothFlipScale. This animation's
|
---|
405 | start and end values are both 1.0, but at animation step 0.5 the
|
---|
406 | animation's value is 0.7. This means that after 50% of the animation's
|
---|
407 | duration, or 250 milliseconds, the pad will be scaled down to 0.7x of its
|
---|
408 | original size, which gives a great visual effect while flipping.
|
---|
409 |
|
---|
410 | \snippet examples/graphicsview/padnavigator/padnavigator.cpp 8
|
---|
411 |
|
---|
412 | This section uses a trick to ensure that certain properties are assigned
|
---|
413 | precisely when the flip animation passes 50%, or 90 degrees, rotation. In
|
---|
414 | short, the pad's icons and selection item are all hidden, the pad's \c fill
|
---|
415 | property is enabled, and \c backItem is shown when flipping over. When
|
---|
416 | flipping back, the reverse properties are applied.
|
---|
417 |
|
---|
418 | The way this is achieved is by running a sequential animation in parallel
|
---|
419 | to the other animations. This sequence, dubbed \c setVariablesSequence,
|
---|
420 | starts with a 250 millisecond pause, and then executes several animations
|
---|
421 | with a duration of 0. Each animation will ensure that properties are set
|
---|
422 | immediate at this point.
|
---|
423 |
|
---|
424 | This approach can also be used to call functions or set any other
|
---|
425 | properties at a specific time while an animation is running.
|
---|
426 |
|
---|
427 | \snippet examples/graphicsview/padnavigator/padnavigator.cpp 9
|
---|
428 |
|
---|
429 | We will now create the state machine. The whole \c PadNavigator state
|
---|
430 | machinery is controlled by one single state machine that has a
|
---|
431 | straight-forward state structure. The state engine itself is created
|
---|
432 | as a child of the \c PadNavigator itself. We then create three top level
|
---|
433 | states:
|
---|
434 |
|
---|
435 | \list
|
---|
436 | \o \c splashState: The initial state where the splash item is visible.
|
---|
437 | \o \c frontState: The base state where the splash is gone and we can see
|
---|
438 | the front side of the pad, and navigate the selection item.
|
---|
439 | \o \c backState: The flipped state where the \c backItem is visible, and we
|
---|
440 | can interact with the QGraphicsProxyWidget-embedded form.
|
---|
441 | \endlist
|
---|
442 |
|
---|
443 | \snippet examples/graphicsview/padnavigator/padnavigator.cpp 10
|
---|
444 |
|
---|
445 | Each state assigns specific properties to objects on entry. Most
|
---|
446 | interesting perhaps is the assignment of the value 0.0 to the pad's \c
|
---|
447 | flipRotation angle property when in \c frontState, and 180.0 when in \c
|
---|
448 | backState. At the end of this section we register default animations with
|
---|
449 | the state engine; these animations will apply to their respective objects
|
---|
450 | and properties for any state transition. Otherwise it's common to assign
|
---|
451 | animations to specific transitions.
|
---|
452 |
|
---|
453 | The \c splashState state is set as the initial state. This is required
|
---|
454 | before we start the state engine. We proceed with creating some
|
---|
455 | transitions.
|
---|
456 |
|
---|
457 | \snippet examples/graphicsview/padnavigator/padnavigator.cpp 11
|
---|
458 |
|
---|
459 | QEventTransition defines a very flexible transition type. You can use this
|
---|
460 | class to trigger a transition based on an object receiving an event of a
|
---|
461 | specific type. In this case, we would like to transition from \c
|
---|
462 | splashState into \c frontState if \c PadNavigator receives any key press
|
---|
463 | event (QEvent::KeyPress).
|
---|
464 |
|
---|
465 | We register the \c splashItem's animations to this transition to ensure they
|
---|
466 | are used to animate the item's movement and opacity.
|
---|
467 |
|
---|
468 | \snippet examples/graphicsview/padnavigator/padnavigator.cpp 12
|
---|
469 |
|
---|
470 | We use QKeyEventTransition to capture specific key events. In this case, we
|
---|
471 | detect that the user presses Qt::Key_Return or Qt::Key_Enter, and use this
|
---|
472 | to trigger transitions between \c frontState and backState. We register \c
|
---|
473 | flipAnimation, our complex parallel animation group, with these
|
---|
474 | transitions.
|
---|
475 |
|
---|
476 | We continue by defining the states for each of the icons in the grid.
|
---|
477 |
|
---|
478 | \snippet examples/graphicsview/padnavigator/padnavigator.cpp 13
|
---|
479 |
|
---|
480 | We will use state groups to control transitions between icons. Each icon
|
---|
481 | represents a \e substate of \c frontState. We will then define transitions
|
---|
482 | between the states by detecting key presses, using QKeyEventTransition.
|
---|
483 |
|
---|
484 | We start by creating all the substates, and at the same time we create a
|
---|
485 | temporary grid structure for the states to make it easier to find which
|
---|
486 | states represents icons that are up, down, left and to the right each
|
---|
487 | other.
|
---|
488 |
|
---|
489 | Once the first substate is known, we set this up as the initial substate of
|
---|
490 | \c frontState. We will use the (0, 0), or top-left, icon for the initial
|
---|
491 | substate. We initialze the selection item's position to be exactly where
|
---|
492 | the top-left icon is.
|
---|
493 |
|
---|
494 | \snippet examples/graphicsview/padnavigator/padnavigator.cpp 14
|
---|
495 |
|
---|
496 | We can now create four transitions for each icon. Each transition ensures
|
---|
497 | that we move to the state corresponding to which arrow key has been
|
---|
498 | pressed. It's clear from this techinique that we could design any other
|
---|
499 | specific transitions to and from each of the sub states depending on these
|
---|
500 | and other keys.
|
---|
501 |
|
---|
502 | \snippet examples/graphicsview/padnavigator/padnavigator.cpp 15
|
---|
503 |
|
---|
504 | Also, for each of the icons, we assign suitable values to the \c xRotation
|
---|
505 | and \c yRotation objects' "angle"-properties. If you recall, these
|
---|
506 | properties "tilt" the pad corresponding to which item is currently
|
---|
507 | selected. We ensure each icon is invisible when the pad is flipped, and
|
---|
508 | visible when the pad is not flipped. To ensure the visible property is
|
---|
509 | assigned at the right time, we add property-controlling animations to the
|
---|
510 | \c setVariableSequence animation defined earlier.
|
---|
511 |
|
---|
512 | \snippet examples/graphicsview/padnavigator/padnavigator.cpp 16
|
---|
513 |
|
---|
514 | We are now finished with all states, transitions, and animations. We now
|
---|
515 | create the scene that will contain all our items. The scene gets a defined
|
---|
516 | background pixmap, and we disable item indexing (as most items in this
|
---|
517 | scene are animated). We add our \c pad item to the scene, and use its
|
---|
518 | bounding rectangle to fixate the scene rectangle. This rectangle is used by
|
---|
519 | the view to find a suitable size for the application window.
|
---|
520 |
|
---|
521 | Then the scene is assigned to the view, or in our case, \c PadNavigator
|
---|
522 | itself.
|
---|
523 |
|
---|
524 | \snippet examples/graphicsview/padnavigator/padnavigator.cpp 17
|
---|
525 |
|
---|
526 | Now that the scene has received its final size, we can position the splash
|
---|
527 | item at the very top, find its fade-out position, and add it to the scene.
|
---|
528 |
|
---|
529 | \snippet examples/graphicsview/padnavigator/padnavigator.cpp 18
|
---|
530 |
|
---|
531 | The view toggles a few necessary properties:
|
---|
532 |
|
---|
533 | \list
|
---|
534 | \o It disables its scroll bars - this application has no use for scroll bars.
|
---|
535 | \o It assigns a minimum size. This is necessary to avoid numerical errors
|
---|
536 | in our fit-in-view \c resizeEvent() implementation.
|
---|
537 | \o It sets \l{QGraphicsView::FullViewportUpdate}{FullViewportUpdate}, to
|
---|
538 | ensure QGraphicsView doesn't spend time figuring out precisely what needs
|
---|
539 | to be redrawn. This application is very simple - if anything changes,
|
---|
540 | everything is updated.
|
---|
541 | \o It enables background caching - this makes no performance difference
|
---|
542 | with OpenGL, but without OpenGL it avoids unnecessary re-scaling of the
|
---|
543 | background pixmap.
|
---|
544 | \o It sets render hints that increase rendering quality.
|
---|
545 | \o If OpenGL is supported, a QGLWidget viewport is assigned to the view.
|
---|
546 | \endlist
|
---|
547 |
|
---|
548 | Finally, we start the state engine.
|
---|
549 |
|
---|
550 | \snippet examples/graphicsview/padnavigator/padnavigator.cpp 19
|
---|
551 |
|
---|
552 | The \l{QGraphicsView::resizeEvent()}{resizeEvent()} implementation calls
|
---|
553 | the base implementation, and then calls QGraphicsView::fitInView() to scale
|
---|
554 | the scene so that it fits perfectly inside the view.
|
---|
555 |
|
---|
556 | By resizing the main application window, you can see this effect yourself.
|
---|
557 | The scene contents grow when you make the window larger, and shrink when
|
---|
558 | you make it smaller, while keeping the aspect ratio intact.
|
---|
559 |
|
---|
560 | \section1 The main() Function
|
---|
561 |
|
---|
562 | \snippet examples/graphicsview/padnavigator/main.cpp 0
|
---|
563 |
|
---|
564 | The \c main function creates the QApplication instance, uses
|
---|
565 | Q_INIT_RESOURCE to ensure our compiled-in resources aren't removed by the
|
---|
566 | linker, and then creates a 3x3 \c PadNavigator instance and shows it.
|
---|
567 |
|
---|
568 | Our flippable pad shows up with a suitable splash item once control returns
|
---|
569 | to the event loop.
|
---|
570 |
|
---|
571 | \section1 Performance Notes
|
---|
572 |
|
---|
573 | The example uses OpenGL if this is available, to achieve optimal
|
---|
574 | performance; otherwise perspective tranformations can be quite costly.
|
---|
575 |
|
---|
576 | Although this example does use QGraphicsProxyWidget to demonstrate
|
---|
577 | integration of Qt widget components integrated into Graphics View, using
|
---|
578 | QGraphicsProxyWidget comes with a performance penalty, and is therefore not
|
---|
579 | recommended for embedded development.
|
---|
580 |
|
---|
581 | This example uses extensive item caching to avoid rerendering of static
|
---|
582 | elements, at the expense of graphics memory.
|
---|
583 | */
|
---|