doc/src/examples/scribble.qdoc
branchRCL_3
changeset 7 3f74d0d4af4c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/src/examples/scribble.qdoc	Thu Apr 08 14:19:33 2010 +0300
@@ -0,0 +1,431 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 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. 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.
+*/