source: trunk/doc/src/examples/scribble.qdoc@ 1168

Last change on this file since 1168 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: 18.6 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation ([email protected])
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 [email protected].
24** $QT_END_LICENSE$
25**
26****************************************************************************/
27
28/*!
29 \example widgets/scribble
30 \title Scribble Example
31
32 The Scribble example shows how to reimplement some of QWidget's
33 event handlers to receive the events generated for the
34 application's widgets.
35
36 We reimplement the mouse event handlers to implement drawing, the
37 paint event handler to update the application and the resize event
38 handler to optimize the application's appearance. In addition we
39 reimplement the close event handler to intercept the close events
40 before terminating the application.
41
42 The example also demonstrates how to use QPainter to draw an image
43 in real time, as well as to repaint widgets.
44
45 \image scribble-example.png Screenshot of the Scribble example
46
47 With the Scribble application the users can draw an image. The
48 \gui File menu gives the users the possibility to open and edit an
49 existing image file, save an image and exit the application. While
50 drawing, the \gui Options menu allows the users to to choose the
51 pen color and pen width, as well as clear the screen. In addition
52 the \gui Help menu provides the users with information about the
53 Scribble example in particular, and about Qt in general.
54
55 The example consists of two classes:
56
57 \list
58 \o \c ScribbleArea is a custom widget that displays a QImage and
59 allows to the user to draw on it.
60 \o \c MainWindow provides a menu above the \c ScribbleArea.
61 \endlist
62
63 We will start by reviewing the \c ScribbleArea class. Then we will
64 review the \c MainWindow class, which uses \c ScribbleArea.
65
66 \section1 ScribbleArea Class Definition
67
68 \snippet examples/widgets/scribble/scribblearea.h 0
69
70 The \c ScribbleArea class inherits from QWidget. We reimplement
71 the \c mousePressEvent(), \c mouseMoveEvent() and \c
72 mouseReleaseEvent() functions to implement the drawing. We
73 reimplement the \c paintEvent() function to update the scribble
74 area, and the \c resizeEvent() function to ensure that the QImage
75 on which we draw is at least as large as the widget at any time.
76
77 We need several public functions: \c openImage() loads an image
78 from a file into the scribble area, allowing the user to edit the
79 image; \c save() writes the currently displayed image to file; \c
80 clearImage() slot clears the image displayed in the scribble
81 area. We need the private \c drawLineTo() function to actually do
82 the drawing, and \c resizeImage() to change the size of a
83 QImage. The \c print() slot handles printing.
84
85 We also need the following private variables:
86
87 \list
88 \o \c modified is \c true if there are unsaved
89 changes to the image displayed in the scribble area.
90 \o \c scribbling is \c true while the user is pressing
91 the left mouse button within the scribble area.
92 \o \c penWidth and \c penColor hold the currently
93 set width and color for the pen used in the application.
94 \o \c image stores the image drawn by the user.
95 \o \c lastPoint holds the position of the cursor at the last
96 mouse press or mouse move event.
97 \endlist
98
99 \section1 ScribbleArea Class Implementation
100
101 \snippet examples/widgets/scribble/scribblearea.cpp 0
102
103 In the constructor, we set the Qt::WA_StaticContents
104 attribute for the widget, indicating that the widget contents are
105 rooted to the top-left corner and don't change when the widget is
106 resized. Qt uses this attribute to optimize paint events on
107 resizes. This is purely an optimization and should only be used
108 for widgets whose contents are static and rooted to the top-left
109 corner.
110
111 \snippet examples/widgets/scribble/scribblearea.cpp 1
112 \snippet examples/widgets/scribble/scribblearea.cpp 2
113
114 In the \c openImage() function, we load the given image. Then we
115 resize the loaded QImage to be at least as large as the widget in
116 both directions using the private \c resizeImage() function and
117 we set the \c image member variable to be the loaded image. At
118 the end, we call QWidget::update() to schedule a repaint.
119
120 \snippet examples/widgets/scribble/scribblearea.cpp 3
121 \snippet examples/widgets/scribble/scribblearea.cpp 4
122
123 The \c saveImage() function creates a QImage object that covers
124 only the visible section of the actual \c image and saves it using
125 QImage::save(). If the image is successfully saved, we set the
126 scribble area's \c modified variable to \c false, because there is
127 no unsaved data.
128
129 \snippet examples/widgets/scribble/scribblearea.cpp 5
130 \snippet examples/widgets/scribble/scribblearea.cpp 6
131 \codeline
132 \snippet examples/widgets/scribble/scribblearea.cpp 7
133 \snippet examples/widgets/scribble/scribblearea.cpp 8
134
135 The \c setPenColor() and \c setPenWidth() functions set the
136 current pen color and width. These values will be used for future
137 drawing operations.
138
139 \snippet examples/widgets/scribble/scribblearea.cpp 9
140 \snippet examples/widgets/scribble/scribblearea.cpp 10
141
142 The public \c clearImage() slot clears the image displayed in the
143 scribble area. We simply fill the entire image with white, which
144 corresponds to RGB value (255, 255, 255). As usual when we modify
145 the image, we set \c modified to \c true and schedule a repaint.
146
147 \snippet examples/widgets/scribble/scribblearea.cpp 11
148 \snippet examples/widgets/scribble/scribblearea.cpp 12
149
150 For mouse press and mouse release events, we use the
151 QMouseEvent::button() function to find out which button caused
152 the event. For mose move events, we use QMouseEvent::buttons()
153 to find which buttons are currently held down (as an OR-combination).
154
155 If the users press the left mouse button, we store the position
156 of the mouse cursor in \c lastPoint. We also make a note that the
157 user is currently scribbling. (The \c scribbling variable is
158 necessary because we can't assume that a mouse move and mouse
159 release event is always preceded by a mouse press event on the
160 same widget.)
161
162 If the user moves the mouse with the left button pressed down or
163 releases the button, we call the private \c drawLineTo() function
164 to draw.
165
166 \snippet examples/widgets/scribble/scribblearea.cpp 13
167 \snippet examples/widgets/scribble/scribblearea.cpp 14
168
169 In the reimplementation of the \l
170 {QWidget::paintEvent()}{paintEvent()} function, we simply create
171 a QPainter for the scribble area, and draw the image.
172
173 At this point, you might wonder why we don't just draw directly
174 onto the widget instead of drawing in a QImage and copying the
175 QImage onto screen in \c paintEvent(). There are at least three
176 good reasons for this:
177
178 \list
179 \o The window system requires us to be able to redraw the widget
180 \e{at any time}. For example, if the window is minimized and
181 restored, the window system might have forgotten the contents
182 of the widget and send us a paint event. In other words, we
183 can't rely on the window system to remember our image.
184
185 \o Qt normally doesn't allow us to paint outside of \c
186 paintEvent(). In particular, we can't paint from the mouse
187 event handlers. (This behavior can be changed using the
188 Qt::WA_PaintOnScreen widget attribute, though.)
189
190 \o If initialized properly, a QImage is guaranteed to use 8-bit
191 for each color channel (red, green, blue, and alpha), whereas
192 a QWidget might have a lower color depth, depending on the
193 monitor configuration. This means that if we load a 24-bit or
194 32-bit image and paint it onto a QWidget, then copy the
195 QWidget into a QImage again, we might lose some information.
196 \endlist
197
198 \snippet examples/widgets/scribble/scribblearea.cpp 15
199 \snippet examples/widgets/scribble/scribblearea.cpp 16
200
201 When the user starts the Scribble application, a resize event is
202 generated and an image is created and displayed in the scribble
203 area. We make this initial image slightly larger than the
204 application's main window and scribble area, to avoid always
205 resizing the image when the user resizes the main window (which
206 would be very inefficient). But when the main window becomes
207 larger than this initial size, the image needs to be resized.
208
209 \snippet examples/widgets/scribble/scribblearea.cpp 17
210 \snippet examples/widgets/scribble/scribblearea.cpp 18
211
212 In \c drawLineTo(), we draw a line from the point where the mouse
213 was located when the last mouse press or mouse move occurred, we
214 set \c modified to true, we generate a repaint event, and we
215 update \c lastPoint so that next time \c drawLineTo() is called,
216 we continue drawing from where we left.
217
218 We could call the \c update() function with no parameter, but as
219 an easy optimization we pass a QRect that specifies the rectangle
220 inside the scribble are needs updating, to avoid a complete
221 repaint of the widget.
222
223 \snippet examples/widgets/scribble/scribblearea.cpp 19
224 \snippet examples/widgets/scribble/scribblearea.cpp 20
225
226 QImage has no nice API for resizing an image. There's a
227 QImage::copy() function that could do the trick, but when used to
228 expand an image, it fills the new areas with black, whereas we
229 want white.
230
231 So the trick is to create a brand new QImage with the right size,
232 to fill it with white, and to draw the old image onto it using
233 QPainter. The new image is given the QImage::Format_RGB32
234 format, which means that each pixel is stored as 0xffRRGGBB
235 (where RR, GG, and BB are the red, green and blue
236 color channels, ff is the hexadecimal value 255).
237
238 Printing is handled by the \c print() slot:
239
240 \snippet examples/widgets/scribble/scribblearea.cpp 21
241
242 We construct a high resolution QPrinter object for the required
243 output format, using a QPrintDialog to ask the user to specify a
244 page size and indicate how the output should be formatted on the page.
245
246 If the dialog is accepted, we perform the task of printing to the paint
247 device:
248
249 \snippet examples/widgets/scribble/scribblearea.cpp 22
250
251 Printing an image to a file in this way is simply a matter of
252 painting onto the QPrinter. We scale the image to fit within the
253 available space on the page before painting it onto the paint
254 device.
255
256 \section1 MainWindow Class Definition
257
258 \snippet examples/widgets/scribble/mainwindow.h 0
259
260 The \c MainWindow class inherits from QMainWindow. We reimplement
261 the \l{QWidget::closeEvent()}{closeEvent()} handler from QWidget.
262 The \c open(), \c save(), \c penColor() and \c penWidth()
263 slots correspond to menu entries. In addition we create four
264 private functions.
265
266 We use the boolean \c maybeSave() function to check if there are
267 any unsaved changes. If there are unsaved changes, we give the
268 user the opportunity to save these changes. The function returns
269 \c false if the user clicks \gui Cancel. We use the \c saveFile()
270 function to let the user save the image currently displayed in
271 the scribble area.
272
273 \section1 MainWindow Class Implementation
274
275 \snippet examples/widgets/scribble/mainwindow.cpp 0
276
277 In the constructor, we create a scribble area which we make the
278 central widget of the \c MainWindow widget. Then we create the
279 associated actions and menus.
280
281 \snippet examples/widgets/scribble/mainwindow.cpp 1
282 \snippet examples/widgets/scribble/mainwindow.cpp 2
283
284 Close events are sent to widgets that the users want to close,
285 usually by clicking \gui{File|Exit} or by clicking the \gui X
286 title bar button. By reimplementing the event handler, we can
287 intercept attempts to close the application.
288
289 In this example, we use the close event to ask the user to save
290 any unsaved changes. The logic for that is located in the \c
291 maybeSave() function. If \c maybeSave() returns true, there are
292 no modifications or the users successfully saved them, and we
293 accept the event. The application can then terminate normally. If
294 \c maybeSave() returns false, the user clicked \gui Cancel, so we
295 "ignore" the event, leaving the application unaffected by it.
296
297 \snippet examples/widgets/scribble/mainwindow.cpp 3
298 \snippet examples/widgets/scribble/mainwindow.cpp 4
299
300 In the \c open() slot we first give the user the opportunity to
301 save any modifications to the currently displayed image, before a
302 new image is loaded into the scribble area. Then we ask the user
303 to choose a file and we load the file in the \c ScribbleArea.
304
305 \snippet examples/widgets/scribble/mainwindow.cpp 5
306 \snippet examples/widgets/scribble/mainwindow.cpp 6
307
308 The \c save() slot is called when the users choose the \gui {Save
309 As} menu entry, and then choose an entry from the format menu. The
310 first thing we need to do is to find out which action sent the
311 signal using QObject::sender(). This function returns the sender
312 as a QObject pointer. Since we know that the sender is an action
313 object, we can safely cast the QObject. We could have used a
314 C-style cast or a C++ \c static_cast<>(), but as a defensive
315 programming technique we use a qobject_cast(). The advantage is
316 that if the object has the wrong type, a null pointer is
317 returned. Crashes due to null pointers are much easier to diagnose
318 than crashes due to unsafe casts.
319
320 Once we have the action, we extract the chosen format using
321 QAction::data(). (When the actions are created, we use
322 QAction::setData() to set our own custom data attached to the
323 action, as a QVariant. More on this when we review \c
324 createActions().)
325
326 Now that we know the format, we call the private \c saveFile()
327 function to save the currently displayed image.
328
329 \snippet examples/widgets/scribble/mainwindow.cpp 7
330 \snippet examples/widgets/scribble/mainwindow.cpp 8
331
332 We use the \c penColor() slot to retrieve a new color from the
333 user with a QColorDialog. If the user chooses a new color, we
334 make it the scribble area's color.
335
336 \snippet examples/widgets/scribble/mainwindow.cpp 9
337 \snippet examples/widgets/scribble/mainwindow.cpp 10
338
339 To retrieve a new pen width in the \c penWidth() slot, we use
340 QInputDialog. The QInputDialog class provides a simple
341 convenience dialog to get a single value from the user. We use
342 the static QInputDialog::getInt() function, which combines a
343 QLabel and a QSpinBox. The QSpinBox is initialized with the
344 scribble area's pen width, allows a range from 1 to 50, a step of
345 1 (meaning that the up and down arrow increment or decrement the
346 value by 1).
347
348 The boolean \c ok variable will be set to \c true if the user
349 clicked \gui OK and to \c false if the user pressed \gui Cancel.
350
351 \snippet examples/widgets/scribble/mainwindow.cpp 11
352 \snippet examples/widgets/scribble/mainwindow.cpp 12
353
354 We implement the \c about() slot to create a message box
355 describing what the example is designed to show.
356
357 \snippet examples/widgets/scribble/mainwindow.cpp 13
358 \snippet examples/widgets/scribble/mainwindow.cpp 14
359
360 In the \c createAction() function we create the actions
361 representing the menu entries and connect them to the appropiate
362 slots. In particular we create the actions found in the \gui
363 {Save As} sub-menu. We use QImageWriter::supportedImageFormats()
364 to get a list of the supported formats (as a QList<QByteArray>).
365
366 Then we iterate through the list, creating an action for each
367 format. We call QAction::setData() with the file format, so we
368 can retrieve it later as QAction::data(). We could also have
369 deduced the file format from the action's text, by truncating the
370 "...", but that would have been inelegant.
371
372 \snippet examples/widgets/scribble/mainwindow.cpp 15
373 \snippet examples/widgets/scribble/mainwindow.cpp 16
374
375 In the \c createMenu() function, we add the previously created
376 format actions to the \c saveAsMenu. Then we add the rest of the
377 actions as well as the \c saveAsMenu sub-menu to the \gui File,
378 \gui Options and \gui Help menus.
379
380 The QMenu class provides a menu widget for use in menu bars,
381 context menus, and other popup menus. The QMenuBar class provides
382 a horizontal menu bar with a list of pull-down \l{QMenu}s. At the
383 end we put the \gui File and \gui Options menus in the \c
384 {MainWindow}'s menu bar, which we retrieve using the
385 QMainWindow::menuBar() function.
386
387 \snippet examples/widgets/scribble/mainwindow.cpp 17
388 \snippet examples/widgets/scribble/mainwindow.cpp 18
389
390 In \c mayBeSave(), we check if there are any unsaved changes. If
391 there are any, we use QMessageBox to give the user a warning that
392 the image has been modified and the opportunity to save the
393 modifications.
394
395 As with QColorDialog and QFileDialog, the easiest way to create a
396 QMessageBox is to use its static functions. QMessageBox provides
397 a range of different messages arranged along two axes: severity
398 (question, information, warning and critical) and complexity (the
399 number of necessary response buttons). Here we use the \c
400 warning() function sice the message is rather important.
401
402 If the user chooses to save, we call the private \c saveFile()
403 function. For simplicitly, we use PNG as the file format; the
404 user can always press \gui Cancel and save the file using another
405 format.
406
407 The \c maybeSave() function returns \c false if the user clicks
408 \gui Cancel; otherwise it returns \c true.
409
410 \snippet examples/widgets/scribble/mainwindow.cpp 19
411 \snippet examples/widgets/scribble/mainwindow.cpp 20
412
413 In \c saveFile(), we pop up a file dialog with a file name
414 suggestion. The static QFileDialog::getSaveFileName() function
415 returns a file name selected by the user. The file does not have
416 to exist.
417*/
Note: See TracBrowser for help on using the repository browser.