python - 使用线程和队列将记录器消息重定向到 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()
不要开始并立即加入(== 等待线程完成)。
推荐阅读
- .net - 实体框架 - 如何将复杂类型/表类型传递给函数
- kubernetes - Kubernetes + Helm - 仅在新版本/更改时重新启动 pod
- python - pandas to_sql giving UnicodeEncodeError for table with JSON column
- java - 我可以在一个 JSP 中有两个按钮吗?
- ansible - Ansible - 无法打印出空变量
- excel - Microsoft Excel -> 如何将颜色应用于交替行,但仅限于 X 和 Y 列(通过公式)
- sql - 如何在 SQLAlchemy > Presto 连接中指定 https 协议?
- python - 重命名图像文件时系统找不到指定的文件
- powershell-5.0 - 如何将参数传递给新进程中的脚本块
- attachment - 无法删除某人已打开的列表附件