summaryrefslogtreecommitdiffstats
path: root/doc/src/examples/scribble.qdoc
diff options
context:
space:
mode:
authorQt by Nokia <qt-info@nokia.com>2011-04-27 12:05:43 +0200
committeraxis <qt-info@nokia.com>2011-04-27 12:05:43 +0200
commit38be0d13830efd2d98281c645c3a60afe05ffece (patch)
tree6ea73f3ec77f7d153333779883e8120f82820abe /doc/src/examples/scribble.qdoc
Initial import from the monolithic Qt.
This is the beginning of revision history for this module. If you want to look at revision history older than this, please refer to the Qt Git wiki for how to use Git history grafting. At the time of writing, this wiki is located here: http://qt.gitorious.org/qt/pages/GitIntroductionWithQt If you have already performed the grafting and you don't see any history beyond this commit, try running "git log" with the "--follow" argument. Branched from the monolithic repo, Qt master branch, at commit 896db169ea224deb96c59ce8af800d019de63f12
Diffstat (limited to 'doc/src/examples/scribble.qdoc')
-rw-r--r--doc/src/examples/scribble.qdoc417
1 files changed, 417 insertions, 0 deletions
diff --git a/doc/src/examples/scribble.qdoc b/doc/src/examples/scribble.qdoc
new file mode 100644
index 0000000000..de60eab670
--- /dev/null
+++ b/doc/src/examples/scribble.qdoc
@@ -0,0 +1,417 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 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:FDL$
+** 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 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.
+**
+** 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. Then we will
+ review the \c MainWindow class, which uses \c ScribbleArea.
+
+ \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.
+*/