summaryrefslogtreecommitdiffstats
path: root/examples/widgets/doc/imageviewer.qdoc
blob: 3556d52f7e3ac7b06d14679012fb7c5d3856aba0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
/****************************************************************************
**
** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/
**
** This file is part of the documentation of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:FDL$
** GNU Free Documentation License
** 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.
**
** Other Usage
** Alternatively, this file may be used in accordance with the terms
** and conditions contained in a signed written agreement between you
** and Nokia.
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/

/*!
    \example widgets/imageviewer
    \title Image Viewer Example

    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.

    \image imageviewer-example.png 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
    \snippet widgets/imageviewer/imageviewer.cpp 2

    In the \c open() slot, we show a file dialog to the user. The
    easiest way to create a QFileDialog is to use the static
    convenience functions. QFileDialog::getOpenFileName() returns an
    existing file selected by the user. If the user presses \uicontrol
    Cancel, QFileDialog returns an empty string.

    Unless the file name is a empty string, we check if the file's
    format is an image format by constructing a QImage which tries to
    load the image from the file. If the constructor returns a null
    image, 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 3
    \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.

    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}.

    \snippet widgets/imageviewer/imageviewer.cpp 19
    \snippet widgets/imageviewer/imageviewer.cpp 20

    In the private \c createMenu() function, we add the previously
    created actions to the \uicontrol File, \uicontrol View 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 that consists of a list of pull-down menu
    items. So at the end 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}).
*/