簡體   English   中英

tkinter:如何使用隊列從另一個線程更新 Treeview 項目?

[英]tkinter: How to update Treeview item from another thread using queue?

我正在嘗試了解面向 object 的 python 並使用 tkinter 和線程。 我已經在線閱讀了大量的 SO 問題和其他各種資源。 我已經能夠解決大部分問題,但我對如何從另一個線程更新 Treeview 項目有點迷茫。 我無法找到以我能理解的方式說明這一點的東西。

我讀過 tkinter 必須在主線程中運行,並且您不能從另一個線程更新它,所以我需要使用隊列將內容傳遞給它。

以下代碼有效,盡管它可能不是最好的方法。 任何改進都絕對受歡迎,但我要發布的主要內容是 Treeview 問題。

在我看到的示例中,人們通常只是使用隨機數生成器或其他東西通過隊列傳遞整數來演示一些簡單的東西。 我明白這一點,但是我如何使用隊列來傳遞和運行實際命令或以其他方式更新位於主線程中的 Treeview 項目?

如果不是在另一個線程中,我會使用類似的東西: tv_files.item('File1', values=('tst1', 'tst2'))

import tkinter as tk
from tkinter import ttk
from threading import Thread
from queue import Queue
import time

class TkThread:
  def __init__(self):
    self.tk = tk.Tk()
    self.message_queue = Queue()
    self.message_event = '<<message>>'
    self.tk.bind(self.message_event, self.process_message_queue)

  def run_tk(self):
    self.tk.title('My Window')

    self.tv_files = ttk.Treeview(
      self.tk,
      columns=('Filename', 'Status'),
      show='headings')
    self.tv_files.pack(
      side='top',
      padx=10,
      pady=10)

    # Set up columns
    self.tv_files.heading('#1', anchor='w', text='Filename')
    self.tv_files.column('#1', anchor='w', width=150)
    self.tv_files.heading('#2', anchor='w', text='Status')
    self.tv_files.column('#2', anchor='w', width=150)

    self.tv_files.insert('', 'end', id='File1', values=('File1', 'Status1'))

    self.btn_run = ttk.Button(
      self.tk,
      text= 'Run',
      compound='top',
      command = run_thread)
    self.btn_run.pack(
      side='top',
      padx=10,
      pady=10)

    self.tk.lift(); self.tk.mainloop()

  def send_message_to_ui(self, message):
    self.message_queue.put(message)
    self.tk.event_generate(self.message_event, when='tail')

  def process_message_queue(self, event):
    while self.message_queue.empty() is False:
      message = self.message_queue.get(block=False)
      # process the message here
      print(message)

def work():
  time.sleep(3)
  tk_thread.send_message_to_ui('Test Message')

def run_thread():
  thread = Thread(target=work)
  thread.start()

if __name__ == '__main__':
  tk_thread = TkThread()
  tk_thread.run_tk()

你似乎已經掌握了基礎知識。 只需將項目添加到您可以解包並插入樹的隊列中。

例如,您可以將字典添加到隊列中:

def work():
  time.sleep(3)
  message = {
      "id": "File1",
      "values": ('tst1', 'tst2')
  }
  tk_thread.send_message_to_ui(message)

在事件處理程序中,您可以從消息中提取數據並將其插入樹中:

def process_message_queue(self, event):
    while self.message_queue.empty() is False:
        message = self.message_queue.get(block=False)
        self.tv_files.insert(message['id'], "end", values=message['values'])

如果你想變得非常花哨,你可以傳遞 function、args 和 kwargs 的名稱,這樣你的線程就可以在 GUI 中做任何事情:

發送消息:

def work():
    time.sleep(3)
    message = {
        "attr": "tv_files",
        "method": "insert",
        "args": ("File1", "end"),
        "kwargs": {
            "values": ['tst1', 'tst2']
        },
    }
    tk_thread.send_message_to_ui(message)

處理消息:

def process_message_queue(self, event):
    while self.message_queue.empty() is False:
        message = self.message_queue.get(block=False)
        attr = getattr(self, message['attr'])
        method = getattr(attr, message['method'])
        method(*message['args'], **message['kwargs'])

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM