Qt控制流简析
前言:
Qt库及其绑定python语言的PySide库、PyQt库在圈中已经是TD的标配了,Qt提供了多种快速绘制图形窗口的方式。但正是因为这个原因,导致很多TD局限在设计窗口外观的桎梏中,而忽略了Qt更为本质的控制流。大家都写过类似这样的代码:
# test.py class Window(QtGui.QWidget):
def __init__(self, parent = None):
QtGui.QWidget.__init__(self, parent)
"do something" def func(self,*args,**kwargs):
"do something" if __name__ == '__main__':
app =QtGui.QApplication(sys.argv)win = Window()
win.show() sys.exit(app.exec_())
相信很多人在使用Qt的过程中都会有这样的疑问:
1、为什么要判断__name__ == '__main__';
2、为什么已经通过win.show()显示了自定义的窗口了,还要再添加一句sys.exit(app.exec_());
本文将通过两个简单的代码案例来分析这两个疑问,帮助TD了解Qt的控制流,从而更好的设计自己的程序,让应用更为健壮稳定。
正文:
在解答前言中两个疑问之前,首先看这样一段c++代码,并通过这段代码来解释Qt的控制流:
// test.cpp #include "stdafx.h"
#include <iostream>
#include <conio.h>
#include <windows.h> int main()
{
while (1) { Sleep(1000); char b;
b = _getche();
std::cout << '\n' << b << std::endl; }
return 0;
}
这段代码以main为程序入口向下执行,到while(1)这句时进入一个无穷循环,在这个循环中,首先_getche()会等待并获取console的输入字符,然后再将该字符输出到console中,最后结束本次循环,进入下一次循环。而main函数中最后一句return 0则必须等待while循环中断才能得到执行。
需要值得注意的是,每一次循环main函数作为主线程都会等待console的外界输入,如果console一直没有接受到外界输入,本次循环就会阻塞在这个地方,之后的输出语句也得不到执行。
实际上第二个问题中的app.exec_()就相当于这个while循环,app.exec_()启动了QEventLoop事件循环机制,我们可以将while循环中的外界输入视作Qt中的Signal,将内部输出视作Qt中的Slot,主线程中的QEventLoop循环会一直接受信号输入,然后处理信号(Signal),执行对应的槽函数(Slot)。
app.exec_()不仅启动了QEventLoop事件循环机制,还阻塞了app.exec_()之后的语句的执行,这意味着main函数不会结束,main函数中的局部变量都不会被析构,而我们在main函数中实例化的QWidget对象也不会被析构,会在main函数的生命周期中一直驻留内存,这就意味着QWdiget通过show()函数显示后会一直显示在屏幕中。
当然你也可以尝试去掉app.exec_()语句,再运行一次test.py文件,这时你会发现你的QWidget会在屏幕上一闪而过,这是因为程序没有了app.exec_()的阻塞,会非常快速的执行完毕,这意味着main函数的生命周期会很短,main函数中的局部变量都会被快速的析构,所以你的窗口会一闪而过。
看完上面的分析希望你已经初步了解了app.exec_()在Qt控制流中的作用,接下来会进一步分析如何优化Qt的控制流。
我们看到test.cpp中有一句Sleep(1000),这会导致当前循环会在此处等待1秒钟,如果等待的是1分钟会有什么后果呢?这会意味着while循环会有一分钟的时间无法处理外界输入和内部输出。在Qt中则意味着主线程会等待1分钟,在这段时间中信号得不到响应,槽函数不会被触发,语句之前显示的QWidget会得不到刷新,这就是Qt程序运行时遇到的假死情况。这个情况发生的根本原因在于有一段耗时较长的业务代码执行时阻塞了QWidget的刷新,如果要完整执行完这段业务代码,QWidget就会有对应的一段时间得不到响应。这实际上就是单一线程的性能瓶颈,单一主线程无法保证QWidget得到流畅响应的同时还能处理多个高耗时的计算任务。app.exec_()
想要解决这个单一线程的性能瓶颈问题,就需要将耗时间较长的业务代码放到另外一个线程中去执行,这样主线程就不会被阻塞,QWidget就会一直处于流畅响应的状态。
在Qt中实际上已经提供了QThread类来解决这个问题。QThread与python自带threading.Thread接口设计非常相似,你需要添加一个类继承QThread,并重新实现run()方法,将高耗时的业务代码放到run方法中,再通过QPushButton一类的控件来触发执行这样一段伪代码:
def newThread(self):
thread = SubQThread()
thread.start()
start()方法将会调用run方法在一个新的线程中来执行你的高耗时代码块。注意,新的线程是从属于主线程的,新的线程实际上主线程的子线程,在子线程中是不允许创建QWidget实例的,因为父子线程共享同一块内存空间,在子线程中创建QWidget实例会在内存逻辑中产生冲突,Qt也不允许这样做。所以推荐在主线程中创建QWidget实例,再通过信号槽机制来控制QWidget状态的更新。
现在回到前言中第一个疑问,相信大家已经明白了,__name__ == '__main__'就是判断当前线程是否是主线程,如果不是主线程,就不会创建QWidget实例并显示,也不会执行app.exec_()所包含的一系列行为,通过这种方式就避免了非主线程执行这些代码导致的内存逻辑错误。
以上就是Qt的一个基本的控制流。
结语:
写程序的本质其实就是给出一个控制流,告诉计算机应该按照怎样的行为步骤来达到我们想要的结果,本文通过两个简单的例子试图以小见大的展现Qt的控制流。希望这篇文章能够帮助相关从业者能更好的设计自己的程序。
Qt控制流简析的更多相关文章
- 简析平衡树(四)——FHQ Treap
前言 好久没码过平衡树了! 这次在闪指导的指导下学会了\(FHQ\ Treap\),一方面是因为听说它可以可持久化,另一方面则是因为听说它是真的好写. 简介 \(FHQ\ Treap\),又称作非旋\ ...
- 简析.NET Core 以及与 .NET Framework的关系
简析.NET Core 以及与 .NET Framework的关系 一 .NET 的 Framework 们 二 .NET Core的到来 1. Runtime 2. Unified BCL 3. W ...
- 简析 .NET Core 构成体系
简析 .NET Core 构成体系 Roslyn 编译器 RyuJIT 编译器 CoreCLR & CoreRT CoreFX(.NET Core Libraries) .NET Core 代 ...
- RecycleView + CardView 控件简析
今天使用了V7包加入的RecycleView 和 CardView,写篇简析. 先上效果图: 原理图: 这是RecycleView的工作原理: 1.LayoutManager用来处理RecycleVi ...
- Java Android 注解(Annotation) 及几个常用开源项目注解原理简析
不少开源库(ButterKnife.Retrofit.ActiveAndroid等等)都用到了注解的方式来简化代码提高开发效率. 本文简单介绍下 Annotation 示例.概念及作用.分类.自定义. ...
- PHP的错误报错级别设置原理简析
原理简析 摘录php.ini文件的默认配置(php5.4): ; Common Values: ; E_ALL (Show all errors, warnings and notices inclu ...
- Android 启动过程简析
首先我们先来看android构架图: android系统是构建在linux系统上面的. 所以android设备启动经历3个过程. Boot Loader,Linux Kernel & Andr ...
- Android RecycleView + CardView 控件简析
今天使用了V7包加入的RecycleView 和 CardView,写篇简析. 先上效果图: 原理图: 这是RecycleView的工作原理: 1.LayoutManager用来处理RecycleVi ...
- Java Annotation 及几个常用开源项目注解原理简析
PDF 版: Java Annotation.pdf, PPT 版:Java Annotation.pptx, Keynote 版:Java Annotation.key 一.Annotation 示 ...
随机推荐
- 自学python之路(day6)
一 函数的定义与调用 现在需要一个程序来实现len()的功能. 计算字符串 s 长度 s='好好学习' #函数的定义def my_len(): i = for k in s: i += print(i ...
- Prime ring problem,递归,广搜,回溯法枚举,很好的题
题目描述: A ring is compose of n circles as shown in diagram. Put natural number 1, 2, ..., n into each ...
- js中bind的用法,及与call和apply的区别
call和apply的使用和区别不再做阐述,可以参考我的另一篇随笔<JavaScript中call和apply方法的使用>(https://www.cnblogs.com/lcr-smg/ ...
- 记reinforcement learning double DQNS
传统的DQN算法会导致overestimate.因为在训练开始时,最大的Q值并不一定是最好的行为. 也就是说较差的行为Q值相对较大,较好的行为Q值相对较小.这时我们在更新Q值时用最大期望来计算我们作为 ...
- SpringBoot统一异常处理
/** * 异常处理器 */ @RestControllerAdvice // public class BDExceptionHandler { private Logger logger = Lo ...
- C++进程间通信的十一种方法
转载: https://www.cnblogs.com/swunield/articles/3893250.html 进程通常被定义为一个正在运行的程序的实例,它由两个部分组成: 一个是操作系统用来管 ...
- css+jquery 实现图片局部放大预览
今天有时间开始动手,使用css+jquery实现了图片局部放大的组件,首先看看效果图: 界面设计思路如下: 1.两个div,左边放图片的缩略图 2.在左边缩略图鼠标移动的时候,区域(效果图中的网格) ...
- ES6学习重难点总结(持续更新)
Symbol 1.Symbol.iterator Symbol.iterator指向对象的默认遍历器方法: String.prototype[Symbol.iterator] //ƒ [Symbol. ...
- 用chrome和anywhere,配合安卓机搭建最简单的移动端页面测试。
很多时候,我们前端在写移动端页面的时候,虽然目前chrome有调试模式,可以模拟手机的部分效果,但仍有部分效果需要直接在手机上进行页面的调试,今天就在这里推荐一个适合windows+安卓的无需连接局域 ...
- 2018 CCPC网络赛 hdu6444 Neko's loop
题目描述: Neko has a loop of size n.The loop has a happy value ai on the i−th(0≤i≤n−1) grid. Neko likes ...