打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
Qt的多线程——后台创建缩略图
userphoto

2013.04.19

关注

Qt的多线程——后台创建缩略图  

2012-03-12 16:06:05|  分类: HGolf开发笔记 |字号 订阅

关于多线程可讲的东西实在太多了,从多线程本身的概念,到Qt的多线程相关的类, 再到多线程编程相关的内容,方方面面, 不是一两篇贴子能讲明白的。 笔者准备尽我所能写一些通俗易懂的文章, 介绍一下和Qt多线程相关的内容, 争取写上几篇凑成一个系列(但不保证会写几篇,按照笔者的懒惰程度,也许第二篇永远不会面世也不一定…)。 本来开篇的帖子应该八一八多线程概念性的东西,不过为了应景,先来一个使用Qt多线程创建缩略图的小例子。

起因是在qtcn上看到有人说创建缩略图的API执行很慢,并且阻塞了gui的正常运转,需要改成多线程来实现, 但他不清楚如何实现,希望能有个例子。 这位同学的问题揭示了Qt里最需要用到多线程的一个用例,那就是防止一切阻塞GUI线程的操作。 Qt GUI编程有个最基本但很多人不熟悉的原则是所有的操作都不能是阻塞的, 同时所有的操作都不能占用很多CPU, 这个原则是针对GUI线程来说的, 但后者对子线程也同样适用(这一点我们后边继续八)。

熟悉Qt的人都应该有所了解, Qt的图形处理是基于事件循环的, 这一点和大多数的GUI工具库一致。 事件循环简单来说就是个死循环, 里面读取系统外设的事件, 然后见招拆招。 Qt里的Events, Signals, Timer都在事件循环里分发, 所以,如果阻塞了事件循环用脚趾头想想也能知道会有多么严重的后果, 最明显的就是界面不刷新,也不能和用户交互, 所以这就要求我们在写程序的时候不管是事件处理函数还是槽函数里都不能进行占用CPU时间的操作, 特别要杜绝阻塞性的操作如串口数据读取、杜绝长时间的高CPU占用率的操作如死循环和大数据量的计算等。 However, “人在河边走, 哪能不湿鞋”呢, 总有需要用到这些操作的时候, Qt也提供了解决方案, 那就是多线程。

Qt的多线程类有很多, 改天开帖详细介绍, 这里只八最核心的QThread类。 该类的用法是Posix多线程的用法的简化版, 只需要派生一个QThread的子类,实现其中的run虚函数就大功告成, 用的时候创建该类的实例,调用它的start方法, 理论上非常简单。 笔者个人觉得写多线程的程序首先要去恶补一下多线程的知识, Qt说到底只是一个工具,它的API包装得再好也得用的人懂得如何去使用才能发挥最好的作用。 Qt的文档中关于多线程的编程文章挺多, 另外还列出了一系列的推荐读物, 想把多线程程序写好的人一定要看。

前面啰嗦了这么多无非是想告诉大家,写多线程的程序单靠Qt的知识是不够的(更别说有些人还没用过Qt呢), 多线程的知识更加重要。 而且单靠一两篇简单的文章是学不明白的,你看人家多线程编程能写一大章书呢! 好了, 强调一下背景知识的重要性,下面要提供一个最简单的Qt多线程的例子程序, 它的功能是打开多个图片文件之后创建线程执行图片缩放的操作, 创建图片的缩略图,完成后子线程会把缩略图传给GUI线程,由GUI线程创建Label控件来显示缩略图。 各位看官注意了,这个例子很简单, 和多线程相关的部分其实很少, 没有用到Mutex也没涉及并发等等概念, 单一的演示如何防止大数据量的计算阻塞GUI线程。

例子的源码见附件,这里先列出一些关键的流程和代码, 方便大家理解:

第一步:打开图片文件
用QFileDialog::getOpenFileNames获得图片文件名的StringList

第二步:遍历List创建线程

void MainWin::createThumbnail(const QString& filename)  {   QThread* thread = new ThumbnailThread(filename, 10 - waitseconds);   connect(thread, SIGNAL(thumbnailFinished(QImage)), this, SLOT(addThumbnail(QImage)));   connect(thread, SIGNAL(thumbnailFailed(const QString)), this, SLOT(showError(const QString)));   connect(thread, SIGNAL(finished()), this, SLOT(deleteThread()));   thread->start();  }

finished是QThread类自带的信号,在线程结束执行时发出; 其他两个信号是自定义信号, 用于在GUI线程和子线程之间传递数据。

第三步:子线程内创建缩略图
使用了QImage的scaled方法做图片缩放。

void ThumbnailThread::run()  {   if( bigpm.isNull())   {    emit thumbnailFailed(pmfilename);   }   else   {    smallpm = bigpm.scaled(TN_WIDTH, TN_HEIGHT, Qt::KeepAspectRatio);    emit thumbnailFinished(smallpm);   }  }

第四步:GUI线程处理thumbnailFinished信号

为每个图片创建一个Label, 将之加到预先定义好的GridLayout中

void MainWin::addThumbnail(QImage smallpm)  {   static int i = 0;   static int j = 0;     QLabel* label = new QLabel;   label->setPixmap(QPixmap::fromImage(smallpm));   QGridLayout* gl = qobject_cast(previewwidget->layout());   gl->addWidget(label, j, i);   label->show();   qWarning() << "Label:" <isVisible();   i++;   if( i > previewwidget->width() / smallpm.width())   {    i = 0;    j ++;   }  }  

关键点提示:
- Qt的signal可跨线程传递, 但要注意slot执行的线程。 connect时可以在第五个参数的位置指定连接的属性(这里用了默认值), 如DirectConnection表示在connect函数执行的线程里执行slot, QueuedConnection表示slot执行在接受者所在的线程, (还有其他选项参看文档)本例中的slot都执行在GUI线程中。

- 在收到finished信号后要清除执行完毕的线程, 为了防止删除后对线程实例的访问这里用了个deleteLater方法 — 在大家都不再访问thread实例时再删除。

- Qt中所有和GUI相关的操作都要放在GUI线程里执行,包括所有Widget的创建和访问,QPixmap的类也算GUI的类, 所以本例中只能用QImage来处理图片。

- 这个例子其实用一个子线程就够了,给每张图片创建一个线程有点浪费。 例子里有些为了测试需要加的代码可能会影响阅读(比如那个waitseconds), 请大家自动忽略。 比如为了更好的看到效果,给线程里加了个sleep延缓处理图片的速度…

threadthumbnail.tar

 

本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
Qt之保持GUI响应
Qt多线程编程注意事项
qt线程(转)
QT中线程调用GUI主线程控件的问题
qt的Qt::ConnectionType详细以及信号参数传递
Linux下Qt多线程编程
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服