python pyqt5label被点击触发_Python 做 UI 超 easy!(6.3)——分身有术,利用多线程并行处理...

v2-2c02d99ba259a2741c1830b6d41b8fe3_1440w.jpg?source=172ae18b

上一节已经完成对服务端的改造,服务端可以正常将客户端 POST 过来的数据 print 出来,起码逻辑上服务端已经具备接收能力,虽然很糙。

TIPS
无论大产品、小产品,都是从粗糙到细致,不断 ”打磨“,最终逼近目标,不要对半成品有偏见。

有同学在 6.2 节遇到问题: 没有合适的 HTTP 调试工具,对命令行使用 Curl 也不熟悉。下午抽空写了个简单的图形化 HTTP 调试工具 ,纯 Python 的,大家可以按需取用。下面是使用场景。

JiangChuanGo/tools​github.com
v2-2dce1af1396e9f161fe10a266165d629_ipico.jpg

v2-351e92da8c866250c7f12fc8f8f35a9b_b.gif

继续上一节的问题,服务端收到数据,如何在视窗中输出?

一个程序,两个死循环!

按找常识来说,一个流程一旦陷入死循环,就不可能再执行其他的死循环了。

v2-e45ca59305237a238998c29480add28d_b.jpg

但是我们确实需要两个死循环分别用于:

  • 客户端可能随时发来数据,在死循环中等待来自网络的数据;
  • 一个循环来等待 window.read 函数返回。

按照常规方法,显然是二者无法兼顾了。

TIPS
有同学可能会问,为什么两个逻辑不在同一个循环里解决呢?这是个很好的想法,但是我们使用的 bottle 库没有直接暴露事件循环给我们,暂时不考虑这种做法。不过这是一个很棒的想法,工程中也确实有这种做法:多种库由统一的事件循环驱动。

如果计算机一次只能干一件事,那太无聊了,现实中我们也会一边听歌、一边码字。计算机具备同时干多件事的能力。

TIPS
实际上即使是听歌一件事,对计算机来说也是多个任务构成的:听歌不停变化的软件界面、音频解码等等。

计算机同时处理多个事务,就是它的并行计算。并行处理可选的技术有:

  • 多线程
  • 多进程

等技术,在逻辑上它们二者差异不大,差异可以百度。我们今天使用多线程技术。

TIPS
Python 编程一定要站在巨人的肩膀上,多借助第三方库。

threading 库可以帮助我们在一个程序里,运行多个业务流,实现并行处理。

利用并行处理技术,前面遇到的两个死循环的流程,看起来就会是这样:

v2-ced16fec2fd7b5f1090077cfeb681a11_b.jpg

图最底下的 Queue 是两个线程之间的红色连接线的放大。

左边是视窗的事件循环,与之前天气预报程序的循环是一样的;右边是 bottle 的事件循环,功能是上一节实现的 web 服务,可以接受客户端 post 的数据。它们各自所在的程序执行流,称之为线程,它们的执行进程互不干扰,各自为政。

先有沟通,才能协作

解决了一个程序执行两个事件循环的问题之后,两个线程不能各自割据,它们被创造出来是为了共同协作,实现我们要的功能:接收客户端的数据,并显示在视窗里。web 服务线程需要将收到的消息,发送给视窗线程。

多个线程之间协作,与多人协作一样,取拿之间如果协商不好,就会发生 “竞争”,比如刚取了一半新数据就覆盖了剩下的数据,这当然不是我们想看到的。一种做法是在两个线程之间放置一个管道,有点像传送带。web 服务线程一收到消息,就放到这个管道的一端,视窗线程则从另外一边不断的取出消息。

v2-eb40727c80e4422bb1d342ed3d027035_b.jpg
TIPS
这种管道实际上是先进先出(FIFO)队列,与平时排队类似,出入口各一;其他的还有先进后出队列,出入口共用。

我们会使用 queue 库来实现这样的功能。

视窗的同步和异步

bottle 库的事件处理本身就是异步的,当有新的消息的时候,消息处理函数才会被执行(称为回调用函数 callback function),新消息总是会被及时放到管道中。但是视窗事件循环却不是这样。

观察之前天气预报程序中 window.read() 一行,如果在其后一行设置断点,你会发现如果视窗中没有按钮被按下,Python 执行流会永远 “卡” 在 window.read(),无法执行。

v2-fec558db8fab09a5b4b4e2a1df8e026a_b.gif
TIPS
在第 31 行设置了断点,这一行已经处于 “事件循环” 中。
点击按钮以后,Python 才会停在第 31 行的断点处。
之后单步执行,一轮循环结束重新进入第 29 行的时候,可以看到 Python 没有继续向下执行,而是卡在第 29 行,直到 read 函数再次被按钮触发返回,才继续执行。

我们一直以来用的都是 PySimpleGUI 的默认模式——同步模式,除此之外还有异步模式,正好可以解决手头的问题。

v2-fa26b83ecb162c257ffa10784305667a_b.jpg

同步模式是完全由用户操作驱动的,“敌不动,我不动”,如果用户不点击按钮,整个程序就死等。好比有的人比较憨,打一下动一下,不打不动。

同步模式有什么好处呢?逻辑简单!来活干活调整视窗,没活歇着,简单明了。同时带来的局限性就是,程序无法主动更新视窗,因为没有事件触发的话, 除了 read 函数,其他什么指令都不会被执行。

这种情况下,如果没有用户干预,管道里 web 服务发来的消息就永远不会被取出了,肯定是不行的。总不能让用户守着,过一会而点一下按钮:“你看看有人给我发消息没有”。这就成了大家所谓的 “智障” 产品了。我们需要视窗更主动一些:

  • 如果有用户触发,处理用户行为;
  • 如果有空(比如过了 100 毫秒用户也没有动作),就去看看有没有新消息,如果有的话,update 到视窗中。

这样程序就可以抽空( 100 毫秒没有用户事件发生 )去检查新消息。在异步模式下,有两种事件让 read 函数返回:

  • 用户触发;
  • 达到指定的时间,超时触发;

与用户点击按钮会将 event 设置为触发组件的 key 类似(希望你还记得,它是字符串,比如 “-CITY-”),超时触发的时候 event 也会被设置为一个 key:"__TIMEOUT__"。

TIPS
"__TIMEOUT__" 是超时事件的默认 event 名称,你也可以在 read 函数中用 timeout_key 参数指定一个新的字符串,作为超时事件的 event 名称。

我们做一个简单的 demo 演示这种超时,我们做一个计时器,每 1s 加 1,设置这么长的间隔,目的是让大家理解,PySimpleGUI 是如何抽空去更新视窗中的 “计数” 的。

代码在这里!

https://github.com/JiangChuanGo/examples/blob/master/PySimpleGUIDemos/bottle_demo/simpleCounter.py​github.com

v2-63eaf457bdaeb2ac7efc0b6dd0e00748_b.gif

可以看到,如果用户不点击触发按钮事件,read 就会因为被超时触发,进入处理超时事件的代码:增加计数。

一旦我们快速的触发按钮事件,发现计数器不会增加了,这是因为每触发一次事件,read 函数的计数器就会重置,重新开始计数,下一轮依然有用户输入的话,超时任务就不会被执行。

实际应用中,超时事件在十几毫秒到 200 毫秒之间,可以保证在用户的两次点击之间,超时逻辑肯定会被执行几次。

只要没有用户输入,超时逻辑就会被周期性的执行,这非常适合主动更新视窗的工作,我们稍后的工作也是围绕着超时事件的处理来做,完成从管道中取数据,更新显示的工作。

俯瞰全局

前面探讨的内容,主要解决了几个问题:

  • 怎么在一个程序里跑多个死循环:用多线程;
  • 多个线程怎么协作:用先进先出队列当作通信管道;
  • 视窗线程无法主动更新显示:使用 read 函数的异步模式,产生超时事件来主动更新显示。

让我们将上一节中的这个图:

v2-f478733e934452f8ce105a99badfb36b_b.jpg

结合今天得到的三个新解决方案,得到这样的图示:

v2-7036bf969a74d8af788047d1a7793f93_b.jpg

我们会在 web 服务线程的 post 方法请求函数中将收到的消息放入管道;视窗线程定时检查管道,有消息的话,就更新显示。

其实逻辑不是很复杂,这一图足以,除了我的艺术细菌有点变异之外,笔芯~

总结:

本来今天想一鼓作气把代码讲完,但是一不小心已经这么长的内容了,不如放到明天,从容的展开好了。

编程其实只要能过了语言关,剩下的主要就是:

  • 逻辑问题、
  • 第三方库、

问题,假以时日打通任督二脉,什么语言就都不在话下。

编程的核心在于思想不在编程语言,就像写作的精髓在故事,而不是写字本身。

加油了,明天见~

今天用到的代码

https://github.com/JiangChuanGo/examples/blob/master/PySimpleGUIDemos/bottle_demo/simpleCounter.py​github.com

以及 HTTP 调试工具

JiangChuanGo/tools​github.com
v2-2dce1af1396e9f161fe10a266165d629_ipico.jpg

关注我,了解程序员的烧脑日常,还有开源的视频教程。

v2-798e16418d4982ad9a60e316232b7dd3_b.jpg
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值