doc/src/examples/scribble.qdoc
changeset 7 f7bc934e204c
equal deleted inserted replaced
3:41300fa6a67c 7:f7bc934e204c
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2010 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. Then we will
       
    78     review the \c MainWindow class, which uses \c ScribbleArea.
       
    79 
       
    80     \section1 ScribbleArea Class Definition
       
    81 
       
    82     \snippet examples/widgets/scribble/scribblearea.h 0
       
    83 
       
    84     The \c ScribbleArea class inherits from QWidget. We reimplement
       
    85     the \c mousePressEvent(), \c mouseMoveEvent() and \c
       
    86     mouseReleaseEvent() functions to implement the drawing. We
       
    87     reimplement the \c paintEvent() function to update the scribble
       
    88     area, and the \c resizeEvent() function to ensure that the QImage
       
    89     on which we draw is at least as large as the widget at any time.
       
    90 
       
    91     We need several public functions: \c openImage() loads an image
       
    92     from a file into the scribble area, allowing the user to edit the
       
    93     image; \c save() writes the currently displayed image to file; \c
       
    94     clearImage() slot clears the image displayed in the scribble
       
    95     area. We need the private \c drawLineTo() function to actually do
       
    96     the drawing, and \c resizeImage() to change the size of a
       
    97     QImage. The \c print() slot handles printing.
       
    98 
       
    99     We also need the following private variables:
       
   100 
       
   101     \list
       
   102     \o \c modified is \c true if there are unsaved
       
   103        changes to the image displayed in the scribble area.
       
   104     \o \c scribbling is \c true while the user is pressing
       
   105        the left mouse button within the scribble area.
       
   106     \o \c penWidth and \c penColor hold the currently
       
   107        set width and color for the pen used in the application.
       
   108     \o \c image stores the image drawn by the user.
       
   109     \o \c lastPoint holds the position of the cursor at the last
       
   110        mouse press or mouse move event.
       
   111     \endlist
       
   112 
       
   113     \section1 ScribbleArea Class Implementation
       
   114 
       
   115     \snippet examples/widgets/scribble/scribblearea.cpp 0
       
   116 
       
   117     In the constructor, we set the Qt::WA_StaticContents
       
   118     attribute for the widget, indicating that the widget contents are
       
   119     rooted to the top-left corner and don't change when the widget is
       
   120     resized. Qt uses this attribute to optimize paint events on
       
   121     resizes. This is purely an optimization and should only be used
       
   122     for widgets whose contents are static and rooted to the top-left
       
   123     corner.
       
   124 
       
   125     \snippet examples/widgets/scribble/scribblearea.cpp 1
       
   126     \snippet examples/widgets/scribble/scribblearea.cpp 2
       
   127 
       
   128     In the \c openImage() function, we load the given image. Then we
       
   129     resize the loaded QImage to be at least as large as the widget in
       
   130     both directions using the private \c resizeImage() function and
       
   131     we set the \c image member variable to be the loaded image. At
       
   132     the end, we call QWidget::update() to schedule a repaint.
       
   133 
       
   134     \snippet examples/widgets/scribble/scribblearea.cpp 3
       
   135     \snippet examples/widgets/scribble/scribblearea.cpp 4
       
   136 
       
   137     The \c saveImage() function creates a QImage object that covers
       
   138     only the visible section of the actual \c image and saves it using
       
   139     QImage::save(). If the image is successfully saved, we set the
       
   140     scribble area's \c modified variable to \c false, because there is
       
   141     no unsaved data.
       
   142 
       
   143     \snippet examples/widgets/scribble/scribblearea.cpp 5
       
   144     \snippet examples/widgets/scribble/scribblearea.cpp 6
       
   145     \codeline
       
   146     \snippet examples/widgets/scribble/scribblearea.cpp 7
       
   147     \snippet examples/widgets/scribble/scribblearea.cpp 8
       
   148 
       
   149     The \c setPenColor() and \c setPenWidth() functions set the
       
   150     current pen color and width. These values will be used for future
       
   151     drawing operations.
       
   152 
       
   153     \snippet examples/widgets/scribble/scribblearea.cpp 9
       
   154     \snippet examples/widgets/scribble/scribblearea.cpp 10
       
   155 
       
   156     The public \c clearImage() slot clears the image displayed in the
       
   157     scribble area. We simply fill the entire image with white, which
       
   158     corresponds to RGB value (255, 255, 255). As usual when we modify
       
   159     the image, we set \c modified to \c true and schedule a repaint.
       
   160 
       
   161     \snippet examples/widgets/scribble/scribblearea.cpp 11
       
   162     \snippet examples/widgets/scribble/scribblearea.cpp 12
       
   163 
       
   164     For mouse press and mouse release events, we use the
       
   165     QMouseEvent::button() function to find out which button caused
       
   166     the event. For mose move events, we use QMouseEvent::buttons()
       
   167     to find which buttons are currently held down (as an OR-combination).
       
   168 
       
   169     If the users press the left mouse button, we store the position
       
   170     of the mouse cursor in \c lastPoint. We also make a note that the
       
   171     user is currently scribbling. (The \c scribbling variable is
       
   172     necessary because we can't assume that a mouse move and mouse
       
   173     release event is always preceded by a mouse press event on the
       
   174     same widget.)
       
   175 
       
   176     If the user moves the mouse with the left button pressed down or
       
   177     releases the button, we call the private \c drawLineTo() function
       
   178     to draw.
       
   179 
       
   180     \snippet examples/widgets/scribble/scribblearea.cpp 13
       
   181     \snippet examples/widgets/scribble/scribblearea.cpp 14
       
   182 
       
   183     In the reimplementation of the \l
       
   184     {QWidget::paintEvent()}{paintEvent()} function, we simply create
       
   185     a QPainter for the scribble area, and draw the image.
       
   186 
       
   187     At this point, you might wonder why we don't just draw directly
       
   188     onto the widget instead of drawing in a QImage and copying the
       
   189     QImage onto screen in \c paintEvent(). There are at least three
       
   190     good reasons for this:
       
   191 
       
   192     \list
       
   193     \o The window system requires us to be able to redraw the widget
       
   194        \e{at any time}. For example, if the window is minimized and
       
   195        restored, the window system might have forgotten the contents
       
   196        of the widget and send us a paint event. In other words, we
       
   197        can't rely on the window system to remember our image.
       
   198 
       
   199     \o Qt normally doesn't allow us to paint outside of \c
       
   200        paintEvent(). In particular, we can't paint from the mouse
       
   201        event handlers. (This behavior can be changed using the
       
   202        Qt::WA_PaintOnScreen widget attribute, though.)
       
   203 
       
   204     \o If initialized properly, a QImage is guaranteed to use 8-bit
       
   205        for each color channel (red, green, blue, and alpha), whereas
       
   206        a QWidget might have a lower color depth, depending on the
       
   207        monitor configuration. This means that if we load a 24-bit or
       
   208        32-bit image and paint it onto a QWidget, then copy the
       
   209        QWidget into a QImage again, we might lose some information.
       
   210     \endlist
       
   211 
       
   212     \snippet examples/widgets/scribble/scribblearea.cpp 15
       
   213     \snippet examples/widgets/scribble/scribblearea.cpp 16
       
   214 
       
   215     When the user starts the Scribble application, a resize event is
       
   216     generated and an image is created and displayed in the scribble
       
   217     area. We make this initial image slightly larger than the
       
   218     application's main window and scribble area, to avoid always
       
   219     resizing the image when the user resizes the main window (which
       
   220     would be very inefficient). But when the main window becomes
       
   221     larger than this initial size, the image needs to be resized.
       
   222 
       
   223     \snippet examples/widgets/scribble/scribblearea.cpp 17
       
   224     \snippet examples/widgets/scribble/scribblearea.cpp 18
       
   225 
       
   226     In \c drawLineTo(), we draw a line from the point where the mouse
       
   227     was located when the last mouse press or mouse move occurred, we
       
   228     set \c modified to true, we generate a repaint event, and we
       
   229     update \c lastPoint so that next time \c drawLineTo() is called,
       
   230     we continue drawing from where we left.
       
   231 
       
   232     We could call the \c update() function with no parameter, but as
       
   233     an easy optimization we pass a QRect that specifies the rectangle
       
   234     inside the scribble are needs updating, to avoid a complete
       
   235     repaint of the widget.
       
   236 
       
   237     \snippet examples/widgets/scribble/scribblearea.cpp 19
       
   238     \snippet examples/widgets/scribble/scribblearea.cpp 20
       
   239 
       
   240     QImage has no nice API for resizing an image. There's a
       
   241     QImage::copy() function that could do the trick, but when used to
       
   242     expand an image, it fills the new areas with black, whereas we
       
   243     want white.
       
   244 
       
   245     So the trick is to create a brand new QImage with the right size,
       
   246     to fill it with white, and to draw the old image onto it using
       
   247     QPainter. The new image is given the QImage::Format_RGB32
       
   248     format, which means that each pixel is stored as 0xffRRGGBB
       
   249     (where RR, GG, and BB are the red, green and blue
       
   250     color channels, ff is the hexadecimal value 255).
       
   251 
       
   252     Printing is handled by the \c print() slot:
       
   253 
       
   254     \snippet examples/widgets/scribble/scribblearea.cpp 21
       
   255 
       
   256     We construct a high resolution QPrinter object for the required
       
   257     output format, using a QPrintDialog to ask the user to specify a
       
   258     page size and indicate how the output should be formatted on the page.
       
   259 
       
   260     If the dialog is accepted, we perform the task of printing to the paint
       
   261     device:
       
   262 
       
   263     \snippet examples/widgets/scribble/scribblearea.cpp 22
       
   264 
       
   265     Printing an image to a file in this way is simply a matter of
       
   266     painting onto the QPrinter. We scale the image to fit within the
       
   267     available space on the page before painting it onto the paint
       
   268     device.
       
   269 
       
   270     \section1 MainWindow Class Definition
       
   271 
       
   272     \snippet examples/widgets/scribble/mainwindow.h 0
       
   273 
       
   274     The \c MainWindow class inherits from QMainWindow. We reimplement
       
   275     the \l{QWidget::closeEvent()}{closeEvent()} handler from QWidget.
       
   276     The \c open(), \c save(), \c penColor() and \c penWidth()
       
   277     slots correspond to menu entries. In addition we create four
       
   278     private functions.
       
   279 
       
   280     We use the boolean \c maybeSave() function to check if there are
       
   281     any unsaved changes. If there are unsaved changes, we give the
       
   282     user the opportunity to save these changes. The function returns
       
   283     \c false if the user clicks \gui Cancel. We use the \c saveFile()
       
   284     function to let the user save the image currently displayed in
       
   285     the scribble area.
       
   286 
       
   287     \section1 MainWindow Class Implementation
       
   288 
       
   289     \snippet examples/widgets/scribble/mainwindow.cpp 0
       
   290 
       
   291     In the constructor, we create a scribble area which we make the
       
   292     central widget of the \c MainWindow widget. Then we create the
       
   293     associated actions and menus.
       
   294 
       
   295     \snippet examples/widgets/scribble/mainwindow.cpp 1
       
   296     \snippet examples/widgets/scribble/mainwindow.cpp 2
       
   297 
       
   298     Close events are sent to widgets that the users want to close,
       
   299     usually by clicking \gui{File|Exit} or by clicking the \gui X
       
   300     title bar button. By reimplementing the event handler, we can
       
   301     intercept attempts to close the application.
       
   302 
       
   303     In this example, we use the close event to ask the user to save
       
   304     any unsaved changes. The logic for that is located in the \c
       
   305     maybeSave() function. If \c maybeSave() returns true, there are
       
   306     no modifications or the users successfully saved them, and we
       
   307     accept the event. The application can then terminate normally. If
       
   308     \c maybeSave() returns false, the user clicked \gui Cancel, so we
       
   309     "ignore" the event, leaving the application unaffected by it.
       
   310 
       
   311     \snippet examples/widgets/scribble/mainwindow.cpp 3
       
   312     \snippet examples/widgets/scribble/mainwindow.cpp 4
       
   313 
       
   314     In the \c open() slot we first give the user the opportunity to
       
   315     save any modifications to the currently displayed image, before a
       
   316     new image is loaded into the scribble area. Then we ask the user
       
   317     to choose a file and we load the file in the \c ScribbleArea.
       
   318 
       
   319     \snippet examples/widgets/scribble/mainwindow.cpp 5
       
   320     \snippet examples/widgets/scribble/mainwindow.cpp 6
       
   321 
       
   322     The \c save() slot is called when the users choose the \gui {Save
       
   323     As} menu entry, and then choose an entry from the format menu. The
       
   324     first thing we need to do is to find out which action sent the
       
   325     signal using QObject::sender(). This function returns the sender
       
   326     as a QObject pointer. Since we know that the sender is an action
       
   327     object, we can safely cast the QObject. We could have used a
       
   328     C-style cast or a C++ \c static_cast<>(), but as a defensive
       
   329     programming technique we use a qobject_cast(). The advantage is
       
   330     that if the object has the wrong type, a null pointer is
       
   331     returned. Crashes due to null pointers are much easier to diagnose
       
   332     than crashes due to unsafe casts.
       
   333 
       
   334     Once we have the action, we extract the chosen format using
       
   335     QAction::data(). (When the actions are created, we use
       
   336     QAction::setData() to set our own custom data attached to the
       
   337     action, as a QVariant. More on this when we review \c
       
   338     createActions().)
       
   339 
       
   340     Now that we know the format, we call the private \c saveFile()
       
   341     function to save the currently displayed image.
       
   342 
       
   343     \snippet examples/widgets/scribble/mainwindow.cpp 7
       
   344     \snippet examples/widgets/scribble/mainwindow.cpp 8
       
   345 
       
   346     We use the \c penColor() slot to retrieve a new color from the
       
   347     user with a QColorDialog. If the user chooses a new color, we
       
   348     make it the scribble area's color.
       
   349 
       
   350     \snippet examples/widgets/scribble/mainwindow.cpp 9
       
   351     \snippet examples/widgets/scribble/mainwindow.cpp 10
       
   352 
       
   353     To retrieve a new pen width in the \c penWidth() slot, we use
       
   354     QInputDialog. The QInputDialog class provides a simple
       
   355     convenience dialog to get a single value from the user. We use
       
   356     the static QInputDialog::getInt() function, which combines a
       
   357     QLabel and a QSpinBox. The QSpinBox is initialized with the
       
   358     scribble area's pen width, allows a range from 1 to 50, a step of
       
   359     1 (meaning that the up and down arrow increment or decrement the
       
   360     value by 1).
       
   361 
       
   362     The boolean \c ok variable will be set to \c true if the user
       
   363     clicked \gui OK and to \c false if the user pressed \gui Cancel.
       
   364 
       
   365     \snippet examples/widgets/scribble/mainwindow.cpp 11
       
   366     \snippet examples/widgets/scribble/mainwindow.cpp 12
       
   367 
       
   368     We implement the \c about() slot to create a message box
       
   369     describing what the example is designed to show.
       
   370 
       
   371     \snippet examples/widgets/scribble/mainwindow.cpp 13
       
   372     \snippet examples/widgets/scribble/mainwindow.cpp 14
       
   373 
       
   374     In the \c createAction() function we create the actions
       
   375     representing the menu entries and connect them to the appropiate
       
   376     slots. In particular we create the actions found in the \gui
       
   377     {Save As} sub-menu. We use QImageWriter::supportedImageFormats()
       
   378     to get a list of the supported formats (as a QList<QByteArray>).
       
   379 
       
   380     Then we iterate through the list, creating an action for each
       
   381     format. We call QAction::setData() with the file format, so we
       
   382     can retrieve it later as QAction::data(). We could also have
       
   383     deduced the file format from the action's text, by truncating the
       
   384     "...", but that would have been inelegant.
       
   385 
       
   386     \snippet examples/widgets/scribble/mainwindow.cpp 15
       
   387     \snippet examples/widgets/scribble/mainwindow.cpp 16
       
   388 
       
   389     In the \c createMenu() function, we add the previously created
       
   390     format actions to the \c saveAsMenu. Then we add the rest of the
       
   391     actions as well as the \c saveAsMenu sub-menu to the \gui File,
       
   392     \gui Options and \gui Help menus.
       
   393 
       
   394     The QMenu class provides a menu widget for use in menu bars,
       
   395     context menus, and other popup menus. The QMenuBar class provides
       
   396     a horizontal menu bar with a list of pull-down \l{QMenu}s. At the
       
   397     end we put the \gui File and \gui Options menus in the \c
       
   398     {MainWindow}'s menu bar, which we retrieve using the
       
   399     QMainWindow::menuBar() function.
       
   400 
       
   401     \snippet examples/widgets/scribble/mainwindow.cpp 17
       
   402     \snippet examples/widgets/scribble/mainwindow.cpp 18
       
   403 
       
   404     In \c mayBeSave(), we check if there are any unsaved changes. If
       
   405     there are any, we use QMessageBox to give the user a warning that
       
   406     the image has been modified and the opportunity to save the
       
   407     modifications.
       
   408 
       
   409     As with QColorDialog and QFileDialog, the easiest way to create a
       
   410     QMessageBox is to use its static functions. QMessageBox provides
       
   411     a range of different messages arranged along two axes: severity
       
   412     (question, information, warning and critical) and complexity (the
       
   413     number of necessary response buttons). Here we use the \c
       
   414     warning() function sice the message is rather important.
       
   415 
       
   416     If the user chooses to save, we call the private \c saveFile()
       
   417     function. For simplicitly, we use PNG as the file format; the
       
   418     user can always press \gui Cancel and save the file using another
       
   419     format.
       
   420 
       
   421     The \c maybeSave() function returns \c false if the user clicks
       
   422     \gui Cancel; otherwise it returns \c true.
       
   423 
       
   424     \snippet examples/widgets/scribble/mainwindow.cpp 19
       
   425     \snippet examples/widgets/scribble/mainwindow.cpp 20
       
   426 
       
   427     In \c saveFile(), we pop up a file dialog with a file name
       
   428     suggestion. The static QFileDialog::getSaveFileName() function
       
   429     returns a file name selected by the user. The file does not have
       
   430     to exist.
       
   431 */