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 <QDesktopWidget>
#include "screenshot.h"
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
Screenshot screenshot;
screenshot.move(QApplication::desktop()->availableGeometry(&screenshot).topLeft() + QPoint(20, 20));
screenshot.show();
return app.exec();
}

View File

@ -44,20 +44,48 @@
//! [0]
Screenshot::Screenshot()
: screenshotLabel(new QLabel(this))
{
screenshotLabel = new QLabel;
screenshotLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
screenshotLabel->setAlignment(Qt::AlignCenter);
screenshotLabel->setMinimumSize(240, 160);
createOptionsGroupBox();
createButtonsLayout();
const QRect screenGeometry = QApplication::desktop()->screenGeometry(this);
screenshotLabel->setMinimumSize(screenGeometry.width() / 8, screenGeometry.height() / 8);
mainLayout = new QVBoxLayout;
QVBoxLayout *mainLayout = new QVBoxLayout(this);
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);
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);
setLayout(mainLayout);
shootScreen();
delaySpinBox->setValue(5);
@ -84,44 +112,59 @@ void Screenshot::newScreenshot()
hide();
newScreenshotButton->setDisabled(true);
QTimer::singleShot(delaySpinBox->value() * 1000, this, SLOT(shootScreen()));
QTimer::singleShot(delaySpinBox->value() * 1000, this, &Screenshot::shootScreen);
}
//! [2]
//! [3]
void Screenshot::saveScreenshot()
{
QString format = "png";
QString initialPath = QDir::currentPath() + tr("/untitled.") + format;
const QString format = "png";
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,
tr("%1 Files (*.%2);;All Files (*)")
.arg(format.toUpper())
.arg(format));
if (!fileName.isEmpty())
originalPixmap.save(fileName, format.toLatin1().constData());
QFileDialog fileDialog(this, tr("Save As"), initialPath);
fileDialog.setAcceptMode(QFileDialog::AcceptSave);
fileDialog.setFileMode(QFileDialog::AnyFile);
fileDialog.setDirectory(initialPath);
QStringList mimeTypes;
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]
//! [4]
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();
if (screen)
originalPixmap = screen->grabWindow(0);
if (const QWindow *window = windowHandle())
screen = window->screen();
if (!screen)
return;
if (delaySpinBox->value() != 0)
QApplication::beep();
originalPixmap = screen->grabWindow(0);
updateScreenshotLabel();
newScreenshotButton->setDisabled(false);
if (hideThisWindowCheckBox->isChecked())
show();
}
//! [5]
//! [4]
//! [6]
void Screenshot::updateCheckBox()
@ -135,52 +178,6 @@ void Screenshot::updateCheckBox()
}
//! [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]
void Screenshot::updateScreenshotLabel()

View File

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

View File

@ -33,7 +33,7 @@
desktop.
\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
reimplement the QWidget::resizeEvent() event handler to make sure
that an application resizes smoothly and without data loss.
@ -73,12 +73,9 @@
\uicontrol {Hide This Window} option.
\endlist
We also declare some private functions: We use the \c
createOptionsGroupBox(), \c createButtonsLayout() and \c
createButton() functions when we construct the widget. And we call
the private \c updateScreenshotLabel() function whenever a new
screenshot is taken or when a resize event changes the size of the
screenshot preview label.
We also declare the private function \c updateScreenshotLabel() which
is called 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
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
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
the application's options, and put it all into a main
layout. Finally we take the initial screenshot, and set the initial
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
@ -151,34 +155,37 @@
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
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
dialog's initial path the path the application is run from. We
create the file dialog using the static
QFileDialog::getSaveFileName() function which returns a file name
selected by the user. The file does not have to exist. If the file
dialog's initial path the location of pictures as obtained from
QStandardPaths, defaulting to the path the application is run from.
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
screenshot's original pixmap in that file.
\snippet desktop/screenshot/screenshot.cpp 4
The \c shootScreen() slot is called to take the screenshot. If the
user has chosen to delay the screenshot, we make the application
The \c shootScreen() slot is called to take the screenshot.
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
QApplication::beep() function.
The QApplication class manages the GUI application's control flow
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()
We then take the screenshot using the QScreen::grabWindow()
function. The function grabs the contents of the window passed as
an argument, makes a pixmap out of it and returns that pixmap.
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 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
The private \c updateScreenshotLabel() function is called whenever