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 sql/drilldown
|
---|
30 | \title Drill Down Example
|
---|
31 |
|
---|
32 | The Drill Down example shows how to read data from a database as
|
---|
33 | well as submit changes, using the QSqlRelationalTableModel and
|
---|
34 | QDataWidgetMapper classes.
|
---|
35 |
|
---|
36 | \image drilldown-example.png Screenshot of the Drill Down Example
|
---|
37 |
|
---|
38 | When running the example application, a user can retrieve
|
---|
39 | information about each of Nokia's Qt offices by clicking the
|
---|
40 | corresponding image. The application pops up an information window
|
---|
41 | displaying the data, and allows the users to alter the location
|
---|
42 | description as well as the image. The main view will be updated
|
---|
43 | when the users submit their changes.
|
---|
44 |
|
---|
45 | The example consists of three classes:
|
---|
46 |
|
---|
47 | \list
|
---|
48 | \o \c ImageItem is a custom graphics item class used to
|
---|
49 | display the office images.
|
---|
50 |
|
---|
51 | \o \c View is the main application widget allowing the user to
|
---|
52 | browse through the various locations.
|
---|
53 |
|
---|
54 | \o \c InformationWindow displays the requested information,
|
---|
55 | allowing the users to alter it and submit their changes to the
|
---|
56 | database.
|
---|
57 | \endlist
|
---|
58 |
|
---|
59 | We will first take a look at the \c InformationWindow class to see
|
---|
60 | how you can read and modify data from a database. Then we will
|
---|
61 | review the main application widget, i.e., the \c View class, and
|
---|
62 | the associated \c ImageItem class.
|
---|
63 |
|
---|
64 | \section1 InformationWindow Class Definition
|
---|
65 |
|
---|
66 | The \c InformationWindow class is a custom widget inheriting
|
---|
67 | QWidget:
|
---|
68 |
|
---|
69 | \snippet examples/sql/drilldown/informationwindow.h 0
|
---|
70 |
|
---|
71 | When we create an information window, we pass the associated
|
---|
72 | location ID, a parent, and a pointer to the database, to the
|
---|
73 | constructor. We will use the database pointer to populate our
|
---|
74 | window with data, while passing the parent parameter on to the
|
---|
75 | base class. The ID is stored for future reference.
|
---|
76 |
|
---|
77 | Once a window is created, we will use the public \c id() function
|
---|
78 | to locate it whenever information for the given location is
|
---|
79 | requested. We will also use the ID to update the main application
|
---|
80 | widget when the users submit their changes to the database, i.e.,
|
---|
81 | we will emit a signal carrying the ID and file name as parameters
|
---|
82 | whenever the users changes the associated image.
|
---|
83 |
|
---|
84 | \snippet examples/sql/drilldown/informationwindow.h 1
|
---|
85 |
|
---|
86 | Since we allow the users to alter some of the location data, we
|
---|
87 | must provide functionality for reverting and submitting their
|
---|
88 | changes. The \c enableButtons() slot is provided for convenience
|
---|
89 | to enable and disable the various buttons when required.
|
---|
90 |
|
---|
91 | \snippet examples/sql/drilldown/informationwindow.h 2
|
---|
92 |
|
---|
93 | The \c createButtons() function is also a convenience function,
|
---|
94 | provided to simplify the constructor. As mentioned above we store
|
---|
95 | the location ID for future reference. We also store the name of
|
---|
96 | the currently displayed image file to be able to determine when to
|
---|
97 | emit the \c imageChanged() signal.
|
---|
98 |
|
---|
99 | The information window uses the QLabel class to display the office
|
---|
100 | location and the country. The associated image file is displayed
|
---|
101 | using a QComboBox instance while the description is displayed using
|
---|
102 | QTextEdit. In addition, the window has three buttons to control
|
---|
103 | the data flow and whether the window is shown or not.
|
---|
104 |
|
---|
105 | Finally, we declare a \e mapper. The QDataWidgetMapper class
|
---|
106 | provides mapping between a section of a data model to widgets. We
|
---|
107 | will use the mapper to extract data from the given database,
|
---|
108 | updating the database whenever the user modifies the data.
|
---|
109 |
|
---|
110 | \section1 InformationWindow Class Implementation
|
---|
111 |
|
---|
112 | The constructor takes three arguments: a location ID, a database
|
---|
113 | pointer and a parent widget. The database pointer is actually a
|
---|
114 | pointer to a QSqlRelationalTableModel object providing an editable
|
---|
115 | data model (with foreign key support) for our database table.
|
---|
116 |
|
---|
117 | \snippet examples/sql/drilldown/informationwindow.cpp 0
|
---|
118 | \snippet examples/sql/drilldown/informationwindow.cpp 1
|
---|
119 |
|
---|
120 | First we create the various widgets required to display the data
|
---|
121 | contained in the database. Most of the widgets are created in a
|
---|
122 | straight forward manner. But note the combobox displaying the
|
---|
123 | name of the image file:
|
---|
124 |
|
---|
125 | \snippet examples/sql/drilldown/informationwindow.cpp 2
|
---|
126 |
|
---|
127 | In this example, the information about the offices are stored in a
|
---|
128 | database table called "offices". When creating the model,
|
---|
129 | we will use a foreign key to establish a relation between this
|
---|
130 | table and a second data base table, "images", containing the names
|
---|
131 | of the available image files. We will get back to how this is done
|
---|
132 | when reviewing the \c View class. The rationale for creating such
|
---|
133 | a relation though, is that we want to ensure that the user only
|
---|
134 | can choose between predefined image files.
|
---|
135 |
|
---|
136 | The model corresponding to the "images" database table, is
|
---|
137 | available through the QSqlRelationalTableModel's \l
|
---|
138 | {QSqlRelationalTableModel::}{relationModel()} function, requiring
|
---|
139 | the foreign key (in this case the "imagefile" column number) as
|
---|
140 | argument. We use QComboBox's \l {QComboBox::}{setModel()} function
|
---|
141 | to make the combobox use the "images" model. And, since this model
|
---|
142 | has two columns ("locationid" and "file"), we also specify which
|
---|
143 | column we want to be visible using the QComboBox::setModelColumn()
|
---|
144 | function.
|
---|
145 |
|
---|
146 | \snippet examples/sql/drilldown/informationwindow.cpp 3
|
---|
147 |
|
---|
148 | Then we create the mapper. The QDataWidgetMapper class allows us
|
---|
149 | to create data-aware widgets by mapping them to sections of an
|
---|
150 | item model.
|
---|
151 |
|
---|
152 | The \l {QDataWidgetMapper::}{addMapping()} function adds a mapping
|
---|
153 | between the given widget and the specified section of the
|
---|
154 | model. If the mapper's orientation is horizontal (the default) the
|
---|
155 | section is a column in the model, otherwise it is a row. We call
|
---|
156 | the \l {QDataWidgetMapper::}{setCurrentIndex()} function to
|
---|
157 | initialize the widgets with the data associated with the given
|
---|
158 | location ID. Every time the current index changes, all the widgets
|
---|
159 | are updated with the contents from the model.
|
---|
160 |
|
---|
161 | We also set the mapper's submit policy to
|
---|
162 | QDataWidgetMapper::ManualSubmit. This means that no data is
|
---|
163 | submitted to the database until the user expliclity requests a
|
---|
164 | submit (the alternative is QDataWidgetMapper::AutoSubmit,
|
---|
165 | automatically submitting changes when the corresponding widget
|
---|
166 | looses focus). Finally, we specify the item delegate the mapper
|
---|
167 | view should use for its items. The QSqlRelationalDelegate class
|
---|
168 | represents a delegate that unlike the default delegate, enables
|
---|
169 | combobox functionality for fields that are foreign keys into other
|
---|
170 | tables (like "imagefile" in our "trolltechoffices" table).
|
---|
171 |
|
---|
172 | \snippet examples/sql/drilldown/informationwindow.cpp 4
|
---|
173 |
|
---|
174 | Finally, we connect the "something's changed" signals in the
|
---|
175 | editors to our custom \c enableButtons() slot, enabling the users
|
---|
176 | to either submit or revert their changes. We add all the widgets
|
---|
177 | into a layout, store the location ID and the name of the displayed
|
---|
178 | image file for future reference, and set the window title and
|
---|
179 | initial size.
|
---|
180 |
|
---|
181 | Note that we also set the Qt::Window window flag to indicate that
|
---|
182 | our widget is in fact a window, with a window system frame and a
|
---|
183 | title bar.
|
---|
184 |
|
---|
185 | \snippet examples/sql/drilldown/informationwindow.cpp 5
|
---|
186 |
|
---|
187 | When a window is created, it is not deleted until the main
|
---|
188 | application exits (i.e., if the user closes the information
|
---|
189 | window, it is only hidden). For this reason we do not want to
|
---|
190 | create more than one \c InformationWindow object for each
|
---|
191 | location, and we provide the public \c id() function to be able to
|
---|
192 | determine whether a window already exists for a given location
|
---|
193 | when the user requests information about it.
|
---|
194 |
|
---|
195 | \snippet examples/sql/drilldown/informationwindow.cpp 6
|
---|
196 |
|
---|
197 | The \c revert() slot is triggered whenever the user hits the \gui
|
---|
198 | Revert button.
|
---|
199 |
|
---|
200 | Since we set the QDataWidgetMapper::ManualSubmit submit policy,
|
---|
201 | none of the user's changes are written back to the model unless
|
---|
202 | the user expliclity choose to submit all of them. Nevertheless, we
|
---|
203 | can use the QDataWidgetMapper's \l {QDataWidgetMapper::}{revert()}
|
---|
204 | slot to reset the editor widgets, repopulating all widgets with
|
---|
205 | the current data of the model.
|
---|
206 |
|
---|
207 | \snippet examples/sql/drilldown/informationwindow.cpp 7
|
---|
208 |
|
---|
209 | Likewise, the \c submit() slot is triggered whenever the users
|
---|
210 | decide to submit their changes by pressing the \gui Submit button.
|
---|
211 |
|
---|
212 | We use QDataWidgetMapper's \l {QDataWidgetMapper::}{submit()} slot
|
---|
213 | to submit all changes from the mapped widgets to the model,
|
---|
214 | i.e. to the database. For every mapped section, the item delegate
|
---|
215 | will then read the current value from the widget and set it in the
|
---|
216 | model. Finally, the \e model's \l {QAbstractItemModel::}{submit()}
|
---|
217 | function is invoked to let the model know that it should submit
|
---|
218 | whatever it has cached to the permanent storage.
|
---|
219 |
|
---|
220 | Note that before any data is submitted, we check if the user has
|
---|
221 | chosen another image file using the previously stored \c
|
---|
222 | displayedImage variable as reference. If the current and stored
|
---|
223 | file names differ, we store the new file name and emit the \c
|
---|
224 | imageChanged() signal.
|
---|
225 |
|
---|
226 | \snippet examples/sql/drilldown/informationwindow.cpp 8
|
---|
227 |
|
---|
228 | The \c createButtons() function is provided for convenience, i.e.,
|
---|
229 | to simplify the constructor.
|
---|
230 |
|
---|
231 | We make the \gui Close button the default button, i.e., the button
|
---|
232 | that is pressed when the user presses \gui Enter, and connect its
|
---|
233 | \l {QPushButton::}{clicked()} signal to the widget's \l
|
---|
234 | {QWidget::}{close()} slot. As mentioned above closing the window
|
---|
235 | only hides the widget; it is not deleted. We also connect the \gui
|
---|
236 | Submit and \gui Revert buttons to the corresponding \c submit()
|
---|
237 | and \c revert() slots.
|
---|
238 |
|
---|
239 | \snippet examples/sql/drilldown/informationwindow.cpp 9
|
---|
240 |
|
---|
241 | The QDialogButtonBox class is a widget that presents buttons in a
|
---|
242 | layout that is appropriate to the current widget style. Dialogs
|
---|
243 | like our information window, typically present buttons in a layout
|
---|
244 | that conforms to the interface guidelines for that
|
---|
245 | platform. Invariably, different platforms have different layouts
|
---|
246 | for their dialogs. QDialogButtonBox allows us to add buttons,
|
---|
247 | automatically using the appropriate layout for the user's desktop
|
---|
248 | environment.
|
---|
249 |
|
---|
250 | Most buttons for a dialog follow certain roles. We give the \gui
|
---|
251 | Submit and \gui Revert buttons the \l
|
---|
252 | {QDialogButtonBox::ButtonRole}{reset} role, i.e., indicating that
|
---|
253 | pressing the button resets the fields to the default values (in
|
---|
254 | our case the information contained in the database). The \l
|
---|
255 | {QDialogButtonBox::ButtonRole}{reject} role indicates that
|
---|
256 | clicking the button causes the dialog to be rejected. On the other
|
---|
257 | hand, since we only hide the information window, any changes that
|
---|
258 | the user has made wil be preserved until the user expliclity
|
---|
259 | revert or submit them.
|
---|
260 |
|
---|
261 | \snippet examples/sql/drilldown/informationwindow.cpp 10
|
---|
262 |
|
---|
263 | The \c enableButtons() slot is called to enable the buttons
|
---|
264 | whenever the user changes the presented data. Likewise, when the
|
---|
265 | data the user choose to submit the changes, the buttons are
|
---|
266 | disabled to indicate that the current data is stored in the
|
---|
267 | database.
|
---|
268 |
|
---|
269 | This completes the \c InformationWindow class. Let's take a look
|
---|
270 | at how we have used it in our example application.
|
---|
271 |
|
---|
272 | \section1 View Class Definition
|
---|
273 |
|
---|
274 | The \c View class represents the main application window and
|
---|
275 | inherits QGraphicsView:
|
---|
276 |
|
---|
277 | \snippet examples/sql/drilldown/view.h 0
|
---|
278 | \codeline
|
---|
279 | \snippet examples/sql/drilldown/view.h 1
|
---|
280 |
|
---|
281 | The QGraphicsView class is part of the \l {Graphics View
|
---|
282 | Framework} which we will use to display the images of Nokia's
|
---|
283 | Qt offices. To be able to respond to user interaction;
|
---|
284 | i.e., showing the
|
---|
285 | appropriate information window whenever the user clicks one of the
|
---|
286 | office images, we reimplement QGraphicsView's \l
|
---|
287 | {QGraphicsView::}{mouseReleaseEvent()} function.
|
---|
288 |
|
---|
289 | Note that the constructor expects the names of two database
|
---|
290 | tables: One containing the detailed information about the offices,
|
---|
291 | and another containing the names of the available image files. We
|
---|
292 | also provide a private \c updateImage() slot to catch \c
|
---|
293 | {InformationWindow}'s \c imageChanged() signal that is emitted
|
---|
294 | whenever the user changes a location's image.
|
---|
295 |
|
---|
296 | \snippet examples/sql/drilldown/view.h 2
|
---|
297 |
|
---|
298 | The \c addItems() function is a convenience function provided to
|
---|
299 | simplify the constructor. It is called only once, creating the
|
---|
300 | various items and adding them to the view.
|
---|
301 |
|
---|
302 | The \c findWindow() function, on the other hand, is frequently
|
---|
303 | used. It is called from the \c showInformation() function to
|
---|
304 | detemine whether a window is already created for the given
|
---|
305 | location (whenever we create an \c InformationWindow object, we
|
---|
306 | store a reference to it in the \c informationWindows list). The
|
---|
307 | latter function is in turn called from our custom \c
|
---|
308 | mouseReleaseEvent() implementation.
|
---|
309 |
|
---|
310 | \snippet examples/sql/drilldown/view.h 3
|
---|
311 |
|
---|
312 | Finally we declare a QSqlRelationalTableModel pointer. As
|
---|
313 | previously mentioned, the QSqlRelationalTableModel class provides
|
---|
314 | an editable data model with foreign key support. There are a
|
---|
315 | couple of things you should keep in mind when using the
|
---|
316 | QSqlRelationalTableModel class: The table must have a primary key
|
---|
317 | declared and this key cannot contain a relation to another table,
|
---|
318 | i.e., it cannot be a foreign key. Note also that if a relational
|
---|
319 | table contains keys that refer to non-existent rows in the
|
---|
320 | referenced table, the rows containing the invalid keys will not be
|
---|
321 | exposed through the model. It is the user's or the database's
|
---|
322 | responsibility to maintain referential integrity.
|
---|
323 |
|
---|
324 | \section1 View Class Implementation
|
---|
325 |
|
---|
326 | Although the constructor requests the names of both the table
|
---|
327 | containing office details as well as the table containing the
|
---|
328 | names of the available image files, we only have to create a
|
---|
329 | QSqlRelationalTableModel object for the office table:
|
---|
330 |
|
---|
331 | \snippet examples/sql/drilldown/view.cpp 0
|
---|
332 |
|
---|
333 | The reason is that once we have a model with the office details,
|
---|
334 | we can create a relation to the available image files using
|
---|
335 | QSqlRelationalTableModel's \l
|
---|
336 | {QSqlRelationalTableModel::}{setRelation()} function. This
|
---|
337 | function creates a foreign key for the given model column. The key
|
---|
338 | is specified by the provided QSqlRelation object constructed by
|
---|
339 | the name of the table the key refers to, the field the key is
|
---|
340 | mapping to and the field that should be presented to the user.
|
---|
341 |
|
---|
342 | Note that setting the table only specifies which table the model
|
---|
343 | operates on, i.e., we must explicitly call the model's \l
|
---|
344 | {QSqlRelationalTableModel::}{select()} function to populate our
|
---|
345 | model.
|
---|
346 |
|
---|
347 | \snippet examples/sql/drilldown/view.cpp 1
|
---|
348 |
|
---|
349 | Then we create the contents of our view, i.e., the scene and its
|
---|
350 | items. The location labels are regular QGraphicsTextItem objects,
|
---|
351 | and the "Qt" logo is represented by a QGraphicsPixmapItem
|
---|
352 | object. The images, on the other hand, are instances of the \c
|
---|
353 | ImageItem class (derived from QGraphicsPixmapItem). We will get
|
---|
354 | back to this shortly when reviewing the \c addItems() function.
|
---|
355 |
|
---|
356 | Finally, we set the main application widget's size constraints and
|
---|
357 | window title.
|
---|
358 |
|
---|
359 | \snippet examples/sql/drilldown/view.cpp 3
|
---|
360 |
|
---|
361 | The \c addItems() function is called only once, i.e., when
|
---|
362 | creating the main application window. For each row in the database
|
---|
363 | table, we first extract the corresponding record using the model's
|
---|
364 | \l {QSqlRelationalTableModel::}{record()} function. The QSqlRecord
|
---|
365 | class encapsulates both the functionality and characteristics of a
|
---|
366 | database record, and supports adding and removing fields as well
|
---|
367 | as setting and retrieving field values. The QSqlRecord::value()
|
---|
368 | function returns the value of the field with the given name or
|
---|
369 | index as a QVariant object.
|
---|
370 |
|
---|
371 | For each record, we create a label item as well as an image item,
|
---|
372 | calculate their position and add them to the scene. The image
|
---|
373 | items are represented by instances of the \c ImageItem class. The
|
---|
374 | reason we must create a custom item class is that we want to catch
|
---|
375 | the item's hover events, animating the item when the mouse cursor
|
---|
376 | is hovering over the image (by default, no items accept hover
|
---|
377 | events). Please see the \l{Graphics View Framework} documentation
|
---|
378 | and the \l{Graphics View Examples} for more details.
|
---|
379 |
|
---|
380 | \snippet examples/sql/drilldown/view.cpp 5
|
---|
381 |
|
---|
382 | We reimplement QGraphicsView's \l
|
---|
383 | {QGraphicsView::}{mouseReleaseEvent()} event handler to respond to
|
---|
384 | user interaction. If the user clicks any of the image items, this
|
---|
385 | function calls the private \c showInformation() function to pop up
|
---|
386 | the associated information window.
|
---|
387 |
|
---|
388 | The \l {Graphics View Framework} provides the qgraphicsitem_cast()
|
---|
389 | function to determine whether the given QGraphicsItem instance is
|
---|
390 | of a given type. Note that if the event is not related to any of
|
---|
391 | our image items, we pass it on to the base class implementation.
|
---|
392 |
|
---|
393 | \snippet examples/sql/drilldown/view.cpp 6
|
---|
394 |
|
---|
395 | The \c showInformation() function is given an \c ImageItem object
|
---|
396 | as argument, and starts off by extracting the item's location
|
---|
397 | ID. Then it determines if there already is created an information
|
---|
398 | window for this location. If it is, and the window is visible, it
|
---|
399 | ensures that the window is raised to the top of the widget stack
|
---|
400 | and activated. If the window exists but is hidden, calling its \l
|
---|
401 | {QWidget::}{show()} slot gives the same result.
|
---|
402 |
|
---|
403 | If no window for the given location exists, we create one by
|
---|
404 | passing the location ID, a pointer to the model, and our view as a
|
---|
405 | parent, to the \c InformationWindow constructor. Note that we
|
---|
406 | connect the information window's \c imageChanged() signal to \e
|
---|
407 | this widget's \c updateImage() slot, before we give it a suitable
|
---|
408 | position and add it to the list of existing windows.
|
---|
409 |
|
---|
410 | \snippet examples/sql/drilldown/view.cpp 7
|
---|
411 |
|
---|
412 | The \c updateImage() slot takes a location ID and the name of an
|
---|
413 | image files as arguments. It filters out the image items, and
|
---|
414 | updates the one that correspond to the given location ID, with the
|
---|
415 | provided image file.
|
---|
416 |
|
---|
417 | \snippet examples/sql/drilldown/view.cpp 8
|
---|
418 |
|
---|
419 | The \c findWindow() function simply searches through the list of
|
---|
420 | existing windows, returning a pointer to the window that matches
|
---|
421 | the given location ID, or 0 if the window doesn't exists.
|
---|
422 |
|
---|
423 | Finally, let's take a quick look at our custom \c ImageItem class:
|
---|
424 |
|
---|
425 | \section1 ImageItem Class Definition
|
---|
426 |
|
---|
427 | The \c ImageItem class is provided to facilitate animation of the
|
---|
428 | image items. It inherits QGraphicsPixmapItem and reimplements its
|
---|
429 | hover event handlers:
|
---|
430 |
|
---|
431 | \snippet examples/sql/drilldown/imageitem.h 0
|
---|
432 |
|
---|
433 | In addition, we implement a public \c id() function to be able to
|
---|
434 | identify the associated location and a public \c adjust() function
|
---|
435 | that can be called to ensure that the image item is given the
|
---|
436 | preferred size regardless of the original image file.
|
---|
437 |
|
---|
438 | The animation is implemented using the QTimeLine class together
|
---|
439 | with the event handlers and the private \c setFrame() slot: The
|
---|
440 | image item will expand when the mouse cursor hovers over it,
|
---|
441 | returning back to its orignal size when the cursor leaves its
|
---|
442 | borders.
|
---|
443 |
|
---|
444 | Finally, we store the location ID that this particular record is
|
---|
445 | associated with as well as a z-value. In the \l {Graphics View
|
---|
446 | Framework}, an item's z-value determines its position in the item
|
---|
447 | stack. An item of high Z-value will be drawn on top of an item
|
---|
448 | with a lower z-value if they share the same parent item. We also
|
---|
449 | provide an \c updateItemPosition() function to refresh the view
|
---|
450 | when required.
|
---|
451 |
|
---|
452 | \section1 ImageItem Class Implementation
|
---|
453 |
|
---|
454 | The \c ImageItem class is really only a QGraphicsPixmapItem with
|
---|
455 | some additional features, i.e., we can pass most of the
|
---|
456 | constructor's arguments (the pixmap, parent and scene) on to the
|
---|
457 | base class constructor:
|
---|
458 |
|
---|
459 | \snippet examples/sql/drilldown/imageitem.cpp 0
|
---|
460 |
|
---|
461 | Then we store the ID for future reference, and ensure that our
|
---|
462 | item will accept hover events. Hover events are delivered when
|
---|
463 | there is no current mouse grabber item. They are sent when the
|
---|
464 | mouse cursor enters an item, when it moves around inside the item,
|
---|
465 | and when the cursor leaves an item. As we mentioned earlier, none
|
---|
466 | of the \l {Graphics View Framework}'s items accept hover
|
---|
467 | event's by default.
|
---|
468 |
|
---|
469 | The QTimeLine class provides a timeline for controlling
|
---|
470 | animations. Its \l {QTimeLine::}{duration} property holds the
|
---|
471 | total duration of the timeline in milliseconds. By default, the
|
---|
472 | time line runs once from the beginning and towards the end. The
|
---|
473 | QTimeLine::setFrameRange() function sets the timeline's frame
|
---|
474 | counter; when the timeline is running, the \l
|
---|
475 | {QTimeLine::}{frameChanged()} signal is emitted each time the
|
---|
476 | frame changes. We set the duration and frame range for our
|
---|
477 | animation, and connect the time line's \l
|
---|
478 | {QTimeLine::}{frameChanged()} and \l {QTimeLine::}{finished()}
|
---|
479 | signals to our private \c setFrame() and \c updateItemPosition()
|
---|
480 | slots.
|
---|
481 |
|
---|
482 | Finally, we call \c adjust() to ensure that the item is given the
|
---|
483 | preferred size.
|
---|
484 |
|
---|
485 | \snippet examples/sql/drilldown/imageitem.cpp 1
|
---|
486 | \codeline
|
---|
487 | \snippet examples/sql/drilldown/imageitem.cpp 2
|
---|
488 |
|
---|
489 | Whenever the mouse cursor enters or leave the image item, the
|
---|
490 | corresponding event handlers are triggered: We first set the time
|
---|
491 | line's direction, making the item expand or shrink,
|
---|
492 | respectively. Then we alter the item's z-value if it is not already
|
---|
493 | set to the expected value.
|
---|
494 |
|
---|
495 | In the case of hover \e enter events, we immediately update the
|
---|
496 | item's position since we want the item to appear on top of all
|
---|
497 | other items as soon as it starts expanding. In the case of hover
|
---|
498 | \e leave events, on the other hand, we postpone the actual update
|
---|
499 | to achieve the same result. But remember that when we constructed
|
---|
500 | our item, we connected the time line's \l
|
---|
501 | {QTimeLine::}{finished()} signal to the \c updateItemPosition()
|
---|
502 | slot. In this way the item is given the correct position in the
|
---|
503 | item stack once the animation is completed. Finally, if the time
|
---|
504 | line is not already running, we start it.
|
---|
505 |
|
---|
506 | \snippet examples/sql/drilldown/imageitem.cpp 3
|
---|
507 |
|
---|
508 | When the time line is running, it triggers the \c setFrame() slot
|
---|
509 | whenever the current frame changes due to the connection we
|
---|
510 | created in the item constructor. It is this slot that controls the
|
---|
511 | animation, expanding or shrinking the image item step by step.
|
---|
512 |
|
---|
513 | We first call the \c adjust() function to ensure that we start off
|
---|
514 | with the item's original size. Then we scale the item with a
|
---|
515 | factor depending on the animation's progress (using the \c frame
|
---|
516 | parameter). Note that by default, the transformation will be
|
---|
517 | relative to the item's top-left corner. Since we want the item to
|
---|
518 | be transformed relative to its center, we must translate the
|
---|
519 | coordinate system before we scale the item.
|
---|
520 |
|
---|
521 | In the end, only the following convenience functions remain:
|
---|
522 |
|
---|
523 | \snippet examples/sql/drilldown/imageitem.cpp 4
|
---|
524 | \codeline
|
---|
525 | \snippet examples/sql/drilldown/imageitem.cpp 5
|
---|
526 | \codeline
|
---|
527 | \snippet examples/sql/drilldown/imageitem.cpp 6
|
---|
528 |
|
---|
529 | The \c adjust() function defines and applies a transformation
|
---|
530 | matrix, ensuring that our image item appears with the preferred
|
---|
531 | size regardless of the size of the source image. The \c id()
|
---|
532 | function is trivial, and is simply provided to be able to identify
|
---|
533 | the item. In the \c updateItemPosition() slot we call the
|
---|
534 | QGraphicsItem::setZValue() function, setting the elevation (i.e.,
|
---|
535 | the position) of the item.
|
---|
536 | */
|
---|