スクリーンショットの撮り方

スクリーンショットの例では、デスクトップのスクリーンショットを撮る方法を紹介しています。

このアプリケーションで、ユーザーはデスクトップのスクリーンショットを撮ることができます。いくつかのオプションがあります:

  • スクリーンショットを遅延させ、デスクトップを再配置する時間を与える。
  • スクリーンショットを撮る間、アプリケーションのウィンドウを隠す。

さらに、スクリーンショットを保存することもできます。

スクリーンショット・クラスの定義

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

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

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

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

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

さらに、スクリーンショットの元のピクセルマップを保存する必要があります。スクリーンショットのプレビューを表示するときに、そのピクセルマップを拡大縮小する必要があるためで、元のピクセルマップを保存することで、その過程でデータが失われないようにします。

スクリーンショット・クラスの実装

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

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

QLabel のサイズ・ポリシーは、水平方向と垂直方向の両方でQSizePolicy::Expanding になるように設定します。これは、QLabel'のサイズ・ヒントが適切なサイズであることを意味しますが、ウィジェットは縮小しても有用です。また、ウィジェットは余分なスペースを利用することができるので、できるだけ多くのスペースを確保する必要があります。次に、QLabelScreenshot ウィジェットの中央に配置されていることを確認し、最小サイズを設定します。

次に、すべてのオプションのウィジェットを含むグループボックスを作成します。次に、Screenshot Delay オプション用にQSpinBoxQLabel を作成し、スピンボックスをupdateCheckBox() スロットに接続します。最後に、Hide This Window オプション用にQCheckBox を作成し、グループボックス上に設置したQGridLayout にすべてのオプションのウィジェットを追加します。

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

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

resizeEvent() 関数は、ウィジェットにディスパッチされたリサイズ・イベントを受け取るために再実装されています。その目的は、プレビュー・スクリーンショットのピクセルマップの内容を変形させずに拡大縮小することと、アプリケーションのサイズをスムーズに変更できるようにすることです。

最初の目的を達成するために、Qt::KeepAspectRatio を使用してスクリーンショットの pixmap を拡大縮小します。スクリーンショット・プレビュー・ラベルの現在のサイズ内で、縦横比を保ったまま、できるだけ大きな矩形にピクセルマップを拡大縮小します。これは、ユーザーがアプリケーション・ウィンドウのサイズを一方向にだけ変更しても、プレビュー・スクリーンショットが同じサイズを維持することを意味します。

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

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 クラスを使用してタイマーを作成します。タイマーを一度だけタイムアウトするように設定するには、staticQTimer::singleShot() 関数を使用します。この関数は、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つまたは複数のファイルやディレクトリを選択するために、ファイルシステムをトラバースすることができます。 を作成する最も簡単な方法は、便利な静的関数を使用することです。ここでは、 のサポートされる MIME タイプを設定できるように、スタック上でダイアログをインスタンス化し、ユーザーがさまざまな形式で保存できるようにしています。QFileDialog QImageWriter

デフォルトのファイル形式をpngに定義し、ファイルダイアログの初期パスをQStandardPaths から取得した画像の場所にします。

QDialog::exec ()を呼び出してダイアログを実行し、ユー ザーがダイアログをキャンセルした場合はリターンする。ダイアログが受け入れられた場合、QFileDialog::selectedFiles ()を呼び出してファイル名を取得する。このファイルは存在する必要はない。ファイル名が有効であれば、QPixmap::save ()関数を使用して、スクリーンショットの元のpixmapをそのファイルに保存します。

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

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

まず、QWindow とそのインスタンスであるQScreen を検索して、ウィンドウが配置されているQScreen のインスタンスを見つけます。スクリーンが見つからなければ、リターンする。このようなことが起こる可能性は低いが、スクリーンが接続されていない状況があるかもしれないので、アプリケーションはNULLポインタをチェックすべきである。

ユーザーがスクリーンショットを遅延させることを選択した場合、静的なQApplication::beep ()関数を使用して、スクリーンショットが撮影されるときにアプリケーションをビープ音で知らせます。

次に、QScreen::grabWindow ()関数を使用してスクリーンショットを撮影します。この関数は、引数として渡されたウィンドウの内容を取得し、そこからpixmapを作成し、そのpixmapを返します。ウィンドウ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()
{
    screenshotLabel->setPixmap(originalPixmap.scaled(screenshotLabel->size(),
                                                     Qt::KeepAspectRatio,
                                                     Qt::SmoothTransformation));
}

privateupdateScreenshotLabel() 関数は、スクリーンショットが変更されるとき、またはリサイズイベントによってスクリーンショットプレビューラベルのサイズが変更されるときに呼び出されます。この関数は、QLabel::setPixmap() およびQPixmap::scaled() 関数を使用して、スクリーンショットプレビューのラベルを更新します。

QPixmap::scaled() は、与えられた pixmap のコピーを、与えられたQt::AspectRatioModeQt::TransformationMode に従って、与えられたサイズの矩形に拡大縮小して返します。

元の pixmap を現在のスクリーンショットラベルのサイズに合うように拡大縮小し、アスペクト比を保持し、結果の pixmap のエッジを滑らかにします。

プロジェクト例 @ code.qt.io

©2024 The Qt Company Ltd. 本書に含まれる文書の著作権は、それぞれの所有者に帰属します。 本書で提供されるドキュメントは、Free Software Foundation が発行したGNU Free Documentation License version 1.3に基づいてライセンスされています。 Qtおよびそれぞれのロゴは、フィンランドおよびその他の国におけるThe Qt Company Ltd.の 商標です。その他すべての商標は、それぞれの所有者に帰属します。