Polish the Icons example.

- Remove unneeded member variables.
- Use new connection syntax in createActions()
  to assemble the menu there, removing the createMenus()
  function.
- Use a QButtonGroup for the sizes with the style metrics as id.
- Streamline code, rename variables for clarity.
- Introduce static functions returning the list of states/modes and
  their names in display order to IconPreviewArea, removing the
  duplicates.
- Split actions to open sample icons provided from source directory
  or standard pictures location to make sample icons more prominently
  visible.
- Check and display @2x images and add tooltips showing device pixel
  ratio and actual size.
- HighDPI: Add groupbox displaying screen name and device pixel
  ratio, connect to screen changed signal and update icons panel
  accordingly. Add check box for toggling Qt::AA_UseHighDpiPixmaps.
- Adjust documentation

Task-number: QTBUG-18680
Task-number: QTBUG-46615
Change-Id: Ice96e9898f168ef2a30e9f46cb260ed57ae015f0
Reviewed-by: Topi Reiniö <topi.reinio@digia.com>
This commit is contained in:
Friedemann Kleint 2015-09-01 09:56:28 +02:00
parent f3fd7b3d96
commit 8132cb655a
11 changed files with 528 additions and 308 deletions

View File

@ -254,6 +254,21 @@
generated pixmaps corresponding to an icon's possible states and
modes at a given size.
\snippet widgets/icons/iconpreviewarea.cpp 42
We would like the table columns to be in the order QIcon::Normal,
QIcon::Active, QIcon::Disabled, QIcon::Selected and the rows in the order
QIcon::Off, QIcon::On, which does not match the enumeration. The above code
provides arrays allowing to map from enumeration value to row/column
(by using QVector::indexOf()) and back by using the array index and lists
of the matching strings. Qt's containers can be easily populated by
using C++ 11 initializer lists. If the compiler does not provide that feature,
a pattern like
\snippet widgets/icons/iconpreviewarea.cpp 43
can be used.
We need two public functions to set the current icon and the
icon's size. In addition the class has three private functions: We
use the \c createHeaderLabel() and \c createPixmapLabel()
@ -326,7 +341,12 @@
For each mode, and for each state, we retrieve a pixmap using the
QIcon::pixmap() function, which generates a pixmap corresponding
to the given state, mode and size.
to the given state, mode and size. We pass the QWindows instance
obtained by calling QWidget::windowHandle() on the top level
widget (QWidget::nativeParentWidget()) in order to retrieve
the pixmap that matches best.
We format a tooltip displaying size, actual size and device pixel
ratio.
\section2 MainWindow Class Definition
@ -347,8 +367,14 @@
\li The \c changeSize() slot changes the size of the preview area's icon.
\li The \c changeIcon() slot updates the set of pixmaps available to the
icon displayed in the preview area.
\li The \c addImage() slot allows the user to load a new image into the
application.
\li The \c addSampleImages() slot allows the user to load a new image
from the samples provided into the application.
\li The \c addOtherImages() slot allows the user to load a new image from
the directory obtained by calling
QStandardPaths::standardLocations(QStandardPaths::PicturesLocation).
\li The \c screenChanged() updates the display in the \uicontrol{High DPI}
group box to correctly display the parameters of the current screen
the window is located on.
\endlist
In addition we declare several private functions to simplify the
@ -362,8 +388,7 @@
widget and its child widgets, and put them in a grid layout. Then
we create the menus with their associated entries and actions.
Before we resize the application window to a suitable size, we set
the window title and determine the current style for the
We set the window title and determine the current style for the
application. We also enable the icon size spin box by clicking the
associated radio button, making the current value of the spin box
the icon's initial size.
@ -421,21 +446,27 @@
The \c changeSize() slot sets the size for the preview area's
icon.
To determine the new size we first check if the spin box is
It is invoked by the QButtonGroup whose members are radio buttons for
controlling the icon size. In \c createIconSizeGroupBox(), each button is
assigned a QStyle::PixelMetric value as an id, which is passed as a
parameter to the slot.
The special value \c OtherSize indicates that the spin box is
enabled. If it is, we extract the extent of the new size from the
box. If it's not, we search through the predefined size options,
extract the QStyle::PixelMetric and use the QStyle::pixelMetric()
function to determine the extent. Then we create a QSize object
based on the extent, and use that object to set the size of the
preview area's icon.
box. If it's not, we query the style for the metric. Then we create
a QSize object based on the extent, and use that object to set the
size of the preview area's icon.
\snippet widgets/icons/mainwindow.cpp 12
The first thing we do when the \c addImage() slot is called, is to
show a file dialog to the user. The easiest way to create a file
dialog is to use QFileDialog's static functions. Here we use the
\l {QFileDialog::getOpenFileNames()}{getOpenFileNames()} function
that will return one or more existing files selected by the user.
The function \c addImages() is called by the slot addSampleImages()
passing the samples directory, or by the slot addOtherImages()
passing the directory obtained by querying
QStandardPaths::standardLocations().
The first thing we do is to show a file dialog to the user.
We initialize it to show the filters returned by
QImageReader::supportedMimeTypes().
For each of the files the file dialog returns, we add a row to the
table widget. The table widget is listing the images the user has
@ -446,9 +477,13 @@
We retrieve the image name using the QFileInfo::baseName()
function that returns the base name of the file without the path,
and create the first table widget item in the row. Then we add the
file's complete name to the item's data. Since an item can hold
several information pieces, we need to assign the file name a role
and create the first table widget item in the row.
We check if a high resolution version of the image exists (identified by
the suffix \c @2x on the base name) and display that along with the size
in the tooltip.
We add the file's complete name to the item's data. Since an item can
hold several information pieces, we need to assign the file name a role
that will distinguish it from other data. This role can be Qt::UserRole
or any value above it.
@ -465,7 +500,7 @@
contains "_act", "_dis", or "_sel", the modes are changed to
Active, Disabled, or Selected. And if the file name contains
"_on", the state is changed to On. The sample files in the
example's \c images subdirectory respect this naming convension.
example's \c images subdirectory respect this naming convention.
\snippet widgets/icons/mainwindow.cpp 18
\snippet widgets/icons/mainwindow.cpp 19
@ -482,7 +517,6 @@
make sure that the new image's check box is enabled.
\snippet widgets/icons/mainwindow.cpp 6
\snippet widgets/icons/mainwindow.cpp 7
The \c changeIcon() slot is called when the user alters the set
of images listed in the QTableWidget, to update the QIcon object
@ -562,7 +596,7 @@
\snippet widgets/icons/mainwindow.cpp 25
At the end, we connect the QTableWidget::itemChanged() signal to
the \c changeIcon() slot to ensuret that the preview area is in
the \c changeIcon() slot to ensure that the preview area is in
sync with the image table.
\image icons_size_groupbox.png Screenshot of the icon size group box
@ -574,7 +608,14 @@
\snippet widgets/icons/mainwindow.cpp 26
First we create a group box that will contain all the widgets;
then we create the radio buttons and the spin box.
then we create the radio buttons and the spin box. We add the
radio buttons to an instance of QButtonGroup, using the value
of the QStyle::PixelMetric they represent as an integer id.
\snippet widgets/icons/mainwindow.cpp 40
We introduce an enumeration constant \c OtherSize to represent
a custom size.
The spin box is not a regular QSpinBox but an \c IconSizeSpinBox.
The \c IconSizeSpinBox class inherits QSpinBox and reimplements
@ -602,19 +643,16 @@
In particular we create the \c styleActionGroup based on the
currently available GUI styles using
QStyleFactory. QStyleFactory::keys() returns a list of valid keys,
typically including "windows", "cleanlooks" and
"plastique". Depending on the platform, "windowsxp", "windowsvista", "gtk" and
"macintosh" may be available.
typically including "windows" and "fusion". Depending on the platform,
"windowsvista" and "macintosh" may be available.
We create one action for each key, and adds the action to the
action group. Also, for each action, we call QAction::setData()
with the style name. We will retrieve it later using
QAction::data().
\snippet widgets/icons/mainwindow.cpp 29
In the \c createMenu() function, we add the previously created
actions to the \uicontrol File, \uicontrol View and \uicontrol Help menus.
As we go along, we create the \uicontrol File, \uicontrol View and
\uicontrol Help menus and add the actions to them.
The QMenu class provides a menu widget for use in menu bars,
context menus, and other popup menus. We put each menu in the
@ -656,6 +694,13 @@
Q_ASSERT() macro to make sure that QStyleFactory::create()
returned a valid pointer.
\snippet widgets/icons/mainwindow.cpp 44
We overload the show() function to set up the updating of the
current screen in \c screenChanged(). After calling QWidget::show(),
the QWindow associated with the QWidget is created and we can
connect to its QWindow::screenChanged() signal.
\section2 IconSizeSpinBox Class Definition
\snippet widgets/icons/iconsizespinbox.h 0
@ -690,7 +735,7 @@
reimplement the \c valueFromText() function to interpret the
parameter text and return the associated int value.
We parse the text using a regular expression (a QRegExp). We
We parse the text using a regular expression (a QRegularExpression). We
define an expression that matches one or several digits,
optionally followed by whitespace, an "x" or the times symbol,
whitespace and one or several digits again.
@ -748,7 +793,7 @@
index for editing. The parent widget and style option are used to
control the appearance of the editor widget.
Our reimplementation create and populate a combobox instead of
Our reimplementation creates and populates a combobox instead of
the default line edit. The contents of the combobox depends on
the column in the table for which the editor is requested. Column
1 contains the QIcon modes, whereas column 2 contains the QIcon
@ -786,4 +831,12 @@
triggered the slot. This signal must be emitted when the editor
widget has completed editing the data, and wants to write it back
into the model.
\section2 The Implementation of the Function main()
\snippet widgets/icons/main.cpp 45
We use QCommandLineParser to handle any command line options or parameters
passed to the application. Then, we resize the main window according
to the available screen geometry and show it.
*/

View File

@ -46,33 +46,95 @@
IconPreviewArea::IconPreviewArea(QWidget *parent)
: QWidget(parent)
{
QGridLayout *mainLayout = new QGridLayout;
setLayout(mainLayout);
QGridLayout *mainLayout = new QGridLayout(this);
stateLabels[0] = createHeaderLabel(tr("Off"));
stateLabels[1] = createHeaderLabel(tr("On"));
for (int row = 0; row < NumStates; ++row) {
stateLabels[row] = createHeaderLabel(IconPreviewArea::iconStateNames().at(row));
mainLayout->addWidget(stateLabels[row], row + 1, 0);
}
Q_ASSERT(NumStates == 2);
modeLabels[0] = createHeaderLabel(tr("Normal"));
modeLabels[1] = createHeaderLabel(tr("Active"));
modeLabels[2] = createHeaderLabel(tr("Disabled"));
modeLabels[3] = createHeaderLabel(tr("Selected"));
for (int column = 0; column < NumModes; ++column) {
modeLabels[column] = createHeaderLabel(IconPreviewArea::iconModeNames().at(column));
mainLayout->addWidget(modeLabels[column], 0, column + 1);
}
Q_ASSERT(NumModes == 4);
for (int j = 0; j < NumStates; ++j)
mainLayout->addWidget(stateLabels[j], j + 1, 0);
for (int i = 0; i < NumModes; ++i) {
mainLayout->addWidget(modeLabels[i], 0, i + 1);
for (int j = 0; j < NumStates; ++j) {
pixmapLabels[i][j] = createPixmapLabel();
mainLayout->addWidget(pixmapLabels[i][j], j + 1, i + 1);
for (int column = 0; column < NumModes; ++column) {
for (int row = 0; row < NumStates; ++row) {
pixmapLabels[column][row] = createPixmapLabel();
mainLayout->addWidget(pixmapLabels[column][row], row + 1, column + 1);
}
}
}
//! [0]
#ifdef Q_COMPILER_INITIALIZER_LISTS
//! [42]
QVector<QIcon::Mode> IconPreviewArea::iconModes()
{
static const QVector<QIcon::Mode> result = {QIcon::Normal, QIcon::Active, QIcon::Disabled, QIcon::Selected};
return result;
}
QVector<QIcon::State> IconPreviewArea::iconStates()
{
static const QVector<QIcon::State> result = {QIcon::Off, QIcon::On};
return result;
}
QStringList IconPreviewArea::iconModeNames()
{
static const QStringList result = {tr("Normal"), tr("Active"), tr("Disabled"), tr("Selected")};
return result;
}
QStringList IconPreviewArea::iconStateNames()
{
static const QStringList result = {tr("Off"), tr("On")};
return result;
}
//! [42]
#else // Q_COMPILER_INITIALIZER_LISTS
//! [43]
QVector<QIcon::Mode> IconPreviewArea::iconModes()
{
static QVector<QIcon::Mode> result;
if (result.isEmpty())
result << QIcon::Normal << QIcon::Active << QIcon::Disabled << QIcon::Selected;
return result;
}
//! [43]
QVector<QIcon::State> IconPreviewArea::iconStates()
{
static QVector<QIcon::State> result;
if (result.isEmpty())
result << QIcon::Off << QIcon::On;
return result;
}
QStringList IconPreviewArea::iconModeNames()
{
static QStringList result;
if (result.isEmpty())
result << tr("Normal") << tr("Active") << tr("Disabled") << tr("Selected");
return result;
}
QStringList IconPreviewArea::iconStateNames()
{
static QStringList result;
if (result.isEmpty())
result << tr("Off") << tr("On");
return result;
}
#endif // !Q_COMPILER_INITIALIZER_LISTS
//! [1]
void IconPreviewArea::setIcon(const QIcon &icon)
{
@ -118,23 +180,27 @@ QLabel *IconPreviewArea::createPixmapLabel()
//! [5]
void IconPreviewArea::updatePixmapLabels()
{
for (int i = 0; i < NumModes; ++i) {
QIcon::Mode mode;
if (i == 0) {
mode = QIcon::Normal;
} else if (i == 1) {
mode = QIcon::Active;
} else if (i == 2) {
mode = QIcon::Disabled;
} else {
mode = QIcon::Selected;
}
for (int j = 0; j < NumStates; ++j) {
QIcon::State state = (j == 0) ? QIcon::Off : QIcon::On;
QPixmap pixmap = icon.pixmap(size, mode, state);
pixmapLabels[i][j]->setPixmap(pixmap);
pixmapLabels[i][j]->setEnabled(!pixmap.isNull());
QWindow *window = Q_NULLPTR;
if (const QWidget *nativeParent = nativeParentWidget())
window = nativeParent->windowHandle();
for (int column = 0; column < NumModes; ++column) {
for (int row = 0; row < NumStates; ++row) {
const QPixmap pixmap =
icon.pixmap(window, size, IconPreviewArea::iconModes().at(column),
IconPreviewArea::iconStates().at(row));
QLabel *pixmapLabel = pixmapLabels[column][row];
pixmapLabel->setPixmap(pixmap);
pixmapLabel->setEnabled(!pixmap.isNull());
QString toolTip;
if (!pixmap.isNull()) {
const QSize actualSize = icon.actualSize(size);
toolTip =
tr("Size: %1x%2\nActual size: %3x%4\nDevice pixel ratio: %5")
.arg(size.width()).arg(size.height())
.arg(actualSize.width()).arg(actualSize.height())
.arg(pixmap.devicePixelRatioF());
}
pixmapLabel->setToolTip(toolTip);
}
}
}

View File

@ -43,6 +43,8 @@
#include <QIcon>
#include <QWidget>
#include <QStringList>
#include <QVector>
QT_BEGIN_NAMESPACE
class QLabel;
@ -54,11 +56,16 @@ class IconPreviewArea : public QWidget
Q_OBJECT
public:
IconPreviewArea(QWidget *parent = 0);
explicit IconPreviewArea(QWidget *parent = Q_NULLPTR);
void setIcon(const QIcon &icon);
void setSize(const QSize &size);
static QVector<QIcon::Mode> iconModes();
static QVector<QIcon::State> iconStates();
static QStringList iconModeNames();
static QStringList iconStateNames();
private:
QLabel *createHeaderLabel(const QString &text);
QLabel *createPixmapLabel();

View File

@ -10,6 +10,8 @@ SOURCES = iconpreviewarea.cpp \
main.cpp \
mainwindow.cpp
DEFINES += SRCDIR=\\\"$$PWD/\\\"
EXAMPLE_FILES = images/*
# install

View File

@ -52,13 +52,13 @@ IconSizeSpinBox::IconSizeSpinBox(QWidget *parent)
//! [1]
int IconSizeSpinBox::valueFromText(const QString &text) const
{
QRegExp regExp(tr("(\\d+)(\\s*[xx]\\s*\\d+)?"));
static const QRegularExpression regExp(tr("(\\d+)(\\s*[xx]\\s*\\d+)?"));
Q_ASSERT(regExp.isValid());
if (regExp.exactMatch(text)) {
return regExp.cap(1).toInt();
} else {
return 0;
}
const QRegularExpressionMatch match = regExp.match(text);
if (match.isValid())
return match.captured(1).toInt();
return 0;
}
//! [1]

View File

@ -49,7 +49,7 @@ class IconSizeSpinBox : public QSpinBox
Q_OBJECT
public:
IconSizeSpinBox(QWidget *parent = 0);
explicit IconSizeSpinBox(QWidget *parent = Q_NULLPTR);
int valueFromText(const QString &text) const Q_DECL_OVERRIDE;
QString textFromValue(int value) const Q_DECL_OVERRIDE;

View File

@ -41,6 +41,7 @@
#include <QtWidgets>
#include "imagedelegate.h"
#include "iconpreviewarea.h"
//! [0]
ImageDelegate::ImageDelegate(QObject *parent)
@ -55,17 +56,14 @@ QWidget *ImageDelegate::createEditor(QWidget *parent,
const QModelIndex &index) const
{
QComboBox *comboBox = new QComboBox(parent);
if (index.column() == 1) {
comboBox->addItem(tr("Normal"));
comboBox->addItem(tr("Active"));
comboBox->addItem(tr("Disabled"));
comboBox->addItem(tr("Selected"));
} else if (index.column() == 2) {
comboBox->addItem(tr("Off"));
comboBox->addItem(tr("On"));
}
if (index.column() == 1)
comboBox->addItems(IconPreviewArea::iconModeNames());
else if (index.column() == 2)
comboBox->addItems(IconPreviewArea::iconStateNames());
connect(comboBox, SIGNAL(activated(int)), this, SLOT(emitCommitData()));
typedef void (QComboBox::*QComboBoxIntSignal)(int);
connect(comboBox, static_cast<QComboBoxIntSignal>(&QComboBox::activated),
this, &ImageDelegate::emitCommitData);
return comboBox;
}

View File

@ -49,7 +49,7 @@ class ImageDelegate : public QItemDelegate
Q_OBJECT
public:
ImageDelegate(QObject *parent = 0);
explicit ImageDelegate(QObject *parent = Q_NULLPTR);
//! [0]
//! [1]

View File

@ -39,13 +39,39 @@
****************************************************************************/
#include <QApplication>
#include <QCommandLineParser>
#include <QDesktopWidget>
#include "mainwindow.h"
//! [45]
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QCoreApplication::setApplicationName(MainWindow::tr("Icons"));
QCoreApplication::setApplicationVersion(QT_VERSION_STR);
QCommandLineParser commandLineParser;
commandLineParser.setSingleDashWordOptionMode(QCommandLineParser::ParseAsLongOptions);
commandLineParser.addHelpOption();
commandLineParser.addVersionOption();
QCommandLineOption noHighDpiPixmapOption("no-highdpi-pixmaps",
"Disable High DPI image loading (Qt::AA_UseHighDpiPixmaps)");
commandLineParser.addOption(noHighDpiPixmapOption);
commandLineParser.addPositionalArgument(MainWindow::tr("[file]"), MainWindow::tr("Icon file(s) to open."));
commandLineParser.process(QCoreApplication::arguments());
if (!commandLineParser.isSet(noHighDpiPixmapOption))
QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
MainWindow mainWin;
if (!commandLineParser.positionalArguments().isEmpty())
mainWin.loadImages(commandLineParser.positionalArguments());
const QRect availableGeometry = QApplication::desktop()->availableGeometry(&mainWin);
mainWin.resize(availableGeometry.width() / 2, availableGeometry.height() * 2 / 3);
mainWin.move((availableGeometry.width() - mainWin.width()) / 2, (availableGeometry.height() - mainWin.height()) / 2);
mainWin.show();
return app.exec();
}
//! [45]

View File

@ -45,34 +45,49 @@
#include "imagedelegate.h"
#include "mainwindow.h"
//! [40]
enum { OtherSize = QStyle::PM_CustomBase };
//! [40]
//! [0]
MainWindow::MainWindow()
{
centralWidget = new QWidget;
QWidget *centralWidget = new QWidget(this);
setCentralWidget(centralWidget);
createPreviewGroupBox();
createImagesGroupBox();
createIconSizeGroupBox();
createActions();
createMenus();
createContextMenu();
QGridLayout *mainLayout = new QGridLayout;
QGridLayout *mainLayout = new QGridLayout(centralWidget);
QGroupBox *previewGroupBox = new QGroupBox(tr("Preview"));
previewArea = new IconPreviewArea(previewGroupBox);
QVBoxLayout *previewLayout = new QVBoxLayout(previewGroupBox);
previewLayout->addWidget(previewArea);
mainLayout->addWidget(previewGroupBox, 0, 0, 1, 2);
mainLayout->addWidget(imagesGroupBox, 1, 0);
mainLayout->addWidget(iconSizeGroupBox, 1, 1);
centralWidget->setLayout(mainLayout);
mainLayout->addWidget(createImagesGroupBox(), 1, 0);
QVBoxLayout *vBox = new QVBoxLayout;
vBox->addWidget(createIconSizeGroupBox());
vBox->addWidget(createHighDpiIconSizeGroupBox());
vBox->addItem(new QSpacerItem(0, 0, QSizePolicy::Ignored, QSizePolicy::MinimumExpanding));
mainLayout->addLayout(vBox, 1, 1);
createContextMenu();
setWindowTitle(tr("Icons"));
checkCurrentStyle();
otherRadioButton->click();
resize(minimumSizeHint());
sizeButtonGroup->button(OtherSize)->click();
}
//! [0]
//! [44]
void MainWindow::show()
{
QMainWindow::show();
connect(windowHandle(), &QWindow::screenChanged, this, &MainWindow::screenChanged);
screenChanged();
}
//! [44]
//! [1]
void MainWindow::about()
{
@ -89,60 +104,62 @@ void MainWindow::changeStyle(bool checked)
if (!checked)
return;
QAction *action = qobject_cast<QAction *>(sender());
const QAction *action = qobject_cast<QAction *>(sender());
//! [2] //! [3]
QStyle *style = QStyleFactory::create(action->data().toString());
//! [3] //! [4]
Q_ASSERT(style);
QApplication::setStyle(style);
smallRadioButton->setText(tr("Small (%1 x %1)")
.arg(style->pixelMetric(QStyle::PM_SmallIconSize)));
largeRadioButton->setText(tr("Large (%1 x %1)")
.arg(style->pixelMetric(QStyle::PM_LargeIconSize)));
toolBarRadioButton->setText(tr("Toolbars (%1 x %1)")
.arg(style->pixelMetric(QStyle::PM_ToolBarIconSize)));
listViewRadioButton->setText(tr("List views (%1 x %1)")
.arg(style->pixelMetric(QStyle::PM_ListViewIconSize)));
iconViewRadioButton->setText(tr("Icon views (%1 x %1)")
.arg(style->pixelMetric(QStyle::PM_IconViewIconSize)));
tabBarRadioButton->setText(tr("Tab bars (%1 x %1)")
.arg(style->pixelMetric(QStyle::PM_TabBarIconSize)));
foreach (QAbstractButton *button, sizeButtonGroup->buttons()) {
const QStyle::PixelMetric metric = static_cast<QStyle::PixelMetric>(sizeButtonGroup->id(button));
const int value = style->pixelMetric(metric);
switch (metric) {
case QStyle::PM_SmallIconSize:
button->setText(tr("Small (%1 x %1)").arg(value));
break;
case QStyle::PM_LargeIconSize:
button->setText(tr("Large (%1 x %1)").arg(value));
break;
case QStyle::PM_ToolBarIconSize:
button->setText(tr("Toolbars (%1 x %1)").arg(value));
break;
case QStyle::PM_ListViewIconSize:
button->setText(tr("List views (%1 x %1)").arg(value));
break;
case QStyle::PM_IconViewIconSize:
button->setText(tr("Icon views (%1 x %1)").arg(value));
break;
case QStyle::PM_TabBarIconSize:
button->setText(tr("Tab bars (%1 x %1)").arg(value));
break;
default:
break;
}
}
changeSize();
triggerChangeSize();
}
//! [4]
//! [5]
void MainWindow::changeSize(bool checked)
void MainWindow::changeSize(int id, bool checked)
{
if (!checked)
return;
int extent;
const bool other = id == int(OtherSize);
const int extent = other
? otherSpinBox->value()
: QApplication::style()->pixelMetric(static_cast<QStyle::PixelMetric>(id));
if (otherRadioButton->isChecked()) {
extent = otherSpinBox->value();
} else {
QStyle::PixelMetric metric;
if (smallRadioButton->isChecked()) {
metric = QStyle::PM_SmallIconSize;
} else if (largeRadioButton->isChecked()) {
metric = QStyle::PM_LargeIconSize;
} else if (toolBarRadioButton->isChecked()) {
metric = QStyle::PM_ToolBarIconSize;
} else if (listViewRadioButton->isChecked()) {
metric = QStyle::PM_ListViewIconSize;
} else if (iconViewRadioButton->isChecked()) {
metric = QStyle::PM_IconViewIconSize;
} else {
metric = QStyle::PM_TabBarIconSize;
}
extent = QApplication::style()->pixelMetric(metric);
}
previewArea->setSize(QSize(extent, extent));
otherSpinBox->setEnabled(otherRadioButton->isChecked());
otherSpinBox->setEnabled(other);
}
void MainWindow::triggerChangeSize()
{
changeSize(sizeButtonGroup->checkedId(), true);
}
//! [5]
@ -152,33 +169,21 @@ void MainWindow::changeIcon()
QIcon icon;
for (int row = 0; row < imagesTable->rowCount(); ++row) {
QTableWidgetItem *item0 = imagesTable->item(row, 0);
QTableWidgetItem *item1 = imagesTable->item(row, 1);
QTableWidgetItem *item2 = imagesTable->item(row, 2);
const QTableWidgetItem *fileItem = imagesTable->item(row, 0);
const QTableWidgetItem *modeItem = imagesTable->item(row, 1);
const QTableWidgetItem *stateItem = imagesTable->item(row, 2);
if (item0->checkState() == Qt::Checked) {
QIcon::Mode mode;
if (item1->text() == tr("Normal")) {
mode = QIcon::Normal;
} else if (item1->text() == tr("Active")) {
mode = QIcon::Active;
} else if (item1->text() == tr("Disabled")) {
mode = QIcon::Disabled;
} else {
mode = QIcon::Selected;
}
QIcon::State state;
if (item2->text() == tr("On")) {
state = QIcon::On;
} else {
state = QIcon::Off;
//! [6] //! [7]
}
//! [7]
if (fileItem->checkState() == Qt::Checked) {
const int modeIndex = IconPreviewArea::iconModeNames().indexOf(modeItem->text());
Q_ASSERT(modeIndex >= 0);
const int stateIndex = IconPreviewArea::iconStateNames().indexOf(stateItem->text());
Q_ASSERT(stateIndex >= 0);
const QIcon::Mode mode = IconPreviewArea::iconModes().at(modeIndex);
const QIcon::State state = IconPreviewArea::iconStates().at(stateIndex);
//! [6]
//! [8]
QString fileName = item0->data(Qt::UserRole).toString();
const QString fileName = fileItem->data(Qt::UserRole).toString();
QImage image(fileName);
if (!image.isNull())
icon.addPixmap(QPixmap::fromImage(image), mode, state);
@ -193,61 +198,109 @@ void MainWindow::changeIcon()
}
//! [11]
//! [12]
void MainWindow::addImages()
void MainWindow::addSampleImages()
{
QStringList fileNames = QFileDialog::getOpenFileNames(this,
tr("Open Images"), "",
tr("Images (*.png *.xpm *.jpg);;"
"All Files (*)"));
if (!fileNames.isEmpty()) {
foreach (QString fileName, fileNames) {
int row = imagesTable->rowCount();
imagesTable->setRowCount(row + 1);
//! [12]
addImages(QLatin1String(SRCDIR) + QLatin1String("/images"));
}
void MainWindow::addOtherImages()
{
static bool firstInvocation = true;
QString directory;
if (firstInvocation) {
firstInvocation = false;
directory = QStandardPaths::standardLocations(QStandardPaths::PicturesLocation).value(0, QString());
}
addImages(directory);
}
//! [12]
void MainWindow::addImages(const QString &directory)
{
QFileDialog fileDialog(this, tr("Open Images"), directory);
QStringList mimeTypeFilters;
foreach (const QByteArray &mimeTypeName, QImageReader::supportedMimeTypes())
mimeTypeFilters.append(mimeTypeName);
mimeTypeFilters.sort();
fileDialog.setMimeTypeFilters(mimeTypeFilters);
fileDialog.selectMimeTypeFilter(QLatin1String("image/png"));
fileDialog.setAcceptMode(QFileDialog::AcceptOpen);
fileDialog.setFileMode(QFileDialog::ExistingFiles);
if (!nativeFileDialogAct->isChecked())
fileDialog.setOption(QFileDialog::DontUseNativeDialog);
if (fileDialog.exec() == QDialog::Accepted)
loadImages(fileDialog.selectedFiles());
//! [12]
}
void MainWindow::loadImages(const QStringList &fileNames)
{
foreach (const QString &fileName, fileNames) {
const int row = imagesTable->rowCount();
imagesTable->setRowCount(row + 1);
//! [13]
QString imageName = QFileInfo(fileName).baseName();
const QFileInfo fileInfo(fileName);
const QString imageName = fileInfo.baseName();
const QString fileName2x = fileInfo.absolutePath()
+ QLatin1Char('/') + imageName + QLatin1String("@2x.") + fileInfo.suffix();
const QFileInfo fileInfo2x(fileName2x);
const QImage image(fileName);
const QString toolTip =
tr("Directory: %1\nFile: %2\nFile@2x: %3\nSize: %4x%5")
.arg(QDir::toNativeSeparators(fileInfo.absolutePath()), fileInfo.fileName())
.arg(fileInfo2x.exists() ? fileInfo2x.fileName() : tr("<None>"))
.arg(image.width()).arg(image.height());
//! [13] //! [14]
QTableWidgetItem *item0 = new QTableWidgetItem(imageName);
item0->setData(Qt::UserRole, fileName);
item0->setFlags(item0->flags() & ~Qt::ItemIsEditable);
QTableWidgetItem *fileItem = new QTableWidgetItem(imageName);
fileItem->setData(Qt::UserRole, fileName);
fileItem->setIcon(QPixmap::fromImage(image));
fileItem->setFlags((fileItem->flags() | Qt::ItemIsUserCheckable) & ~Qt::ItemIsEditable);
fileItem->setToolTip(toolTip);
//! [14]
//! [15]
QTableWidgetItem *item1 = new QTableWidgetItem(tr("Normal"));
QIcon::Mode mode = QIcon::Normal;
//! [15] //! [16]
QTableWidgetItem *item2 = new QTableWidgetItem(tr("Off"));
QIcon::State state = QIcon::Off;
if (guessModeStateAct->isChecked()) {
if (imageName.contains(QLatin1String("_act"), Qt::CaseInsensitive))
mode = QIcon::Active;
else if (imageName.contains(QLatin1String("_dis"), Qt::CaseInsensitive))
mode = QIcon::Disabled;
else if (imageName.contains(QLatin1String("_sel"), Qt::CaseInsensitive))
mode = QIcon::Selected;
if (guessModeStateAct->isChecked()) {
if (fileName.contains("_act")) {
item1->setText(tr("Active"));
} else if (fileName.contains("_dis")) {
item1->setText(tr("Disabled"));
} else if (fileName.contains("_sel")) {
item1->setText(tr("Selected"));
}
if (fileName.contains("_on"))
item2->setText(tr("On"));
if (imageName.contains(QLatin1String("_on"), Qt::CaseInsensitive))
state = QIcon::On;
//! [16] //! [17]
}
}
//! [17]
//! [18]
imagesTable->setItem(row, 0, item0);
imagesTable->setItem(row, 0, fileItem);
//! [18] //! [19]
imagesTable->setItem(row, 1, item1);
imagesTable->setItem(row, 2, item2);
imagesTable->openPersistentEditor(item1);
imagesTable->openPersistentEditor(item2);
QTableWidgetItem *modeItem =
new QTableWidgetItem(IconPreviewArea::iconModeNames().at(IconPreviewArea::iconModes().indexOf(mode)));
modeItem->setToolTip(toolTip);
imagesTable->setItem(row, 1, modeItem);
QTableWidgetItem *stateItem =
new QTableWidgetItem(IconPreviewArea::iconStateNames().at(IconPreviewArea::iconStates().indexOf(state)));
stateItem->setToolTip(toolTip);
imagesTable->setItem(row, 2, stateItem);
imagesTable->openPersistentEditor(modeItem);
imagesTable->openPersistentEditor(stateItem);
item0->setCheckState(Qt::Checked);
}
fileItem->setCheckState(Qt::Checked);
}
}
//! [19]
void MainWindow::useHighDpiPixmapsChanged(int checkState)
{
QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps, checkState == Qt::Checked);
changeIcon();
}
//! [20]
void MainWindow::removeAllImages()
{
@ -256,21 +309,10 @@ void MainWindow::removeAllImages()
}
//! [20]
void MainWindow::createPreviewGroupBox()
{
previewGroupBox = new QGroupBox(tr("Preview"));
previewArea = new IconPreviewArea;
QVBoxLayout *layout = new QVBoxLayout;
layout->addWidget(previewArea);
previewGroupBox->setLayout(layout);
}
//! [21]
void MainWindow::createImagesGroupBox()
QWidget *MainWindow::createImagesGroupBox()
{
imagesGroupBox = new QGroupBox(tr("Images"));
QGroupBox *imagesGroupBox = new QGroupBox(tr("Images"));
imagesTable = new QTableWidget;
imagesTable->setSelectionMode(QAbstractItemView::NoSelection);
@ -292,57 +334,63 @@ void MainWindow::createImagesGroupBox()
//! [23]
//! [24]
connect(imagesTable, SIGNAL(itemChanged(QTableWidgetItem*)),
connect(imagesTable, &QTableWidget::itemChanged,
//! [24] //! [25]
this, SLOT(changeIcon()));
this, &MainWindow::changeIcon);
QVBoxLayout *layout = new QVBoxLayout;
QVBoxLayout *layout = new QVBoxLayout(imagesGroupBox);
layout->addWidget(imagesTable);
imagesGroupBox->setLayout(layout);
return imagesGroupBox;
}
//! [25]
//! [26]
void MainWindow::createIconSizeGroupBox()
QWidget *MainWindow::createIconSizeGroupBox()
{
iconSizeGroupBox = new QGroupBox(tr("Icon Size"));
QGroupBox *iconSizeGroupBox = new QGroupBox(tr("Icon Size"));
smallRadioButton = new QRadioButton;
largeRadioButton = new QRadioButton;
toolBarRadioButton = new QRadioButton;
listViewRadioButton = new QRadioButton;
iconViewRadioButton = new QRadioButton;
tabBarRadioButton = new QRadioButton;
otherRadioButton = new QRadioButton(tr("Other:"));
sizeButtonGroup = new QButtonGroup(this);
sizeButtonGroup->setExclusive(true);
typedef void (QButtonGroup::*QButtonGroupIntBoolSignal)(int, bool);
connect(sizeButtonGroup, static_cast<QButtonGroupIntBoolSignal>(&QButtonGroup::buttonToggled),
this, &MainWindow::changeSize);
QRadioButton *smallRadioButton = new QRadioButton;
sizeButtonGroup->addButton(smallRadioButton, QStyle::PM_SmallIconSize);
QRadioButton *largeRadioButton = new QRadioButton;
sizeButtonGroup->addButton(largeRadioButton, QStyle::PM_LargeIconSize);
QRadioButton *toolBarRadioButton = new QRadioButton;
sizeButtonGroup->addButton(toolBarRadioButton, QStyle::PM_ToolBarIconSize);
QRadioButton *listViewRadioButton = new QRadioButton;
sizeButtonGroup->addButton(listViewRadioButton, QStyle::PM_ListViewIconSize);
QRadioButton *iconViewRadioButton = new QRadioButton;
sizeButtonGroup->addButton(iconViewRadioButton, QStyle::PM_IconViewIconSize);
QRadioButton *tabBarRadioButton = new QRadioButton;
sizeButtonGroup->addButton(tabBarRadioButton, QStyle::PM_TabBarIconSize);
QRadioButton *otherRadioButton = new QRadioButton(tr("Other:"));
sizeButtonGroup->addButton(otherRadioButton, OtherSize);
otherSpinBox = new IconSizeSpinBox;
otherSpinBox->setRange(8, 128);
otherSpinBox->setRange(8, 256);
const QString spinBoxToolTip =
tr("Enter a custom size within %1..%2")
.arg(otherSpinBox->minimum()).arg(otherSpinBox->maximum());
otherSpinBox->setValue(64);
otherSpinBox->setToolTip(spinBoxToolTip);
otherRadioButton->setToolTip(spinBoxToolTip);
//! [26]
//! [27]
connect(smallRadioButton, SIGNAL(toggled(bool)),
this, SLOT(changeSize(bool)));
connect(largeRadioButton, SIGNAL(toggled(bool)),
this, SLOT(changeSize(bool)));
connect(toolBarRadioButton, SIGNAL(toggled(bool)),
this, SLOT(changeSize(bool)));
connect(listViewRadioButton, SIGNAL(toggled(bool)),
this, SLOT(changeSize(bool)));
connect(iconViewRadioButton, SIGNAL(toggled(bool)),
this, SLOT(changeSize(bool)));
connect(tabBarRadioButton, SIGNAL(toggled(bool)),
this, SLOT(changeSize(bool)));
connect(otherRadioButton, SIGNAL(toggled(bool)),
this, SLOT(changeSize(bool)));
connect(otherSpinBox, SIGNAL(valueChanged(int)), this, SLOT(changeSize()));
typedef void (QSpinBox::*QSpinBoxIntSignal)(int);
connect(otherSpinBox, static_cast<QSpinBoxIntSignal>(&QSpinBox::valueChanged),
this, &MainWindow::triggerChangeSize);
QHBoxLayout *otherSizeLayout = new QHBoxLayout;
otherSizeLayout->addWidget(otherRadioButton);
otherSizeLayout->addWidget(otherSpinBox);
otherSizeLayout->addStretch();
QGridLayout *layout = new QGridLayout;
QGridLayout *layout = new QGridLayout(iconSizeGroupBox);
layout->addWidget(smallRadioButton, 0, 0);
layout->addWidget(largeRadioButton, 1, 0);
layout->addWidget(toolBarRadioButton, 2, 0);
@ -351,75 +399,99 @@ void MainWindow::createIconSizeGroupBox()
layout->addWidget(tabBarRadioButton, 2, 1);
layout->addLayout(otherSizeLayout, 3, 0, 1, 2);
layout->setRowStretch(4, 1);
iconSizeGroupBox->setLayout(layout);
return iconSizeGroupBox;
}
//! [27]
void MainWindow::screenChanged()
{
devicePixelRatioLabel->setText(QString::number(devicePixelRatioF()));
if (const QWindow *window = windowHandle()) {
const QScreen *screen = window->screen();
const QString screenDescription =
tr("\"%1\" (%2x%3)").arg(screen->name())
.arg(screen->geometry().width()).arg(screen->geometry().height());
screenNameLabel->setText(screenDescription);
}
changeIcon();
}
QWidget *MainWindow::createHighDpiIconSizeGroupBox()
{
QGroupBox *highDpiGroupBox = new QGroupBox(tr("High DPI Scaling"));
QFormLayout *layout = new QFormLayout(highDpiGroupBox);
devicePixelRatioLabel = new QLabel(highDpiGroupBox);
screenNameLabel = new QLabel(highDpiGroupBox);
layout->addRow(tr("Screen:"), screenNameLabel);
layout->addRow(tr("Device pixel ratio:"), devicePixelRatioLabel);
QCheckBox *highDpiPixmapsCheckBox = new QCheckBox(QLatin1String("Qt::AA_UseHighDpiPixmaps"));
highDpiPixmapsCheckBox->setChecked(QCoreApplication::testAttribute(Qt::AA_UseHighDpiPixmaps));
connect(highDpiPixmapsCheckBox, &QCheckBox::stateChanged, this, &MainWindow::useHighDpiPixmapsChanged);
layout->addRow(highDpiPixmapsCheckBox);
return highDpiGroupBox;
}
//! [28]
void MainWindow::createActions()
{
addImagesAct = new QAction(tr("&Add Images..."), this);
addImagesAct->setShortcut(tr("Ctrl+A"));
connect(addImagesAct, SIGNAL(triggered()), this, SLOT(addImages()));
QMenu *fileMenu = menuBar()->addMenu(tr("&File"));
addSampleImagesAct = new QAction(tr("Add &Sample Images..."), this);
addSampleImagesAct->setShortcut(tr("Ctrl+A"));
connect(addSampleImagesAct, &QAction::triggered, this, &MainWindow::addSampleImages);
fileMenu->addAction(addSampleImagesAct);
addOtherImagesAct = new QAction(tr("&Add Images..."), this);
addOtherImagesAct->setShortcut(QKeySequence::Open);
connect(addOtherImagesAct, &QAction::triggered, this, &MainWindow::addOtherImages);
fileMenu->addAction(addOtherImagesAct);
removeAllImagesAct = new QAction(tr("&Remove All Images"), this);
removeAllImagesAct->setShortcut(tr("Ctrl+R"));
connect(removeAllImagesAct, SIGNAL(triggered()),
this, SLOT(removeAllImages()));
connect(removeAllImagesAct, &QAction::triggered,
this, &MainWindow::removeAllImages);
fileMenu->addAction(removeAllImagesAct);
exitAct = new QAction(tr("&Quit"), this);
fileMenu->addSeparator();
QAction *exitAct = fileMenu->addAction(tr("&Quit"), this, &QWidget::close);
exitAct->setShortcuts(QKeySequence::Quit);
connect(exitAct, SIGNAL(triggered()), this, SLOT(close()));
QMenu *viewMenu = menuBar()->addMenu(tr("&View"));
styleActionGroup = new QActionGroup(this);
foreach (QString styleName, QStyleFactory::keys()) {
QAction *action = new QAction(styleActionGroup);
action->setText(tr("%1 Style").arg(styleName));
foreach (const QString &styleName, QStyleFactory::keys()) {
QAction *action = new QAction(tr("%1 Style").arg(styleName), styleActionGroup);
action->setData(styleName);
action->setCheckable(true);
connect(action, SIGNAL(triggered(bool)), this, SLOT(changeStyle(bool)));
connect(action, &QAction::triggered, this, &MainWindow::changeStyle);
viewMenu->addAction(action);
}
QMenu *settingsMenu = menuBar()->addMenu(tr("&Settings"));
guessModeStateAct = new QAction(tr("&Guess Image Mode/State"), this);
guessModeStateAct->setCheckable(true);
guessModeStateAct->setChecked(true);
settingsMenu->addAction(guessModeStateAct);
aboutAct = new QAction(tr("&About"), this);
connect(aboutAct, SIGNAL(triggered()), this, SLOT(about()));
nativeFileDialogAct = new QAction(tr("&Use Native File Dialog"), this);
nativeFileDialogAct->setCheckable(true);
nativeFileDialogAct->setChecked(true);
settingsMenu->addAction(nativeFileDialogAct);
aboutQtAct = new QAction(tr("About &Qt"), this);
connect(aboutQtAct, SIGNAL(triggered()), qApp, SLOT(aboutQt()));
QMenu *helpMenu = menuBar()->addMenu(tr("&Help"));
helpMenu->addAction(tr("&About"), this, &MainWindow::about);
helpMenu->addAction(tr("About &Qt"), qApp, &QApplication::aboutQt);
}
//! [28]
//! [29]
void MainWindow::createMenus()
{
fileMenu = menuBar()->addMenu(tr("&File"));
fileMenu->addAction(addImagesAct);
fileMenu->addAction(removeAllImagesAct);
fileMenu->addSeparator();
fileMenu->addAction(exitAct);
viewMenu = menuBar()->addMenu(tr("&View"));
foreach (QAction *action, styleActionGroup->actions())
viewMenu->addAction(action);
viewMenu->addSeparator();
viewMenu->addAction(guessModeStateAct);
menuBar()->addSeparator();
helpMenu = menuBar()->addMenu(tr("&Help"));
helpMenu->addAction(aboutAct);
helpMenu->addAction(aboutQtAct);
}
//! [29]
//! [30]
void MainWindow::createContextMenu()
{
imagesTable->setContextMenuPolicy(Qt::ActionsContextMenu);
imagesTable->addAction(addImagesAct);
imagesTable->addAction(addSampleImagesAct);
imagesTable->addAction(addOtherImagesAct);
imagesTable->addAction(removeAllImagesAct);
}
//! [30]
@ -429,14 +501,13 @@ void MainWindow::checkCurrentStyle()
{
foreach (QAction *action, styleActionGroup->actions()) {
QString styleName = action->data().toString();
QStyle *candidate = QStyleFactory::create(styleName);
Q_ASSERT(candidate);
QScopedPointer<QStyle> candidate(QStyleFactory::create(styleName));
Q_ASSERT(!candidate.isNull());
if (candidate->metaObject()->className()
== QApplication::style()->metaObject()->className()) {
action->trigger();
return;
}
delete candidate;
}
}
//! [31]

View File

@ -49,9 +49,10 @@
QT_BEGIN_NAMESPACE
class QAction;
class QActionGroup;
class QGroupBox;
class QLabel;
class QMenu;
class QRadioButton;
class QButtonGroup;
class QTableWidget;
QT_END_NAMESPACE
class IconPreviewArea;
@ -65,51 +66,47 @@ class MainWindow : public QMainWindow
public:
MainWindow();
void loadImages(const QStringList &fileNames);
void show();
private slots:
void about();
void changeStyle(bool checked);
void changeSize(bool checked = true);
void changeSize(int, bool);
void triggerChangeSize();
void changeIcon();
void addImages();
void addSampleImages();
void addOtherImages();
void removeAllImages();
void useHighDpiPixmapsChanged(int checkState);
void screenChanged();
private:
void createPreviewGroupBox();
void createImagesGroupBox();
void createIconSizeGroupBox();
QWidget *createImagesGroupBox();
QWidget *createIconSizeGroupBox();
QWidget *createHighDpiIconSizeGroupBox();
void createActions();
void createMenus();
void createContextMenu();
void checkCurrentStyle();
void addImages(const QString &directory);
QWidget *centralWidget;
QGroupBox *previewGroupBox;
IconPreviewArea *previewArea;
QGroupBox *imagesGroupBox;
QTableWidget *imagesTable;
QGroupBox *iconSizeGroupBox;
QRadioButton *smallRadioButton;
QRadioButton *largeRadioButton;
QRadioButton *toolBarRadioButton;
QRadioButton *listViewRadioButton;
QRadioButton *iconViewRadioButton;
QRadioButton *tabBarRadioButton;
QRadioButton *otherRadioButton;
QButtonGroup *sizeButtonGroup;
IconSizeSpinBox *otherSpinBox;
QMenu *fileMenu;
QMenu *viewMenu;
QMenu *helpMenu;
QAction *addImagesAct;
QLabel *devicePixelRatioLabel;
QLabel *screenNameLabel;
QAction *addOtherImagesAct;
QAction *addSampleImagesAct;
QAction *removeAllImagesAct;
QAction *exitAct;
QAction *guessModeStateAct;
QAction *nativeFileDialogAct;
QActionGroup *styleActionGroup;
QAction *aboutAct;
QAction *aboutQtAct;
};
//! [0]