截屏
“屏幕截图”示例演示了如何截取桌面屏幕截图。

通过该应用程序,用户可以截取桌面屏幕截图。系统提供了以下几种选项:
- 延迟截图,以便用户有时间调整桌面布局。
- 在截图过程中隐藏应用程序窗口。
此外,该应用程序还允许用户根据需要保存截图。
截图类定义
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()
: 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 ”控件中居中对齐,并设置其最小尺寸。
接下来,我们创建一个组框(group box)来容纳所有选项的小部件。然后,针对“Screenshot Delay ”选项,我们创建一个旋转框(QSpinBox )和一个旋转框(QLabel ),并将旋转框连接到updateCheckBox() 的槽(slot)。最后,针对“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` 对截图位图进行缩放。我们将位图缩放为在当前截图预览标签尺寸范围内尽可能大的矩形,同时保持其纵横比。这意味着,如果用户仅沿一个方向调整应用程序窗口的大小,预览截图将保持原尺寸不变。
为了实现第二个目标,我们确保只有当预览截图实际改变尺寸时,才会(通过私有函数 `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 按钮,以确保用户每次只能请求一张截图。
我们使用QTimer 类创建一个定时器,该类提供了循环定时器和单次定时器。我们通过静态函数QTimer::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)));
}
}当用户点击Save 按钮时,会调用saveScreenshot() 槽,该槽会通过QFileDialog 类弹出文件对话框。
QFileDialog 该类允许用户浏览文件系统,以选择一个或多个文件,或一个目录。创建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() 进行截图时,让应用程序发出蜂鸣声。
随后,我们使用 `QScreen::grabWindow()` 函数对 `QWidget::screen()` 返回的屏幕内容进行截图。 该函数会捕获作为参数传递的窗口内容,将其转换为像素图并返回该像素图。窗口 ID 可通过 `QWidget::winId()` 或 `QWindow::winId()` 获取。不过在此处,我们仅将 0 作为窗口 ID 传递,表示要捕获整个屏幕。
我们使用私有函数updateScreenshotLabel() 更新屏幕截图预览标签。然后启用New Screenshot 按钮,最后若Screenshot 小部件在截图过程中被隐藏,则将其显示出来。
void Screenshot::updateCheckBox()
{
if (delaySpinBox->value() == 0) {
hideThisWindowCheckBox->setDisabled(true);
hideThisWindowCheckBox->setChecked(false);
} else {
hideThisWindowCheckBox->setDisabled(false);
}
}Hide This Window 选项的启用与否取决于截图的延迟设置。如果不设置延迟,应用程序窗口将无法被隐藏,且该选项的复选框将被禁用。
每当用户通过Screenshot Delay 选项更改延迟时,都会调用updateCheckBox() 插槽。
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() 所示,像素图为空。我们为标签设置一条错误字符串,并禁用保存按钮。
如果成功获取了 pixmap,我们将使用QLabel::setPixmap() 和QPixmap::scaled() 函数更新截图预览的标签。
QPixmap::scaled() 函数会返回给定位图的副本,该副本根据给定的Qt::AspectRatioMode 和Qt::TransformationMode 参数,被缩放为指定大小的矩形。
我们将原始位图缩放以适应当前截图标签的大小,同时保持宽高比,并使生成的位图边缘平滑。
© 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.