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

Last change on this file since 357 was 2, checked in by Dmitry A. Kuminov, 16 years ago

Initially imported qt-all-opensource-src-4.5.1 from Trolltech.

File size: 25.6 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4** Contact: Qt Software Information (qt-info@nokia.com)
5**
6** This file is part of the documentation of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial Usage
10** Licensees holding valid Qt Commercial licenses may use this file in
11** accordance with the Qt Commercial License Agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and Nokia.
14**
15** GNU Lesser General Public License Usage
16** Alternatively, this file may be used under the terms of the GNU Lesser
17** General Public License version 2.1 as published by the Free Software
18** Foundation and appearing in the file LICENSE.LGPL included in the
19** packaging of this file. Please review the following information to
20** ensure the GNU Lesser General Public License version 2.1 requirements
21** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
22**
23** In addition, as a special exception, Nokia gives you certain
24** additional rights. These rights are described in the Nokia Qt LGPL
25** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
26** 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 are unsure which license is appropriate for your use, please
37** contact the sales department at qt-sales@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 Software 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 Software 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
393 \l{Qt Examples#Graphics View}{Graphics View examples} for more
394 details.
395
396 \snippet examples/sql/drilldown/view.cpp 5
397
398 We reimplement QGraphicsView's \l
399 {QGraphicsView::}{mouseReleaseEvent()} event handler to respond to
400 user interaction. If the user clicks any of the image items, this
401 function calls the private \c showInformation() function to pop up
402 the associated information window.
403
404 \l {The Graphics View Framework} provides the qgraphicsitem_cast()
405 function to determine whether the given QGraphicsItem instance is
406 of a given type. Note that if the event is not related to any of
407 our image items, we pass it on to the base class implementation.
408
409 \snippet examples/sql/drilldown/view.cpp 6
410
411 The \c showInformation() function is given an \c ImageItem object
412 as argument, and starts off by extracting the item's location
413 ID. Then it determines if there already is created an information
414 window for this location. If it is, and the window is visible, it
415 ensures that the window is raised to the top of the widget stack
416 and activated. If the window exists but is hidden, calling its \l
417 {QWidget::}{show()} slot gives the same result.
418
419 If no window for the given location exists, we create one by
420 passing the location ID, a pointer to the model, and our view as a
421 parent, to the \c InformationWindow constructor. Note that we
422 connect the information window's \c imageChanged() signal to \e
423 this widget's \c updateImage() slot, before we give it a suitable
424 position and add it to the list of existing windows.
425
426 \snippet examples/sql/drilldown/view.cpp 7
427
428 The \c updateImage() slot takes a location ID and the name of an
429 image files as arguments. It filters out the image items, and
430 updates the one that correspond to the given location ID, with the
431 provided image file.
432
433 \snippet examples/sql/drilldown/view.cpp 8
434
435 The \c findWindow() function simply searches through the list of
436 existing windows, returning a pointer to the window that matches
437 the given location ID, or 0 if the window doesn't exists.
438
439 Finally, let's take a quick look at our custom \c ImageItem class:
440
441 \section1 ImageItem Class Definition
442
443 The \c ImageItem class is provided to facilitate animation of the
444 image items. It inherits QGraphicsPixmapItem and reimplements its
445 hover event handlers:
446
447 \snippet examples/sql/drilldown/imageitem.h 0
448
449 In addition, we implement a public \c id() function to be able to
450 identify the associated location and a public \c adjust() function
451 that can be called to ensure that the image item is given the
452 preferred size regardless of the original image file.
453
454 The animation is implemented using the QTimeLine class together
455 with the event handlers and the private \c setFrame() slot: The
456 image item will expand when the mouse cursor hovers over it,
457 returning back to its orignal size when the cursor leaves its
458 borders.
459
460 Finally, we store the location ID that this particular record is
461 associated with as well as a z-value. In the \l {The Graphics View
462 Framework}, an item's z-value determines its position in the item
463 stack. An item of high Z-value will be drawn on top of an item
464 with a lower z-value if they share the same parent item. We also
465 provide an \c updateItemPosition() function to refresh the view
466 when required.
467
468 \section1 ImageItem Class Implementation
469
470 The \c ImageItem class is really only a QGraphicsPixmapItem with
471 some additional features, i.e., we can pass most of the
472 constructor's arguments (the pixmap, parent and scene) on to the
473 base class constructor:
474
475 \snippet examples/sql/drilldown/imageitem.cpp 0
476
477 Then we store the ID for future reference, and ensure that our
478 item will accept hover events. Hover events are delivered when
479 there is no current mouse grabber item. They are sent when the
480 mouse cursor enters an item, when it moves around inside the item,
481 and when the cursor leaves an item. As we mentioned earlier, none
482 of the \l {The Graphics View Framework}'s items accept hover
483 event's by default.
484
485 The QTimeLine class provides a timeline for controlling
486 animations. Its \l {QTimeLine::}{duration} property holds the
487 total duration of the timeline in milliseconds. By default, the
488 time line runs once from the beginning and towards the end. The
489 QTimeLine::setFrameRange() function sets the timeline's frame
490 counter; when the timeline is running, the \l
491 {QTimeLine::}{frameChanged()} signal is emitted each time the
492 frame changes. We set the duration and frame range for our
493 animation, and connect the time line's \l
494 {QTimeLine::}{frameChanged()} and \l {QTimeLine::}{finished()}
495 signals to our private \c setFrame() and \c updateItemPosition()
496 slots.
497
498 Finally, we call \c adjust() to ensure that the item is given the
499 preferred size.
500
501 \snippet examples/sql/drilldown/imageitem.cpp 1
502 \codeline
503 \snippet examples/sql/drilldown/imageitem.cpp 2
504
505 Whenever the mouse cursor enters or leave the image item, the
506 corresponding event handlers are triggered: We first set the time
507 line's direction, making the item expand or shrink,
508 respectively. Then we alter the item's z-value if it is not already
509 set to the expected value.
510
511 In the case of hover \e enter events, we immediately update the
512 item's position since we want the item to appear on top of all
513 other items as soon as it starts expanding. In the case of hover
514 \e leave events, on the other hand, we postpone the actual update
515 to achieve the same result. But remember that when we constructed
516 our item, we connected the time line's \l
517 {QTimeLine::}{finished()} signal to the \c updateItemPosition()
518 slot. In this way the item is given the correct position in the
519 item stack once the animation is completed. Finally, if the time
520 line is not already running, we start it.
521
522 \snippet examples/sql/drilldown/imageitem.cpp 3
523
524 When the time line is running, it triggers the \c setFrame() slot
525 whenever the current frame changes due to the connection we
526 created in the item constructor. It is this slot that controls the
527 animation, expanding or shrinking the image item step by step.
528
529 We first call the \c adjust() function to ensure that we start off
530 with the item's original size. Then we scale the item with a
531 factor depending on the animation's progress (using the \c frame
532 parameter). Note that by default, the transformation will be
533 relative to the item's top-left corner. Since we want the item to
534 be transformed relative to its center, we must translate the
535 coordinate system before we scale the item.
536
537 In the end, only the following convenience functions remain:
538
539 \snippet examples/sql/drilldown/imageitem.cpp 4
540 \codeline
541 \snippet examples/sql/drilldown/imageitem.cpp 5
542 \codeline
543 \snippet examples/sql/drilldown/imageitem.cpp 6
544
545 The \c adjust() function defines and applies a transformation
546 matrix, ensuring that our image item appears with the preferred
547 size regardless of the size of the source image. The \c id()
548 function is trivial, and is simply provided to be able to identify
549 the item. In the \c updateItemPosition() slot we call the
550 QGraphicsItem::setZValue() function, setting the elevation (i.e.,
551 the position) of the item.
552*/
Note: See TracBrowser for help on using the repository browser.