大家好,最近在完善手头上一个基于事件系统的 GUI 。
现在遇到一个问题,就是当我在执行一个按钮点击事件的时候,实际会开一个子线程进行业务逻辑的处理,这个处理过程可能会比较长,并且中间可能会出现一些不符合预期的情况发生,当发生这种情况时,我希望会借由发送一个错误的事件通知 EventManager,然后调用 listener 的方法来立刻终结这个对应的错误线程(因为之前已经发生了错误了,后续逻辑代码继续执行没有意义了)
代码如下,大佬们直接复制运行即可观察。如能解答,万分感谢!
from queue import Queue, Empty
from threading import *
from tkinter import *
import time
from tkinter import ttk
EVENT_TYPE_1 = "Count"
EVENT_TYPE_2 = "Error"
MAX_NUMBER = 10
CUR_NUMBER = 0
class event_manager:
def __init__(self):
self._eventQueue = Queue()
self._thread = Thread(target=self.Run, daemon=True)
self._handlers = {}
self._active = False
def Start(self):
self._active = True
self._thread.start()
def Run(self):
while self._active is True:
try:
event = self._eventQueue.get(block=True, timeout=1)
self.Process(event)
except Empty:
pass
def Process(self, event):
if event.type in self._handlers:
for handler in self._handlers[event.type]:
handler()
else:
pass
def Stop(self):
self._active = False
self._thread.join()
def addEventListenter(self, type_, handler):
try:
handlerList = self._handlers[type_]
except KeyError:
handlerList = []
self._handlers[type_] = handlerList
if handler not in handlerList:
handlerList.append(handler)
def removeEventListenter(self, type_, handler):
try:
handlerList = self._handlers[type_]
if handler in handlerList:
handlerList.remove(handler)
if not handlerList:
del self._handlers[type_]
except KeyError:
pass
def sendEvent(self, event):
self._eventQueue.put(event)
class Event:
def __init__(self, event_event_name, cur_done_task, type_=None):
self.type = type_
self._event_name = event_event_name
self._curDoneTask = cur_done_task
class EventSource:
def __init__(self, event_name, event_mgr, max_number, type):
self._event_name = event_name
self._event_manager = event_mgr
self._type = type
self._max_number = max_number
def count(self):
global CUR_NUMBER
for i in range(self._max_number):
CUR_NUMBER = i + 1
if CUR_NUMBER == 4: # 在业务逻辑线程中增加检测环节,如果发生错误就会发送错误事件,希望可以立刻终结当前的线程,不执行后续的代码
print("************ detect error occurred , this thread should be terminated immediately !")
errorEvent = Event("error", CUR_NUMBER, type_=EVENT_TYPE_2)
self._event_manager.sendEvent(errorEvent)
print(
"************ main thread start:now start process {} - count : {}".format(self._event_name, CUR_NUMBER))
event = Event("test", CUR_NUMBER, type_=self._type)
self._event_manager.sendEvent(event)
time.sleep(1)
class GUIListener(Tk):
def __init__(self):
super(GUIListener, self).__init__()
self.title("Progress GUI")
self.geometry("1200x805+600+100")
self.config(bg="#535353")
self.resizable(True, True)
self.taskThread = None
self.progressBar = ttk.Progressbar(master=self, orient=HORIZONTAL, maximum=MAX_NUMBER, length=300)
self.progressBar.pack()
self.button = ttk.Button(self, text="Run", command=lambda: self.button_function(MAX_NUMBER))
self.button.pack()
def update_progress_value(self):
print("************Sub thread start: detect progress bar value is now...{}".format(self.progressBar['value']))
self.progressBar['value'] = CUR_NUMBER
self.progressBar.update_idletasks()
print("************Sub thread start: update progress bar value to...{}".format(CUR_NUMBER))
def button_function(self, max_number):
# 在正式开始执行逻辑子线程之前,确实可以提前做一些判断,来决定是否满足条件,开始接下来的逻辑子线程,但是这个不在本次讨论范围内
es = EventSource("eventSource", eventMgr, max_number, EVENT_TYPE_1)
self.taskThread = Thread(target=es.count, daemon=True).start() # 这里开始执行实际的业务逻辑子线程
def terminate_error_thread(self): # 这个方法在 GUIListener 接受到事件源发出的错误逻辑时被唤起,用来立刻终结正在执行事件源的线程,不做后续无用的代码逻辑处理
pass
# TODO: but how to implement this method ?
if __name__ == '__main__':
gui = GUIListener()
eventMgr = event_manager()
eventMgr.addEventListenter(EVENT_TYPE_1, gui.update_progress_value)
eventMgr.addEventListenter(EVENT_TYPE_2, gui.terminate_error_thread)
eventMgr.Start()
gui.mainloop()
顺便一提,希望得到的结果是这样的
************ main thread start:now start process eventSource - count : 1
************Sub thread start: detect progress bar value is now...0.0
************Sub thread start: update progress bar value to...1
************ main thread start:now start process eventSource - count : 2
************Sub thread start: detect progress bar value is now...1
************Sub thread start: update progress bar value to...2
************ main thread start:now start process eventSource - count : 3
************Sub thread start: detect progress bar value is now...2
************Sub thread start: update progress bar value to...3
************ detect error occurred , this thread should be terminated immediately !
到这里就应该自然停止。
这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。
V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。
V2EX is a community of developers, designers and creative people.