screen.h
#ifndef SCREEN_H
#define SCREEN_H
#include
class QLabel;
class QSpinBox;
class QCheckBox;
class QPushButton;
class Screen : public QWidget
{
Q_OBJECT
public:
Screen(QWidget *parent = nullptr);
~Screen();
protected:
void resizeEvent(QResizeEvent* event) override;
/*
这个事件处理程序可以在子类中重新实现,以接收在event参数中传递的小部件调整大小事件。
当调用resizeEvent()时,小部件已经有了新的几何形状。旧的size可以通过QResizeEvent::oldsizeO访问。
在处理调整大小事件后,小部件将被擦除并接收一个paint事件。在这个处理程序中不需要(或应该)绘制任何图形。
*/
private slots:
void newScreenshot();//准备了一个新的截图
void saveScreenshot();//保存最后一个截图
void shootScreen();//截屏
void updateCheckBox();//启用或禁用隐藏此窗口选项。
private:
void updateScreenshotLabel();
QPixmap originalPixmap;
QLabel* screenshotLabel;
QSpinBox* delaySpinBox;
QCheckBox* hideThisWindowCheckBox;
QPushButton* newScreenshotButton;
};
#endif // SCREEN_H
screen.cpp
#include "screen.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
Screen::Screen(QWidget* parent)
: screenshotLabel(new QLabel(this))
{
screenshotLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
screenshotLabel->setAlignment(Qt::AlignCenter);//设置对齐方式
const QRect screenGeometry = screen()->geometry();
/*
* screen返回小部件所在的屏幕
*
*/
screenshotLabel->setMinimumSize(screenGeometry.width() / 8, screenGeometry.height() / 8);
QVBoxLayout* mainLayout = new QVBoxLayout(this);
mainLayout->addWidget(screenshotLabel);
QGroupBox* optionsGroupBox = new QGroupBox(QStringLiteral("设置"), this);
delaySpinBox = new QSpinBox(optionsGroupBox);
delaySpinBox->setSuffix(tr(" s"));
delaySpinBox->setMaximum(60);
connect(delaySpinBox, QOverload::of(&QSpinBox::valueChanged),
this, &Screen::updateCheckBox);
hideThisWindowCheckBox = new QCheckBox(QStringLiteral("隐藏窗口"), optionsGroupBox);
QGridLayout* optionsGroupBoxLayout = new QGridLayout(optionsGroupBox);
optionsGroupBoxLayout->addWidget(new QLabel(QStringLiteral("截图延迟:"),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(QStringLiteral("新截图"), this);
connect(newScreenshotButton, &QPushButton::clicked, this, &Screen::newScreenshot);
buttonsLayout->addWidget(newScreenshotButton);
QPushButton* saveScreenshotButton = new QPushButton(QStringLiteral("保存截图"), this);
connect(saveScreenshotButton, &QPushButton::clicked, this, &Screen::saveScreenshot);
buttonsLayout->addWidget(saveScreenshotButton);
QPushButton* quitScreenshotButton = new QPushButton(QStringLiteral("退出"), 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(QStringLiteral("截屏"));
resize(300, 200);
}
Screen::~Screen()
{
}
void Screen::resizeEvent(QResizeEvent* event)
{
/*
* scale()
*这是一个重载函数。根据指定的模式,将大小缩放为具有给定大小的矩形。
*Qt::KeepAspectRatio
*在给定的矩形内,大小被缩放为尽可能大的矩形,保持长宽比。
*
*/
QSize scaledSize = originalPixmap.size();
scaledSize.scale(screenshotLabel->size(), Qt::KeepAspectRatio);
if (!screenshotLabel->pixmap() || scaledSize != screenshotLabel->pixmap()->size())
updateScreenshotLabel();
}
void Screen::newScreenshot()
{
if (hideThisWindowCheckBox->isChecked())
hide();
newScreenshotButton->setDisabled(true);
QTimer::singleShot(delaySpinBox->value() * 1000, this, &Screen::shootScreen);
}
void Screen::saveScreenshot()
{
/*
* 当用户按下Save按钮时,saveScreenshot()槽被调用,它使用QFileDialog类显示一个文件对话框。
QFile对话框使用户能够遍历文件系统以选择一个或多个文件或目录。
创建QFile对话框最简单的方法是使用方便的静态函数。
这里,我们在堆栈上实例化对话框,以便能够设置支持的Qlmage Writer的mime类型,允许用户以各种格式保存。
我们将默认文件格式定义为png,并将文件对话框的初始路径设置为从QStandardPaths获得的图片的位置,
默认为运行应用程序的路径。我们通过调用QDialog::exec()来运行对话框,
并在用户取消对话框时返回。如果对话框被接受,我们通过调用QFile dialog::selectedFiles()获得一个文件名。
该文件不必存在。如果文件名有效,我们使用QPixmap::save()函数将截图的原始pixmap保存到该文件中。
*/
const QString format = "png"; //格式
QString initialPath = QStandardPaths::writableLocation(QStandardPaths::PicturesLocation);
/*返回包含用户图片或照片的目录。这是一个通用值。如果不存在特定于图片文件的目录,则返回用于存储用户文档的合理回退。*/
if (initialPath.isEmpty())
initialPath = QDir::currentPath();
initialPath += tr("/unittled.") + format;
QFileDialog fileDialog(this, QStringLiteral("另存为"), initialPath);
fileDialog.setAcceptMode(QFileDialog::AcceptSave);
/*
* 这个属性保存对话框的接受模式操作模式定义对话框是用于打开还是保存文件。默认情况下,该属性设置为AcceptOpen。
*/
fileDialog.setFileMode(QFileDialog::AnyFile);
/*
* 这个属性保存对话框的文件模式文件模式定义了用户希望在对话框中选择的项的数量和类型。
*默认情况下,此属性设置为AnyFile。这个函数将为FileNane和Accept DialogLabels设置标签。
*可以在调用setFileMode O之后设置自定义文本。
*/
fileDialog.setDirectory(initialPath);
/*设置文件对话框的当前目录。*/
QStringList mimeTypes;
const QListbaMimeTypes = QImageWriter::supportedMimeTypes();
/*返回QImageWriter支持的MIME类型列表。*/
for (const QByteArray& bf : baMimeTypes)
mimeTypes.append(QLatin1String(bf));
/*定义OT_NO_CAST_FROM_ASCII(如QString文档中解释的那样)
的应用程序不能访问QString的const char * API。
为了提供一种高效的指定常量Latin-1字符串的方法,
Qt提供了QLatinlString,它只是对const char *的一个非常薄的包装。
使用QLatinlString,上面的示例代码就变成了*/
fileDialog.setMimeTypeFilters(mimeTypes);
fileDialog.selectMimeTypeFilter("image/" + format);
fileDialog.setDefaultSuffix(format);
/*
* 如果没有指定其他后缀,则为文件名添加后缀此属性指定一个字符串,
*如果该字符串已经没有后缀,则将添加到该filenane。
*后缀通常用于表示文件类型(例如:txt表示文本文件)。
*如果第一个字符是点('.'),它将被删除。
*/
if (fileDialog.exec() != QDialog::Accepted)
return;
const QString fileName = fileDialog.selectedFiles().first();
/*返回字符串列表,其中包含对话框中所选文件的绝对路径。
如果没有选择文件,或者模式不是ExistingFiles或ExistingFile,
selectedFiles O包含视口中的当前路径。*/
if (!originalPixmap.save(fileName)) {
QMessageBox::warning(this, QStringLiteral("保存失败"), tr("The image could not be saved to \"%1\".")
.arg(QDir::toNativeSeparators(fileName)));
}
}
void Screen::shootScreen()
{
/*
调用shootScreen()槽来获取屏幕截图。
首先,我们通过检索QWindow及其QScreen找到窗口所在的QScreen实例,默认为主屏幕。
如果找不到屏幕,则返回。
虽然这种情况不太可能发生,但应用程序应该检查空指针,因为可能存在没有连接屏幕的情况。
•如果用户选择延迟截图,我们使用静态QApplication::beep()函数使应用程序在截图时发出哔哔声。
然后使用QScreen.grabWindow()函数获取屏幕截图。
该函数获取作为参数传递的窗口内容,从中生成像素图并返回该像素图。
窗口id可以通过QWidget:winld()或QWindow::winld()获取。
然而,在这里,我们只是传递O作为窗口id,表明我们想要获取整个屏幕。
我们使用私有的updateScreenshotLabel()函数来更新截图预览标签。
然后启用New截屏按钮,最后,如果截屏窗口小部件在截屏时是隐藏的,则该窗口小部件可见。
*/
QScreen* screen = QGuiApplication::primaryScreen();
/*此属性保存应用程序的主(或默认)屏幕。
除非另有说明,否则这将是QWindows最初显示的屏幕。
prinaryScreenChanged信号是在Qt 5.6中引入的。*/
if (const QWindow* window = windowHandle())
screen = window->screen();
if (!screen)
return;
if (delaySpinBox->value() != 0)
QApplication::beep();
originalPixmap = screen->grabWindow(0);
/*
创建并返回由QRect (x, y, width, height)限制的给定窗口的内容构建的像素图。
参数(x, y)指定窗口中的偏移量,而(width, height)指定要复制的区域。
如果width为负,则该函数将所有内容复制到窗口的右边框。
如果height为负,该函数将所有内容复制到窗口底部。
offset和size参数以与设备无关的像素指定。
当从高dpi屏幕抓取时,返回的像素图可能比请求的大小更大。
调用QPixmap:: devicePixelRatioO来确定是否存在这种情况。
可以使用QWidget::winIdO函数检索窗口系统标识符(wId)。
使用窗口标识符而不是Qwidget的基本原理是支持抓取不属于应用程序的窗口、窗口系统框架等。*/
updateScreenshotLabel();
newScreenshotButton->setDisabled(false);
if (hideThisWindowCheckBox->isChecked())
show();
}
void Screen::updateCheckBox()
{
/*
隐藏此窗口选项的启用或禁用取决于屏幕截图的延迟。
如果没有延迟,应用程序窗口不能被隐藏,并且该选项的复选框被禁用。
每当用户使用截图延迟选项更改延迟时,就会调用updatecheckBox()插槽。*/
if (delaySpinBox->value() == 0) {
hideThisWindowCheckBox->setDisabled(true);
hideThisWindowCheckBox->setChecked(false);
}
else {
hideThisWindowCheckBox->setDisabled(false);
}
}
void Screen::updateScreenshotLabel()
{
screenshotLabel->setPixmap(originalPixmap.scaled(screenshotLabel->size(),
Qt::KeepAspectRatio,
Qt::SmoothTransformation));
}
main.cpp
#include "screen.h"
#include
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Screen w;
w.show();
return a.exec();
}