[2] | 1 | /****************************************************************************
|
---|
| 2 | **
|
---|
[846] | 3 | ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
|
---|
[561] | 4 | ** All rights reserved.
|
---|
| 5 | ** Contact: Nokia Corporation ([email protected])
|
---|
[2] | 6 | **
|
---|
| 7 | ** This file is part of the documentation of the Qt Toolkit.
|
---|
| 8 | **
|
---|
[846] | 9 | ** $QT_BEGIN_LICENSE:FDL$
|
---|
[2] | 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
|
---|
[846] | 13 | ** Software or, alternatively, in accordance with the terms contained in a
|
---|
| 14 | ** written agreement between you and Nokia.
|
---|
[2] | 15 | **
|
---|
[846] | 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.
|
---|
[2] | 21 | **
|
---|
[561] | 22 | ** If you have questions regarding the use of this file, please contact
|
---|
| 23 | ** Nokia at [email protected].
|
---|
[2] | 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
|
---|
[561] | 39 | information about each of Nokia's Qt offices by clicking the
|
---|
[2] | 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 |
|
---|
[846] | 281 | The QGraphicsView class is part of the \l {Graphics View
|
---|
[2] | 282 | Framework} which we will use to display the images of Nokia's
|
---|
[561] | 283 | Qt offices. To be able to respond to user interaction;
|
---|
[2] | 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
|
---|
[846] | 377 | events). Please see the \l{Graphics View Framework} documentation
|
---|
| 378 | and the \l{Graphics View Examples} for more details.
|
---|
[2] | 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 |
|
---|
[846] | 388 | The \l {Graphics View Framework} provides the qgraphicsitem_cast()
|
---|
[2] | 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
|
---|
[846] | 445 | associated with as well as a z-value. In the \l {Graphics View
|
---|
[2] | 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
|
---|
[846] | 466 | of the \l {Graphics View Framework}'s items accept hover
|
---|
[2] | 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 | */
|
---|