文章目录
Python在使用tkinter库制作图形化界面的时候,‘点击按钮-事件处理-显示结果’是很常见的操作,当程序遇到一个比较耗时的事件时,界面就会卡死无响应。
问题出现的原因在于图形化界面的本质是一个循环,通过不断刷新界面来实现事件的实时响应和更新,如果有一个事件耗时过长,就会导致循环阻塞,界面无法刷新,窗口就会无响应。
那么该如何解决?
这是一个非常简单的界面,当点击按钮时,文本框会自动填入文本
from tkinter import *
class Main():
def __init__(self):
root = Tk()
self.entry = Entry(root)
self.button = Button(root, text='执行',command=self.do_something)
self.grid()
root.mainloop()
def grid(self):
self.entry.grid(row=0,column=0)
self.button.grid(row=0,column=1)
def do_something(self):
self.entry.insert('0', '结束了')
if __name__ == '__main__':
Main()
现在,我需要让事件执行的时间长一些,使用sleep强制等待来模拟事件执行。
def do_something(self):
sleep(10)
self.entry.insert('0', '结束了')
点击按钮10秒后,文本框填入“结束了”。
但是窗口无法移动,无法操作,甚至无响应。
导入Python线程库Threading,定义一个生成子线程的函数,并由子线程来触发事件
def start_thread(self):
# 使self.do_something函数在子线程中运行
insert_data = threading.Thread(target=self.do_something)
insert_data.start()
然后,我们就可以通过调用该函数生成子线程并由子线程完成事件,所以按钮的command参数应该传入这个线程函数
self.button = Button(root, text='执行',command=self.start_thread)
如果还需要传参,可以将参数放在线程函数中或__init__中
def do_something(self,data):
sleep(10)
self.entry.insert('0', data)
def start_thread(self):
data = '结束了'
insert_data = threading.Thread(
target=self.do_something,
args=(data,))
insert_data.start()
if __name__ == '__main__':
Main()
但是,我觉得这并不是很方便,在按钮和事件中插入了第三者,而这个线程函数并不复杂,只有传参和调用,于是我们可以应用lambda替换它。
self.button = Button(root, text='执行',
command=lambda :threading.Thread(
target=self.do_something,args=('结束了',)
).start())
总结
当按钮事件耗时较长时,我们可以在原来’的按钮->事件’间插入子线程,变成’按钮->子线程,子线程->事件’,从而实现子线程处理事件;当子线程函数很简单时,用lambda替换。
——————————————————————————
以下是完整代码:(倒计时10秒)
import threading
from time import sleep
from tkinter import *
class Main():
def __init__(self):
root = Tk()
self.entry = Entry(root)
time = 10
self.button = Button(root, text='执行',
command=lambda :threading.Thread(
target=self.do_something,
args=(time,)
).start())
self.grid()
root.mainloop()
def grid(self):
self.entry.grid(row=0,column=0)
self.button.grid(row=0,column=1)
def do_something(self,data):
for i in range(data,0,-1):
self.entry.insert('0', f'倒计时{i}秒')
sleep(1)
self.entry.delete('0',END)
else:
self.entry.insert('0', '倒计时结束')
if __name__ == '__main__':
Main()
版权声明:本文为qq_51188076原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。