Screenshot Example展示了怎么用QApplication和QDesktopWidget获取桌面的截屏。它也展示了怎么用QTimer提供一个单发时间(single-shot timer)、怎么样为了确保应用程序没有数据丢失地调整大小而重新实现QWidget::resizeEvent()事件处理器。
通过程序,使用者可以获取自己桌面的截图。其中有两个设置:
1. 延迟截屏时间,让用户可以重新排列他的桌面。
2. 当程序截图时隐藏应用程序窗口。
除此之外,还可以让用户保存自己的截图。
该程序只有一个类:Screenshot类。先看看main.cpp文件。
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
Screenshot screenshot;
screenshot.show();
return app.exec();
}
再看看screenshot类定义:
class Screenshot : public QWidget
{
Q_OBJECT
public:
Screenshot();
protected:
void resizeEvent(QResizeEvent *event);
private slots:
void newScreenshot(); // 新建截图槽
void saveScreenshot(); // 保存截图槽
void shootScreen(); // 截图槽
void updateCheckBox(); // 是否隐藏窗体槽
private:
void createOptionsGroupBox(); // 构造groupbox窗体部件
void createButtonsLayout(); // 窗体部件布局管理
QPushButton *createButton(const QString &text, QWidget *receiver,
const char *member);
void updateScreenshotLabel();
QPixmap originalPixmap; // 保存获取到的桌面截屏
QLabel *screenshotLabel; // 以下为控件和布局管理器
QGroupBox *optionsGroupBox;
QSpinBox *delaySpinBox;
QLabel *delaySpinBoxLabel;
QCheckBox *hideThisWindowCheckBox;
QPushButton *newScreenshotButton;
QPushButton *saveScreenshotButton;
QPushButton *quitScreenshotButton;
QVBoxLayout *mainLayout;
QGridLayout *optionsGroupBoxLayout;
QHBoxLayout *buttonsLayout;
};
Screenshot继承自QWidget,重写了QWidget::resizeEvent(), 为了确保用户调整窗口大小时预览截图可能的缩放调整。
所有控件以及布局管理器都作为数据成员
//! [0]
Screenshot::Screenshot()
{
screenshotLabel = new QLabel; // 用啦显示截图的Label
screenshotLabel->setSizePolicy(QSizePolicy::Expanding, // 大小策略
QSizePolicy::Expanding);
screenshotLabel->setAlignment(Qt::AlignCenter); // 居中对齐
screenshotLabel->setMinimumSize(240, 160); // 最小大小
createOptionsGroupBox(); // 调用创建groupbox窗体部件及布局
createButtonsLayout(); // 调用创建buttonlayout以及布局
mainLayout = new QVBoxLayout; // 主布局:label+groupbox+buttonlayout
mainLayout->addWidget(screenshotLabel);
mainLayout->addWidget(optionsGroupBox);
mainLayout->addLayout(buttonsLayout);
setLayout(mainLayout);
shootScreen(); // 截图
delaySpinBox->setValue(5); // 设置默认延迟时间5秒
setWindowTitle(tr("Screenshot")); // 主窗口标题
resize(300, 200); // 大小
}
//! [0]
//! [1]
// 窗体调整大小时,调整截图label的大小
void Screenshot::resizeEvent(QResizeEvent * /* event */)
{
QSize scaledSize = originalPixmap.size();
scaledSize.scale(screenshotLabel->size(), Qt::KeepAspectRatio);
if (!screenshotLabel->pixmap() // 当没有设置位图或者这里的缩放大小和screenshotLabel的大小不一致时
|| scaledSize != screenshotLabel->pixmap()->size())
updateScreenshotLabel(); // 更新screenshotLabel
}
//! [1]
QSize::scale()函数,有三种模式:IgnoreAspectRatio,KeepAspectRatio,KeepAspectRatioByExpanding
QSize t1(10, 12);
t1.scale(60, 60, Qt::IgnoreAspectRatio);
// t1 is (60, 60)
QSize t2(10, 12);
t2.scale(60, 60, Qt::KeepAspectRatio);
// t2 is (50, 60)
QSize t3(10, 12);
t3.scale(60, 60, Qt::KeepAspectRatioByExpanding);
// t3 is (60, 72)
//! [2]
void Screenshot::newScreenshot()
{
if (hideThisWindowCheckBox->isChecked()) // 检查是否隐藏主窗体
hide();
newScreenshotButton->setDisabled(true); // 新建时新建截屏按钮不可用
// 设置一个单发时间器,时间为延迟时间spinBox里头的值,时间到时就发送信息到shootScreen()槽
QTimer::singleShot(delaySpinBox->value() * 1000, this, SLOT(shootScreen()));
}
//! [2]
QTimer提供重复和单发时间器。当时间结束时会发送信号:timeout()。例如:
QTimer *timer = new QTimer(this);
connect(timer, SIGNAL(timeout()), this, SLOT(update()));
timer->start(1000);
update()将会每一秒被触发一次。
使用singleShot就只会被发送一次。例如:
QTimer::singleShot(200, this, SLOT(updateCaption()));
updateCaption()就0.2秒后被触发。
//! [3]
void Screenshot::saveScreenshot()
{
QString format = "png"; // 保存图片的格式
// 初始化路径:当前程序路径
QString initialPath = QDir::currentPath() + tr("/untitled.") + format;
// 用getSaveFileName对话框获取保存的路径
QString fileName = QFileDialog::getSaveFileName(this, tr("Save As"),
initialPath,
tr("%1 Files (*.%2);;All Files (*)")
.arg(format.toUpper())
.arg(format));
if (!fileName.isEmpty()) // 保存对话框里路径不为空就执行保存QPixmap::save(..)
originalPixmap.save(fileName, format.toAscii());
}
//! [3]
以下shootScreen就是程序的核心
//! [4]
void Screenshot::shootScreen()
{
// 这里的qApp是应用程序QApplication对象的一个全局指针,用它来轻松获取主程序的控制
if (delaySpinBox->value() != 0)
qApp->beep(); // 一声响铃
//! [4]
originalPixmap = QPixmap(); // clear image for low memory situations
// on embedded devices.
//! [5]
// 窗体抓图:grabWindow( WId window, int x = 0, int y = 0, int width = -1, int height = -1)
// WId每一个窗体都有一个ID,可以用 QWidget::winId()获取
originalPixmap = QPixmap::grabWindow(QApplication::desktop()->winId());
updateScreenshotLabel();
newScreenshotButton->setDisabled(false); // 在执行截图时,使newScreenshotButton不可用
if (hideThisWindowCheckBox->isChecked()) // 截图完时要将隐藏的主窗体显示回来(如果隐藏了)
show();
}
//! [5]
原来程序就是使用了grabWindow来获取的截图
对于QApplication
QApplication::desktop()返回桌面,对于有多屏幕的桌面的可能不正确。可以获取它的返回值QDesktopWidget, 它有一些函数让你获取一些有用的信息,例如:availableGeometry() screenGeometry()
//! [6]
void Screenshot::updateCheckBox()
{
if (delaySpinBox->value() == 0) { // 若延迟时间为零时,CheckBox不可用并且为没打勾状态
hideThisWindowCheckBox->setDisabled(true);
hideThisWindowCheckBox->setChecked(false);
}
else // 否则为可用
hideThisWindowCheckBox->setDisabled(false);
}
//! [6]
//! [7]
void Screenshot::createOptionsGroupBox()
{
optionsGroupBox = new QGroupBox(tr("Options")); // 组框
delaySpinBox = new QSpinBox; // spinbox用于调整延迟时间
delaySpinBox->setSuffix(tr(" s"));
delaySpinBox->setMaximum(60);
// 连接delaySpinBox的valueChanged(int)信号和updateCheckBox()槽
connect(delaySpinBox, SIGNAL(valueChanged(int)), this, SLOT(updateCheckBox()));
// 用于提示的label
delaySpinBoxLabel = new QLabel(tr("Screenshot Delay:"));
// 是否隐藏的CheckBox
hideThisWindowCheckBox = new QCheckBox(tr("Hide This Window"));
// GridLayout布局管理label,spinBox,checkBox
optionsGroupBoxLayout = new QGridLayout;
optionsGroupBoxLayout->addWidget(delaySpinBoxLabel, 0, 0);
optionsGroupBoxLayout->addWidget(delaySpinBox, 0, 1);
optionsGroupBoxLayout->addWidget(hideThisWindowCheckBox, 1, 0, 1, 2);
optionsGroupBox->setLayout(optionsGroupBoxLayout); // 设置到组框groupBox
}
//! [7]
//! [8]
void Screenshot::createButtonsLayout()
{
// buttonLayout里三个PushButton
newScreenshotButton = createButton(tr("New Screenshot"), // 自定义的一个创建函数
this, SLOT(newScreenshot()));
saveScreenshotButton = createButton(tr("Save Screenshot"),
this, SLOT(saveScreenshot()));
quitScreenshotButton = createButton(tr("Quit"), this, SLOT(close()));
buttonsLayout = new QHBoxLayout;
buttonsLayout->addStretch(); // 在最左边加入一个stretch占位
buttonsLayout->addWidget(newScreenshotButton);
buttonsLayout->addWidget(saveScreenshotButton);
buttonsLayout->addWidget(quitScreenshotButton);
}
//! [8]
//! [9]
// 方便创建pushButton时初始化
QPushButton *Screenshot::createButton(const QString &text, QWidget *receiver,
const char *member)
{
QPushButton *button = new QPushButton(text);
button->connect(button, SIGNAL(clicked()), receiver, member);
return button;
}
//! [9]
//! [10]
void Screenshot::updateScreenshotLabel()
{
// 更新screenshotLabel,放置一张缩放了的截图
screenshotLabel->setPixmap(originalPixmap.scaled(screenshotLabel->size(),
Qt::KeepAspectRatio,
Qt::SmoothTransformation));
}
//! [10]