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