blob: 91b638535680b15591e16dae1e2901d70931ba56 [file] [log] [blame]
/****************************************************************************
**
** 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/imageviewer
\title Image Viewer Example
\ingroup examples-widgets
\brief The example shows how to combine QLabel and QScrollArea to
display an image.
QLabel is typically used for displaying text,
but it can also display an image. QScrollArea provides a
scrolling view around another widget. If the child widget exceeds
the size of the frame, QScrollArea automatically provides scroll
bars.
The example demonstrates how QLabel's ability to scale its
contents (QLabel::scaledContents), and QScrollArea's ability to
automatically resize its contents (QScrollArea::widgetResizable),
can be used to implement zooming and scaling features. In
addition the example shows how to use QPainter to print an image.
\borderedimage imageviewer-example.png
\caption Screenshot of the Image Viewer example
With the Image Viewer application, the users can view an image of
their choice. The \uicontrol File menu gives the user the possibility
to:
\list
\li \uicontrol{Open...} - Open an image file
\li \uicontrol{Print...} - Print an image
\li \uicontrol{Exit} - Exit the application
\endlist
Once an image is loaded, the \uicontrol View menu allows the users to:
\list
\li \uicontrol{Zoom In} - Scale the image up by 25%
\li \uicontrol{Zoom Out} - Scale the image down by 25%
\li \uicontrol{Normal Size} - Show the image at its original size
\li \uicontrol{Fit to Window} - Stretch the image to occupy the entire window
\endlist
In addition the \uicontrol Help menu provides the users with information
about the Image Viewer example in particular, and about Qt in
general.
\section1 ImageViewer Class Definition
\snippet widgets/imageviewer/imageviewer.h 0
The \c ImageViewer class inherits from QMainWindow. We reimplement
the constructor, and create several private slots to facilitate
the menu entries. In addition we create four private functions.
We use \c createActions() and \c createMenus() when constructing
the \c ImageViewer widget. We use the \c updateActions() function
to update the menu entries when a new image is loaded, or when
the \uicontrol {Fit to Window} option is toggled. The zoom slots use \c
scaleImage() to perform the zooming. In turn, \c
scaleImage() uses \c adjustScrollBar() to preserve the focal point after
scaling an image.
\section1 ImageViewer Class Implementation
\snippet widgets/imageviewer/imageviewer.cpp 0
In the constructor we first create the label and the scroll area.
We set \c {imageLabel}'s size policy to \l
{QSizePolicy::Ignored}{ignored}, making the users able to scale
the image to whatever size they want when the \uicontrol {Fit to Window}
option is turned on. Otherwise, the default size polizy (\l
{QSizePolicy::Preferred}{preferred}) will make scroll bars appear
when the scroll area becomes smaller than the label's minimum size
hint.
We ensure that the label will scale its contents to fill all
available space, to enable the image to scale properly when
zooming. If we omitted to set the \c {imageLabel}'s \l
{QLabel::scaledContents}{scaledContents} property, zooming in
would enlarge the QLabel, but leave the pixmap at
its original size, exposing the QLabel's background.
We make \c imageLabel the scroll area's child widget, and we make
\c scrollArea the central widget of the QMainWindow. At the end
we create the associated actions and menus, and customize the \c
{ImageViewer}'s appearance.
\snippet widgets/imageviewer/imageviewer.cpp 1
In the \c open() slot, we show a file dialog to the user. We compile
a list of mime types for use as a filter by querying QImageReader
for the available mime type names.
We show the file dialog until a valid file name is entered or
the user cancels.
The function \c loadFile() is used to load the image.
\snippet widgets/imageviewer/imageviewer.cpp 2
In the \c loadFile() function, we instantiate a QImageReader
and enable automatic transformations by calling
QImageReader::setAutoTransform(). For files in JPEG format,
this ensures that portrait mode images of digital cameras are shown
correctly by applying the appropriate orientation read from the
EXIF meta data stored in the image file.
We then load the image using QImageReader::read(). If this returns
a null image, indicating that the file is not an image file,
we use a QMessageBox to alert the user.
The QMessageBox class provides a modal dialog with a short
message, an icon, and some buttons. As with QFileDialog the
easiest way to create a QMessageBox is to use its static
convenience 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). In this particular example an
information message with an \uicontrol OK button (the default) is
sufficient, since the message is part of a normal operation.
\snippet widgets/imageviewer/imageviewer.cpp 4
If the format is supported, we display the image in \c imageLabel
by setting the label's \l {QLabel::pixmap}{pixmap}. Then we enable
the \uicontrol Print and \uicontrol {Fit to Window} menu entries and update
the rest of the view menu entries. The \uicontrol Open and \uicontrol Exit
entries are enabled by default.
If the \uicontrol {Fit to Window} option is turned off, the
QScrollArea::widgetResizable property is \c false and it is
our responsibility (not QScrollArea's) to give the QLabel a
reasonable size based on its contents. We call
\{QWidget::adjustSize()}{adjustSize()} to achieve this, which is
essentially the same as
\code
imageLabel->resize(imageLabel->pixmap()->size());
\endcode
In the \c print() slot, we first make sure that an image has been
loaded into the application:
\snippet widgets/imageviewer/imageviewer.cpp 5
\snippet widgets/imageviewer/imageviewer.cpp 6
If the application is built in debug mode, the \c Q_ASSERT() macro
will expand to
\code
if (!imageLabel->pixmap())
qFatal("ASSERT: "imageLabel->pixmap()" in file ...");
\endcode
In release mode, the macro simply disappear. The mode can be set
in the application's \c .pro file. One way to do so is to add an
option to \uicontrol qmake when building the application:
\code
qmake "CONFIG += debug" foo.pro
\endcode
or
\code
qmake "CONFIG += release" foo.pro
\endcode
Another approach is to add this line directly to the \c .pro
file.
\snippet widgets/imageviewer/imageviewer.cpp 7
\snippet widgets/imageviewer/imageviewer.cpp 8
Then we present a print dialog allowing the user to choose a
printer and to set a few options. We construct a painter with a
QPrinter as the paint device. We set the painter's window
and viewport in such a way that the image is as large as possible
on the paper, but without altering its
\l{Qt::KeepAspectRatio}{aspect ratio}.
In the end we draw the pixmap at position (0, 0).
\snippet widgets/imageviewer/imageviewer.cpp 9
\snippet widgets/imageviewer/imageviewer.cpp 10
We implement the zooming slots using the private \c scaleImage()
function. We set the scaling factors to 1.25 and 0.8,
respectively. These factor values ensure that a \uicontrol {Zoom In}
action and a \uicontrol {Zoom Out} action will cancel each other (since
1.25 * 0.8 == 1), and in that way the normal image size can be
restored using the zooming features.
The screenshots below show an image in its normal size, and the
same image after zooming in:
\table
\row
\li \inlineimage imageviewer-original_size.png
\li \inlineimage imageviewer-zoom_in_1.png
\li \inlineimage imageviewer-zoom_in_2.png
\endtable
\snippet widgets/imageviewer/imageviewer.cpp 11
\snippet widgets/imageviewer/imageviewer.cpp 12
When zooming, we use the QLabel's ability to scale its contents.
Such scaling doesn't change the actual size hint of the contents.
And since the \l {QLabel::adjustSize()}{adjustSize()} function
use those size hint, the only thing we need to do to restore the
normal size of the currently displayed image is to call \c
adjustSize() and reset the scale factor to 1.0.
\snippet widgets/imageviewer/imageviewer.cpp 13
\snippet widgets/imageviewer/imageviewer.cpp 14
The \c fitToWindow() slot is called each time the user toggled
the \uicontrol {Fit to Window} option. If the slot is called to turn on
the option, we tell the scroll area to resize its child widget
with the QScrollArea::setWidgetResizable() function. Then we
disable the \uicontrol {Zoom In}, \uicontrol {Zoom Out} and \uicontrol {Normal
Size} menu entries using the private \c updateActions() function.
If the \l {QScrollArea::widgetResizable} property is set to \c
false (the default), the scroll area honors the size of its child
widget. If this property is set to \c true, the scroll area will
automatically resize the widget in order to avoid scroll bars
where they can be avoided, or to take advantage of extra space.
But the scroll area will honor the minimum size hint of its child
widget independent of the widget resizable property. So in this
example we set \c {imageLabel}'s size policy to \l
{QSizePolicy::Ignored}{ignored} in the constructor, to avoid that
scroll bars appear when the scroll area becomes smaller than the
label's minimum size hint.
The screenshots below shows an image in its normal size, and the
same image with the \uicontrol {Fit to window} option turned on.
Enlarging the window will stretch the image further, as shown in
the third screenshot.
\table
\row
\li \inlineimage imageviewer-original_size.png
\li \inlineimage imageviewer-fit_to_window_1.png
\li \inlineimage imageviewer-fit_to_window_2.png
\endtable
If the slot is called to turn off the option, the
{QScrollArea::setWidgetResizable} property is set to \c false. We
also restore the image pixmap to its normal size by adjusting the
label's size to its content. And in the end we update the view
menu entries.
\snippet widgets/imageviewer/imageviewer.cpp 15
\snippet widgets/imageviewer/imageviewer.cpp 16
We implement the \c about() slot to create a message box
describing what the example is designed to show.
\snippet widgets/imageviewer/imageviewer.cpp 17
\snippet widgets/imageviewer/imageviewer.cpp 18
In the private \c createAction() function, we create the
actions providing the application features and populate
a menu with them.
We assign a short-cut key to each action and connect them to the
appropriate slots. We only enable the \c openAct and \c exitAct at
the time of creation, the others are updated once an image has
been loaded into the application. In addition we make the \c
fitToWindowAct \l {QAction::checkable}{checkable}.
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 that consists of a list of pull-down menu
items. So we put the menus in the \c {ImageViewer}'s
menu bar which we retrieve with the QMainWindow::menuBar()
function.
\snippet widgets/imageviewer/imageviewer.cpp 21
\snippet widgets/imageviewer/imageviewer.cpp 22
The private \c updateActions() function enables or disables the
\uicontrol {Zoom In}, \uicontrol {Zoom Out} and \uicontrol {Normal Size} menu
entries depending on whether the \uicontrol {Fit to Window} option is
turned on or off.
\snippet widgets/imageviewer/imageviewer.cpp 23
\snippet widgets/imageviewer/imageviewer.cpp 24
In \c scaleImage(), we use the \c factor parameter to calculate
the new scaling factor for the displayed image, and resize \c
imageLabel. Since we set the
\l{QLabel::scaledContents}{scaledContents} property to \c true in
the constructor, the call to QWidget::resize() will scale the
image displayed in the label. We also adjust the scroll bars to
preserve the focal point of the image.
At the end, if the scale factor is less than 33.3% or greater
than 300%, we disable the respective menu entry to prevent the
image pixmap from becoming too large, consuming too much
resources in the window system.
\snippet widgets/imageviewer/imageviewer.cpp 25
\snippet widgets/imageviewer/imageviewer.cpp 26
Whenever we zoom in or out, we need to adjust the scroll bars in
consequence. It would have been tempting to simply call
\code
scrollBar->setValue(int(factor * scrollBar->value()));
\endcode
but this would make the top-left corner the focal point, not the
center. Therefore we need to take into account the scroll bar
handle's size (the \l{QScrollBar::pageStep}{page step}).
*/