需求提出:
有时,我们需要对当前窗体内容进行拷贝。如:
- 用户要求每隔几秒就要知道当前窗体屏幕都有些啥,让程序每隔几秒将当前屏幕快照,发送到他们程序进行显示。
- 程序需要记录当前屏幕内容,每隔一段时间以图片的形式,将当前窗体的内容保存起来,以作为日志记录。
实现如下:
.h文件
#pragma once
#include <QtWidgets/QMainWindow>
#include "ui_QtWidgetsApplication1.h"
#include<QTimer>
#include <QRandomGenerator>
class QtWidgetsApplication1 : public QMainWindow
{
Q_OBJECT
public:
QtWidgetsApplication1(QWidget *parent = nullptr);
~QtWidgetsApplication1();
protected:
void paintEvent(QPaintEvent* event);
void copyWnd();
private:
Ui::QtWidgetsApplication1Class ui;
QTimer* m_pTimer{nullptr}; // 保存当前窗口内容到图片的定时器
QTimer* m_pChangeColorTimer{ nullptr }; // 改变颜色的定时器
QRandomGenerator m_rndColorGenerator; // 产生颜色的随机器
int m_nRed{255}; // 颜色的红色分量值
int m_nGreen{ 0 }; // 颜色的绿色分量值
int m_nBlue{ 0 }; // 颜色的蓝色分量值
};
.cpp文件
#include "QtWidgetsApplication1.h"
#include<QWidgetAction>
#include<QCheckBox>
#include<QPainter>
#include<QWindow>
#include<QDateTime>
#include<QDebug>
QtWidgetsApplication1::QtWidgetsApplication1(QWidget *parent)
: QMainWindow(parent)
{
ui.setupUi(this);
m_pTimer = new QTimer(this);
m_pTimer->setInterval(3000);
connect(m_pTimer, &QTimer::timeout, this, [=] {copyWnd(); });
m_pChangeColorTimer = new QTimer(this);
m_pChangeColorTimer->setInterval(1000);
auto seedTime = QDateTime::currentDateTime().currentMSecsSinceEpoch();
m_rndColorGenerator.seed(seedTime);
connect(m_pChangeColorTimer, &QTimer::timeout, this, [=] {
m_nRed = m_rndColorGenerator.bounded(0, 256);
m_nGreen = m_rndColorGenerator.bounded(0, 256);
m_nBlue = m_rndColorGenerator.bounded(0, 256);
qInfo() << m_nRed << " " << m_nGreen << " " << m_nBlue << " ";
update();
});
m_pChangeColorTimer->start();
auto pCopyWndMenu = new QMenu(QString::fromLocal8Bit("窗体拷贝"), this);
auto pStartCopyWndAction = new QAction(QString::fromLocal8Bit("开始拷贝"), this);
connect(pStartCopyWndAction, &QAction::triggered, this, [=] {m_pTimer->start(); });
pCopyWndMenu->addAction(pStartCopyWndAction);
this->menuBar()->addMenu(pCopyWndMenu);
}
QtWidgetsApplication1::~QtWidgetsApplication1()
{}
void QtWidgetsApplication1::paintEvent(QPaintEvent* event)
{
QPainter p(this);
p.setBrush(QBrush(QColor(m_nRed, m_nGreen, m_nBlue)));
p.drawEllipse(this->centralWidget()->rect().adjusted(20, 40, 20, 30));
}
void QtWidgetsApplication1::copyWnd()
{
qreal dpr = window()->windowHandle()->devicePixelRatio();
QPixmap pixmap(this->size() * dpr);
pixmap.setDevicePixelRatio(dpr);
this->render(&pixmap);
auto qsCurrentTime = QDateTime::currentDateTime().toString();
auto qsFileName = QString::fromLocal8Bit("%1.png").arg(qsCurrentTime);
qsFileName.replace(":", "-"); // :是非法文件名,:不能构成文件名,用-替代
auto b = pixmap.save(qsFileName, "PNG");
}
定时器每次都会将当前窗体拷贝并存为png图片放在当前目录下。如下是结果:
说明:
在实际项目中,可以开启一个线程,进行轮替删除,即只保留离现在最近的某几张图片,将 时间久远的图片删除,否则图片占用硬盘会越来越大。
QWidget的render函数定义如下:
void QWidget::render(QPaintDevice *target, const QPoint &targetOffset = QPoint(), const QRegion &sourceRegion = QRegion(), QWidget::RenderFlags renderFlags = RenderFlags(DrawWindowBackground | DrawChildren))
用法及参数说明:
target:这个参数是指将QWidget对象的内容绘制、渲染到哪里,本例是渲染即绘制到QPixmap对象上,即图片上。
targetOffset:这个参数是指渲染到目标上时,从目标的何处绘制、渲染,如从图片左边框50像素、上边框50像素的地方绘制,如下:
this->render(&pixmap, QPoint(50, 50));
结果如下:
sourceRegion:表示源被渲染到目标区域,本例中的源是指整个窗体。如果不设置,则默认是当前整个窗体,你可以设置窗体的某部分区域。
renderFlags:渲染标识,决定渲染动作如何进行。其值为以下三个枚举:
QWidget::DrawWindowBackground | 如果该标识被启用,即使 autoFillBackground未被设置, 源窗体的背景也会被渲染到目标中去。该标识默认是启用的。 |
QWidget::DrawChildren | 如果该标识被启用,该窗体下的所有子窗体会被递归地渲染到目标中去,该标识默认是启用的。 |
QWidget::IgnoreMask | 如果该标识被启用,该窗体的 QWidget::mask()不会被渲染到目标,默认该标识是禁用的 |
注意:
- 在调用render之前,确保在目标上调用了QPainter::end()。如下:
QPainter painter(this);
...
painter.end(); // 先必须保证调用了目标的QPainter对象的end函数
myWidget->render(this);
- 如果需要对QOpenGLWidget对象实现同样的功能,请用QOpenGLWidget::grabFramebuffer()方法替代,而不要用本方法,本方法只使用一般QWidget对象。
上述copyWnd()中的devicePixelRatio函数的用法及理解,请参考:
《QWindow类devicePixelRatio函数作用》博文。
上述copyWnd()中window()返回本窗体的QWindow对象,而window()->windowHandle()则返回QWindow对象的native widget对象。关于QWidget的parentWidget、window、nativeParent窗体的概念及用法,请参考:《QWidget的parentWidget、window、nativeParentWidget区别与理解》。