| /**************************************************************************** |
| ** |
| ** Copyright (C) 2016 The Qt Company Ltd. |
| ** Contact: https://www.qt.io/licensing/ |
| ** |
| ** This file is part of the documentation of the Qt Toolkit. |
| ** |
| ** $QT_BEGIN_LICENSE:FDL$ |
| ** Commercial License Usage |
| ** Licensees holding valid commercial Qt licenses may use this file in |
| ** accordance with the commercial license agreement provided with the |
| ** Software or, alternatively, in accordance with the terms contained in |
| ** a written agreement between you and The Qt Company. For licensing terms |
| ** and conditions see https://www.qt.io/terms-conditions. For further |
| ** information use the contact form at https://www.qt.io/contact-us. |
| ** |
| ** GNU Free Documentation License Usage |
| ** 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. Please review the following information to ensure |
| ** the GNU Free Documentation License version 1.3 requirements |
| ** will be met: https://www.gnu.org/licenses/fdl-1.3.html. |
| ** $QT_END_LICENSE$ |
| ** |
| ****************************************************************************/ |
| |
| /*! |
| \example widgets/scribble |
| \title Scribble Example |
| \ingroup examples-widgets |
| \brief 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 |
| \uicontrol 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 \uicontrol Options menu allows the users to choose the |
| pen color and pen width, as well as clear the screen. In addition |
| the \uicontrol 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 |
| \li \c ScribbleArea is a custom widget that displays a QImage and |
| allows to the user to draw on it. |
| \li \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 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 |
| \li \c modified is \c true if there are unsaved |
| changes to the image displayed in the scribble area. |
| \li \c scribbling is \c true while the user is pressing |
| the left mouse button within the scribble area. |
| \li \c penWidth and \c penColor hold the currently |
| set width and color for the pen used in the application. |
| \li \c image stores the image drawn by the user. |
| \li \c lastPoint holds the position of the cursor at the last |
| mouse press or mouse move event. |
| \endlist |
| |
| \section1 ScribbleArea Class Implementation |
| |
| \snippet 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 widgets/scribble/scribblearea.cpp 1 |
| \snippet 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 widgets/scribble/scribblearea.cpp 3 |
| \snippet 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 widgets/scribble/scribblearea.cpp 5 |
| \snippet widgets/scribble/scribblearea.cpp 6 |
| \codeline |
| \snippet widgets/scribble/scribblearea.cpp 7 |
| \snippet 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 widgets/scribble/scribblearea.cpp 9 |
| \snippet 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 widgets/scribble/scribblearea.cpp 11 |
| \snippet 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 mouse 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 widgets/scribble/scribblearea.cpp 13 |
| \snippet 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 |
| \li 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. |
| |
| \li 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.) |
| |
| \li 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 widgets/scribble/scribblearea.cpp 15 |
| \snippet 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 widgets/scribble/scribblearea.cpp 17 |
| \snippet 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 widgets/scribble/scribblearea.cpp 19 |
| \snippet 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 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 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 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 \uicontrol 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 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 widgets/scribble/mainwindow.cpp 1 |
| \snippet widgets/scribble/mainwindow.cpp 2 |
| |
| Close events are sent to widgets that the users want to close, |
| usually by clicking \uicontrol{File|Exit} or by clicking the \uicontrol 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 \uicontrol Cancel, so we |
| "ignore" the event, leaving the application unaffected by it. |
| |
| \snippet widgets/scribble/mainwindow.cpp 3 |
| \snippet 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 widgets/scribble/mainwindow.cpp 5 |
| \snippet widgets/scribble/mainwindow.cpp 6 |
| |
| The \c save() slot is called when the users choose the \uicontrol {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 widgets/scribble/mainwindow.cpp 7 |
| \snippet 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 widgets/scribble/mainwindow.cpp 9 |
| \snippet 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 \uicontrol OK and to \c false if the user pressed \uicontrol Cancel. |
| |
| \snippet widgets/scribble/mainwindow.cpp 11 |
| \snippet 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 widgets/scribble/mainwindow.cpp 13 |
| \snippet widgets/scribble/mainwindow.cpp 14 |
| |
| In the \c createAction() function we create the actions |
| representing the menu entries and connect them to the appropriate |
| slots. In particular we create the actions found in the \uicontrol |
| {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 widgets/scribble/mainwindow.cpp 15 |
| \snippet 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 \uicontrol File, |
| \uicontrol Options and \uicontrol 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 \uicontrol File and \uicontrol Options menus in the \c |
| {MainWindow}'s menu bar, which we retrieve using the |
| QMainWindow::menuBar() function. |
| |
| \snippet widgets/scribble/mainwindow.cpp 17 |
| \snippet 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 \uicontrol Cancel and save the file using another |
| format. |
| |
| The \c maybeSave() function returns \c false if the user clicks |
| \uicontrol Cancel; otherwise it returns \c true. |
| |
| \snippet widgets/scribble/mainwindow.cpp 19 |
| \snippet 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. |
| */ |