簡體   English   中英

在 Python 線程期間更新 Tkinter 標簽

[英]Update Tkinter label during threading in Python

我目前正在嘗試對由 Raspberry Pi 控制的機械臂進行編程。

到目前為止一切正常,除了一件事,我已經在谷歌上搜索並嘗試了好幾個小時,但找不到可行的解決方案。

對於機器人手臂的運動,必須使用螺紋“同時”運行所有電機(工作正常)。

我遇到的問題是,我需要更新一個標簽,該標簽在軸(電機)完成運動后立即顯示其當前角度,但其他電機仍在運行(線程)。

經過大量研究,我認為我通過使用隊列和 Tkinters 后方法找到了解決方案。 但它仍然不起作用,因為標簽文本只有在所有線程終止后才會更新。

我寫了一個示例代碼,我想獲得電機“一”的標簽更新,它將在電機“二”(500 次迭代)之前完成其 for 循環(100 次迭代)。 我希望一旦電機 1 達到目標,而電機 2 仍在運行,標簽就會更新。

但是雖然我使用了after-method,它仍然要等到電機二完成后才更新標簽。

希望你能幫我!

from tkinter import *
import threading
import time
from queue import * 


class StepperMotors:

    def __init__(self, root):
        self.root = root

        self.start_btn = Button(root, text="Start", command=lambda:self.start_movement())
        self.start_btn.config(width = 10)
        self.start_btn.grid(row=1,column=1)

        self.label_one = Label(root, text='')
        self.label_one.config(width = 10)
        self.label_one.grid(row=2, column=1)

        self.label_two = Label(root, text='')
        self.label_two.config(width = 10)
        self.label_two.grid(row=3, column=1)

    def start_movement(self):
        self.thread_queue = Queue()
        self.root.after(100, self.wait_for_finish) 

        thread_one = threading.Thread(target=self.motor_actuation, args=(1,100))
        thread_two = threading.Thread(target=self.motor_actuation, args=(2,500))

        thread_one.start()
        thread_two.start()

        thread_one.join()
        thread_two.join()

    def motor_actuation(self, motor, iterations):  
        for i in range(iterations):
            i = i+1  
            update_text = str(motor) + " " + str(i) + "\n"
            print(update_text)
            time.sleep(0.01)

        self.thread_queue.put(update_text)

    def wait_for_finish(self):
        try:      
            self.text = self.thread_queue.get()  
            self.label_one.config(text=self.text)  

        except self.thread_queue.empty():
            self.root.after(100, self.wait_for_finish)

if __name__ == "__main__":
    root = Tk()
    root.title("test")
    stepper = StepperMotors(root)
    root.mainloop()

最好使用非阻塞的守護線程。

此外,最好將關注點分開:機器人(或機器人手臂)可以是具有自己生命周期的對象:守護線程。 同上,您可以定義一個“LabelUpdater”來讀取機器人的狀態並更新標簽。

讓我們定義一個機器人:

  • 它是在應用程序初始化時創建的,並在用戶單擊“開始”按鈕時運行,
  • 機器人在應用程序級多線程隊列中移動並報告其角度,
class Robot(threading.Thread):

    def __init__(self, name: str, label_queue: queue.Queue, end_pos: int):
        super().__init__(name=name)
        self.daemon = True
        self.label_queue = label_queue
        self.end_pos = end_pos

    def run(self) -> None:
        for angle in range(self.end_pos):
            self.label_queue.put(angle)
            time.sleep(0.01)

讓我們定義一個 LabelUpdater:

  • 它在應用程序初始化時創建並永遠運行(即使機器人沒有運行,它也可以觀察機器人)。
  • 它讀取機器人隊列(例如每秒以避免閃爍)並更新標簽
class LabelUpdater(threading.Thread):
    def __init__(self, name: str, label_queue: queue.Queue, root_app: tkinter.Tk, variable: tkinter.Variable):
        super().__init__(name=name)
        self.daemon = True
        self.label_queue = label_queue
        self.root_app = root_app
        self.variable = variable

    def run(self) -> None:
        # run forever
        while True:
            # wait a second please
            time.sleep(1)
            # consume all the queue and keep only the last message
            last_msg = None
            while True:
                try:
                    msg = self.label_queue.get(block=False)
                except queue.Empty:
                    break
                last_msg = msg
                self.label_queue.task_done()
            if last_msg:
                self.variable.set(last_msg)

然后,主應用程序應定義:

  • 2 個多線程隊列:每個標簽一個,
  • 2 tkinter.StringVar變量將被更新,
  • 機器人和標簽更新器,
  • 兩個更新程序已啟動,並將永遠運行。
class StepperMotors:
    def __init__(self, root):
        self.root = root
        self.label_one_queue = queue.Queue()
        self.label_two_queue = queue.Queue()

        self.start_btn = tkinter.Button(root, text="Start", command=lambda: self.start_movement())
        self.start_btn.config(width=10)
        self.start_btn.grid(row=1, column=1)

        self.text_one = tkinter.StringVar()
        self.text_one.set("one")
        self.label_one = tkinter.Label(root, textvariable=self.text_one)
        self.label_one.config(width=10)
        self.label_one.grid(row=2, column=1)

        self.text_two = tkinter.StringVar()
        self.text_two.set("two")
        self.label_two = tkinter.Label(root, textvariable=self.text_two)
        self.label_two.config(width=10)
        self.label_two.grid(row=3, column=1)

        self.robot_one = Robot("robot_one", self.label_one_queue, 100)
        self.robot_two = Robot("robot_two", self.label_two_queue, 500)

        self.updater_one = LabelUpdater("updater_one", self.label_one_queue, self.root, self.text_one)
        self.updater_two = LabelUpdater("updater_two", self.label_two_queue, self.root, self.text_two)
        self.updater_one.start()
        self.updater_two.start()

    def start_movement(self):
        self.robot_one.start()
        self.robot_two.start()

當然,您需要一個標志或其他東西來檢查每個機器人是否已經在運行。

暫無
暫無

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

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