IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Python知识库 -> PySimpleGUI教程3 - 事件逻辑速成 -> 正文阅读

[Python知识库]PySimpleGUI教程3 - 事件逻辑速成

PySimpleGUI教程3 - 事件逻辑速成

这一部分主要是来解决怎么“优雅”的解决事件问题。我们已经知道了PySimpleGUI使用事件循环来处理GUI事件,这里,我们考虑怎么把我们的功能代码和显示代码分离,以及解决多线程问题。

事件循环

我们用一个简单的例子来回顾一遍我们学过了什么。我们简单的来写一个计算器:

import PySimpleGUI as sg

progress = sg.ProgressBar(100)
num1_inp = sg.Input(size=5)
num2_inp = sg.Input(size=5)
result_txt = sg.Text()
add_btn = sg.Button('add')

layout = [
    [num1_inp, num2_inp, result_txt],
    [progress],
    [add_btn]
]

window = sg.Window('slow calculator', layout)

while True:
    event, value = window.read()
    if event == sg.WINDOW_CLOSED: break
    if event == 'add':
        num1 = int(num1_inp.get())
        num2 = int(num2_inp.get())
        result_txt.update(str(num1 + num2))

我们可以看到,确实可以正常运行:

在这里插入图片描述

但是很明显,这不是一个好的代码规范。我们把GUI代码和逻辑代码混写在一起,现在看起来代码还可以,等我们要实现的功能多了,代码可就要向“屎山”进化了。我们先把界面响应和功能实现分别抽离出来:

# omitted...

def add(a, b):
    return a + b

def add_btn_click():
    num1 = int(num1_inp.get())
    num2 = int(num2_inp.get())
    result_txt.update(add(num1, num2))

while True:
    event, value = window.read()
    if event == sg.WINDOW_CLOSED: break
    if event == 'add':
        add_btn_click()

我们这次把功能代码 add() 和界面触发 add_btn_click() 单独写成一个方法,这样GUI和功能实现就不会相互干扰,你就可以专注不同部分的代码编写了。这时,功能代码完全可以单独抽到一个文件里编写,再也不用看着GUI代码头疼了。当然,这里的代码还可以做的更好:

import PySimpleGUI as sg
import api

# omitted...

def add_btn_click(value):
    num1 = int(num1_inp.get())
    num2 = int(num2_inp.get())
    result_txt.update(api.add(num1, num2))

event_callbacks = {
    'add': add_btn_click
}

while True:
    event, value = window.read()
    if event == sg.WINDOW_CLOSED: break
    if event in event_callbacks:
        event_callbacks[event](value)

这次,我们把功能代码单独放在了外部文件 api.py 里。并且,添加了一个event_callbacks的map,以后只需要管理这个map就好了!

长时任务

接下来的问题也是编写GUI头疼的一大难题:长时任务。当你的功能代码很耗时的时候,GUI就会直接卡在那里…看下面的代码:

import time

def add(a, b):
    time.sleep(5)
    return a + b

假设我们的大聪明功能 add 非常耗时,算一次加法要5秒。按照之前的代码,界面就真的会卡住5秒…下面给两种解决方法:

多线程(不推荐)

一个常见的解决方案是多线程。这里需要注意的是, 谨慎使用多线程!! 看官方的解释:

在这里插入图片描述

当然了,这里的意思不是说不能用,只是说最好确保GUI线程是主线程。那么,直接用多线程的话,我们可以使用Window.write_event_value 函数。可以在新线程里调用这个函数来通知窗体事件。

多线程的话,我们需要让 add 函数通知主窗体一个事件,当完成运算时发送一个事件来通知结果:

def add(a, b, window):
    time.sleep(5)
    window.write_event_value("add_output", a+b)

write_event_value需要传入两个参,key和value。它们和 window.read() 获取的值基本是一样的。

接着GUI代码:

import PySimpleGUI as sg
import threading
import api

# omitted...

def add_btn_click(value):
    num1 = int(num1_inp.get())
    num2 = int(num2_inp.get())
    threading.Thread(target=api.add, args=(num1, num2, window)).start()

def add_output(value):
    result_txt.update(str(value['add_output']))

event_callbacks = {
    'add': add_btn_click,
    'add_output': add_output
}

while True:
    event, value = window.read()
    if event == sg.WINDOW_CLOSED: break
    if event in event_callbacks:
        event_callbacks[event](value)

这里要额外添加一个 add_output 事件来接收值。是的,不推荐这种写法的原因是因为太麻烦了…官方本身有更好的封装。

perform_long_operation(推荐)

使用 window.perform_long_operation 可以更简单的解决问题。我们在事件循环里使用它。第一个参数是需要执行的函数,第二个参数是执行后需要触发的事件。对应函数的返回值会作为window.read()获得的value。和刚刚几乎一样的代码,只不过没有用多线程:

import PySimpleGUI as sg
import api

# omitted...

def add_btn_click(value):
    num1 = int(num1_inp.get())
    num2 = int(num2_inp.get())
    window.perform_long_operation(lambda :api.add(num1, num2), 'add_output')

def add_output(value):
    result_txt.update(str(value['add_output']))

event_callbacks = {
    'add': add_btn_click,
    'add_output': add_output
}

while True:
    event, value = window.read()
    if event == sg.WINDOW_CLOSED: break
    if event in event_callbacks:
        event_callbacks[event](value)

不过这次,可以保留原来的add函数:

import time

def add(a, b):
    time.sleep(5)
    return a+b

这里注意,当需要让perform_long_operation触发带参方法时,需要使用lambda。直接按上文写法就好了。

偷懒写法(不推荐)

这里就直接把要更新的组件给传进新线程了,新线程里会直接调用组件的方法来修改值。下面的代码使用了perform_long_operation直接让add_btn_click在新进程里调用,这样连接收值的事件都不用了,直接在新线程里修改result_txt。当然不推荐这种写法,虽然我在测试里是可以这样成功运行,但官方貌似没有这种示例,所以可能有bug。这次是完整的代码:

import PySimpleGUI as sg
import api

progress = sg.ProgressBar(100)
num1_inp = sg.Input(size=5)
num2_inp = sg.Input(size=5)
result_txt = sg.Text()
add_btn = sg.Button('add')

layout = [
    [num1_inp, num2_inp, result_txt],
    [progress],
    [add_btn]
]

window = sg.Window('slow calculator', layout)

def add_btn_click(value): # multi-thread this method directly
    num1 = int(num1_inp.get())
    num2 = int(num2_inp.get())
    output = api.add(num1, num2)
    result_txt.update(str(output))

event_callbacks = {
    'add': add_btn_click
}

while True:
    event, value = window.read()
    if event == sg.WINDOW_CLOSED: break
    if event in event_callbacks:
        window.perform_long_operation(lambda :event_callbacks[event](value), '')

不过确实很方便就是了,目前我也没碰着过bug,写写代码自用应该没有什么问题。

进度条

最后以一个常见需求结束吧。进度条需要功能代码运行时实时提醒界面更新进度条的值。可以用write_event_value实现,也可以偷懒直接把进度条组件作为入参修改值。这里还是用偷懒写法了(因为真的懒…),所以直接在偷懒写法的版本上做修改了。

修改add方法,现在add时同时更新进度条:

import time

def add(a, b, progress_bar):
    for i in range(100):
        time.sleep(0.05)
        progress_bar.update(i+1)
    return a+b

把进组条组件传进去:

def add_btn_click(value):
    num1 = int(num1_inp.get())
    num2 = int(num2_inp.get())
    output = api.add(num1, num2, progress)
    result_txt.update(str(output))

现在进度条可以正常显示了:

在这里插入图片描述

  Python知识库 最新文章
Python中String模块
【Python】 14-CVS文件操作
python的panda库读写文件
使用Nordic的nrf52840实现蓝牙DFU过程
【Python学习记录】numpy数组用法整理
Python学习笔记
python字符串和列表
python如何从txt文件中解析出有效的数据
Python编程从入门到实践自学/3.1-3.2
python变量
上一篇文章      下一篇文章      查看所有文章
加:2022-05-10 11:52:00  更:2022-05-10 11:53:54 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/15 15:14:06-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码