このページについて

スクリーンショットの撮影

「スクリーンショット」の例では、デスクトップのスクリーンショットを撮る方法について説明しています。

デスクトップのスクリーンショットを撮影する機能を備えたアプリケーション

このアプリケーションを使用すると、ユーザーはデスクトップのスクリーンショットを撮影できます。ユーザーには以下のオプションが提供されています:

  • スクリーンショットの撮影を遅延させ、デスクトップの配置を整える時間を確保する。
  • スクリーンショットの撮影中は、アプリケーションのウィンドウを非表示にする。

さらに、このアプリケーションでは、ユーザーが希望する場合、スクリーンショットを保存することもできます。

Screenshotクラスの定義

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

Screenshot クラスはQWidget を継承しており、アプリケーションのメインウィジェットです。このクラスは、アプリケーションのオプションとスクリーンショットのプレビューを表示します。

ユーザーがアプリケーションウィジェットのサイズを変更した際に、スクリーンショットのプレビューが適切に拡大縮小されるよう、QWidget::resizeEvent() 関数を再実装しています。また、オプションを扱うためにいくつかのプライベートスロットも必要です:

  • newScreenshot() スロットは、新しいスクリーンショットを準備します。
  • saveScreenshot() スロットは、最新のスクリーンショットを保存します。
  • shootScreen() スロットは、スクリーンショットを撮影します。
  • updateCheckBox() スロットは、Hide This Window オプションを有効または無効にします。

また、新しいスクリーンショットが撮影されたとき、またはリサイズイベントによってスクリーンショットプレビューラベルのサイズが変更されたときに呼び出されるプライベート関数updateScreenshotLabel() も宣言します。

さらに、スクリーンショットの元のピクマップを保存する必要があります。その理由は、スクリーンショットのプレビューを表示する際にピクマップを拡大縮小する必要があるためであり、元のピクマップを保存しておくことで、その過程でデータが失われるのを防ぐことができるからです。

Screenshotクラスの実装

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

コンストラクタでは、まずスクリーンショットのプレビューを表示するQLabel を作成します。

QLabel のサイズポリシーを、水平・垂直方向ともにQSizePolicy::Expanding に設定します。これは、QLabel のサイズヒントが妥当なサイズである一方で、ウィジェットを縮小しても依然として有用に機能することを意味します。 また、このウィジェットは余分なスペースを活用できるため、可能な限り多くのスペースを確保すべきです。次に、QLabel がScreenshot ウィジェットの中央に配置されるようにし、その最小サイズを設定します。

次に、すべてのオプションウィジェットを格納するグループボックスを作成します。その後、Screenshot Delay オプション用にQSpinBox とQLabel を作成し、スピンボックスをupdateCheckBox() のスロットに接続します。最後に、Hide This Window オプション用にQCheckBox を作成し、グループボックスに配置されたQGridLayout にすべてのオプションウィジェットを追加します。

アプリケーションのボタンと、アプリケーションのオプションを含むグループボックスを作成し、それらすべてをメインレイアウトに配置します。最後に、初期のスクリーンショットを取得し、初期遅延とウィンドウタイトルを設定してから、画面のジオメトリに応じてウィジェットを適切なサイズにリサイズします。

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

resizeEvent() 関数は、ウィジェットにディスパッチされるリサイズイベントを受け取るように再実装されます。その目的は、プレビュースクリーンショットのピクマップを、その内容を歪めることなくスケーリングすること、およびアプリケーションがスムーズにリサイズできるようにすることです。

最初の目標を達成するために、Qt::KeepAspectRatio を使用してスクリーンショットのピクマップをスケーリングします。アスペクト比を維持しつつ、スクリーンショットプレビューラベルの現在のサイズ内で可能な限り大きな矩形にピクマップをスケーリングします。これにより、ユーザーがアプリケーションウィンドウを一方の方向にのみリサイズしても、プレビュースクリーンショットのサイズは変わりません。

2つ目の目標を達成するために、プレビュースクリーンショットのサイズが実際に変更された場合にのみ、(プライベート関数updateScreenshotLabel() を使用して)プレビュースクリーンショットのみを再描画するようにしています。

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

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

プライベートなnewScreenshot() スロットは、ユーザーが新しいスクリーンショットを要求した際に呼び出されますが、このスロットは新しいスクリーンショットの準備を行うのみです。

まず、「Hide This Window 」オプションがチェックされているかを確認し、チェックされている場合は「Screenshot 」ウィジェットを非表示にします。次に、「New Screenshot 」ボタンを無効化し、ユーザーが一度に1つのスクリーンショットしかリクエストできないようにします。

QTimer クラスを使用してタイマーを作成します。このクラスには、繰り返し実行型および単発型のタイマーが用意されています。静的関数QTimer::singleShot()を使用して、タイマーが1回だけタイムアウトするように設定します。この関数は、Screenshot Delay オプションで指定された時間間隔が経過した後、プライベートスロットshootScreen() を呼び出します。実際にスクリーンショットを撮影するのはshootScreen() です。

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

saveScreenshot() スロットは、ユーザーがSave ボタンを押したときに呼び出され、QFileDialog クラスを使用してファイルダイアログを表示します。

QFileDialog これにより、ユーザーはファイルシステムを閲覧して、1つまたは複数のファイル、あるいはディレクトリを選択することができます。QFileDialog を作成する最も簡単な方法は、便利な静的関数を使用することです。ここでは、QImageWriter がサポートする MIME タイプを設定し、ユーザーがさまざまな形式で保存できるようにするために、スタック上でダイアログをインスタンス化します。

デフォルトのファイル形式を png と定義し、ファイルダイアログの初期パスをQStandardPaths から取得した画像の保存場所に設定します。デフォルトでは、アプリケーションが実行されているパスになります。

QDialog::exec()を呼び出してダイアログを表示し、ユーザーがダイアログをキャンセルした場合は処理を終了します。ダイアログが承認された場合は、QFileDialog::selectedFiles()を呼び出してファイル名を取得します。ファイルは存在していなくても構いません。ファイル名が有効な場合、QPixmap::save()関数を使用して、スクリーンショットの元のピクマップをそのファイルに保存します。

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

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

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

スクリーンショットを撮影するには、shootScreen() スロットが呼び出されます。

ユーザーがスクリーンショットの遅延を選択している場合、静的関数 `QApplication::beep()` を使用してスクリーンショットを撮影する際、アプリケーションにビープ音を鳴らします。

その後、QWidget::screen() が返す画面に対して、QScreen::grabWindow() 関数を使用してスクリーンショットを撮影します。 この関数は、引数として渡されたウィンドウの内容を取得し、それをピクマップに変換して返します。ウィンドウIDは、QWidget::winId() またはQWindow::winId() で取得できます。ただしここでは、画面全体を取得したいことを示すため、ウィンドウIDとして単に 0 を渡します。

非公開関数updateScreenshotLabel() を使用して、スクリーンショットのプレビューラベルを更新します。次に、New Screenshot ボタンを有効にし、最後に、スクリーンショット撮影中に非表示になっていた場合は、Screenshot ウィジェットを表示します。

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

Hide This Window オプションは、スクリーンショットの遅延設定に応じて有効または無効になります。遅延がない場合、アプリケーションウィンドウを非表示にすることはできず、このオプションのチェックボックスは無効になります。

updateCheckBox() スロットは、ユーザーが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));
    }
}

プライベート関数 `updateScreenshotLabel() ` は、スクリーンショットが変更されたとき、またはリサイズイベントによってスクリーンショットプレビューラベルのサイズが変更されたときに呼び出されます。

まず、失敗時の処理を行います。Wayland などの一部のプラットフォームでは、画面コンテンツを取得することができません。この場合、QPixmap::isNull() が示すように、ピクマップは空になります。ラベルにエラー文字列を設定し、保存ボタンを無効にします。

ピクマップが取得できた場合は、QLabel::setPixmap() およびQPixmap::scaled() 関数を使用して、スクリーンショットプレビューのラベルを更新します。

QPixmap::scaled() は、指定されたQt::AspectRatioMode およびQt::TransformationMode に基づいて、指定されたサイズの矩形にスケーリングされた、指定されたピクマップのコピーを返します。

元のピクマップを、アスペクト比を維持し、結果のピクマップのエッジを滑らかにしながら、現在のスクリーンショットラベルのサイズに合うようにスケーリングします。

サンプルプロジェクト @ 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.