[英]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)
然后,主應用程序應定義:
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.