Qt4简单截图功能的实现
来源:互联网 发布:巨人网络offer 编辑:程序博客网 时间:2024/06/01 17:15
1、概述
截图是一个非常常见的功能。但是Windows自带的截图功能我们发现并不好用。所以很多时候我们打开QQ就是为了做一个截图。QQ的截图功能还是非常强大的,今天,我将用Qt4手工做一个简单的截图。这个截图比不上QQ的截图,只实现了基本功能。没有复杂的操作,供大家学习、参考。
2、 需求描述
最终成品是一个可执行的exe文件,运行平台是Windows操作系统(经过我的测试,在Windows7、10下均可以完美运行)。
打开软件后,屏幕变暗,进入“自由模式”,在这个模式下,鼠标可以自由移动。自由模式可以做以下的事情:
- 自由模式上面显示一个图片表示当前位于自由模式下,鼠标左键单击图片,图片会消失。
- 单击鼠标左键,开始抓图,进入“截屏模式”,上面的图片自动消失。在这个模式下,鼠标按住拖动会产生一个橡皮框。表示要截图的区域。橡皮框左上角会产生一个QLabel指示这个橡皮框的大小(像素)。
- 松开鼠标后,程序会询问用户是否要保存图片,如果用户选择“yes”,那么会弹出一个保存文件的对话框,图片只能保存为png格式的。程序这时候会抓取橡皮框区域的图片,保存并且退出。如果用户选择“no”,橡皮框和QLabel均消失。
- 在自由模式下双击鼠标左键自动截取全屏。
- 在自由模式下单击鼠标右键不截图,直接退出程序。
3、软件截图
自由模式:
截图模式:
询问用户:
3、 开发环境
本软件采用Qt 4.8.7 开发,操作系统为Windows10 64位。没有使用QtDesigner、QtCreator。
编译环境为mingw32 4.8.2
4、 分析
注意:下面只分析和截图功能有关的代码,很多细枝末节(如图片提示、文件保存)代码不再解释。
要想实现这个程序,思路其实很简单。我们只要获取当前整个桌面的图片,把这个图片设置为主窗口的背景图片。然后让主窗口充斥整个桌面。
获取整个桌面并不难,Qt是有直接提供实现的。我们看QPixmap的如下成员函数:
QPixmap grabWindow ( WId window, int x = 0, int y = 0, int width = -1, int height = -1 )
关心第一个参数,它表示要截取的Window ID。这里我们要截取整个桌面,使用QApplication::desktop()->winId()即可。后面的参数不需要关心,默认它就是截取整个窗口的。
我们只需要把主窗口的背景设置为这个图片,大小设置为图片的大小。还需要把主窗口的状态栏隐藏,实现也很简单,利用setWindowFlags()设置Qt::FramelessWindowHint即可。
获取整个桌面的QPixmap以后,我们需要两份副本。一份用来变暗并显示在主窗口上,另外一份是原本,用于稍后真正的截图。
一个懊恼的问题是我们如何把图片变暗。我找了很多方法,这里采用一个简单的:把图片的三原色全部乘以一个倍数。注意,QPixmap并没有获取三原色的方法,所以我只好把QPixmap转换为QImage。这里我使用了一个中间文件temp.png。效率略低,有更好方法的小伙伴欢迎评论。
whole_window.save("temp.png"); bg = QImage("temp.png"); QFile *temp = new QFile("temp.png"); temp->remove();
whole_window表示用grabWindow抓取的QPixmap,bg是一个QImage类对象,它们都是主窗口类的成员。
接着,使用以下手段使QImage变暗:
int red,green,blue; for(int i = x; i < width; i++){ for(int j = y; j < height; j++){ //三原色全部乘以一个倍数,实现屏幕的变暗 red = qRed(bg.pixel(i, j)) * bright ; green = qGreen(bg.pixel(i, j)) * bright; blue = qBlue(bg.pixel(i, j)) * bright; bg.setPixel( i, j, qRgb(red, green, blue)); } }
然后我们重写paintEvent事件把bg设置为主窗口的背景图片:
void screenShot::paintEvent(QPaintEvent *event){ QPainter painter(this); //把bg设置为整个窗口的背景图片 painter.drawImage(0, 0, bg);}
这就实现了点开程序,整个桌面变暗的假象了。
接下来,整个程序的关键来了:在用户点击鼠标左键的时候,我们需要开始抓图。这需要我们重写主窗口的鼠标事件:
void mousePressEvent(QMouseEvent *e);void mouseMoveEvent(QMouseEvent *e); void mouseReleaseEvent(QMouseEvent *e); void mouseDoubleClickEvent(QMouseEvent *event);
四个事件分别对应于鼠标移按下、移动、松开、双击。
我们重点关心鼠标按下。
在鼠标按下的时候,我们需要创建一个橡皮框显示抓图的区域。这使用到一个叫做QRubberBand的类。Qt的帮助文档是这样介绍这个类的:
The QRubberBand class provides a rectangle or line that can indicate a selection or a boundary.
You can create a QRubberBand whenever you need to render a rubber band around a given area (or to represent a single line), then call setGeometry(), move() or resize() to position and size it. A common pattern is to do this in conjunction with mouse events.
文档说,这个类可以表示一块区域。我们可以通过resize改变大小,move改变框的位置。
那么,把它设置为主窗口的一个成员,当鼠标单击时,实例化橡皮框。鼠标移动时,改变橡皮框的大小。(具体大小需要计算)
为了实现截图,我们需要记录鼠标按下和松开的位置(计算出截图区域的长宽)。在主窗口的成员中保存两个QPoint对象start和end即可。
这需要我们取得鼠标的位置,很简单,通过QMouseEvent的pos()即可完成。注意它返回的是QPoint类对象。
在鼠标左键按下时,记录鼠标的初始位置:
start = e->pos();
鼠标移动时,我们需要实时改变橡皮框的大小,实现如下:
//记录鼠标移动的位置end = e->pos();//计算框的大小int width = abs(end.x() - start.x());int height = abs(end.y() - start.y());//计算框的位置int x = start.x() < end.x() ? start.x() : end.x();int y = start.y() < end.y() ? start.y() : end.y();
在鼠标松开以后,我们需要记录鼠标点击和松开的位置,计算出要抓取屏幕的大小。然后开始截屏,截屏完成后,退出程序:
//取得鼠标松开的位置end = e->pos();//隐藏框rubber->hide();//调用截屏函数this->grapScreen();//关闭this->close();
在grapScreen()成员函数中,我们需要实现真正的截图,这里使用QPixmap的copy函数取得桌面原本图片(注意,不是变暗的bg)的一个子图片。copy函数的原型如下:
QPixmap QPixmap::copy ( int x, int y, int width, int height ) const
只要提供初始位置(x,y),图片大小(width,height),我们就可以获取要抓取的图片。初始位置就是start或者end坐标。大小我们可以利用start和end计算得出:
int width = abs(start.x() - end.x());int height = abs(start.y() - end.y());int x = start.x() < end.x() ? start.x() : end.x();int y = start.y() < end.y() ? start.y() : end.y();
接着调用QPixmap的save即可保存图片。
以上就是截图核心功能的一个解释,还有很多细节我没有解释,大家看代码自行理解。
5、 代码实现
- screenShot.h 主窗口类声明
#ifndef _SCREEN_SHOT_H_#define _SCREEN_SHOT_H_#include <QWidget>#include <QRubberBand>#include <QLabel>#include <QScreen>#include <QPoint>#include <QString>#include <QPalette>#include <QFileDialog>#include <QDesktopServices>#include <QPixmap>#include <QImage>class screenShot : public QWidget{ Q_OBJECTpublic: screenShot(QWidget *parent = 0); void mouseMoveEvent(QMouseEvent *e); void mousePressEvent(QMouseEvent *e); void mouseReleaseEvent(QMouseEvent *e); void mouseDoubleClickEvent(QMouseEvent *event); void keyPressEvent(QKeyEvent *e); void setLabel(); void changeLight(int x, int y, int width, int height, double bright); void paintEvent(QPaintEvent *event); void show(); void grapScreen(); ~screenShot();private: QRubberBand *rubber; QLabel *label; QPoint start; QPoint end; QPixmap whole_window; QPixmap image; QImage *info; QLabel *info_label; QImage bg; int s_height; int s_width; QPushButton *button;};#endif //screenShot.h
- screenShot.cpp 主窗口类实现
#include "screenShot.h"#include <QtGui>screenShot::screenShot(QWidget *parent) :QWidget(parent), rubber(NULL), label(new QLabel("")), start(QPoint(0, 0)), end(QPoint(0, 0)), whole_window(QPixmap::grabWindow(QApplication::desktop()->winId())), image(QPixmap()), info(new QImage("infoa.png")), info_label(new QLabel("")), s_height(0), s_width(0), button(new QPushButton()){ //此语句解决中文乱码情况 QTextCodec::setCodecForCStrings(QTextCodec::codecForName("UTF-8")); //此处把QPixmap转换为QImage //因为只有QImage才可以设置三原色 whole_window.save("temp.png"); bg = QImage("temp.png"); QFile *temp = new QFile("temp.png"); temp->remove(); changeLight(0, 0, bg.width(), bg.height(), 0.6); //把图片变暗 //获取桌面尺寸 QDesktopWidget *d_widget = QApplication::desktop(); QRect d_rect = d_widget->screenGeometry(); s_width = d_rect.width(); s_height = d_rect.height(); //设置窗口大小为桌面尺寸 this->resize(s_width, s_height); //这个按钮是用户第一次打开程序的提示 button->setIcon(QIcon("infob.png")); button->setWindowFlags(Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint); button->setIconSize(QSize(211, 123)); button->setGeometry(s_width / 2, 0, 211, 123); button->show(); //用户点击按钮后,按钮消失 connect(button, SIGNAL(clicked()), button, SLOT(close())); //窗口和坐标信息置于整个屏幕顶端 label->setWindowFlags(Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint); this->setWindowFlags(Qt::FramelessWindowHint);}void screenShot::changeLight(int x, int y, int width, int height, double bright){ int red,green,blue; for(int i = x; i < width; i++){ for(int j = y; j < height; j++){ //三原色全部乘以一个倍数,实现屏幕的变暗 red = qRed(bg.pixel(i, j)) * bright ; green = qGreen(bg.pixel(i, j)) * bright; blue = qBlue(bg.pixel(i, j)) * bright; bg.setPixel( i, j, qRgb(red, green, blue)); } }}void screenShot::mousePressEvent(QMouseEvent *e){ if(e->button() == Qt::LeftButton){ //初始化提示信息框 info_label->setPixmap(QPixmap::fromImage(*info)); info_label->setWindowFlags(Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint); info_label->move(QPoint(e->pos().x() + 30, e->pos().y() + 30)); info_label->show(); button->close(); if(!rubber){ //初始化框 rubber = new QRubberBand(QRubberBand::Line, this); } //显示框 rubber->show(); //记录初始鼠标位置 start = e->pos(); //将框放置于起始位置,且大小为0x0 rubber->setGeometry(start.x(), start.y(), 0, 0); //调用此函数,显示坐标 setLabel(); } else if(e->button() == Qt::RightButton) { //鼠标右键点击,退出程序 button->close(); info_label->close(); this->close(); }}void screenShot::mouseDoubleClickEvent(QMouseEvent *event){ // 此处应该截取全屏 if(event->button() == Qt::LeftButton){ button->close(); QString fileName = QFileDialog::getSaveFileName(this, tr("保存图片"), ".", tr("Image Files(*.PNG)")); //此处只支持png格式的图片 whole_window.save(fileName); info_label->close(); this->close(); }}void screenShot::mouseMoveEvent(QMouseEvent *e){ if(rubber){ info_label->move(QPoint(e->pos().x() + 30, e->pos().y() + 30)); //记录鼠标移动的位置 end = e->pos(); //计算框的大小 int width = abs(end.x() - start.x()); int height = abs(end.y() - start.y()); //计算框的位置 int x = start.x() < end.x() ? start.x() : end.x(); int y = start.y() < end.y() ? start.y() : end.y(); //更新框的位置 rubber->setGeometry(x, y, width, height); //changeLight(x, y, width, height, 1.25); //改变坐标显示 setLabel(); }}void screenShot::mouseReleaseEvent(QMouseEvent *e){ if(rubber){ info_label->hide(); label->hide(); QMessageBox::StandardButton res = QMessageBox::question(this, "询问", "您要保存这个截图吗?", QMessageBox::Yes | QMessageBox::No); if(res == QMessageBox::Yes){ //取得鼠标松开的位置 end = e->pos(); //隐藏框 rubber->hide(); //调用截屏函数 this->grapScreen(); //关闭 this->close(); } else { //用户不保存图片,返回自由模式 button->show(); rubber->close(); info_label->hide(); label->hide(); } }}void screenShot::grapScreen(){ int width = abs(start.x() - end.x()); int height = abs(start.y() - end.y()); int x = start.x() < end.x() ? start.x() : end.x(); int y = start.y() < end.y() ? start.y() : end.y(); //截取指定大小的图片 image = whole_window.copy(x, y, width, height); QString fileName = QFileDialog::getSaveFileName(this, tr("保存图片"), ".", tr("Image Files(*.PNG)")); //保存图片 bool flag = image.save(fileName); if(flag){ QMessageBox::information(this, "信息", "图片保存成功!"); }}void screenShot::setLabel(){ int width = abs(start.x() - end.x()); int height = abs(start.y() - end.y()); int x = start.x() < end.x() ? start.x() : end.x(); int y = start.y() < end.y() ? start.y() : end.y(); //设置标签内容 QString str = QString(" %1 x %2 ").arg(width).arg(height); label->setText(str); //重定位标签的位置 QRect rect(label->contentsRect()); label->move(QPoint(x, y - rect.height())); label->show();}void screenShot::keyPressEvent(QKeyEvent *event){ this->close(); }screenShot::~screenShot(){ //...}void screenShot::show(){ QWidget::show();}void screenShot::paintEvent(QPaintEvent *event){ QPainter painter(this); //把bg设置为整个窗口的背景图片 painter.drawImage(0, 0, bg);}
- main.cpp
#include "screenShot.h"#include <QApplication>#include <QCoreApplication>#include <QTextCodec>#include <windows.h>int main(int argc, char *argv[]){ QTextCodec *codec = QTextCodec::codecForName("UTF-8"); QTextCodec::setCodecForTr(codec); QTextCodec::setCodecForLocale(codec); QTextCodec::setCodecForCStrings(codec); QApplication a(argc, argv); screenShot *ss = new screenShot(); ss->show(); return a.exec();}
以上代码仅作参考!
6、问题
本程序尚存在如下问题:
- 图片只能保存png格式的,这是硬伤;
- 截图并没有做到顶置(为了显示提示信息),这导致一些顶置的程序无法截进去,如输入法等;
- 把QPixmap转换为QImage对象用到中间文件,效率低;
- 三原色全部乘以倍数的手法导致程序效率不高。在一些配置较差电脑上,点开程序可能要等一段时间才能开始截图,这可能导致用户无法准确截取一些视频动画;
- 单机鼠标右键退出以后,返回桌面也会产生鼠标右键点击的效果,这个问题我认为还是窗口没有顶置造成的。
- Qt4简单截图功能的实现
- 截图功能的简单实现
- 简单截图功能实现
- 实现类似的抓屏功能(简单的截图功能)
- 截图功能的实现
- Qt4.8.2 实现简单的界面换肤功能
- Qt4.8.2 实现简单的界面换肤功能
- 截图保存功能的实现
- QT4 一个简单的打开文件功能
- delphi 截图简单的实现
- WP简单截图功能
- MATLAB实现截图功能,返回屏幕截图的RGB数组
- extjs的头像截图功能的实现
- C# 实现完整功能的截图控件
- JAVA的web截图功能实现
- Silverlight/WPF 截图保存功能的实现
- Qt中截图功能的实现
- Qt中截图功能的实现
- MFC中的CListCtrl的最左边一列必须左对齐吗?
- 技术主管和架构师的职责
- python字符串格式化输出
- 欢迎使用CSDN-markdown编辑器
- Android中获取颜色的几种方法
- Qt4简单截图功能的实现
- 第四方支付平台哪个好?
- Cause: net.sf.cglib.beans.BulkBeanException 解决方法
- 离线百度地图
- 对象类型
- java日期处理类
- The FEDERATED Storage Engine
- 一种高级的DoS攻击-Hash碰撞攻击
- Android中改变不同控件的样式(一)