使用线程和队列将记录器消息重定向到python中的tkinter小部件



向社区致以问候。我面临以下问题:我有一个简单的tkinter GUI,它有一个按钮,可以启动许多计算,同时在tkinter小部件中打印与计算进度有关的各种消息。

我读了几篇文章,据我所知,最有效的方法是在主线程中创建一个队列,将队列绑定到记录器,在单独的线程中运行计算,并将其消息重定向到由主线程使用间隔轮询的队列。

但我显然做错了什么。这些消息是在我的滚动框架小部件中编写的,但在线程操作完成后会同时编写。

这是我的代码(我会尽我所能简化它(:

class GFTGUI(ttk.Frame):
def __init__(self, parent):
ttk.Frame.__init__(self, parent)
self.app = parent
# create widget to store messages  
logger_pane = ttk.PanedWindow(self.app, orient=VERTICAL)
logger_pane.grid(row=2, column=0, sticky=(N, W, E, S), padx=10, pady=10)
self.logger_frame = ttk.Labelframe(logger_pane, text="Message Board")
logger_pane.add(self.logger_frame, weight=1)
self.scrolled_text = ScrolledText(self.logger_frame, state='disabled')
self.scrolled_text.grid(row=0, column=0, sticky=(N, S, W, E))
self.scrolled_text.configure(font='TkFixedFont')
# configure GUI logger
ConfigureTkFrameLogger(self.logger_frame, self.scrolled_text, self.gft)
# create the run button
self.run_button = Button(self.gft, text="Run!", command=self.run_command, height=35)
self.run_button.grid(column=0, row=1, padx=3, pady=4)
def run_command(self): 
th = threading.Thread(target=run_calculations)  # see final block of code
th.start()
th.join()
def start_gui():
app = Tk()
GFTGUI(app)
app.mainloop()
if __name__ == '__main__': start_gui()

然后我有一个单独的文件(称为helpers.py(,它存储记录器的类:

logger_user = logging.getLogger('user')
class ConfigureTkFrameLogger:
def __init__(self, logger_frame, scrolled_text):
self.logger_frame = logger_frame
self.scrolled_text = scrolled_text
# Create a logging handler using a queue
self.log_queue = queue.Queue()
self.queue_handler = QueueHandler(self.log_queue)
formatter = logging.Formatter('%(asctime)s: %(message)s', datefmt='%H:%M:%S')
self.queue_handler.setFormatter(formatter)
self.queue_handler.set_name('gui')
logger_user.addHandler(self.queue_handler)
# Start polling messages from the queue
self.logger_frame.after(100, self.poll_log_queue)
# Check every 100ms if there is a new message in the queue to display
def poll_log_queue(self):
while True:
try: record = self.log_queue.get(block=False)
except queue.Empty: break
else: self.display(record)
self.logger_frame.after(100, self.poll_log_queue)
def display(self, record):
msg = self.queue_handler.format(record)
self.scrolled_text.configure(state='normal')
self.scrolled_text.insert(END, msg + 'n', record.levelname)
self.scrolled_text.configure(state='disabled')
self.scrolled_text.yview(END)  # Autoscroll to the bottom
class QueueHandler(logging.Handler):
def __init__(self, log_queue):
super().__init__()
self.log_queue = log_queue
def emit(self, record): self.log_queue.put(record)

最后,我的计算是用一种存储在单独文件中的方法完成的:

logger = logging.getLogger('user')
def run_calculations(some_args...):
# this message should be printed as soon as the calculations begin
logger.info('Reading input file')

do other stuff here 

我想我已经快要成功了。我只是需要一点推力!

这将是一个注释,但我"必须有50个信誉才能评论";,所以。
不看其他任何东西,我看到了这个:

th.start()
th.join()

不要启动&立即加入(==等待线程完成(。

最新更新