1 /****************************************************************************
3 ** Copyright (C) 2015 The Qt Company Ltd.
4 ** Contact: http://www.qt.io/licensing/
6 ** This file is part of the documentation of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:FDL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://www.qt.io/contact-us.
17 ** GNU Free Documentation License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Free
19 ** Documentation License version 1.3 as published by the Free Software
20 ** Foundation and appearing in the file included in the packaging of
21 ** this file. Please review the following information to ensure
22 ** the GNU Free Documentation License version 1.3 requirements
23 ** will be met: http://www.gnu.org/copyleft/fdl.html.
26 ****************************************************************************/
29 \example sql/drilldown
30 \title Drill Down Example
32 \brief 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.
36 \image drilldown-example.png Screenshot of the Drill Down Example
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.
45 The example consists of three classes:
48 \o \c ImageItem is a custom graphics item class used to
49 display the office images.
51 \o \c View is the main application widget allowing the user to
52 browse through the various locations.
54 \o \c InformationWindow displays the requested information,
55 allowing the users to alter it and submit their changes to the
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.
64 \section1 InformationWindow Class Definition
66 The \c InformationWindow class is a custom widget inheriting
69 \snippet examples/sql/drilldown/informationwindow.h 0
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.
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.
84 \snippet examples/sql/drilldown/informationwindow.h 1
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.
91 \snippet examples/sql/drilldown/informationwindow.h 2
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.
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.
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.
110 \section1 InformationWindow Class Implementation
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.
117 \snippet examples/sql/drilldown/informationwindow.cpp 0
118 \snippet examples/sql/drilldown/informationwindow.cpp 1
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:
125 \snippet examples/sql/drilldown/informationwindow.cpp 2
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.
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()
146 \snippet examples/sql/drilldown/informationwindow.cpp 3
148 Then we create the mapper. The QDataWidgetMapper class allows us
149 to create data-aware widgets by mapping them to sections of an
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.
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).
172 \snippet examples/sql/drilldown/informationwindow.cpp 4
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
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
185 \snippet examples/sql/drilldown/informationwindow.cpp 5
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.
195 \snippet examples/sql/drilldown/informationwindow.cpp 6
197 The \c revert() slot is triggered whenever the user hits the \gui
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.
207 \snippet examples/sql/drilldown/informationwindow.cpp 7
209 Likewise, the \c submit() slot is triggered whenever the users
210 decide to submit their changes by pressing the \gui Submit button.
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.
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.
226 \snippet examples/sql/drilldown/informationwindow.cpp 8
228 The \c createButtons() function is provided for convenience, i.e.,
229 to simplify the constructor.
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.
239 \snippet examples/sql/drilldown/informationwindow.cpp 9
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
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.
261 \snippet examples/sql/drilldown/informationwindow.cpp 10
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
269 This completes the \c InformationWindow class. Let's take a look
270 at how we have used it in our example application.
272 \section1 View Class Definition
274 The \c View class represents the main application window and
275 inherits QGraphicsView:
277 \snippet examples/sql/drilldown/view.h 0
279 \snippet examples/sql/drilldown/view.h 1
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;
285 appropriate information window whenever the user clicks one of the
286 office images, we reimplement QGraphicsView's \l
287 {QGraphicsView::}{mouseReleaseEvent()} function.
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.
296 \snippet examples/sql/drilldown/view.h 2
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.
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.
310 \snippet examples/sql/drilldown/view.h 3
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.
324 \section1 View Class Implementation
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:
331 \snippet examples/sql/drilldown/view.cpp 0
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.
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
347 \snippet examples/sql/drilldown/view.cpp 1
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.
356 Finally, we set the main application widget's size constraints and
359 \snippet examples/sql/drilldown/view.cpp 3
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.
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.
380 \snippet examples/sql/drilldown/view.cpp 5
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.
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.
393 \snippet examples/sql/drilldown/view.cpp 6
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.
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.
410 \snippet examples/sql/drilldown/view.cpp 7
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
417 \snippet examples/sql/drilldown/view.cpp 8
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.
423 Finally, let's take a quick look at our custom \c ImageItem class:
425 \section1 ImageItem Class Definition
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:
431 \snippet examples/sql/drilldown/imageitem.h 0
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.
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
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
452 \section1 ImageItem Class Implementation
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:
459 \snippet examples/sql/drilldown/imageitem.cpp 0
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
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()
482 Finally, we call \c adjust() to ensure that the item is given the
485 \snippet examples/sql/drilldown/imageitem.cpp 1
487 \snippet examples/sql/drilldown/imageitem.cpp 2
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.
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.
506 \snippet examples/sql/drilldown/imageitem.cpp 3
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.
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.
521 In the end, only the following convenience functions remain:
523 \snippet examples/sql/drilldown/imageitem.cpp 4
525 \snippet examples/sql/drilldown/imageitem.cpp 5
527 \snippet examples/sql/drilldown/imageitem.cpp 6
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.