Python3 tkinter中使用event.wait()让多线程暂停、恢复和终止
最近使用tkinter完成一个小工具,期间在百度搜索到很多用多线程event.wait()使程序暂停的demo,控制while死循环的暂停。这里分享一个实例,使用event.wait()实现 在tkinter中用按钮让一个耗时程序暂停、恢复和终止。
实现步骤:
1.导入相关库
import threading
import time
from mttkinter import mtTkinter as tk
import tkinter.messagebox # 引入弹窗库,防止解释器弹出报错。
这里使用mttkinter代替,因为在tkinter中使用多线程会报错:
main thread is not in main loop
这是tkinter的问题,使用mttkinter代替就好
2.创建线程类
class MyThread(threading.Thread):
def __init__(self, func, *args):
# 线程初始化
super().__init__(target=func, args=args)
self.func = func
self.args = args
self.__flag = threading.Event() # 用于暂停线程的标识
self.__flag.set() # 设置为True
self.__running = threading.Event() # 用于停止线程的标识
self.__running.set() # 将running设置为True
# 开始执行
self.thread_run()
# 打包进线程(耗时的操作)防止tkinter单线程阻塞
def thread_run(self):
self.__flag.wait() # 为True时立即返回, 为False时阻塞直到内部的标识位为True后返回
self.setDaemon(True) # 守护--就算主界面关闭,线程也会留守后台运行
self.start() # 在这里开始
def pause(self):
self.__flag.clear() # 设置为False,让线程阻塞
print('阻塞线程')
def resume(self):
self.__flag.set() # 设置为True,让线程停止阻塞
print('已恢复线程')
def stop(self):
self.__flag.set() # 将线程从暂停状态恢复, 如何已经暂停的话
self.__running.clear() # 设置为False
这里定义了线程类,原理很简单,传入一个函数,使用Threading为函数创建线程,然后定义暂停,恢复,终止的函数,通过threading.Event()来实现
3.tkinter窗口和业务函数
class TkinterWindow:
def __init__(self):
self.isRun = False # 是否有线程正在执行
self.myThread = None # 正在执行的线程(MyThread类的对象)
# 信息窗口
def messagebox(self):
def main_active(pay_type, act_type):
try:
...
# 这里的函数需要请求多个接口,返回数据需要一定的时间
self.user_login()
# 在每个耗时操作的函数后加上如下代码
self.myThread._MyThread__flag.wait() # 为True时立即返回, 为False时阻塞直到内部的标识位为True后返回
self.zf_adjust_search(paytype=1)
self.myThread._MyThread__flag.wait() # 为True时立即返回, 为False时阻塞直到内部的标识位为True后返回
self.zjzfAdjust() # 耗时函数
except AttributeError:
print("任务已终止") # 捕获异常,当点击终止任务时,清空self.myThread,这时可以用作跳出循环
# 判断是否已被中断(正常结束就关闭线程)
if self.myThread:
self.myThread.stop()
self.myThread = None # 执行结束后,关闭线程
self.isRun = False
self.ternimal_print('任务已结束', 'info')
def thread_it(func, *args):
# 开始线程
if self.myThread:
tkinter.messagebox.showinfo(title='提示', message='当前已存在执行的任务,请结束任务后再试')
else:
self.myThread = MyThread(func, *args) # 用传入的业务函数实例化一个线程
# 暂停或恢复
def pause_resume():
if self.myThread: # 如果存在执行的任务
# 暂停和恢复
if self.isRun: # True表示正在运行
if self.myThread._MyThread__flag.is_set(): # 如果为True表示在执行(返回False表示暂停)
mess = tkinter.messagebox.askquestion(title='提示', message='确认要暂停任务?')
if mess == 'no':
print('Choose no!')
else:
print('choose yes')
self.myThread.pause()
self.isRun = False
self.ternimal_print('暂停程序', 'warning')
# 在label显示任务已暂停
var1.set("任务已暂停")
else:
tkinter.messagebox.showinfo(title='提示', message='没有需要暂停的任务')
else:
self.myThread.resume()
self.isRun = True
# 在label显示任务恢复,并显示正在执行的任务
var1.set("恢复任务程序运行")
self.ternimal_print('恢复程序运行', 'warning')
else:
tkinter.messagebox.showinfo(title='提示', message='当前没有正在执行的任务')
# 终止函数
def stop():
if self.myThread: # 如果存在执行的任务
mess = tkinter.messagebox.askquestion(title='提示', message='确认结束任务?')
if mess == 'no':
print('Choose no!')
else:
print('choose yes')
self.myThread.stop() # 结束任务
self.isRun = False
self.myThread = None
self.ternimal_print('已结束任务', 'warning')
# 在label中显示已结束任务
var1.set("任务已终止")
else:
tkinter.messagebox.showinfo(title='提示', message='当前没有正在执行的任务')
window = tk.Tk() # 生成主窗口,命名 window
window.title('信息查看窗口') # 定义主窗口标题
window.geometry('800x600') # 定义主窗口的长宽
fm_btn = tk.Frame(window) # 按钮
fm_btn.pack()
# 结束按钮
tk.Button(fm_btn, text='结束任务', command=stop).pack(side='right', fill='y', expand='yes')
# 暂停/恢复按钮
tk.Button(fm_btn, text='暂停/恢复', command=pause_resume).pack(side='right', fill='y', expand='yes')
# 按钮选择一项更正 耗时操作,使用线程
tk.Button(fm_btn, text='更正当前选项', command=lambda: thread_it(main_active, 'zjzf', 'get')).pack(side='right', fill='y', expand='yes') # 定义一个按钮,用来后续触发弹窗
# 按钮遍历更正全部
tk.Button(fm_btn, text='更正所有项', command=lambda: thread_it(main_active, 'zjzf', 'loop')).pack(side='right', fill='y', expand='yes')
# 按钮选择一项更正
tk.Button(fm_btn, text='更正当前选项SQ', command=lambda: thread_it(main_active, 'sqzf', 'get')).pack(side='right', fill='y', expand='yes') # 定义一个按钮,用来后续触发弹窗
# 按钮遍历更正全部
tk.Button(fm_btn, text='更正所有项SQ', command=lambda: thread_it(main_active, 'sqzf', 'loop')).pack(side='right', fill='y', expand='yes')
# 三个耗时函数
def user_login(self):
...
def zf_adjust_search(self, paytype):
...
def zjzfAdjust(self) # 耗时函数
...
按钮绑定创建线程函数,业务函数作为该函数的参数,实例化一个线程
然后通过pause_resume和stop函数来控制线程的暂停和恢复