Polish the screen shot example.

- Remove unneeded member variables.
- Set window sizes depending on screen geometry for High DPI screens.
- Flesh out code for saving the image, setting the supported
  mime types on the file dialog.
- Streamline constructor code, remove create...() functions.
- Use new connection syntax in createActions(),
- Obtain the screen from the widget.
- Adapt documentation. Remove note saying that widgets do not need
  the parent parameter (since creating parentless widgets can
  result in flicker in some cases), explain that QScreen pointers
  should be checked.

Change-Id: I0332bbf10eafe861fe3fd5573522694ab5c0183a
Reviewed-by: Topi Reiniö <topi.reinio@digia.com>
This commit is contained in:
Friedemann Kleint 2015-08-28 10:56:58 +02:00 committed by Jędrzej Nowacki
parent 3363398802
commit 01c6f7200a
4 changed files with 103 additions and 138 deletions

View File

@ -39,13 +39,16 @@
****************************************************************************/ ****************************************************************************/
#include <QApplication> #include <QApplication>
#include <QDesktopWidget>
#include "screenshot.h" #include "screenshot.h"
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
QApplication app(argc, argv); QApplication app(argc, argv);
Screenshot screenshot; Screenshot screenshot;
screenshot.move(QApplication::desktop()->availableGeometry(&screenshot).topLeft() + QPoint(20, 20));
screenshot.show(); screenshot.show();
return app.exec(); return app.exec();
} }

View File

@ -44,20 +44,48 @@
//! [0] //! [0]
Screenshot::Screenshot() Screenshot::Screenshot()
: screenshotLabel(new QLabel(this))
{ {
screenshotLabel = new QLabel;
screenshotLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); screenshotLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
screenshotLabel->setAlignment(Qt::AlignCenter); screenshotLabel->setAlignment(Qt::AlignCenter);
screenshotLabel->setMinimumSize(240, 160);
createOptionsGroupBox(); const QRect screenGeometry = QApplication::desktop()->screenGeometry(this);
createButtonsLayout(); screenshotLabel->setMinimumSize(screenGeometry.width() / 8, screenGeometry.height() / 8);
mainLayout = new QVBoxLayout; QVBoxLayout *mainLayout = new QVBoxLayout(this);
mainLayout->addWidget(screenshotLabel); mainLayout->addWidget(screenshotLabel);
QGroupBox *optionsGroupBox = new QGroupBox(tr("Options"), this);
delaySpinBox = new QSpinBox(optionsGroupBox);
delaySpinBox->setSuffix(tr(" s"));
delaySpinBox->setMaximum(60);
typedef void (QSpinBox::*QSpinBoxIntSignal)(int);
connect(delaySpinBox, static_cast<QSpinBoxIntSignal>(&QSpinBox::valueChanged),
this, &Screenshot::updateCheckBox);
hideThisWindowCheckBox = new QCheckBox(tr("Hide This Window"), optionsGroupBox);
QGridLayout *optionsGroupBoxLayout = new QGridLayout(optionsGroupBox);
optionsGroupBoxLayout->addWidget(new QLabel(tr("Screenshot Delay:"), this), 0, 0);
optionsGroupBoxLayout->addWidget(delaySpinBox, 0, 1);
optionsGroupBoxLayout->addWidget(hideThisWindowCheckBox, 1, 0, 1, 2);
mainLayout->addWidget(optionsGroupBox); mainLayout->addWidget(optionsGroupBox);
QHBoxLayout *buttonsLayout = new QHBoxLayout;
newScreenshotButton = new QPushButton(tr("New Screenshot"), this);
connect(newScreenshotButton, &QPushButton::clicked, this, &Screenshot::newScreenshot);
buttonsLayout->addWidget(newScreenshotButton);
QPushButton *saveScreenshotButton = new QPushButton(tr("Save Screenshot"), this);
connect(saveScreenshotButton, &QPushButton::clicked, this, &Screenshot::saveScreenshot);
buttonsLayout->addWidget(saveScreenshotButton);
QPushButton *quitScreenshotButton = new QPushButton(tr("Quit"), this);
quitScreenshotButton->setShortcut(Qt::CTRL + Qt::Key_Q);
connect(quitScreenshotButton, &QPushButton::clicked, this, &QWidget::close);
buttonsLayout->addWidget(quitScreenshotButton);
buttonsLayout->addStretch();
mainLayout->addLayout(buttonsLayout); mainLayout->addLayout(buttonsLayout);
setLayout(mainLayout);
shootScreen(); shootScreen();
delaySpinBox->setValue(5); delaySpinBox->setValue(5);
@ -84,44 +112,59 @@ void Screenshot::newScreenshot()
hide(); hide();
newScreenshotButton->setDisabled(true); newScreenshotButton->setDisabled(true);
QTimer::singleShot(delaySpinBox->value() * 1000, this, SLOT(shootScreen())); QTimer::singleShot(delaySpinBox->value() * 1000, this, &Screenshot::shootScreen);
} }
//! [2] //! [2]
//! [3] //! [3]
void Screenshot::saveScreenshot() void Screenshot::saveScreenshot()
{ {
QString format = "png"; const QString format = "png";
QString initialPath = QDir::currentPath() + tr("/untitled.") + format; QString initialPath = QStandardPaths::writableLocation(QStandardPaths::PicturesLocation);
if (initialPath.isEmpty())
initialPath = QDir::currentPath();
initialPath += tr("/untitled.") + format;
QString fileName = QFileDialog::getSaveFileName(this, tr("Save As"), initialPath, QFileDialog fileDialog(this, tr("Save As"), initialPath);
tr("%1 Files (*.%2);;All Files (*)") fileDialog.setAcceptMode(QFileDialog::AcceptSave);
.arg(format.toUpper()) fileDialog.setFileMode(QFileDialog::AnyFile);
.arg(format)); fileDialog.setDirectory(initialPath);
if (!fileName.isEmpty()) QStringList mimeTypes;
originalPixmap.save(fileName, format.toLatin1().constData()); foreach (const QByteArray &bf, QImageWriter::supportedMimeTypes())
mimeTypes.append(QLatin1String(bf));
fileDialog.setMimeTypeFilters(mimeTypes);
fileDialog.selectMimeTypeFilter("image/" + format);
fileDialog.setDefaultSuffix(format);
if (fileDialog.exec() != QDialog::Accepted)
return;
const QString fileName = fileDialog.selectedFiles().first();
if (!originalPixmap.save(fileName)) {
QMessageBox::warning(this, tr("Save Error"), tr("The image could not be saved to \"%1\".")
.arg(QDir::toNativeSeparators(fileName)));
}
} }
//! [3] //! [3]
//! [4] //! [4]
void Screenshot::shootScreen() void Screenshot::shootScreen()
{ {
if (delaySpinBox->value() != 0)
qApp->beep();
//! [4]
originalPixmap = QPixmap(); // clear image for low memory situations
// on embedded devices.
//! [5]
QScreen *screen = QGuiApplication::primaryScreen(); QScreen *screen = QGuiApplication::primaryScreen();
if (screen) if (const QWindow *window = windowHandle())
originalPixmap = screen->grabWindow(0); screen = window->screen();
if (!screen)
return;
if (delaySpinBox->value() != 0)
QApplication::beep();
originalPixmap = screen->grabWindow(0);
updateScreenshotLabel(); updateScreenshotLabel();
newScreenshotButton->setDisabled(false); newScreenshotButton->setDisabled(false);
if (hideThisWindowCheckBox->isChecked()) if (hideThisWindowCheckBox->isChecked())
show(); show();
} }
//! [5] //! [4]
//! [6] //! [6]
void Screenshot::updateCheckBox() void Screenshot::updateCheckBox()
@ -135,52 +178,6 @@ void Screenshot::updateCheckBox()
} }
//! [6] //! [6]
//! [7]
void Screenshot::createOptionsGroupBox()
{
optionsGroupBox = new QGroupBox(tr("Options"));
delaySpinBox = new QSpinBox;
delaySpinBox->setSuffix(tr(" s"));
delaySpinBox->setMaximum(60);
connect(delaySpinBox, SIGNAL(valueChanged(int)), this, SLOT(updateCheckBox()));
delaySpinBoxLabel = new QLabel(tr("Screenshot Delay:"));
hideThisWindowCheckBox = new QCheckBox(tr("Hide This Window"));
optionsGroupBoxLayout = new QGridLayout;
optionsGroupBoxLayout->addWidget(delaySpinBoxLabel, 0, 0);
optionsGroupBoxLayout->addWidget(delaySpinBox, 0, 1);
optionsGroupBoxLayout->addWidget(hideThisWindowCheckBox, 1, 0, 1, 2);
optionsGroupBox->setLayout(optionsGroupBoxLayout);
}
//! [7]
//! [8]
void Screenshot::createButtonsLayout()
{
newScreenshotButton = createButton(tr("New Screenshot"), this, SLOT(newScreenshot()));
saveScreenshotButton = createButton(tr("Save Screenshot"), this, SLOT(saveScreenshot()));
quitScreenshotButton = createButton(tr("Quit"), this, SLOT(close()));
buttonsLayout = new QHBoxLayout;
buttonsLayout->addStretch();
buttonsLayout->addWidget(newScreenshotButton);
buttonsLayout->addWidget(saveScreenshotButton);
buttonsLayout->addWidget(quitScreenshotButton);
}
//! [8]
//! [9]
QPushButton *Screenshot::createButton(const QString &text, QWidget *receiver,
const char *member)
{
QPushButton *button = new QPushButton(text);
button->connect(button, SIGNAL(clicked()), receiver, member);
return button;
}
//! [9]
//! [10] //! [10]
void Screenshot::updateScreenshotLabel() void Screenshot::updateScreenshotLabel()

View File

@ -73,25 +73,14 @@ private slots:
void updateCheckBox(); void updateCheckBox();
private: private:
void createOptionsGroupBox();
void createButtonsLayout();
QPushButton *createButton(const QString &text, QWidget *receiver, const char *member);
void updateScreenshotLabel(); void updateScreenshotLabel();
QPixmap originalPixmap; QPixmap originalPixmap;
QLabel *screenshotLabel; QLabel *screenshotLabel;
QGroupBox *optionsGroupBox;
QSpinBox *delaySpinBox; QSpinBox *delaySpinBox;
QLabel *delaySpinBoxLabel;
QCheckBox *hideThisWindowCheckBox; QCheckBox *hideThisWindowCheckBox;
QPushButton *newScreenshotButton; QPushButton *newScreenshotButton;
QPushButton *saveScreenshotButton;
QPushButton *quitScreenshotButton;
QVBoxLayout *mainLayout;
QGridLayout *optionsGroupBoxLayout;
QHBoxLayout *buttonsLayout;
}; };
//! [0] //! [0]

View File

@ -33,7 +33,7 @@
desktop. desktop.
\brief The Screenshot example shows how to take a screenshot of the \brief The Screenshot example shows how to take a screenshot of the
desktop using QApplication and QDesktopWidget. It also shows how desktop using QScreen. It also shows how
to use QTimer to provide a single-shot timer, and how to to use QTimer to provide a single-shot timer, and how to
reimplement the QWidget::resizeEvent() event handler to make sure reimplement the QWidget::resizeEvent() event handler to make sure
that an application resizes smoothly and without data loss. that an application resizes smoothly and without data loss.
@ -73,12 +73,9 @@
\uicontrol {Hide This Window} option. \uicontrol {Hide This Window} option.
\endlist \endlist
We also declare some private functions: We use the \c We also declare the private function \c updateScreenshotLabel() which
createOptionsGroupBox(), \c createButtonsLayout() and \c is called whenever a new screenshot is taken or when a resize event
createButton() functions when we construct the widget. And we call changes the size of the screenshot preview label.
the private \c updateScreenshotLabel() function whenever a new
screenshot is taken or when a resize event changes the size of the
screenshot preview label.
In addition we need to store the screenshot's original pixmap. The In addition we need to store the screenshot's original pixmap. The
reason is that when we display the preview of the screenshot, we reason is that when we display the preview of the screenshot, we
@ -100,11 +97,18 @@
aligned in the center of the \c Screenshot widget, and set its aligned in the center of the \c Screenshot widget, and set its
minimum size. minimum size.
Next, we create a group box that will contain all of the options'
widgets. Then we create a QSpinBox and a QLabel for the \uicontrol
{Screenshot Delay} option, and connect the spinbox to the \c
updateCheckBox() slot. Finally, we create a QCheckBox for the \uicontrol
{Hide This Window} option, add all the options' widgets to a
QGridLayout installed on the group box.
We create the applications's buttons and the group box containing We create the applications's buttons and the group box containing
the application's options, and put it all into a main the application's options, and put it all into a main
layout. Finally we take the initial screenshot, and set the initial layout. Finally we take the initial screenshot, and set the initial
delay and the window title, before we resize the widget to a delay and the window title, before we resize the widget to a
suitable size. suitable size depending on the screen geometry.
\snippet desktop/screenshot/screenshot.cpp 1 \snippet desktop/screenshot/screenshot.cpp 1
@ -151,34 +155,37 @@
QFileDialog enables a user to traverse the file system in order to QFileDialog enables a user to traverse the file system in order to
select one or many files or a directory. The easiest way to create select one or many files or a directory. The easiest way to create
a QFileDialog is to use the convenience static a QFileDialog is to use the convenience static
functions. functions. Here, we instantiate the dialog on the stack in order
to be able to set up the supported mime types of QImageWriter,
allowing the user to save in a variety of formats.
We define the default file format to be png, and we make the file We define the default file format to be png, and we make the file
dialog's initial path the path the application is run from. We dialog's initial path the location of pictures as obtained from
create the file dialog using the static QStandardPaths, defaulting to the path the application is run from.
QFileDialog::getSaveFileName() function which returns a file name
selected by the user. The file does not have to exist. If the file We run the dialog by invoking QDialog::exec() and return if the
user canceled the dialog. If the dialog has been accepted, we
obtain a file name by calling QFileDialog::selectedFiles().
The file does not have to exist. If the file
name is valid, we use the QPixmap::save() function to save the name is valid, we use the QPixmap::save() function to save the
screenshot's original pixmap in that file. screenshot's original pixmap in that file.
\snippet desktop/screenshot/screenshot.cpp 4 \snippet desktop/screenshot/screenshot.cpp 4
The \c shootScreen() slot is called to take the screenshot. If the The \c shootScreen() slot is called to take the screenshot.
user has chosen to delay the screenshot, we make the application
First, we find the instance of QScreen the window is located
by retrieving the QWindow and its QScreen, defaulting
to the primary screen. If no screen can be found, we return.
Although this is unlikely to happen, applications should check
for null pointers since there might be situations in which no
screen is connected.
If the user has chosen to delay the screenshot, we make the application
beep when the screenshot is taken using the static beep when the screenshot is taken using the static
QApplication::beep() function. QApplication::beep() function.
The QApplication class manages the GUI application's control flow We then take the screenshot using the QScreen::grabWindow()
and main settings. It contains the main event loop, where all
events from the window system and other sources are processed and
dispatched.
\snippet desktop/screenshot/screenshot.cpp 5
Using the static function QApplication::primaryScreen(), we
obtain the QScreen object for the application's main screen.
We take the screenshot using the QScreen::grabWindow()
function. The function grabs the contents of the window passed as function. The function grabs the contents of the window passed as
an argument, makes a pixmap out of it and returns that pixmap. an argument, makes a pixmap out of it and returns that pixmap.
The window id can be obtained with QWidget::winId() or QWindow::winId(). The window id can be obtained with QWidget::winId() or QWindow::winId().
@ -200,37 +207,6 @@
The \c updateCheckBox() slot is called whenever the user changes The \c updateCheckBox() slot is called whenever the user changes
the delay using the \uicontrol {Screenshot Delay} option. the delay using the \uicontrol {Screenshot Delay} option.
\snippet desktop/screenshot/screenshot.cpp 7
The private \c createOptionsGroupBox() function is called from the
constructor.
First we create a group box that will contain all of the options'
widgets. Then we create a QSpinBox and a QLabel for the \uicontrol
{Screenshot Delay} option, and connect the spinbox to the \c
updateCheckBox() slot. Finally, we create a QCheckBox for the \uicontrol
{Hide This Window} option, add all the options' widgets to a
QGridLayout and install the layout on the group box.
Note that we don't have to specify any parents for the widgets
when we create them. The reason is that when we add a widget to a
layout and install the layout on another widget, the layout's
widgets are automatically reparented to the widget the layout is
installed on.
\snippet desktop/screenshot/screenshot.cpp 8
The private \c createButtonsLayout() function is called from the
constructor. We create the application's buttons using the private
\c createButton() function, and add them to a QHBoxLayout.
\snippet desktop/screenshot/screenshot.cpp 9
The private \c createButton() function is called from the \c
createButtonsLayout() function. It simply creates a QPushButton
with the provided text, connects it to the provided receiver and
slot, and returns a pointer to the button.
\snippet desktop/screenshot/screenshot.cpp 10 \snippet desktop/screenshot/screenshot.cpp 10
The private \c updateScreenshotLabel() function is called whenever The private \c updateScreenshotLabel() function is called whenever