|
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 ** 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 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 */ |