Sur cette page

Réalisation d'une capture d'écran

L'exemple de capture d'écran montre comment faire une capture d'écran du bureau.

Application avec options pour prendre des captures d'écran du bureau

L'application permet aux utilisateurs de prendre une capture d'écran de leur bureau. Plusieurs options leur sont proposées :

  • retarder la capture d'écran, ce qui leur donne le temps de réorganiser leur bureau.
  • Masquer la fenêtre de l'application pendant que la capture d'écran est effectuée.

En outre, l'application permet aux utilisateurs de sauvegarder leur capture d'écran s'ils le souhaitent.

Définition de la classe Capture d'écran

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;
    QPushButton *saveScreenshotButton;
};

La classe Screenshot hérite de QWidget et constitue le widget principal de l'application. Elle affiche les options de l'application et un aperçu de la capture d'écran.

Nous réimplémentons la fonction QWidget::resizeEvent() pour nous assurer que l'aperçu de la capture d'écran s'adapte correctement lorsque l'utilisateur redimensionne le widget de l'application. Nous avons également besoin de plusieurs emplacements privés pour faciliter les options :

  • Le slot newScreenshot() prépare une nouvelle capture d'écran.
  • L'emplacement saveScreenshot() enregistre la dernière capture d'écran.
  • L'emplacement shootScreen() prend la capture d'écran.
  • L'emplacement updateCheckBox() active ou désactive l'option Hide This Window.

Nous déclarons également la fonction privée updateScreenshotLabel() qui est appelée chaque fois qu'une nouvelle capture d'écran est réalisée ou qu'un événement de redimensionnement modifie la taille de l'étiquette de prévisualisation de la capture d'écran.

En outre, nous devons stocker la pixmap originale de la capture d'écran. En effet, lorsque nous affichons l'aperçu de la capture d'écran, nous devons mettre à l'échelle sa pixmap ; en stockant l'original, nous nous assurons qu'aucune donnée n'est perdue au cours de ce processus.

Mise en œuvre de la classe Capture d'écran

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);
    screenshotLabel->setFrameShape(QFrame::Box);

    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);
    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);
}

Dans le constructeur, nous créons d'abord le site QLabel qui affiche l'aperçu de la capture d'écran.

Nous définissons la politique de taille de QLabel comme étant QSizePolicy::Expanding à la fois horizontalement et verticalement. Cela signifie que l'indice de taille de QLabel est une taille raisonnable, mais que le widget peut être réduit et rester utile. De plus, le widget peut utiliser de l'espace supplémentaire, il doit donc disposer d'autant d'espace que possible. Ensuite, nous nous assurons que le site QLabel est aligné au centre du widget Screenshot et nous définissons sa taille minimale.

Ensuite, nous créons un cadre de groupe qui contiendra tous les widgets des options. Nous créons ensuite un QSpinBox et un QLabel pour l'option Screenshot Delay, et nous connectons la spinbox à l'emplacement updateCheckBox(). Enfin, nous créons un QCheckBox pour l'option Hide This Window, ajoutons tous les widgets des options à un QGridLayout installé sur le group box.

Nous créons les boutons de l'application et la boîte de groupe contenant les options de l'application, et nous plaçons le tout dans une disposition principale. Enfin, nous prenons la capture d'écran initiale et définissons le délai initial et le titre de la fenêtre, avant de redimensionner le widget à une taille appropriée en fonction de la géométrie de l'écran.

void Screenshot::resizeEvent(QResizeEvent * /* event */)
{
    if (!originalPixmap.isNull()) {
        QSize scaledSize = originalPixmap.size();
        scaledSize.scale(screenshotLabel->size(), Qt::KeepAspectRatio);
        if (scaledSize != screenshotLabel->pixmap().size())
            updateScreenshotLabel();
    }
}

La fonction resizeEvent() est réimplémentée pour recevoir les événements de redimensionnement envoyés au widget. L'objectif est de mettre à l'échelle la pixmap de la capture d'écran de prévisualisation sans déformer son contenu, et de s'assurer que l'application peut être redimensionnée en douceur.

Pour atteindre le premier objectif, nous mettons à l'échelle la pixmap de la capture d'écran à l'aide de Qt::KeepAspectRatio. Nous redimensionnons la pixmap en un rectangle aussi grand que possible à l'intérieur de la taille actuelle de l'étiquette de prévisualisation de la capture d'écran, en préservant le rapport hauteur/largeur. Cela signifie que si l'utilisateur redimensionne la fenêtre de l'application dans une seule direction, l'aperçu de la capture d'écran conserve la même taille.

Pour atteindre notre deuxième objectif, nous nous assurons que la capture d'écran de prévisualisation n'est repeinte (à l'aide de la fonction privée updateScreenshotLabel() ) que lorsqu'elle change réellement de taille.

void Screenshot::newScreenshot()
{
    if (hideThisWindowCheckBox->isChecked())
        hide();
    newScreenshotButton->setDisabled(true);

    QTimer::singleShot(delaySpinBox->value() * 1000, this, &Screenshot::shootScreen);
}

Le slot privé newScreenshot() est appelé lorsque l'utilisateur demande une nouvelle capture d'écran ; mais le slot ne fait que préparer une nouvelle capture d'écran.

Nous commençons par vérifier si l'option Hide This Window est cochée. Si c'est le cas, nous masquons le widget Screenshot. Ensuite, nous désactivons le bouton New Screenshot, pour nous assurer que l'utilisateur ne peut demander qu'une seule capture d'écran à la fois.

Nous créons un minuteur à l'aide de la classe QTimer qui fournit des minuteurs répétitifs et des minuteurs pour une seule capture d'écran. Nous réglons la minuterie pour qu'elle ne s'arrête qu'une seule fois, à l'aide de la fonction statique QTimer::singleShot(). Cette fonction appelle le slot privé shootScreen() après l'intervalle de temps spécifié par l'option Screenshot Delay. C'est shootScreen() qui réalise la capture d'écran.

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)));
    }
}

Le slot saveScreenshot() est appelé lorsque l'utilisateur appuie sur le bouton Save et présente une boîte de dialogue de fichier à l'aide de la classe QFileDialog.

QFileDialog La classe permet à l'utilisateur de parcourir le système de fichiers afin de sélectionner un ou plusieurs fichiers ou un répertoire. La manière la plus simple de créer une boîte de dialogue QFileDialog est d'utiliser les fonctions statiques de commodité. Ici, nous instancions la boîte de dialogue sur la pile afin de pouvoir configurer les types mime pris en charge par QImageWriter, ce qui permet à l'utilisateur d'enregistrer dans une variété de formats.

Nous définissons le format de fichier par défaut comme étant le format png, et nous faisons en sorte que le chemin initial de la boîte de dialogue soit l'emplacement des images tel qu'il est obtenu à partir de QStandardPaths, par défaut le chemin à partir duquel l'application est exécutée.

Nous lançons le dialogue en invoquant QDialog::exec() et nous retournons si l'utilisateur a annulé le dialogue. Si le dialogue a été accepté, nous obtenons un nom de fichier en appelant QFileDialog::selectedFiles(). Le fichier ne doit pas nécessairement exister. Si le nom du fichier est valide, nous utilisons la fonction QPixmap::save() pour enregistrer le pixmap original de la capture d'écran dans ce fichier.

void Screenshot::shootScreen()
{
    if (delaySpinBox->value() != 0)
        QApplication::beep();

    originalPixmap = screen()->grabWindow(0);
    updateScreenshotLabel();

    newScreenshotButton->setDisabled(false);
    if (hideThisWindowCheckBox->isChecked())
        show();
}

Le slot shootScreen() est appelé pour prendre la capture d'écran.

Si l'utilisateur a choisi de retarder la capture d'écran, l'application émet un bip lorsque la capture d'écran est réalisée à l'aide de la fonction statique QApplication::beep().

Nous effectuons ensuite la capture d'écran à l'aide de la fonction QScreen::grabWindow() sur l'écran renvoyé par QWidget::screen(). La fonction saisit le contenu de la fenêtre passée en argument, en fait un pixmap et renvoie ce pixmap. L'identifiant de la fenêtre peut être obtenu avec QWidget::winId() ou QWindow::winId(). Ici, cependant, nous passons simplement 0 comme identifiant de la fenêtre, ce qui indique que nous voulons capturer l'ensemble de l'écran.

Nous mettons à jour l'étiquette de prévisualisation de la capture d'écran à l'aide de la fonction privée updateScreenshotLabel(). Ensuite, nous activons le bouton New Screenshot et, enfin, nous rendons visible le widget Screenshot s'il a été caché pendant la capture d'écran.

void Screenshot::updateCheckBox()
{
    if (delaySpinBox->value() == 0) {
        hideThisWindowCheckBox->setDisabled(true);
        hideThisWindowCheckBox->setChecked(false);
    } else {
        hideThisWindowCheckBox->setDisabled(false);
    }
}

L'option Hide This Window est activée ou désactivée en fonction du délai de la capture d'écran. S'il n'y a pas de délai, la fenêtre de l'application ne peut pas être cachée et la case à cocher de l'option est désactivée.

Le slot updateCheckBox() est appelé chaque fois que l'utilisateur modifie le délai à l'aide de l'option Screenshot Delay.

void Screenshot::updateScreenshotLabel()
{
    if (originalPixmap.isNull()) {
        saveScreenshotButton->setEnabled(false);
        screenshotLabel->setText(tr("Grabbing \"%1\" failed.").arg(screen()->name()));
    } else {
        saveScreenshotButton->setEnabled(true);
        screenshotLabel->setPixmap(originalPixmap.scaled(screenshotLabel->size(),
                                                         Qt::KeepAspectRatio,
                                                         Qt::SmoothTransformation));
    }
}

La fonction privée updateScreenshotLabel() est appelée chaque fois que la capture d'écran change ou qu'un événement de redimensionnement modifie la taille de l'étiquette de prévisualisation de la capture d'écran.

Nous commençons par traiter les cas d'échec. Sur certaines plateformes, notamment Wayland, il n'est pas possible de récupérer le contenu de l'écran. Dans ce cas, la pixmap est vide, comme l'indique QPixmap::isNull(). Nous plaçons une chaîne d'erreur sur l'étiquette et désactivons le bouton d'enregistrement.

Si une pixmap a été obtenue, nous mettons à jour l'étiquette de l'aperçu de la capture d'écran à l'aide des fonctions QLabel::setPixmap() et QPixmap::scaled().

QPixmap::scaled() renvoie une copie de la pixmap donnée, mise à l'échelle dans un rectangle de la taille donnée, conformément à Qt::AspectRatioMode et Qt::TransformationMode.

Nous mettons à l'échelle la pixmap originale pour l'adapter à la taille de l'étiquette de la capture d'écran actuelle, en préservant le rapport hauteur/largeur et en donnant à la pixmap résultante des bords lissés.

Exemple de projet @ code.qt.io

© 2026 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.