source: trunk/doc/src/examples/drilldown.qdoc@ 651

Last change on this file since 651 was 651, checked in by Dmitry A. Kuminov, 15 years ago

trunk: Merged in qt 4.6.2 sources.

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