Image Viewer Example▲
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.
With the Image Viewer application, the users can view an image of their choice. The File menu gives the user the possibility to:
-
Open... - Open an image file
-
Print... - Print an image
-
Exit - Exit the application
Once an image is loaded, the View menu allows the users to:
-
Zoom In - Scale the image up by 25%
-
Zoom Out - Scale the image down by 25%
-
Normal Size - Show the image at its original size
-
Fit to Window - Stretch the image to occupy the entire window
In addition the Help menu provides the users with information about the Image Viewer example in particular, and about Qt in general.
ImageViewer Class Definition▲
class
ImageViewer : public
QMainWindow
{
Q_OBJECT
public
:
ImageViewer(QWidget *
parent =
nullptr
);
bool
loadFile(const
QString &
amp;);
private
slots:
void
open();
void
saveAs();
void
print();
void
copy();
void
paste();
void
zoomIn();
void
zoomOut();
void
normalSize();
void
fitToWindow();
void
about();
private
:
void
createActions();
void
createMenus();
void
updateActions();
bool
saveFile(const
QString &
amp;fileName);
void
setImage(const
QImage &
amp;newImage);
void
scaleImage(double
factor);
void
adjustScrollBar(QScrollBar *
scrollBar, double
factor);
QImage image;
QLabel *
imageLabel;
QScrollArea *
scrollArea;
double
scaleFactor;
#ifndef QT_NO_PRINTER
QPrinter printer;
#endif
QAction *
saveAsAct;
QAction *
printAct;
QAction *
copyAct;
QAction *
zoomInAct;
QAction *
zoomOutAct;
QAction *
normalSizeAct;
QAction *
fitToWindowAct;
}
;
The 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 createActions() and createMenus() when constructing the ImageViewer widget. We use the updateActions() function to update the menu entries when a new image is loaded, or when the Fit to Window option is toggled. The zoom slots use scaleImage() to perform the zooming. In turn, scaleImage() uses adjustScrollBar() to preserve the focal point after scaling an image.
ImageViewer Class Implementation▲
ImageViewer::
ImageViewer(QWidget *
parent)
:
QMainWindow(parent), imageLabel(new
QLabel),
scrollArea(new
QScrollArea), scaleFactor(1
)
{
imageLabel-&
gt;setBackgroundRole(QPalette::
Base);
imageLabel-&
gt;setSizePolicy(QSizePolicy::
Ignored, QSizePolicy::
Ignored);
imageLabel-&
gt;setScaledContents(true
);
scrollArea-&
gt;setBackgroundRole(QPalette::
Dark);
scrollArea-&
gt;setWidget(imageLabel);
scrollArea-&
gt;setVisible(false
);
setCentralWidget(scrollArea);
createActions();
resize(QGuiApplication::
primaryScreen()-&
gt;availableSize() *
3
/
5
);
}
In the constructor we first create the label and the scroll area.
We set imageLabel's size policy to ignored, making the users able to scale the image to whatever size they want when the Fit to Window option is turned on. Otherwise, the default size polizy (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 imageLabel's scaledContents property, zooming in would enlarge the QLabel, but leave the pixmap at its original size, exposing the QLabel's background.
We make imageLabel the scroll area's child widget, and we make scrollArea the central widget of the QMainWindow. At the end we create the associated actions and menus, and customize the ImageViewer's appearance.
static
void
initializeImageFileDialog(QFileDialog &
amp;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();
for
(const
QByteArray &
amp;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 &
amp;&
amp; !
loadFile(dialog.selectedFiles().first())) {}
}
In the 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 loadFile() is used to load the image.
bool
ImageViewer::
loadFile(const
QString &
amp;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
;
}
In the 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 OK button (the default) is sufficient, since the message is part of a normal operation.
scaleFactor =
1.0
;
scrollArea-&
gt;setVisible(true
);
printAct-&
gt;setEnabled(true
);
fitToWindowAct-&
gt;setEnabled(true
);
updateActions();
if
(!
fitToWindowAct-&
gt;isChecked())
imageLabel-&
gt;adjustSize();
}
If the format is supported, w