Qt 5.11 | Qt Widgets | Image Viewer Example | imageviewer.cpp Example File

Qt 5.11.3 Reference Documentation

# imageviewer.cpp Example File

widgets/imageviewer/imageviewer.cpp

      /****************************************************************************
      **
      ** Copyright (C) 2016 The Qt Company Ltd.
      ** Contact: https://www.qt.io/licensing/
      **
      ** This file is part of the examples of the Qt Toolkit.
      **
      ** $QT_BEGIN_LICENSE:BSD$
      ** 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.
      **
      ** BSD License Usage
      ** Alternatively, you may use this file under the terms of the BSD license
      ** as follows:
      **
      ** "Redistribution and use in source and binary forms, with or without
      ** modification, are permitted provided that the following conditions are
      ** met:
      **   * Redistributions of source code must retain the above copyright
      **     notice, this list of conditions and the following disclaimer.
      **   * Redistributions in binary form must reproduce the above copyright
      **     notice, this list of conditions and the following disclaimer in
      **     the documentation and/or other materials provided with the
      **     distribution.
      **   * Neither the name of The Qt Company Ltd nor the names of its
      **     contributors may be used to endorse or promote products derived
      **     from this software without specific prior written permission.
      **
      **
      ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
      ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
      ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
      ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
      ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
      ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
      ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
      ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
      ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
      ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
      ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
      **
      ** $QT_END_LICENSE$
      **
      ****************************************************************************/

      #include <QtWidgets>
      #if defined(QT_PRINTSUPPORT_LIB)
      #include <QtPrintSupport/qtprintsupportglobal.h>
      #if QT_CONFIG(printdialog)
      #include <QPrintDialog>
      #endif
      #endif

      #include "imageviewer.h"

      ImageViewer::ImageViewer()
         : imageLabel(new QLabel)
         , scrollArea(new QScrollArea)
         , scaleFactor(1)
      {
          imageLabel->setBackgroundRole(QPalette::Base);
          imageLabel->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);
          imageLabel->setScaledContents(true);

          scrollArea->setBackgroundRole(QPalette::Dark);
          scrollArea->setWidget(imageLabel);
          scrollArea->setVisible(false);
          setCentralWidget(scrollArea);

          createActions();

          resize(QGuiApplication::primaryScreen()->availableSize() * 3 / 5);
      }

      bool ImageViewer::loadFile(const QString &fileName)
      {
          QImageReader reader(fileName);
          reader.setAutoTransform(true);
          const QImage newImage = reader.read();
          if (newImage.isNull()) {
              QMessageBox::information(this, QGuiApplication::applicationDisplayName(),
                                       tr("Cannot load %1: %2")
                                       .arg(QDir::toNativeSeparators(fileName), reader.errorString()));
              return false;
          }

          setImage(newImage);

          setWindowFilePath(fileName);

          const QString message = tr("Opened \"%1\", %2x%3, Depth: %4")
              .arg(QDir::toNativeSeparators(fileName)).arg(image.width()).arg(image.height()).arg(image.depth());
          statusBar()->showMessage(message);
          return true;
      }

      void ImageViewer::setImage(const QImage &newImage)
      {
          image = newImage;
          imageLabel->setPixmap(QPixmap::fromImage(image));
          scaleFactor = 1.0;

          scrollArea->setVisible(true);
          printAct->setEnabled(true);
          fitToWindowAct->setEnabled(true);
          updateActions();

          if (!fitToWindowAct->isChecked())
              imageLabel->adjustSize();
      }

      bool ImageViewer::saveFile(const QString &fileName)
      {
          QImageWriter writer(fileName);

          if (!writer.write(image)) {
              QMessageBox::information(this, QGuiApplication::applicationDisplayName(),
                                       tr("Cannot write %1: %2")
                                       .arg(QDir::toNativeSeparators(fileName)), writer.errorString());
              return false;
          }
          const QString message = tr("Wrote \"%1\"").arg(QDir::toNativeSeparators(fileName));
          statusBar()->showMessage(message);
          return true;
      }

      static void initializeImageFileDialog(QFileDialog &dialog, QFileDialog::AcceptMode acceptMode)
      {
          static bool firstDialog = true;

          if (firstDialog) {
              firstDialog = false;
              const QStringList picturesLocations = QStandardPaths::standardLocations(QStandardPaths::PicturesLocation);
              dialog.setDirectory(picturesLocations.isEmpty() ? QDir::currentPath() : picturesLocations.last());
          }

          QStringList mimeTypeFilters;
          const QByteArrayList supportedMimeTypes = acceptMode == QFileDialog::AcceptOpen
              ? QImageReader::supportedMimeTypes() : QImageWriter::supportedMimeTypes();
          foreach (const QByteArray &mimeTypeName, supportedMimeTypes)
              mimeTypeFilters.append(mimeTypeName);
          mimeTypeFilters.sort();
          dialog.setMimeTypeFilters(mimeTypeFilters);
          dialog.selectMimeTypeFilter("image/jpeg");
          if (acceptMode == QFileDialog::AcceptSave)
              dialog.setDefaultSuffix("jpg");
      }

      void ImageViewer::open()
      {
          QFileDialog dialog(this, tr("Open File"));
          initializeImageFileDialog(dialog, QFileDialog::AcceptOpen);

          while (dialog.exec() == QDialog::Accepted && !loadFile(dialog.selectedFiles().first())) {}
      }

      void ImageViewer::saveAs()
      {
          QFileDialog dialog(this, tr("Save File As"));
          initializeImageFileDialog(dialog, QFileDialog::AcceptSave);

          while (dialog.exec() == QDialog::Accepted && !saveFile(dialog.selectedFiles().first())) {}
      }

      void ImageViewer::print()
      {
          Q_ASSERT(imageLabel->pixmap());
      #if QT_CONFIG(printdialog)
          QPrintDialog dialog(&printer, this);
          if (dialog.exec()) {
              QPainter painter(&printer);
              QRect rect = painter.viewport();
              QSize size = imageLabel->pixmap()->size();
              size.scale(rect.size(), Qt::KeepAspectRatio);
              painter.setViewport(rect.x(), rect.y(), size.width(), size.height());
              painter.setWindow(imageLabel->pixmap()->rect());
              painter.drawPixmap(0, 0, *imageLabel->pixmap());
          }
      #endif
      }

      void ImageViewer::copy()
      {
      #ifndef QT_NO_CLIPBOARD
          QGuiApplication::clipboard()->setImage(image);
      #endif // !QT_NO_CLIPBOARD
      }

      #ifndef QT_NO_CLIPBOARD
      static QImage clipboardImage()
      {
          if (const QMimeData *mimeData = QGuiApplication::clipboard()->mimeData()) {
              if (mimeData->hasImage()) {
                  const QImage image = qvariant_cast<QImage>(mimeData->imageData());
                  if (!image.isNull())
                      return image;
              }
          }
          return QImage();
      }
      #endif // !QT_NO_CLIPBOARD

      void ImageViewer::paste()
      {
      #ifndef QT_NO_CLIPBOARD
          const QImage newImage = clipboardImage();
          if (newImage.isNull()) {
              statusBar()->showMessage(tr("No image in clipboard"));
          } else {
              setImage(newImage);
              setWindowFilePath(QString());
              const QString message = tr("Obtained image from clipboard, %1x%2, Depth: %3")
                  .arg(newImage.width()).arg(newImage.height()).arg(newImage.depth());
              statusBar()->showMessage(message);
          }
      #endif // !QT_NO_CLIPBOARD
      }

      void ImageViewer::zoomIn()
      {
          scaleImage(1.25);
      }

      void ImageViewer::zoomOut()
      {
          scaleImage(0.8);
      }

      void ImageViewer::normalSize()
      {
          imageLabel->adjustSize();
          scaleFactor = 1.0;
      }

      void ImageViewer::fitToWindow()
      {
          bool fitToWindow = fitToWindowAct->isChecked();
          scrollArea->setWidgetResizable(fitToWindow);
          if (!fitToWindow)
              normalSize();
          updateActions();
      }

      void ImageViewer::about()
      {
          QMessageBox::about(this, tr("About Image Viewer"),
                  tr("<p>The <b>Image Viewer</b> example shows how to combine QLabel "
                     "and QScrollArea to display an image. QLabel is typically used "
                     "for displaying a 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. </p><p>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. </p><p>In addition the example "
                     "shows how to use QPainter to print an image.</p>"));
      }

      void ImageViewer::createActions()
      {
          QMenu *fileMenu = menuBar()->addMenu(tr("&File"));

          QAction *openAct = fileMenu->addAction(tr("&Open..."), this, &ImageViewer::open);
          openAct->setShortcut(QKeySequence::Open);

          saveAsAct = fileMenu->addAction(tr("&Save As..."), this, &ImageViewer::saveAs);
          saveAsAct->setEnabled(false);

          printAct = fileMenu->addAction(tr("&Print..."), this, &ImageViewer::print);
          printAct->setShortcut(QKeySequence::Print);
          printAct->setEnabled(false);

          fileMenu->addSeparator();

          QAction *exitAct = fileMenu->addAction(tr("E&xit"), this, &QWidget::close);
          exitAct->setShortcut(tr("Ctrl+Q"));

          QMenu *editMenu = menuBar()->addMenu(tr("&Edit"));

          copyAct = editMenu->addAction(tr("&Copy"), this, &ImageViewer::copy);
          copyAct->setShortcut(QKeySequence::Copy);
          copyAct->setEnabled(false);

          QAction *pasteAct = editMenu->addAction(tr("&Paste"), this, &ImageViewer::paste);
          pasteAct->setShortcut(QKeySequence::Paste);

          QMenu *viewMenu = menuBar()->addMenu(tr("&View"));

          zoomInAct = viewMenu->addAction(tr("Zoom &In (25%)"), this, &ImageViewer::zoomIn);
          zoomInAct->setShortcut(QKeySequence::ZoomIn);
          zoomInAct->setEnabled(false);

          zoomOutAct = viewMenu->addAction(tr("Zoom &Out (25%)"), this, &ImageViewer::zoomOut);
          zoomOutAct->setShortcut(QKeySequence::ZoomOut);
          zoomOutAct->setEnabled(false);

          normalSizeAct = viewMenu->addAction(tr("&Normal Size"), this, &ImageViewer::normalSize);
          normalSizeAct->setShortcut(tr("Ctrl+S"));
          normalSizeAct->setEnabled(false);

          viewMenu->addSeparator();

          fitToWindowAct = viewMenu->addAction(tr("&Fit to Window"), this, &ImageViewer::fitToWindow);
          fitToWindowAct->setEnabled(false);
          fitToWindowAct->setCheckable(true);
          fitToWindowAct->setShortcut(tr("Ctrl+F"));

          QMenu *helpMenu = menuBar()->addMenu(tr("&Help"));

          helpMenu->addAction(tr("&About"), this, &ImageViewer::about);
          helpMenu->addAction(tr("About &Qt"), &QApplication::aboutQt);
      }

      void ImageViewer::updateActions()
      {
          saveAsAct->setEnabled(!image.isNull());
          copyAct->setEnabled(!image.isNull());
          zoomInAct->setEnabled(!fitToWindowAct->isChecked());
          zoomOutAct->setEnabled(!fitToWindowAct->isChecked());
          normalSizeAct->setEnabled(!fitToWindowAct->isChecked());
      }

      void ImageViewer::scaleImage(double factor)
      {
          Q_ASSERT(imageLabel->pixmap());
          scaleFactor *= factor;
          imageLabel->resize(scaleFactor * imageLabel->pixmap()->size());

          adjustScrollBar(scrollArea->horizontalScrollBar(), factor);
          adjustScrollBar(scrollArea->verticalScrollBar(), factor);

          zoomInAct->setEnabled(scaleFactor < 3.0);
          zoomOutAct->setEnabled(scaleFactor > 0.333);
      }

      void ImageViewer::adjustScrollBar(QScrollBar *scrollBar, double factor)
      {
          scrollBar->setValue(int(factor * scrollBar->value()
                                  + ((factor - 1) * scrollBar->pageStep()/2)));
      }