/****************************************************************************+ −
**+ −
** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).+ −
** All rights reserved.+ −
** Contact: Nokia Corporation (qt-info@nokia.com)+ −
**+ −
** This file is part of the documentation of the Qt Toolkit.+ −
**+ −
** $QT_BEGIN_LICENSE:LGPL$+ −
** No Commercial Usage+ −
** This file contains pre-release code and may not be distributed.+ −
** You may use this file in accordance with the terms and conditions+ −
** contained in the Technology Preview License Agreement accompanying+ −
** this package.+ −
**+ −
** GNU Lesser General Public License Usage+ −
** Alternatively, this file may be used under the terms of the GNU Lesser+ −
** General Public License version 2.1 as published by the Free Software+ −
** Foundation and appearing in the file LICENSE.LGPL included in the+ −
** packaging of this file. Please review the following information to+ −
** ensure the GNU Lesser General Public License version 2.1 requirements+ −
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.+ −
**+ −
** In addition, as a special exception, Nokia gives you certain additional+ −
** rights. These rights are described in the Nokia Qt LGPL Exception+ −
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.+ −
**+ −
** If you have questions regarding the use of this file, please contact+ −
** Nokia at qt-info@nokia.com.+ −
**+ −
**+ −
**+ −
**+ −
**+ −
**+ −
**+ −
**+ −
** $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+ −
\gui 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 \gui Options menu allows the users to to choose the+ −
pen color and pen width, as well as clear the screen. In addition+ −
the \gui 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+ −
\o \c ScribbleArea is a custom widget that displays a QImage and+ −
allows to the user to draw on it.+ −
\o \c MainWindow provides a menu above the \c ScribbleArea.+ −
\endlist+ −
+ −
We will start by reviewing the \c ScribbleArea class, which+ −
contains the interesting, then we will take a look at the \c+ −
MainWindow class that uses it.+ −
+ −
\section1 ScribbleArea Class Definition+ −
+ −
\snippet examples/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+ −
\o \c modified is \c true if there are unsaved+ −
changes to the image displayed in the scribble area.+ −
\o \c scribbling is \c true while the user is pressing+ −
the left mouse button within the scribble area.+ −
\o \c penWidth and \c penColor hold the currently+ −
set width and color for the pen used in the application.+ −
\o \c image stores the image drawn by the user.+ −
\o \c lastPoint holds the position of the cursor at the last+ −
mouse press or mouse move event.+ −
\endlist+ −
+ −
\section1 ScribbleArea Class Implementation+ −
+ −
\snippet examples/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 examples/widgets/scribble/scribblearea.cpp 1+ −
\snippet examples/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 examples/widgets/scribble/scribblearea.cpp 3+ −
\snippet examples/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 examples/widgets/scribble/scribblearea.cpp 5+ −
\snippet examples/widgets/scribble/scribblearea.cpp 6+ −
\codeline+ −
\snippet examples/widgets/scribble/scribblearea.cpp 7+ −
\snippet examples/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 examples/widgets/scribble/scribblearea.cpp 9+ −
\snippet examples/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 examples/widgets/scribble/scribblearea.cpp 11+ −
\snippet examples/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 examples/widgets/scribble/scribblearea.cpp 13+ −
\snippet examples/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+ −
\o 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.+ −
+ −
\o 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.)+ −
+ −
\o 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 examples/widgets/scribble/scribblearea.cpp 15+ −
\snippet examples/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 examples/widgets/scribble/scribblearea.cpp 17+ −
\snippet examples/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 examples/widgets/scribble/scribblearea.cpp 19+ −
\snippet examples/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 examples/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 examples/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 examples/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 \gui 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 examples/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 examples/widgets/scribble/mainwindow.cpp 1+ −
\snippet examples/widgets/scribble/mainwindow.cpp 2+ −
+ −
Close events are sent to widgets that the users want to close,+ −
usually by clicking \gui{File|Exit} or by clicking the \gui 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 \gui Cancel, so we+ −
"ignore" the event, leaving the application unaffected by it.+ −
+ −
\snippet examples/widgets/scribble/mainwindow.cpp 3+ −
\snippet examples/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 examples/widgets/scribble/mainwindow.cpp 5+ −
\snippet examples/widgets/scribble/mainwindow.cpp 6+ −
+ −
The \c save() slot is called when the users choose the \gui {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 examples/widgets/scribble/mainwindow.cpp 7+ −
\snippet examples/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 examples/widgets/scribble/mainwindow.cpp 9+ −
\snippet examples/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 \gui OK and to \c false if the user pressed \gui Cancel.+ −
+ −
\snippet examples/widgets/scribble/mainwindow.cpp 11+ −
\snippet examples/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 examples/widgets/scribble/mainwindow.cpp 13+ −
\snippet examples/widgets/scribble/mainwindow.cpp 14+ −
+ −
In the \c createAction() function we create the actions+ −
representing the menu entries and connect them to the appropiate+ −
slots. In particular we create the actions found in the \gui+ −
{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 examples/widgets/scribble/mainwindow.cpp 15+ −
\snippet examples/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 \gui File,+ −
\gui Options and \gui 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 \gui File and \gui Options menus in the \c+ −
{MainWindow}'s menu bar, which we retrieve using the+ −
QMainWindow::menuBar() function.+ −
+ −
\snippet examples/widgets/scribble/mainwindow.cpp 17+ −
\snippet examples/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 \gui Cancel and save the file using another+ −
format.+ −
+ −
The \c maybeSave() function returns \c false if the user clicks+ −
\gui Cancel; otherwise it returns \c true.+ −
+ −
\snippet examples/widgets/scribble/mainwindow.cpp 19+ −
\snippet examples/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.+ −
*/+ −