1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the examples of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:BSD$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** BSD License Usage
18** Alternatively, you may use this file under the terms of the BSD license
19** as follows:
20**
21** "Redistribution and use in source and binary forms, with or without
22** modification, are permitted provided that the following conditions are
23** met:
24** * Redistributions of source code must retain the above copyright
25** notice, this list of conditions and the following disclaimer.
26** * Redistributions in binary form must reproduce the above copyright
27** notice, this list of conditions and the following disclaimer in
28** the documentation and/or other materials provided with the
29** distribution.
30** * Neither the name of The Qt Company Ltd nor the names of its
31** contributors may be used to endorse or promote products derived
32** from this software without specific prior written permission.
33**
34**
35** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
36** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
37** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
38** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
39** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
40** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
41** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
42** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
43** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
44** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
45** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
46**
47** $QT_END_LICENSE$
48**
49****************************************************************************/
50
51#include "imageviewer.h"
52
53#include <QApplication>
54#include <QClipboard>
55#include <QColorSpace>
56#include <QDir>
57#include <QFileDialog>
58#include <QImageReader>
59#include <QImageWriter>
60#include <QLabel>
61#include <QMenuBar>
62#include <QMessageBox>
63#include <QMimeData>
64#include <QPainter>
65#include <QScreen>
66#include <QScrollArea>
67#include <QScrollBar>
68#include <QStandardPaths>
69#include <QStatusBar>
70
71#if defined(QT_PRINTSUPPORT_LIB)
72# include <QtPrintSupport/qtprintsupportglobal.h>
73
74# if QT_CONFIG(printdialog)
75# include <QPrintDialog>
76# endif
77#endif
78
79//! [0]
80ImageViewer::ImageViewer(QWidget *parent)
81 : QMainWindow(parent), imageLabel(new QLabel)
82 , scrollArea(new QScrollArea)
83{
84 imageLabel->setBackgroundRole(QPalette::Base);
85 imageLabel->setSizePolicy(hor: QSizePolicy::Ignored, ver: QSizePolicy::Ignored);
86 imageLabel->setScaledContents(true);
87
88 scrollArea->setBackgroundRole(QPalette::Dark);
89 scrollArea->setWidget(imageLabel);
90 scrollArea->setVisible(false);
91 setCentralWidget(scrollArea);
92
93 createActions();
94
95 resize(QGuiApplication::primaryScreen()->availableSize() * 3 / 5);
96}
97
98//! [0]
99//! [2]
100
101bool ImageViewer::loadFile(const QString &fileName)
102{
103 QImageReader reader(fileName);
104 reader.setAutoTransform(true);
105 const QImage newImage = reader.read();
106 if (newImage.isNull()) {
107 QMessageBox::information(parent: this, title: QGuiApplication::applicationDisplayName(),
108 text: tr(s: "Cannot load %1: %2")
109 .arg(args: QDir::toNativeSeparators(pathName: fileName), args: reader.errorString()));
110 return false;
111 }
112//! [2]
113
114 setImage(newImage);
115
116 setWindowFilePath(fileName);
117
118 const QString message = tr(s: "Opened \"%1\", %2x%3, Depth: %4")
119 .arg(a: QDir::toNativeSeparators(pathName: fileName)).arg(a: image.width()).arg(a: image.height()).arg(a: image.depth());
120 statusBar()->showMessage(text: message);
121 return true;
122}
123
124void ImageViewer::setImage(const QImage &newImage)
125{
126 image = newImage;
127 if (image.colorSpace().isValid())
128 image.convertToColorSpace(QColorSpace::SRgb);
129 imageLabel->setPixmap(QPixmap::fromImage(image));
130//! [4]
131 scaleFactor = 1.0;
132
133 scrollArea->setVisible(true);
134 printAct->setEnabled(true);
135 fitToWindowAct->setEnabled(true);
136 updateActions();
137
138 if (!fitToWindowAct->isChecked())
139 imageLabel->adjustSize();
140}
141
142//! [4]
143
144bool ImageViewer::saveFile(const QString &fileName)
145{
146 QImageWriter writer(fileName);
147
148 if (!writer.write(image)) {
149 QMessageBox::information(parent: this, title: QGuiApplication::applicationDisplayName(),
150 text: tr(s: "Cannot write %1: %2")
151 .arg(a: QDir::toNativeSeparators(pathName: fileName)), button0Text: writer.errorString());
152 return false;
153 }
154 const QString message = tr(s: "Wrote \"%1\"").arg(a: QDir::toNativeSeparators(pathName: fileName));
155 statusBar()->showMessage(text: message);
156 return true;
157}
158
159//! [1]
160
161static void initializeImageFileDialog(QFileDialog &dialog, QFileDialog::AcceptMode acceptMode)
162{
163 static bool firstDialog = true;
164
165 if (firstDialog) {
166 firstDialog = false;
167 const QStringList picturesLocations = QStandardPaths::standardLocations(type: QStandardPaths::PicturesLocation);
168 dialog.setDirectory(picturesLocations.isEmpty() ? QDir::currentPath() : picturesLocations.last());
169 }
170
171 QStringList mimeTypeFilters;
172 const QByteArrayList supportedMimeTypes = acceptMode == QFileDialog::AcceptOpen
173 ? QImageReader::supportedMimeTypes() : QImageWriter::supportedMimeTypes();
174 for (const QByteArray &mimeTypeName : supportedMimeTypes)
175 mimeTypeFilters.append(t: mimeTypeName);
176 mimeTypeFilters.sort();
177 dialog.setMimeTypeFilters(mimeTypeFilters);
178 dialog.selectMimeTypeFilter(filter: "image/jpeg");
179 dialog.setAcceptMode(acceptMode);
180 if (acceptMode == QFileDialog::AcceptSave)
181 dialog.setDefaultSuffix("jpg");
182}
183
184void ImageViewer::open()
185{
186 QFileDialog dialog(this, tr(s: "Open File"));
187 initializeImageFileDialog(dialog, acceptMode: QFileDialog::AcceptOpen);
188
189 while (dialog.exec() == QDialog::Accepted && !loadFile(fileName: dialog.selectedFiles().constFirst())) {}
190}
191//! [1]
192
193void ImageViewer::saveAs()
194{
195 QFileDialog dialog(this, tr(s: "Save File As"));
196 initializeImageFileDialog(dialog, acceptMode: QFileDialog::AcceptSave);
197
198 while (dialog.exec() == QDialog::Accepted && !saveFile(fileName: dialog.selectedFiles().constFirst())) {}
199}
200
201//! [5]
202void ImageViewer::print()
203//! [5] //! [6]
204{
205 Q_ASSERT(!imageLabel->pixmap(Qt::ReturnByValue).isNull());
206#if defined(QT_PRINTSUPPORT_LIB) && QT_CONFIG(printdialog)
207//! [6] //! [7]
208 QPrintDialog dialog(&printer, this);
209//! [7] //! [8]
210 if (dialog.exec()) {
211 QPainter painter(&printer);
212 QPixmap pixmap = imageLabel->pixmap(Qt::ReturnByValue);
213 QRect rect = painter.viewport();
214 QSize size = pixmap.size();
215 size.scale(s: rect.size(), mode: Qt::KeepAspectRatio);
216 painter.setViewport(x: rect.x(), y: rect.y(), w: size.width(), h: size.height());
217 painter.setWindow(pixmap.rect());
218 painter.drawPixmap(x: 0, y: 0, pm: pixmap);
219 }
220#endif
221}
222//! [8]
223
224void ImageViewer::copy()
225{
226#ifndef QT_NO_CLIPBOARD
227 QGuiApplication::clipboard()->setImage(image);
228#endif // !QT_NO_CLIPBOARD
229}
230
231#ifndef QT_NO_CLIPBOARD
232static QImage clipboardImage()
233{
234 if (const QMimeData *mimeData = QGuiApplication::clipboard()->mimeData()) {
235 if (mimeData->hasImage()) {
236 const QImage image = qvariant_cast<QImage>(v: mimeData->imageData());
237 if (!image.isNull())
238 return image;
239 }
240 }
241 return QImage();
242}
243#endif // !QT_NO_CLIPBOARD
244
245void ImageViewer::paste()
246{
247#ifndef QT_NO_CLIPBOARD
248 const QImage newImage = clipboardImage();
249 if (newImage.isNull()) {
250 statusBar()->showMessage(text: tr(s: "No image in clipboard"));
251 } else {
252 setImage(newImage);
253 setWindowFilePath(QString());
254 const QString message = tr(s: "Obtained image from clipboard, %1x%2, Depth: %3")
255 .arg(a: newImage.width()).arg(a: newImage.height()).arg(a: newImage.depth());
256 statusBar()->showMessage(text: message);
257 }
258#endif // !QT_NO_CLIPBOARD
259}
260
261//! [9]
262void ImageViewer::zoomIn()
263//! [9] //! [10]
264{
265 scaleImage(factor: 1.25);
266}
267
268void ImageViewer::zoomOut()
269{
270 scaleImage(factor: 0.8);
271}
272
273//! [10] //! [11]
274void ImageViewer::normalSize()
275//! [11] //! [12]
276{
277 imageLabel->adjustSize();
278 scaleFactor = 1.0;
279}
280//! [12]
281
282//! [13]
283void ImageViewer::fitToWindow()
284//! [13] //! [14]
285{
286 bool fitToWindow = fitToWindowAct->isChecked();
287 scrollArea->setWidgetResizable(fitToWindow);
288 if (!fitToWindow)
289 normalSize();
290 updateActions();
291}
292//! [14]
293
294
295//! [15]
296void ImageViewer::about()
297//! [15] //! [16]
298{
299 QMessageBox::about(parent: this, title: tr(s: "About Image Viewer"),
300 text: tr(s: "<p>The <b>Image Viewer</b> example shows how to combine QLabel "
301 "and QScrollArea to display an image. QLabel is typically used "
302 "for displaying a text, but it can also display an image. "
303 "QScrollArea provides a scrolling view around another widget. "
304 "If the child widget exceeds the size of the frame, QScrollArea "
305 "automatically provides scroll bars. </p><p>The example "
306 "demonstrates how QLabel's ability to scale its contents "
307 "(QLabel::scaledContents), and QScrollArea's ability to "
308 "automatically resize its contents "
309 "(QScrollArea::widgetResizable), can be used to implement "
310 "zooming and scaling features. </p><p>In addition the example "
311 "shows how to use QPainter to print an image.</p>"));
312}
313//! [16]
314
315//! [17]
316void ImageViewer::createActions()
317//! [17] //! [18]
318{
319 QMenu *fileMenu = menuBar()->addMenu(title: tr(s: "&File"));
320
321 QAction *openAct = fileMenu->addAction(text: tr(s: "&Open..."), object: this, slot: &ImageViewer::open);
322 openAct->setShortcut(QKeySequence::Open);
323
324 saveAsAct = fileMenu->addAction(text: tr(s: "&Save As..."), object: this, slot: &ImageViewer::saveAs);
325 saveAsAct->setEnabled(false);
326
327 printAct = fileMenu->addAction(text: tr(s: "&Print..."), object: this, slot: &ImageViewer::print);
328 printAct->setShortcut(QKeySequence::Print);
329 printAct->setEnabled(false);
330
331 fileMenu->addSeparator();
332
333 QAction *exitAct = fileMenu->addAction(text: tr(s: "E&xit"), object: this, slot: &QWidget::close);
334 exitAct->setShortcut(tr(s: "Ctrl+Q"));
335
336 QMenu *editMenu = menuBar()->addMenu(title: tr(s: "&Edit"));
337
338 copyAct = editMenu->addAction(text: tr(s: "&Copy"), object: this, slot: &ImageViewer::copy);
339 copyAct->setShortcut(QKeySequence::Copy);
340 copyAct->setEnabled(false);
341
342 QAction *pasteAct = editMenu->addAction(text: tr(s: "&Paste"), object: this, slot: &ImageViewer::paste);
343 pasteAct->setShortcut(QKeySequence::Paste);
344
345 QMenu *viewMenu = menuBar()->addMenu(title: tr(s: "&View"));
346
347 zoomInAct = viewMenu->addAction(text: tr(s: "Zoom &In (25%)"), object: this, slot: &ImageViewer::zoomIn);
348 zoomInAct->setShortcut(QKeySequence::ZoomIn);
349 zoomInAct->setEnabled(false);
350
351 zoomOutAct = viewMenu->addAction(text: tr(s: "Zoom &Out (25%)"), object: this, slot: &ImageViewer::zoomOut);
352 zoomOutAct->setShortcut(QKeySequence::ZoomOut);
353 zoomOutAct->setEnabled(false);
354
355 normalSizeAct = viewMenu->addAction(text: tr(s: "&Normal Size"), object: this, slot: &ImageViewer::normalSize);
356 normalSizeAct->setShortcut(tr(s: "Ctrl+S"));
357 normalSizeAct->setEnabled(false);
358
359 viewMenu->addSeparator();
360
361 fitToWindowAct = viewMenu->addAction(text: tr(s: "&Fit to Window"), object: this, slot: &ImageViewer::fitToWindow);
362 fitToWindowAct->setEnabled(false);
363 fitToWindowAct->setCheckable(true);
364 fitToWindowAct->setShortcut(tr(s: "Ctrl+F"));
365
366 QMenu *helpMenu = menuBar()->addMenu(title: tr(s: "&Help"));
367
368 helpMenu->addAction(text: tr(s: "&About"), object: this, slot: &ImageViewer::about);
369 helpMenu->addAction(text: tr(s: "About &Qt"), object: this, slot: &QApplication::aboutQt);
370}
371//! [18]
372
373//! [21]
374void ImageViewer::updateActions()
375//! [21] //! [22]
376{
377 saveAsAct->setEnabled(!image.isNull());
378 copyAct->setEnabled(!image.isNull());
379 zoomInAct->setEnabled(!fitToWindowAct->isChecked());
380 zoomOutAct->setEnabled(!fitToWindowAct->isChecked());
381 normalSizeAct->setEnabled(!fitToWindowAct->isChecked());
382}
383//! [22]
384
385//! [23]
386void ImageViewer::scaleImage(double factor)
387//! [23] //! [24]
388{
389 scaleFactor *= factor;
390 imageLabel->resize(scaleFactor * imageLabel->pixmap(Qt::ReturnByValue).size());
391
392 adjustScrollBar(scrollBar: scrollArea->horizontalScrollBar(), factor);
393 adjustScrollBar(scrollBar: scrollArea->verticalScrollBar(), factor);
394
395 zoomInAct->setEnabled(scaleFactor < 3.0);
396 zoomOutAct->setEnabled(scaleFactor > 0.333);
397}
398//! [24]
399
400//! [25]
401void ImageViewer::adjustScrollBar(QScrollBar *scrollBar, double factor)
402//! [25] //! [26]
403{
404 scrollBar->setValue(int(factor * scrollBar->value()
405 + ((factor - 1) * scrollBar->pageStep()/2)));
406}
407//! [26]
408

source code of qtbase/examples/widgets/widgets/imageviewer/imageviewer.cpp