0
|
1 |
/****************************************************************************
|
|
2 |
**
|
|
3 |
** Copyright (C) 2009 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, 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::getInt() 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 |
*/
|