如何在午休时间写一个QT程序以便能够挣到同事的五毛

原创 2010年06月24日 23:02:00

作者的话:本文有两个目的:1、Zhuangbility ;2、教你如何变成五毛;3、展示QT的生产力。
译者的话:本文并非虚构,是真实发生过的——如有雷同你肯定认识我,欢迎到我座位这边来喝可乐(你一定知道这个典故的)

在30分钟内用QT写一个抓屏程序

(注:Qt这个词应该读成Cute ,而不是Queue-Tee)

※ ※ ※ ※
叮咚、叮咚、叮咚——00:29:59
叮咚、叮咚、叮咚——00:29:58
叮咚、叮咚、叮咚——00:29:57
※ ※ ※ ※

此截图程序可以实现以下功能:

+跨平台运行
+可以截取全屏幕或者其中一部分
+鼠标选中的区域为高亮,未选中的区域亮度减半
+截屏后可以反复选择,直至满意为止
+保存文件到桌面并退出
+复制图像到剪切板并退出

※ ※ ※ ※
叮咚、叮咚、叮咚——00:25:59
叮咚、叮咚、叮咚——00:25:58
叮咚、叮咚、叮咚——00:25:57
※ ※ ※ ※

整个程序主要有三个关键点:(1)抓屏到内存;(2)随着鼠标选取,加亮显示内存中的图片;(3)将图片的一部分保存到剪切板或者文件。
在QT中,这三个问题很好解决,问题(1)可以直接调用QT函数:

    QPixmap px = QPixmap::grabWindow(QApplication::desktop()->winId())

※ ※ ※ ※
叮咚、叮咚、叮咚——00:24:59
叮咚、叮咚、叮咚——00:24:58
叮咚、叮咚、叮咚——00:24:57
※ ※ ※ ※

问题(2)也不难,只需要提前做一点点工作,在抓图后保存两个图片,一个是正常的,另一个是变暗之后的:

    screen_ = px.toImage();
将刚刚截屏得到的图像转换成设备无关的内存表示
darkScreen_ = screen_;
在内存中对RGB减半处理,使图像变暗,先计算每行的字节数:
int bytesPerLine = darkScreen_.width() * darkScreen_.depth() / 8;
然后处理每个行
for ( int i = 0 ; i < darkScreen_.height() ; ++ i )
{
uchar* lineBuf = darkScreen_.scanLine(i);
scanLine函数可以取得指定行的起始指针,不必再自己计算了。取得行指针后对每像素RGB值进行操作
for ( int x = 0 ; x < bytesPerLine ; ++ x )
lineBuf[x] /= 2;
 }
在这里没有尝试加速图片变暗的过程,效率问题最后再综合考虑。
※ ※ ※ ※
叮咚、叮咚、叮咚——00:22:59
叮咚、叮咚、叮咚——00:22:58
叮咚、叮咚、叮咚——00:22:57
※ ※ ※ ※
有了这两张图片,就可以进行绘图了:
void Widget::paintEvent(QPaintEvent *)
{
声明绘图对象,目标为窗口
QPainter painter(this);
全屏绘制暗图像:
painter.drawImage(0, 0, darkScreen_);
在选定的区域绘制亮图像:
painter.drawImage(currentRect_, screen_, currentRect_, Qt::AutoColor);
}

currentRect_是在鼠标事件中保存的当前选择区域,它的计算也很简单,在鼠标的移动消息处理器中:

void Widget::mouseMoveEvent(QMouseEvent * e)
{
if ( dragging_ )
end_ = e->globalPos();
保存旧区域,用于后面的区域合并
QRect oldRect = currentRect_;
设置新区域
currentRect_.setTopLeft(start_);
currentRect_.setBottomRight(end_);
调用normalized函数解决负宽度或者负高度问题
currentRect_ = currentRect_.normalized();
QRect::united()函数用来求两个rect合并后的rect,可以省去许多手工计算。
且QT的repaint()函数也不需要关心背景擦除问题,QT4以上都是自动double buffer
repaint(oldRect.united(currentRect_));
}

至此,问题(2)处理完毕。

※ ※ ※ ※
叮咚、叮咚、叮咚——00:18:59
叮咚、叮咚、叮咚——00:18:58
叮咚、叮咚、叮咚——00:18:57
※ ※ ※ ※

为处理问题(3),需要得到指定图像的指定区域,并且一个图像对象来表示。将这个功能编写成以下辅助函数:

static QImage clipImage(const QImage& srcImage, QRect rect)
{
QImage image(rect.size(), QImage::Format_RGB32);
const QImage* target = &srcImage;
QImage targetImg;
if ( srcImage.depth() != 32 )
{
targetImg = srcImage.convertToFormat(QImage::Format_RGB32);
target = &targetImg;
}
int bytesPerPixel = image.depth() / 8;
for ( int i = 0 ; i < image.height() ; ++ i )
 {
 uchar * line = image.scanLine(i);
 const uchar * srcLine = target->scanLine(rect.top() + i);
memcpy(line,
srcLine + rect.left() * bytesPerPixel,
rect.width() * bytesPerPixel);
}
return image;
}
这个函数有点长,但思路却很简单:
1、先创建一个小图像,宽高与选区相同
2、取得源图像的数据,复制到小图像中
3、返回小图像

有了这个辅助函数之后,想保存文件,就可以这样写:

QImage img(clipImage(screen_, currentRect_));
img.save(pathName, "png");
想把图片放到剪切板的话,可以这样写:
QApplication::clipboard()->clear();
QApplication::clipboard()->setImage(clipImage(screen_, currentRect_), QClipboard::Clipboard);
QImage(QApplication::clipboard()->image(QClipboard::Clipboard)); // Retrieve data from clipboard to make it owns the data

※ ※ ※ ※
叮咚、叮咚、叮咚——00:10:59
叮咚、叮咚、叮咚——00:10:58
叮咚、叮咚、叮咚——00:10:57
※ ※ ※ ※

再往下就是处理一些鼠标和键盘事件,比如在双击时复制到剪切板并退出,在按Ctrl+S时保存到文件并退出,按ESC时直接退出,等等

最终完成,编译,修改typo并运行,发现速度还可以,故取消profiling和性能调优计划,直接和DLL一起打包。至此任务完成,领取五毛工资。

※ ※ ※ ※
叮咚、叮咚、叮咚——00:00:09
叮咚、叮咚、叮咚——00:00:08
叮咚、叮咚、叮咚——00:00:07
叮咚、叮咚、叮咚
叮咚、叮咚、叮咚——00:00:05
叮咚、叮咚、叮咚
叮咚、叮咚、叮咚——00:00:03
叮咚、叮咚、叮咚——00:00:02
叮咚、叮咚、叮咚——00:00:01
叮咚、叮咚、叮咚——00:00:00

(EOF)

如何让每天工作都有干劲

上班没劲,常常听到许多人发出同样的感慨。第一天上班,兴奋异常;然而今天去上班,却痛苦不已。日子久了,工作似乎变成了枯燥无味的代名词,再也和开心、精彩沾不上边。 然而工作是你我生活中很重要的一部分,若是...
  • why_dac
  • why_dac
  • 2010年01月11日 12:00
  • 480

公司规定中午不能午休!!!

你们公司的研发中午都能休息吗?如果方便的话说一下你们的午休时间。直接上邮件截图:...
  • zl544434558
  • zl544434558
  • 2014年08月07日 13:53
  • 2254

从不适应到不得不离开,我在印度公司的日子(二)

在这三个月当中,我感觉到公司的一切都非常规范,也对公司的前途非常有信心,心想:不想再换工作了,就在这家公司干到退休吧!直到后来,我才感觉到这样的想法是多么的单纯和一厢情愿。每周三的下午,我们的项目经理...
  • jackgogogo
  • jackgogogo
  • 2007年03月28日 09:20
  • 2032

2015-10-28-使用Excel来计算加班时间,剥离午休时间段

需求 实施 绘制草图 代码模拟 代码转换 输出 编写时间进行测试 Excel示例文档需求由于李丹在整理同事们加班时间Excel表格的时候,不想要统计12:00~13:00时段。产生需求。 因此,晚上...
  • bxh7425014
  • bxh7425014
  • 2015年10月28日 21:05
  • 1563

写给同事的一封信

亲爱的同事, 转眼我在这个团队工作已有一年的时光,这一年也完成了我从通讯行业转入互联网圈的过渡。过去的一年给了我很多观察(团队)的机会,也带给了我不少思考,从我过去一年的寥寥几篇博文你应...
  • hzliyun
  • hzliyun
  • 2014年01月05日 17:12
  • 10106

午休时间

 星期一总是五天之中最累的一天,也有同事把这叫成是"星期一综合症",觉得满有道理的,究其原因,绝大部分可能是周末的那几个舒服的懒觉造成的.尽管如此,我还是不会去放弃可爱的周末懒觉.前几天看到一个blo...
  • Sam_Siemens
  • Sam_Siemens
  • 2006年09月11日 12:36
  • 678

在jsp代码中弹出alert令一种写法

  //nextpage 为弹出alert后跳转的页面String nextpage = "_manage.jsp";StringBuffer result = new StringBuffer(""...
  • xiejingtian
  • xiejingtian
  • 2009年03月09日 22:08
  • 385

python学习笔记二:实现一个时钟

前提概要:同事初学python,花了一天的时间用了各种循环控制想实现一个时钟不断刷新的效果,他还要求不要打印多行,每次在原地刷新,本人也是小菜啦,说这个估计要用第三方库来实现,就找度娘问了一下,结果找...
  • chenzui_han_feng
  • chenzui_han_feng
  • 2015年02月06日 18:37
  • 268

基于 控制台 简易 学生信息管理系统 (增、删、改)

l  场景 创建一个学生信息管理系统,具体要求如下: (1)       数据需求 学生信息包括:学号、姓名、年龄、性别、专业、班级。 (2)       基本功能需求(每组可以自行完...
  • u011528448
  • u011528448
  • 2014年05月02日 14:14
  • 1828

如何在SSH中创建一个定时器

新手练习
  • kuanghaohua
  • kuanghaohua
  • 2016年03月29日 15:04
  • 137
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:如何在午休时间写一个QT程序以便能够挣到同事的五毛
举报原因:
原因补充:

(最多只允许输入30个字)