source: trunk/doc/src/examples/dragdroprobot.qdoc@ 1147

Last change on this file since 1147 was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

File size: 16.7 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2011 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:FDL$
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
13** Software or, alternatively, in accordance with the terms contained in a
14** written agreement between you and Nokia.
15**
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.
21**
22** If you have questions regarding the use of this file, please contact
23** Nokia at qt-info@nokia.com.
24** $QT_END_LICENSE$
25**
26****************************************************************************/
27
28/*!
29 \example graphicsview/dragdroprobot
30 \title Drag and Drop Robot Example
31
32 This GraphicsView example shows how to implement Drag and Drop in a
33 QGraphicsItem subclass, as well as how to animate items using Qt's
34 \l{Animation Framework}.
35
36 \image dragdroprobot-example.png
37
38 Graphics View provides the QGraphicsScene class for managing and
39 interacting with a large number of custom-made 2D graphical items derived
40 from the QGraphicsItem class, and a QGraphicsView widget for visualizing
41 the items, with support for zooming and rotation.
42
43 This example consists of a \c Robot class, a \c ColorItem class, and a main
44 function: the \c Robot class describes a simple robot consisting of several
45 \c RobotPart derived limbs, including \c RobotHead and \c RobotLimb, the \c
46 ColorItem class provides a draggable colored ellipse, and the \c main()
47 function provides the main application window.
48
49 We will first review the \c Robot class to see how to assemble the
50 different parts so that they can be individually rotated and animated using
51 QPropertyAnimation, and we will then review the \c ColorItem class to
52 demonstrate how to implement Drag and Drop between items. Finally we will
53 review the main() function to see how we can put all the pieces together,
54 to form the final application.
55
56 \section1 Robot Class Definition
57
58 The robot consists of three main classes: the \c RobotHead, the \c
59 RobotTorso, and the \c RobotLimb, which is used for the upper and lower
60 arms and legs. All parts derive from the \c RobotPart class, which in turn
61 inherits \c QGraphicsObject. The \c Robot class itself has no visual
62 appearance and serves only as a root node for the robot.
63
64 Let's start with the \c RobotPart class declaration.
65
66 \snippet examples/graphicsview/dragdroprobot/robot.h 0
67
68 This base class inherits QGraphicsObject. QGraphicsObject provides signals
69 and slots through inheriting QObject, and it also declares QGraphicsItem's
70 properties using Q_PROPERTY, which makes the properties accessible for
71 QPropertyAnimation.
72
73 RobotPart also implements the three most important event handlers for
74 accepting drop events:
75 \l{QGraphicsItem::dragEnterEvent()}{dragEnterEvent()},
76 \l{QGraphicsItem::dragLeaveEvent()}{dragLeaveEvent()}, and
77 \l{QGraphicsItem::dropEvent()}{dropEvent()}.
78
79 The color is stored as a member variable, along with the \c dragOver
80 variable, which we will use later to indicate visually that the limb can
81 accept colors that are is dragged onto it.
82
83 \snippet examples/graphicsview/dragdroprobot/robot.cpp 0
84
85 \c RobotPart's constructor initializes the dragOver member and sets the
86 color to Qt::lightGray. In the constructor body we enable support for
87 accepting drop events by calling
88 \l{QGraphicsItem::setAcceptDrops()}{setAcceptDrops(true)}.
89
90 The rest of this class's implementation is to support Drag and Drop.
91
92 \snippet examples/graphicsview/dragdroprobot/robot.cpp 1
93
94 The \l{QGraphicsItem::dragEnterEvent()}{dragEnterEvent()} handler is called
95 when a Drag and Drop element is dragged into the robot part's area.
96
97 The handler implementation determines whether or not this item as a whole
98 can accept the mime data assiciated with the incoming drag object. \c
99 RobotPart provides a base behavior for all parts that accepts color drops.
100 So if the incoming drag object contains a color, the event is accepted, we
101 set \c dragOver to \c true and call update() to help provide positive
102 visual feedback to the user; otherwise the event is ignored, which in turn
103 allows the event to propagate to parent elements.
104
105 \snippet examples/graphicsview/dragdroprobot/robot.cpp 2
106
107 The \l{QGraphicsItem::dragLeaveEvent()}{dragLeaveEvent()} handler is called
108 when a Drag and Drop element is dragged away from the robot part's area.
109 Our implementation simply resets \e dragOver to false and calls
110 \l{QGraphicsItem::update()}{update()} to help provide visual feedback that
111 the drag has left this item.
112
113 \snippet examples/graphicsview/dragdroprobot/robot.cpp 3
114
115 The \l{QGraphicsItem::dropEvent()}{dropEvent()} handler is called when a
116 Drag and Drop element is dropped onto an item (i.e., when the mouse button
117 is released over the item while dragging).
118
119 We reset \c dragOver to false, assign the item's new color, and call
120 \l{QGraphicsItem::update()}{update()}.
121
122 The declaration and implementation of \c RobotHead, \c RobotTorso, and \c
123 RobotLimb are practically identical. We will review \c RobotHead in detail,
124 as this class has one minor difference, and leave the other classes as an
125 exercise for the reader.
126
127 \snippet examples/graphicsview/dragdroprobot/robot.h 1
128
129 The \c RobotHead class inherits \c RobotPart and provides the necessary
130 implementations of \l{QGraphicsItem::boundingRect()}{boundingRect()} and
131 \l{QGraphicsItem::paint()}{paint()}. It also reimplements
132 \l{QGraphicsItem::dragEnterEvent()}{dragEnterEvent()} and dropEvent() to
133 provide special handling of image drops.
134
135 The class contains a private pixmap member that we can use to implement
136 support for accepting image drops.
137
138 \snippet examples/graphicsview/dragdroprobot/robot.cpp 4
139
140 \c RobotHead has a rather plain constructor that simply forwards to
141 \c RobotPart's constructor.
142
143 \snippet examples/graphicsview/dragdroprobot/robot.cpp 5
144
145 The \l{QGraphicsItem::boundingRect()}{boundingRect()} reimplementation
146 returns the extents for the head. Because we want the center of rotation to
147 be the bottom center of the item, we have chosen a bounding rectangle that
148 starts at (-15, -50) and extends to 30 units wide and 50 units tall. When
149 rotating the head, the "neck" will stay still while the top of the head
150 tilts from side to side.
151
152 \snippet examples/graphicsview/dragdroprobot/robot.cpp 6
153
154 In \l{QGraphicsItem::paint()}{paint()} we draw the actual head. The
155 implementation is split into two sections; if an image has been dropped
156 onto the head, we draw the image, otherwise we draw a round rectangular
157 robot head with simple vector graphics.
158
159 For performance reasons, depending on the complexity of what is painted, it
160 can often be faster to draw the head as an image rather than using a
161 sequence of vector operations.
162
163 \snippet examples/graphicsview/dragdroprobot/robot.cpp 7
164
165 The robot head can accept image drops. In order to support this, its
166 reimplementation of \l{QGraphicsItem::dragEnterEvent()}{dragEnterEvent()}
167 checks if the drag object contains image data, and if it does, then the
168 event is accepted. Otherwise we fall back to the base \c RobotPart
169 implementation.
170
171 \snippet examples/graphicsview/dragdroprobot/robot.cpp 8
172
173 To follow up on image support, we must also implement
174 \l{QGraphicsItem::dropEvent()}{dropEvent()}. We check if the drag object
175 contains image data, and if it does, we store this data as a member pixmap
176 and call \l{QGraphicsItem::update()}{update()}. This pixmap is used inside
177 the \l{QGraphicsItem::paint()}{paint()} implementation that we reviewed
178 before.
179
180 \c RobotTorso and \c RobotLimb are similar to \c RobotHead, so let's
181 skip directly to the \c Robot class.
182
183 \snippet examples/graphicsview/dragdroprobot/robot.h 4
184
185 The \c Robot class also inherits \c RobotPart, and like the other parts it
186 also implements \l{QGraphicsItem::boundingRect()}{boundingRect()} and
187 \l{QGraphicsItem::paint()}{paint()}. It provides a rather special
188 implementation, though:
189
190 \snippet examples/graphicsview/dragdroprobot/robot.cpp 9
191
192 Because the \c Robot class is only used as a base node for the rest of the
193 robot, it has no visual representation. Its
194 \l{QGraphicsItem::boundingRect()}{boundingRect()} implementation can
195 therefore return a null QRectF, and its paint() function does nothing.
196
197 \snippet examples/graphicsview/dragdroprobot/robot.cpp 10
198
199 The constuctor starts by setting the flag
200 \l{QGraphicsItem::ItemHasNoContents}{ItemHasNoContents}, which is a minor
201 optimization for items that have no visual appearance.
202
203 We then construct all the robot parts (head, torso, and upper/lower arms
204 and legs). The stacking order is very important, and we use the
205 parent-child hierarchy to ensure the elements rotate and move properly. We
206 construct the torso first, as this is the root element. We then construct
207 the head and pass the torso to \c HeadItem's constructor. This will make
208 the head a child of the torso; if you rotate the torso, the head will
209 follow. The same pattern is applied to the rest of the limbs.
210
211 \snippet examples/graphicsview/dragdroprobot/robot.cpp 11
212
213 Each robot part is carefully positioned. For example, the upper left arm is
214 moved precisely to the top-left area of the torso, and the upper right arm
215 is moved to the top-right area.
216
217 \snippet examples/graphicsview/dragdroprobot/robot.cpp 12
218
219 The next section creates all animation objects. This snippet shows the two
220 animations that operate on the head's scale and rotation. The two
221 QPropertyAnimation instances simply set the object, property, and
222 respective start and end values.
223
224 All animations are controlled by one top-level parallel animation group.
225 The scale and rotation animations are added to this group.
226
227 The rest of the animations are defined in a similar way.
228
229 \snippet examples/graphicsview/dragdroprobot/robot.cpp 13
230
231 Finally we set an easing curve and duration on each animation, ensure the
232 toplevel animation group loops forever, and start the toplevel animation.
233
234 \section1 ColorItem Class Definition
235
236 The \c ColorItem class represents a circular item that can be pressed to
237 drag colors onto robot parts.
238
239 \snippet examples/graphicsview/dragdroprobot/coloritem.h 0
240
241 This class is very simple. It does not use animations, and has no need for
242 properties nor signals and slots, so to save resources, it's most natural
243 that it inherits QGraphicsItem (as opposed to QGraphicsObject).
244
245 It declares the mandatory \l{QGraphicsItem::boundingRect()}{boundingRect()}
246 and \l{QGraphicsItem::paint()}{paint()} functions, and adds
247 reimplementations of
248 \l{QGraphicsItem::mousePressEvent()}{mousePressEvent()},
249 \l{QGraphicsItem::mouseMoveEvent()}{mouseMoveEvent()}, and
250 \l{QGraphicsItem::mouseReleaseEvent()}{mouseReleaseEvent()}. It contains a
251 single private color member.
252
253 Let's take a look at its implementation.
254
255 \snippet examples/graphicsview/dragdroprobot/coloritem.cpp 0
256
257 \c ColorItem's constructor assigns an opaque random color to its color
258 member by making use of qrand(). For improved usability, it assigns a
259 tooltip that provides a useful hint to the user, and it also sets a
260 suitable cursor. This ensures that the cursor will chance to
261 Qt::OpenHandCursor when the mouse pointer hovers over the item.
262
263 Finally, we call
264 \l{QGraphicsItem::setAcceptedMouseButtons()}{setAcceptedMouseButtons()} to
265 ensure that this item can only process Qt::LeftButton. This simplifies the
266 mouse event handlers greatly, as we can always assume that only the left
267 mouse button is pressed and released.
268
269 \snippet examples/graphicsview/dragdroprobot/coloritem.cpp 1
270
271 The item's bounding rect is a fixed 30x30 units centered around the item's
272 origin (0, 0), and adjusted by 0.5 units in all directions to allow a
273 scalable pen to draw its outline. For a final visual touch the bounds
274 also compensate with a few units down and to the right to make room
275 for a simple dropshadow.
276
277 \snippet examples/graphicsview/dragdroprobot/coloritem.cpp 2
278
279 The \l{QGraphicsItem::paint()}{paint()} implementation draws an ellipse
280 with a 1-unit black outline, a plain color fill, and a dark gray
281 dropshadow.
282
283 \snippet examples/graphicsview/dragdroprobot/coloritem.cpp 3
284
285 The \l{QGraphicsItem::mousePressEvent()}{mousePressEvent()} handler is
286 called when you press the mouse button inside the item's area. Our
287 implementation simply sets the cursor to Qt::ClosedHandCursor.
288
289 \snippet examples/graphicsview/dragdroprobot/coloritem.cpp 4
290
291 The \l{QGraphicsItem::mouseReleaseEvent()}{mouseReleaseEvent()} handler is
292 called when you release the mouse button after having pressed it inside an
293 item's area. Our implementation sets the cursor back to Qt::OpenHandCursor.
294 The mouse press and release event handlers together provide useful visual
295 feedback to the user: when you move the mouse pointer over a \c CircleItem,
296 the cursor changes to an open hand. Pressing the item will show a closed
297 hand cursor. Releasing will restore to an open hand cursor again.
298
299 \snippet examples/graphicsview/dragdroprobot/coloritem.cpp 5
300
301 The \l{QGraphicsItem::mouseMoveEvent()}{mouseMoveEvent()} handler is called
302 when you move the mouse around after pressing the mouse button inside the
303 \c ColorItem's area. This implementation provides the most important piece
304 of logic for \c CircleItem: the code that starts and manages drags.
305
306 The implementation starts by checking if the mouse has been dragged far
307 enough to eliminate mouse jitter noise. We only want to start a drag if the
308 mouse has been dragged farther than the application start drag distance.
309
310 Continuing, we create a QDrag object, passing the event
311 \l{QGraphicsSceneEvent::widget()}{widget} (i.e., the QGraphicsView
312 viewport) to its constructor. Qt will ensure that this object is deleted at
313 the right time. We also create a QMimeData instance that can contain our
314 color or image data, and assign this to the drag object.
315
316 \snippet examples/graphicsview/dragdroprobot/coloritem.cpp 6
317
318 This snippet has a somewhat random outcome: once in a while, a special
319 image is assigned to the drag object's mime data. The pixmap is also
320 assiged as the drag object's pixmap. This will ensure that you can see the
321 image that is being dragged as a pixmap under the mouse cursor.
322
323 \snippet examples/graphicsview/dragdroprobot/coloritem.cpp 7
324
325 Otherwise, and this is the most common outcome, a simple color is assigned
326 to the drag object's mime data. We render this \c ColorItem into a new
327 pixmap to give the user visual feedback that the color is being "dragged".
328
329 \snippet examples/graphicsview/dragdroprobot/coloritem.cpp 8
330
331 Finally we execute the drag. QDrag::exec() will reenter the event loop, and
332 only exit if the drag has either been dropped, or canceled. In any case we
333 reset the cursor to Qt::OpenHandCursor.
334
335 \section1 The main() Function
336
337 Now that the \c Robot and \c ColorItem classes are complete, we can put all
338 the pieces together inside the main() function.
339
340 \snippet examples/graphicsview/dragdroprobot/main.cpp 0
341
342 We start off by constructing QApplication, and initializing the random
343 number generator. This ensures that the color items have different colors
344 every time the application starts.
345
346 \snippet examples/graphicsview/dragdroprobot/main.cpp 1
347
348 We construct a fixed size scene, and create 10 \c ColorItem instances
349 arranged in a circle. Each item is added to the scene.
350
351 In the center of this circle we create one \c Robot instance. The
352 robot is scaled and moved up a few units. It is then added to the scene.
353
354 \snippet examples/graphicsview/dragdroprobot/main.cpp 2
355
356 Finally we create a QGraphicsView window, and assign the scene to it.
357
358 For increased visual quality, we enable antialiasing. We also choose to use
359 bounding rectangle updates to simplify visual update handling.
360 The view is given a fixed sand-colored background, and a window title.
361
362 We then show the view. The animations start immediately after
363 control enters the event loop.
364*/
365
Note: See TracBrowser for help on using the repository browser.