3ef3c662fe
Change-Id: Ie1fe516f75ca8c1b2233dc6bb2b887b55593e730 Reviewed-by: Martin Smith <martin.smith@nokia.com>
418 lines
18 KiB
Plaintext
418 lines
18 KiB
Plaintext
/****************************************************************************
|
|
**
|
|
** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
|
|
** Contact: http://www.qt-project.org/
|
|
**
|
|
** This file is part of the documentation of the Qt Toolkit.
|
|
**
|
|
** $QT_BEGIN_LICENSE:FDL$
|
|
** GNU Free Documentation License
|
|
** Alternatively, this file may be used under the terms of the GNU Free
|
|
** Documentation License version 1.3 as published by the Free Software
|
|
** Foundation and appearing in the file included in the packaging of
|
|
** this file.
|
|
**
|
|
** Other Usage
|
|
** Alternatively, this file may be used in accordance with the terms
|
|
** and conditions contained in a signed written agreement between you
|
|
** and Nokia.
|
|
**
|
|
**
|
|
**
|
|
**
|
|
**
|
|
** $QT_END_LICENSE$
|
|
**
|
|
****************************************************************************/
|
|
|
|
/*!
|
|
\example widgets/scribble
|
|
\title Scribble Example
|
|
|
|
The Scribble example shows how to reimplement some of QWidget's
|
|
event handlers to receive the events generated for the
|
|
application's widgets.
|
|
|
|
We reimplement the mouse event handlers to implement drawing, the
|
|
paint event handler to update the application and the resize event
|
|
handler to optimize the application's appearance. In addition we
|
|
reimplement the close event handler to intercept the close events
|
|
before terminating the application.
|
|
|
|
The example also demonstrates how to use QPainter to draw an image
|
|
in real time, as well as to repaint widgets.
|
|
|
|
\image scribble-example.png Screenshot of the Scribble example
|
|
|
|
With the Scribble application the users can draw an image. The
|
|
\uicontrol File menu gives the users the possibility to open and edit an
|
|
existing image file, save an image and exit the application. While
|
|
drawing, the \uicontrol Options menu allows the users to to choose the
|
|
pen color and pen width, as well as clear the screen. In addition
|
|
the \uicontrol Help menu provides the users with information about the
|
|
Scribble example in particular, and about Qt in general.
|
|
|
|
The example consists of two classes:
|
|
|
|
\list
|
|
\li \c ScribbleArea is a custom widget that displays a QImage and
|
|
allows to the user to draw on it.
|
|
\li \c MainWindow provides a menu above the \c ScribbleArea.
|
|
\endlist
|
|
|
|
We will start by reviewing the \c ScribbleArea class. Then we will
|
|
review the \c MainWindow class, which uses \c ScribbleArea.
|
|
|
|
\section1 ScribbleArea Class Definition
|
|
|
|
\snippet widgets/scribble/scribblearea.h 0
|
|
|
|
The \c ScribbleArea class inherits from QWidget. We reimplement
|
|
the \c mousePressEvent(), \c mouseMoveEvent() and \c
|
|
mouseReleaseEvent() functions to implement the drawing. We
|
|
reimplement the \c paintEvent() function to update the scribble
|
|
area, and the \c resizeEvent() function to ensure that the QImage
|
|
on which we draw is at least as large as the widget at any time.
|
|
|
|
We need several public functions: \c openImage() loads an image
|
|
from a file into the scribble area, allowing the user to edit the
|
|
image; \c save() writes the currently displayed image to file; \c
|
|
clearImage() slot clears the image displayed in the scribble
|
|
area. We need the private \c drawLineTo() function to actually do
|
|
the drawing, and \c resizeImage() to change the size of a
|
|
QImage. The \c print() slot handles printing.
|
|
|
|
We also need the following private variables:
|
|
|
|
\list
|
|
\li \c modified is \c true if there are unsaved
|
|
changes to the image displayed in the scribble area.
|
|
\li \c scribbling is \c true while the user is pressing
|
|
the left mouse button within the scribble area.
|
|
\li \c penWidth and \c penColor hold the currently
|
|
set width and color for the pen used in the application.
|
|
\li \c image stores the image drawn by the user.
|
|
\li \c lastPoint holds the position of the cursor at the last
|
|
mouse press or mouse move event.
|
|
\endlist
|
|
|
|
\section1 ScribbleArea Class Implementation
|
|
|
|
\snippet widgets/scribble/scribblearea.cpp 0
|
|
|
|
In the constructor, we set the Qt::WA_StaticContents
|
|
attribute for the widget, indicating that the widget contents are
|
|
rooted to the top-left corner and don't change when the widget is
|
|
resized. Qt uses this attribute to optimize paint events on
|
|
resizes. This is purely an optimization and should only be used
|
|
for widgets whose contents are static and rooted to the top-left
|
|
corner.
|
|
|
|
\snippet widgets/scribble/scribblearea.cpp 1
|
|
\snippet widgets/scribble/scribblearea.cpp 2
|
|
|
|
In the \c openImage() function, we load the given image. Then we
|
|
resize the loaded QImage to be at least as large as the widget in
|
|
both directions using the private \c resizeImage() function and
|
|
we set the \c image member variable to be the loaded image. At
|
|
the end, we call QWidget::update() to schedule a repaint.
|
|
|
|
\snippet widgets/scribble/scribblearea.cpp 3
|
|
\snippet widgets/scribble/scribblearea.cpp 4
|
|
|
|
The \c saveImage() function creates a QImage object that covers
|
|
only the visible section of the actual \c image and saves it using
|
|
QImage::save(). If the image is successfully saved, we set the
|
|
scribble area's \c modified variable to \c false, because there is
|
|
no unsaved data.
|
|
|
|
\snippet widgets/scribble/scribblearea.cpp 5
|
|
\snippet widgets/scribble/scribblearea.cpp 6
|
|
\codeline
|
|
\snippet widgets/scribble/scribblearea.cpp 7
|
|
\snippet widgets/scribble/scribblearea.cpp 8
|
|
|
|
The \c setPenColor() and \c setPenWidth() functions set the
|
|
current pen color and width. These values will be used for future
|
|
drawing operations.
|
|
|
|
\snippet widgets/scribble/scribblearea.cpp 9
|
|
\snippet widgets/scribble/scribblearea.cpp 10
|
|
|
|
The public \c clearImage() slot clears the image displayed in the
|
|
scribble area. We simply fill the entire image with white, which
|
|
corresponds to RGB value (255, 255, 255). As usual when we modify
|
|
the image, we set \c modified to \c true and schedule a repaint.
|
|
|
|
\snippet widgets/scribble/scribblearea.cpp 11
|
|
\snippet widgets/scribble/scribblearea.cpp 12
|
|
|
|
For mouse press and mouse release events, we use the
|
|
QMouseEvent::button() function to find out which button caused
|
|
the event. For mose move events, we use QMouseEvent::buttons()
|
|
to find which buttons are currently held down (as an OR-combination).
|
|
|
|
If the users press the left mouse button, we store the position
|
|
of the mouse cursor in \c lastPoint. We also make a note that the
|
|
user is currently scribbling. (The \c scribbling variable is
|
|
necessary because we can't assume that a mouse move and mouse
|
|
release event is always preceded by a mouse press event on the
|
|
same widget.)
|
|
|
|
If the user moves the mouse with the left button pressed down or
|
|
releases the button, we call the private \c drawLineTo() function
|
|
to draw.
|
|
|
|
\snippet widgets/scribble/scribblearea.cpp 13
|
|
\snippet widgets/scribble/scribblearea.cpp 14
|
|
|
|
In the reimplementation of the \l
|
|
{QWidget::paintEvent()}{paintEvent()} function, we simply create
|
|
a QPainter for the scribble area, and draw the image.
|
|
|
|
At this point, you might wonder why we don't just draw directly
|
|
onto the widget instead of drawing in a QImage and copying the
|
|
QImage onto screen in \c paintEvent(). There are at least three
|
|
good reasons for this:
|
|
|
|
\list
|
|
\li The window system requires us to be able to redraw the widget
|
|
\e{at any time}. For example, if the window is minimized and
|
|
restored, the window system might have forgotten the contents
|
|
of the widget and send us a paint event. In other words, we
|
|
can't rely on the window system to remember our image.
|
|
|
|
\li Qt normally doesn't allow us to paint outside of \c
|
|
paintEvent(). In particular, we can't paint from the mouse
|
|
event handlers. (This behavior can be changed using the
|
|
Qt::WA_PaintOnScreen widget attribute, though.)
|
|
|
|
\li If initialized properly, a QImage is guaranteed to use 8-bit
|
|
for each color channel (red, green, blue, and alpha), whereas
|
|
a QWidget might have a lower color depth, depending on the
|
|
monitor configuration. This means that if we load a 24-bit or
|
|
32-bit image and paint it onto a QWidget, then copy the
|
|
QWidget into a QImage again, we might lose some information.
|
|
\endlist
|
|
|
|
\snippet widgets/scribble/scribblearea.cpp 15
|
|
\snippet widgets/scribble/scribblearea.cpp 16
|
|
|
|
When the user starts the Scribble application, a resize event is
|
|
generated and an image is created and displayed in the scribble
|
|
area. We make this initial image slightly larger than the
|
|
application's main window and scribble area, to avoid always
|
|
resizing the image when the user resizes the main window (which
|
|
would be very inefficient). But when the main window becomes
|
|
larger than this initial size, the image needs to be resized.
|
|
|
|
\snippet widgets/scribble/scribblearea.cpp 17
|
|
\snippet widgets/scribble/scribblearea.cpp 18
|
|
|
|
In \c drawLineTo(), we draw a line from the point where the mouse
|
|
was located when the last mouse press or mouse move occurred, we
|
|
set \c modified to true, we generate a repaint event, and we
|
|
update \c lastPoint so that next time \c drawLineTo() is called,
|
|
we continue drawing from where we left.
|
|
|
|
We could call the \c update() function with no parameter, but as
|
|
an easy optimization we pass a QRect that specifies the rectangle
|
|
inside the scribble are needs updating, to avoid a complete
|
|
repaint of the widget.
|
|
|
|
\snippet widgets/scribble/scribblearea.cpp 19
|
|
\snippet widgets/scribble/scribblearea.cpp 20
|
|
|
|
QImage has no nice API for resizing an image. There's a
|
|
QImage::copy() function that could do the trick, but when used to
|
|
expand an image, it fills the new areas with black, whereas we
|
|
want white.
|
|
|
|
So the trick is to create a brand new QImage with the right size,
|
|
to fill it with white, and to draw the old image onto it using
|
|
QPainter. The new image is given the QImage::Format_RGB32
|
|
format, which means that each pixel is stored as 0xffRRGGBB
|
|
(where RR, GG, and BB are the red, green and blue
|
|
color channels, ff is the hexadecimal value 255).
|
|
|
|
Printing is handled by the \c print() slot:
|
|
|
|
\snippet widgets/scribble/scribblearea.cpp 21
|
|
|
|
We construct a high resolution QPrinter object for the required
|
|
output format, using a QPrintDialog to ask the user to specify a
|
|
page size and indicate how the output should be formatted on the page.
|
|
|
|
If the dialog is accepted, we perform the task of printing to the paint
|
|
device:
|
|
|
|
\snippet widgets/scribble/scribblearea.cpp 22
|
|
|
|
Printing an image to a file in this way is simply a matter of
|
|
painting onto the QPrinter. We scale the image to fit within the
|
|
available space on the page before painting it onto the paint
|
|
device.
|
|
|
|
\section1 MainWindow Class Definition
|
|
|
|
\snippet widgets/scribble/mainwindow.h 0
|
|
|
|
The \c MainWindow class inherits from QMainWindow. We reimplement
|
|
the \l{QWidget::closeEvent()}{closeEvent()} handler from QWidget.
|
|
The \c open(), \c save(), \c penColor() and \c penWidth()
|
|
slots correspond to menu entries. In addition we create four
|
|
private functions.
|
|
|
|
We use the boolean \c maybeSave() function to check if there are
|
|
any unsaved changes. If there are unsaved changes, we give the
|
|
user the opportunity to save these changes. The function returns
|
|
\c false if the user clicks \uicontrol Cancel. We use the \c saveFile()
|
|
function to let the user save the image currently displayed in
|
|
the scribble area.
|
|
|
|
\section1 MainWindow Class Implementation
|
|
|
|
\snippet widgets/scribble/mainwindow.cpp 0
|
|
|
|
In the constructor, we create a scribble area which we make the
|
|
central widget of the \c MainWindow widget. Then we create the
|
|
associated actions and menus.
|
|
|
|
\snippet widgets/scribble/mainwindow.cpp 1
|
|
\snippet widgets/scribble/mainwindow.cpp 2
|
|
|
|
Close events are sent to widgets that the users want to close,
|
|
usually by clicking \uicontrol{File|Exit} or by clicking the \uicontrol X
|
|
title bar button. By reimplementing the event handler, we can
|
|
intercept attempts to close the application.
|
|
|
|
In this example, we use the close event to ask the user to save
|
|
any unsaved changes. The logic for that is located in the \c
|
|
maybeSave() function. If \c maybeSave() returns true, there are
|
|
no modifications or the users successfully saved them, and we
|
|
accept the event. The application can then terminate normally. If
|
|
\c maybeSave() returns false, the user clicked \uicontrol Cancel, so we
|
|
"ignore" the event, leaving the application unaffected by it.
|
|
|
|
\snippet widgets/scribble/mainwindow.cpp 3
|
|
\snippet widgets/scribble/mainwindow.cpp 4
|
|
|
|
In the \c open() slot we first give the user the opportunity to
|
|
save any modifications to the currently displayed image, before a
|
|
new image is loaded into the scribble area. Then we ask the user
|
|
to choose a file and we load the file in the \c ScribbleArea.
|
|
|
|
\snippet widgets/scribble/mainwindow.cpp 5
|
|
\snippet widgets/scribble/mainwindow.cpp 6
|
|
|
|
The \c save() slot is called when the users choose the \uicontrol {Save
|
|
As} menu entry, and then choose an entry from the format menu. The
|
|
first thing we need to do is to find out which action sent the
|
|
signal using QObject::sender(). This function returns the sender
|
|
as a QObject pointer. Since we know that the sender is an action
|
|
object, we can safely cast the QObject. We could have used a
|
|
C-style cast or a C++ \c static_cast<>(), but as a defensive
|
|
programming technique we use a qobject_cast(). The advantage is
|
|
that if the object has the wrong type, a null pointer is
|
|
returned. Crashes due to null pointers are much easier to diagnose
|
|
than crashes due to unsafe casts.
|
|
|
|
Once we have the action, we extract the chosen format using
|
|
QAction::data(). (When the actions are created, we use
|
|
QAction::setData() to set our own custom data attached to the
|
|
action, as a QVariant. More on this when we review \c
|
|
createActions().)
|
|
|
|
Now that we know the format, we call the private \c saveFile()
|
|
function to save the currently displayed image.
|
|
|
|
\snippet widgets/scribble/mainwindow.cpp 7
|
|
\snippet widgets/scribble/mainwindow.cpp 8
|
|
|
|
We use the \c penColor() slot to retrieve a new color from the
|
|
user with a QColorDialog. If the user chooses a new color, we
|
|
make it the scribble area's color.
|
|
|
|
\snippet widgets/scribble/mainwindow.cpp 9
|
|
\snippet widgets/scribble/mainwindow.cpp 10
|
|
|
|
To retrieve a new pen width in the \c penWidth() slot, we use
|
|
QInputDialog. The QInputDialog class provides a simple
|
|
convenience dialog to get a single value from the user. We use
|
|
the static QInputDialog::getInt() function, which combines a
|
|
QLabel and a QSpinBox. The QSpinBox is initialized with the
|
|
scribble area's pen width, allows a range from 1 to 50, a step of
|
|
1 (meaning that the up and down arrow increment or decrement the
|
|
value by 1).
|
|
|
|
The boolean \c ok variable will be set to \c true if the user
|
|
clicked \uicontrol OK and to \c false if the user pressed \uicontrol Cancel.
|
|
|
|
\snippet widgets/scribble/mainwindow.cpp 11
|
|
\snippet widgets/scribble/mainwindow.cpp 12
|
|
|
|
We implement the \c about() slot to create a message box
|
|
describing what the example is designed to show.
|
|
|
|
\snippet widgets/scribble/mainwindow.cpp 13
|
|
\snippet widgets/scribble/mainwindow.cpp 14
|
|
|
|
In the \c createAction() function we create the actions
|
|
representing the menu entries and connect them to the appropriate
|
|
slots. In particular we create the actions found in the \uicontrol
|
|
{Save As} sub-menu. We use QImageWriter::supportedImageFormats()
|
|
to get a list of the supported formats (as a QList<QByteArray>).
|
|
|
|
Then we iterate through the list, creating an action for each
|
|
format. We call QAction::setData() with the file format, so we
|
|
can retrieve it later as QAction::data(). We could also have
|
|
deduced the file format from the action's text, by truncating the
|
|
"...", but that would have been inelegant.
|
|
|
|
\snippet widgets/scribble/mainwindow.cpp 15
|
|
\snippet widgets/scribble/mainwindow.cpp 16
|
|
|
|
In the \c createMenu() function, we add the previously created
|
|
format actions to the \c saveAsMenu. Then we add the rest of the
|
|
actions as well as the \c saveAsMenu sub-menu to the \uicontrol File,
|
|
\uicontrol Options and \uicontrol Help menus.
|
|
|
|
The QMenu class provides a menu widget for use in menu bars,
|
|
context menus, and other popup menus. The QMenuBar class provides
|
|
a horizontal menu bar with a list of pull-down \l{QMenu}s. At the
|
|
end we put the \uicontrol File and \uicontrol Options menus in the \c
|
|
{MainWindow}'s menu bar, which we retrieve using the
|
|
QMainWindow::menuBar() function.
|
|
|
|
\snippet widgets/scribble/mainwindow.cpp 17
|
|
\snippet widgets/scribble/mainwindow.cpp 18
|
|
|
|
In \c mayBeSave(), we check if there are any unsaved changes. If
|
|
there are any, we use QMessageBox to give the user a warning that
|
|
the image has been modified and the opportunity to save the
|
|
modifications.
|
|
|
|
As with QColorDialog and QFileDialog, the easiest way to create a
|
|
QMessageBox is to use its static functions. QMessageBox provides
|
|
a range of different messages arranged along two axes: severity
|
|
(question, information, warning and critical) and complexity (the
|
|
number of necessary response buttons). Here we use the \c
|
|
warning() function sice the message is rather important.
|
|
|
|
If the user chooses to save, we call the private \c saveFile()
|
|
function. For simplicitly, we use PNG as the file format; the
|
|
user can always press \uicontrol Cancel and save the file using another
|
|
format.
|
|
|
|
The \c maybeSave() function returns \c false if the user clicks
|
|
\uicontrol Cancel; otherwise it returns \c true.
|
|
|
|
\snippet widgets/scribble/mainwindow.cpp 19
|
|
\snippet widgets/scribble/mainwindow.cpp 20
|
|
|
|
In \c saveFile(), we pop up a file dialog with a file name
|
|
suggestion. The static QFileDialog::getSaveFileName() function
|
|
returns a file name selected by the user. The file does not have
|
|
to exist.
|
|
*/
|