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

Last change on this file since 769 was 769, checked in by Dmitry A. Kuminov, 15 years ago

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

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