| 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 | */
|
|---|