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

Last change on this file since 357 was 2, checked in by Dmitry A. Kuminov, 16 years ago

Initially imported qt-all-opensource-src-4.5.1 from Trolltech.

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