Einen Screenshot machen
Das Screenshot-Beispiel zeigt, wie man einen Screenshot des Desktops macht.
Mit dieser Anwendung können die Benutzer einen Screenshot ihres Desktops erstellen. Dabei stehen ihnen mehrere Optionen zur Verfügung:
- Verzögern des Screenshots, so dass der Benutzer Zeit hat, seinen Desktop neu anzuordnen.
- Ausblenden des Anwendungsfensters, während der Screenshot erstellt wird.
Außerdem ermöglicht die Anwendung den Benutzern, den Screenshot zu speichern, wenn sie dies wünschen.
Definition der Screenshot-Klasse
class Screenshot : public QWidget { Q_OBJECT public: Screenshot(); protected: void resizeEvent(QResizeEvent *event) override; private slots: void newScreenshot(); void saveScreenshot(); void shootScreen(); void updateCheckBox(); private: void updateScreenshotLabel(); QPixmap originalPixmap; QLabel *screenshotLabel; QSpinBox *delaySpinBox; QCheckBox *hideThisWindowCheckBox; QPushButton *newScreenshotButton; };
Die Klasse Screenshot
erbt QWidget und ist das Hauptwidget der Anwendung. Sie zeigt die Anwendungsoptionen und eine Vorschau des Screenshots an.
Wir reimplementieren die Funktion QWidget::resizeEvent(), um sicherzustellen, dass die Vorschau des Screenshots richtig skaliert wird, wenn der Benutzer die Größe des Anwendungs-Widgets ändert. Außerdem benötigen wir mehrere private Slots, um die Optionen zu erleichtern:
- Der Slot
newScreenshot()
bereitet einen neuen Screenshot vor. - Der Slot
saveScreenshot()
speichert den letzten Screenshot. - Der Slot
shootScreen()
nimmt den Screenshot auf. - Der Slot
updateCheckBox()
aktiviert oder deaktiviert die Option Hide This Window.
Wir deklarieren auch die private Funktion updateScreenshotLabel()
, die immer dann aufgerufen wird, wenn ein neuer Screenshot aufgenommen wird oder wenn ein Größenänderungsereignis die Größe des Screenshot-Vorschaulabels ändert.
Darüber hinaus müssen wir die ursprüngliche Pixmap des Screenshots speichern. Der Grund dafür ist, dass wir bei der Anzeige der Screenshot-Vorschau die Pixmap skalieren müssen. Durch die Speicherung des Originals stellen wir sicher, dass dabei keine Daten verloren gehen.
Implementierung der Screenshot-Klasse
Screenshot::Screenshot() : screenshotLabel(new QLabel(this)) { screenshotLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); screenshotLabel->setAlignment(Qt::AlignCenter); const QRect screenGeometry = screen()->geometry(); screenshotLabel->setMinimumSize(screenGeometry.width() / 8, screenGeometry.height() / 8); 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); connect(delaySpinBox, &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); shootScreen(); delaySpinBox->setValue(5); setWindowTitle(tr("Screenshot")); resize(300, 200); }
Im Konstruktor erstellen wir zunächst die QLabel, die die Screenshot-Vorschau anzeigt.
Wir setzen die Größenpolitik von QLabel auf QSizePolicy::Expanding sowohl horizontal als auch vertikal. Das bedeutet, dass der Größenhinweis von QLabel eine vernünftige Größe hat, aber das Widget kann verkleinert werden und ist trotzdem nützlich. Außerdem kann das Widget zusätzlichen Platz nutzen, also sollte es so viel Platz wie möglich bekommen. Dann stellen wir sicher, dass QLabel in der Mitte des Widgets Screenshot
ausgerichtet ist, und legen seine Mindestgröße fest.
Als Nächstes erstellen wir einen Gruppenrahmen, der alle Widgets der Optionen enthalten wird. Dann erstellen wir ein QSpinBox und ein QLabel für die Option Screenshot Delay und verbinden die Spinbox mit dem Steckplatz updateCheckBox()
. Schließlich erstellen wir ein QCheckBox für die Option Hide This Window und fügen alle Widgets der Optionen in ein QGridLayout ein, das auf dem Gruppenrahmen installiert ist.
Wir erstellen die Schaltflächen der Anwendungen und den Gruppenrahmen, der die Optionen der Anwendung enthält, und fügen alles in ein Hauptlayout ein. Schließlich machen wir den ersten Screenshot und stellen die Anfangsverzögerung und den Fenstertitel ein, bevor wir die Größe des Widgets auf eine geeignete Größe je nach Bildschirmgeometrie anpassen.
void Screenshot::resizeEvent(QResizeEvent * /* event */) { QSize scaledSize = originalPixmap.size(); scaledSize.scale(screenshotLabel->size(), Qt::KeepAspectRatio); if (scaledSize != screenshotLabel->pixmap().size()) updateScreenshotLabel(); }
Die Funktion resizeEvent()
wird neu implementiert, um die Größenänderungsereignisse zu empfangen, die an das Widget gesendet werden. Ziel ist es, die Pixmap des Vorschaubildschirms zu skalieren, ohne den Inhalt zu deformieren, und sicherzustellen, dass die Größe der Anwendung reibungslos geändert werden kann.
Um das erste Ziel zu erreichen, skalieren wir die Screenshot-Pixmap mit Qt::KeepAspectRatio. Wir skalieren die Pixmap auf ein Rechteck, das so groß wie möglich innerhalb der aktuellen Größe des Screenshot-Vorschaulabels ist, wobei das Seitenverhältnis erhalten bleibt. Das heißt, wenn der Benutzer die Größe des Anwendungsfensters nur in eine Richtung ändert, behält der Vorschaubildschirm dieselbe Größe.
Um unser zweites Ziel zu erreichen, stellen wir sicher, dass der Vorschau-Screenshot nur dann neu gezeichnet wird (mit der privaten Funktion updateScreenshotLabel()
), wenn er tatsächlich seine Größe ändert.
void Screenshot::newScreenshot() { if (hideThisWindowCheckBox->isChecked()) hide(); newScreenshotButton->setDisabled(true); QTimer::singleShot(delaySpinBox->value() * 1000, this, &Screenshot::shootScreen); }
Der private Slot newScreenshot()
wird aufgerufen, wenn der Benutzer einen neuen Screenshot anfordert; der Slot bereitet aber nur einen neuen Screenshot vor.
Zuerst wird geprüft, ob die Option Hide This Window aktiviert ist, und wenn ja, wird das Widget Screenshot
ausgeblendet. Dann deaktivieren wir die Schaltfläche New Screenshot, um sicherzustellen, dass der Benutzer nur einen Screenshot auf einmal anfordern kann.
Wir erstellen einen Timer mit der Klasse QTimer, die Zeitmesser für wiederholte und einmalige Aufnahmen bietet. Wir stellen den Timer so ein, dass er nur einmal abläuft, indem wir die statische Funktion QTimer::singleShot() verwenden. Diese Funktion ruft den privaten Slot shootScreen()
nach dem in der Option Screenshot Delay angegebenen Zeitintervall auf. Der eigentliche Screenshot wird von shootScreen()
ausgeführt.
void Screenshot::saveScreenshot() { const QString format = "png"; QString initialPath = QStandardPaths::writableLocation(QStandardPaths::PicturesLocation); if (initialPath.isEmpty()) initialPath = QDir::currentPath(); initialPath += tr("/untitled.") + format; QFileDialog fileDialog(this, tr("Save As"), initialPath); fileDialog.setAcceptMode(QFileDialog::AcceptSave); fileDialog.setFileMode(QFileDialog::AnyFile); fileDialog.setDirectory(initialPath); QStringList mimeTypes; const QList<QByteArray> baMimeTypes = QImageWriter::supportedMimeTypes(); for (const QByteArray &bf : baMimeTypes) 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))); } }
Der Slot saveScreenshot()
wird aufgerufen, wenn der Benutzer auf die Schaltfläche Save drückt, und es wird ein Dateidialog mithilfe der Klasse QFileDialog angezeigt.
QFileDialog ermöglicht es dem Benutzer, das Dateisystem zu durchsuchen, um eine oder mehrere Dateien oder ein Verzeichnis auszuwählen. Der einfachste Weg, ein QFileDialog zu erstellen, ist die Verwendung der statischen Komfortfunktionen. Hier instanziieren wir den Dialog auf dem Stack, um die unterstützten Mime-Typen von QImageWriter einrichten zu können, so dass der Benutzer in einer Vielzahl von Formaten speichern kann.
Das Standard-Dateiformat ist png, und der anfängliche Pfad des Dateidialogs ist der Speicherort der Bilder, wie er von QStandardPaths abgerufen wird, wobei standardmäßig der Pfad verwendet wird, von dem aus die Anwendung ausgeführt wird.
Wir führen den Dialog durch den Aufruf von QDialog::exec() aus und geben zurück, wenn der Benutzer den Dialog abgebrochen hat. Wenn der Dialog akzeptiert wurde, erhalten wir einen Dateinamen durch den Aufruf von QFileDialog::selectedFiles(). Die Datei muss nicht unbedingt existieren. Wenn der Dateiname gültig ist, verwenden wir die Funktion QPixmap::save(), um die ursprüngliche Pixmap des Screenshots in dieser Datei zu speichern.
void Screenshot::shootScreen() { QScreen *screen = QGuiApplication::primaryScreen(); 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(); }
Der Slot shootScreen()
wird aufgerufen, um das Bildschirmfoto aufzunehmen.
Zunächst wird die Instanz von QScreen gefunden, in der sich das Fenster befindet, indem QWindow und dessen QScreen abgerufen werden, wobei standardmäßig der primäre Bildschirm verwendet wird. Wenn kein Bildschirm gefunden werden kann, kehren wir zurück. Obwohl dies unwahrscheinlich ist, sollten Anwendungen auf Null-Zeiger prüfen, da es Situationen geben kann, in denen kein Bildschirm angeschlossen ist.
Wenn der Benutzer den Screenshot verzögern möchte, lassen wir die Anwendung mit der statischen Funktion QApplication::beep() einen Piepton ausgeben, wenn der Screenshot aufgenommen wird.
Anschließend wird der Screenshot mit der Funktion QScreen::grabWindow() aufgenommen. Die Funktion erfasst den Inhalt des als Argument übergebenen Fensters, erstellt daraus eine Pixmap und gibt diese Pixmap zurück. Die Fensterkennung kann mit QWidget::winId() oder QWindow::winId() ermittelt werden. Hier geben wir jedoch einfach 0 als Fensterkennung an, was bedeutet, dass wir den gesamten Bildschirm erfassen wollen.
Wir aktualisieren die Beschriftung der Screenshot-Vorschau mit der privaten Funktion updateScreenshotLabel()
. Dann aktivieren wir die Schaltfläche New Screenshot, und schließlich machen wir das Widget Screenshot
sichtbar, falls es während des Screenshots ausgeblendet war.
void Screenshot::updateCheckBox() { if (delaySpinBox->value() == 0) { hideThisWindowCheckBox->setDisabled(true); hideThisWindowCheckBox->setChecked(false); } else { hideThisWindowCheckBox->setDisabled(false); } }
Die Option Hide This Window wird abhängig von der Verzögerung des Screenshots aktiviert oder deaktiviert. Wenn es keine Verzögerung gibt, kann das Anwendungsfenster nicht ausgeblendet werden und das Kontrollkästchen der Option ist deaktiviert.
Der Slot updateCheckBox()
wird immer dann aufgerufen, wenn der Benutzer die Verzögerung über die Option Screenshot Delay ändert.
void Screenshot::updateScreenshotLabel() { screenshotLabel->setPixmap(originalPixmap.scaled(screenshotLabel->size(), Qt::KeepAspectRatio, Qt::SmoothTransformation)); }
Die private Funktion updateScreenshotLabel()
wird immer dann aufgerufen, wenn sich der Screenshot ändert oder wenn ein Größenänderungsereignis die Größe des Labels für die Screenshot-Vorschau ändert. Sie aktualisiert die Beschriftung der Screenshot-Vorschau mithilfe der Funktionen QLabel::setPixmap() und QPixmap::scaled().
QPixmap::scaled() gibt eine Kopie der angegebenen Pixmap zurück, die auf ein Rechteck der angegebenen Größe skaliert ist, entsprechend den Angaben auf Qt::AspectRatioMode und Qt::TransformationMode.
Wir skalieren die ursprüngliche Pixmap auf die Größe des aktuellen Screenshot-Labels, wobei das Seitenverhältnis erhalten bleibt und die resultierende Pixmap geglättete Kanten erhält.
© 2025 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.