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