python多线程处理tkinter用户操作的后台事件,解决tkinter界面卡死无响应的问题

文章目录

Python在使用tkinter库制作图形化界面的时候,‘点击按钮-事件处理-显示结果’是很常见的操作,当程序遇到一个比较耗时的事件时,界面就会卡死无响应。
未响应的tk窗口
问题出现的原因在于图形化界面的本质是一个循环,通过不断刷新界面来实现事件的实时响应和更新,如果有一个事件耗时过长,就会导致循环阻塞,界面无法刷新,窗口就会无响应。

那么该如何解决?

这是一个非常简单的界面,当点击按钮时,文本框会自动填入文本
简易的按钮响应事件

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()
  • 15
    点赞
  • 60
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
好的,您的问题是关于使用Tkinter设计界面,实时获取温度、湿度、光照、土壤等传感器数据并显示在界面上,同时使用多线程库来解决问题,对吗? 首先,我们可以使用PythonTkinter库来实现界面设计。接着,您需要根据传感器的型号和接口来编写读取传感器数据的代码,并将读取到的数据实时显示在界面上。您可以使用多线程库来实现同时读取多个传感器数据的操作,以及将读取到的数据更新到界面上。 下面是一个简单的示例代码,演示如何使用Tkinter多线程库来实现您的需求: ```python import threading import tkinter as tk # 定义传感器数据获取函数 def get_sensor_data(sensor): # TODO: 根据传感器型号和接口,编写读取传感器数据的代码 data = 0 # 这里假设读取到的数据为0 return data # 定义更新界面函数 def update_gui(): # TODO: 获取传感器数据并更新界面 temp = get_sensor_data("temperature") humid = get_sensor_data("humidity") light = get_sensor_data("light") soil = get_sensor_data("soil") time = get_sensor_data("time") # 将数据更新到界面上 temp_label.config(text="Temperature: {} °C".format(temp)) humid_label.config(text="Humidity: {} %".format(humid)) light_label.config(text="Light: {} lx".format(light)) soil_label.config(text="Soil: {} %".format(soil)) time_label.config(text="Time: {}".format(time)) # 每隔1秒钟更新一次界面 gui.after(1000, update_gui) # 创建界面 gui = tk.Tk() gui.title("Sensor Data") # 创建界面元素 temp_label = tk.Label(gui, text="Temperature: -- °C") humid_label = tk.Label(gui, text="Humidity: -- %") light_label = tk.Label(gui, text="Light: -- lx") soil_label = tk.Label(gui, text="Soil: -- %") time_label = tk.Label(gui, text="Time: --") temp_label.pack() humid_label.pack() light_label.pack() soil_label.pack() time_label.pack() # 启动更新界面线程 update_thread = threading.Thread(target=update_gui) update_thread.start() # 启动界面 gui.mainloop() ``` 在这个示例代码中,我们使用了Tkinter库创建了一个界面,并创建了5个Label元素,分别用于显示温度、湿度、光照、土壤和时间数据。我们使用了多线程库,将数据获取和界面更新两个操作分别放在两个线程中运行,以避免界面卡顿。 需要注意的是,这个示例代码只是一个简单的演示,实际应用中需要根据具体的传感器型号和接口来编写相应的数据读取代码。同时,需要注意线程安全问题,避免多个线程同时修改界面元素引起的问题

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值