前言:

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分钟,在这段时间中信号得不到响应,槽函数不会被触发,app.exec_()语句之前显示的QWidget会得不到刷新,这就是Qt程序运行时遇到的假死情况。这个情况发生的根本原因在于有一段耗时较长的业务代码执行时阻塞了QWidget的刷新,如果要完整执行完这段业务代码,QWidget就会有对应的一段时间得不到响应。这实际上就是单一线程的性能瓶颈,单一主线程无法保证QWidget得到流畅响应的同时还能处理多个高耗时的计算任务。

想要解决这个单一线程的性能瓶颈问题,就需要将耗时间较长的业务代码放到另外一个线程中去执行,这样主线程就不会被阻塞,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控制流简析的更多相关文章

  1. 简析平衡树(四)——FHQ Treap

    前言 好久没码过平衡树了! 这次在闪指导的指导下学会了\(FHQ\ Treap\),一方面是因为听说它可以可持久化,另一方面则是因为听说它是真的好写. 简介 \(FHQ\ Treap\),又称作非旋\ ...

  2. 简析.NET Core 以及与 .NET Framework的关系

    简析.NET Core 以及与 .NET Framework的关系 一 .NET 的 Framework 们 二 .NET Core的到来 1. Runtime 2. Unified BCL 3. W ...

  3. 简析 .NET Core 构成体系

    简析 .NET Core 构成体系 Roslyn 编译器 RyuJIT 编译器 CoreCLR & CoreRT CoreFX(.NET Core Libraries) .NET Core 代 ...

  4. RecycleView + CardView 控件简析

    今天使用了V7包加入的RecycleView 和 CardView,写篇简析. 先上效果图: 原理图: 这是RecycleView的工作原理: 1.LayoutManager用来处理RecycleVi ...

  5. Java Android 注解(Annotation) 及几个常用开源项目注解原理简析

    不少开源库(ButterKnife.Retrofit.ActiveAndroid等等)都用到了注解的方式来简化代码提高开发效率. 本文简单介绍下 Annotation 示例.概念及作用.分类.自定义. ...

  6. PHP的错误报错级别设置原理简析

    原理简析 摘录php.ini文件的默认配置(php5.4): ; Common Values: ; E_ALL (Show all errors, warnings and notices inclu ...

  7. Android 启动过程简析

    首先我们先来看android构架图: android系统是构建在linux系统上面的. 所以android设备启动经历3个过程. Boot Loader,Linux Kernel & Andr ...

  8. Android RecycleView + CardView 控件简析

    今天使用了V7包加入的RecycleView 和 CardView,写篇简析. 先上效果图: 原理图: 这是RecycleView的工作原理: 1.LayoutManager用来处理RecycleVi ...

  9. Java Annotation 及几个常用开源项目注解原理简析

    PDF 版: Java Annotation.pdf, PPT 版:Java Annotation.pptx, Keynote 版:Java Annotation.key 一.Annotation 示 ...

随机推荐

  1. java知识点总结--java数据类型

    java中的两大数据类型 1.基本数据类型:也称作内置类型,java语言本身提供的基本数据类型是其他类型(包括java核心类和用户自定义类)的基础 2.引用数据类型:java语言根据基本类型扩展数的其 ...

  2. JavaScript 中的常用12种循环遍历(数组或对象)的方法

    1.for 循环 let arr = [1,2,3]; for (let i=0; i<arr.length; i++){ console.log(i,arr[i]) } // 0 1 // 1 ...

  3. java 反射简说

    1 Class类 就是类的类型.研究反射先了解下Class类. 获取类的类型的三种方法: Class c1 = 对象.getClass(); Class c2 = Class.forName(&quo ...

  4. CSS效果:不怎么样的登录表单

    HTML: <html lang="en"> <head> <meta charset="UTF-8"> <meta ...

  5. npm ERR! Unexpected end of JSON input while parsing near...错误

    问题解决方案在GitHub中: https://github.com/vuejs-templates/webpack/issues/990 总结一下:1.删除package-lock.json 2.进 ...

  6. 面试知识点准备-C++常见问题

    博客园写写格式简单的文章还行,格式一复杂就不行了,可能是我不会用吧,我有强迫症,有道云格式很好用,以后去有道写这种东西了 有道云笔记链接:http://note.youdao.com/noteshar ...

  7. iOS和小米手机拍照上传后,在web端显示旋转

    ( ′◔ ‸◔`)现在的公司啊都流行混合开发,我们公司也不例外,非要把交互非常多的社区模块用内嵌web页展示,好吧好吧,毕竟有的应用也是这么做的,那既然是社区就肯定少不了用户上传图片的操作,在开发阶段 ...

  8. java 基础代码

    /** * Created by y0n on 2017/4/17. * 1.输出控制台传递的默认参数 */public class JavaDay001_1 { public static void ...

  9. redis命令Sortedset 类型(八)

    Sortedset又叫zset Sortedset是有序集合,可排序的,但是唯一. Sortedset和set的不同之处, 是会给set中的元素添加一个分数,然后通过这个分数进行排序. 命令 1> ...

  10. AGC002 F - Leftmost Ball

    貌似哪里讲过这题..总之当时掉线了(理解能力又差水平又低选手的日常).. 看看题目,应该是DP. 尝试了几次换状态,毫无思路.那我们就来继续挖掘性质吧...为了更直观,我们令第i个出现的球颜色就是i( ...