1.需求背景:
为了实现子线程通知主线程刷新UI功能,并且不阻塞其他线程的执行.
2.技术调研
在 Python 中,你可以使用多种方式实现类似于 iOS notification 的通知机制。例如,你可以使用标准库中的 Observer
设计模式,或者使用一些第三方库如 pydispatcher
或 blinker
。
3.代码实现
下面是一个使用 Observer
模式来模拟通知的简单示例:
3.1 tkinter(系统GUI)
python
import threading
import time
import tkinter as tk
import queue
class EventNotifier(threading.Thread):
def __init__(self, event_queue):
threading.Thread.__init__(self)
self.event_queue = event_queue
def run(self):
while True:
time.sleep(2)
self.event_queue.put("Event")
class MainWindow(tk.Tk):
def __init__(self, event_queue):
tk.Tk.__init__(self)
self.event_queue = event_queue
self.label = tk.Label(self, text='Waiting for event')
self.label.pack()
self.check_event_queue()
def check_event_queue(self):
try:
event = self.event_queue.get_nowait()
if event:
self.label['text'] = 'Received event: ' + str(event)
except queue.Empty:
pass
# tk定时器,100毫秒后继续执行self.check_event_queue函数,这个函数的执行不会阻塞前面的程序。
self.after(100, self.check_event_queue)
if __name__ == "__main__":
event_queue = queue.Queue()
notifier = EventNotifier(event_queue)
notifier.start()
window = MainWindow(event_queue)
window.mainloop()
上述代码中,EventNotifier
线程每隔2秒往队列中添加一个事件,MainWindow
是一个Tkinter窗口,它每隔100毫秒检查一次队列中是否有新的事件,如果有,就更新标签的文本。
注意:为了线程安全,我们使用了queue.Queue
这样的线程安全的队列来作为事件的传递。
3.2 pyQt(三方GUI)
python
import threading
import time
import tkinter as tk
import queue
class EventNotifier(threading.Thread):
def __init__(self, event_queue):
threading.Thread.__init__(self)
self.event_queue = event_queue
def run(self):
while True:
time.sleep(2)
self.event_queue.put("Event")
class MainWindow_pyqt(QLabel):
def __init__(self, event_queue):
super().__init__('Waiting for task...')
self.event_queue = event_queue
self.check_event_queue()
def check_event_queue(self):
try:
event = self.event_queue.get_nowait()
if event:
self.setText(str(event))
jmlog(f'处理通知{str(event)}')
except queue.Empty:
pass
# 单次触发的定时器,1s后继续执行self.check_event_queue函数,这个函数的执行不会阻塞前面的程序。
QTimer.singleShot(1000, self.check_event_queue)
if __name__ == "__main__":
app = QApplication(sys.argv)
event_queue = queue.Queue()
notifier = EventNotifier(event_queue)
notifier.start()
window = MainWindow_pyqt(event_queue)
window.show()
sys.exit(app.exec_())
QTimer.singleShot(1000, self.check_queue)
语句的原理是:在调用这个语句的 1 秒后,执行 self.check_queue
函数,然后立即返回不阻塞当前继续执行接下来的代码。这是一个非阻塞的定时任务。