我正在嘗試運行一個使用執行緒異步運行的腳本。我遇到了一個關于如何定期檢查執行緒是否還活著(在 start_thread1 下)的問題。我不想使用 join() 因為它會凍結 GUI,直到執行緒完成。
如果這是不可能的,我對任何其他方法持開放態度。
這是我正在使用的代碼 - 這是代碼的一部分,只是為了概述我的“問題”:
from tkinter.constants import LEFT, RIGHT, S
import tkinter.messagebox
from matplotlib import pyplot as plt
import tkinter as tk, time, threading, random, queue
class GuiPart(object):
def __init__(self, master, queue, queue2, client_instance):
self.queue = queue
self.queue2 = queue2
self.x = []
self.y= []
# Set up the GUI
self.Button2 = tk.Button(master, text="Button2", padx=10,
pady=5, fg="white", bg="#263D42", command=client_instance.start_thread1)
self.Button2.pack(side = RIGHT)
def processIncoming(self):
""" Handle all messages currently in the queue, if any. """
while not self.queue.empty():
msg = self.queue.get_nowait()
self.x.append(msg)
print(msg)
while not self.queue2.empty():
msg2 = self.queue2.get_nowait()
self.y.append(msg2)
fig, ax = plt.subplots()
ax.plot(self.x, self.y)
plt.show()
class ThreadedClient(object):
"""
Launch the main part of the GUI and the worker thread. periodic_call()
and end_application() could reside in the GUI part, but putting them
here means that you have all the thread controls in a single place.
"""
def __init__(self, master):
"""
Start the GUI and the asynchronous threads. We are in the main
(original) thread of the application, which will later be used by
the GUI as well. We spawn a new thread for the worker (I/O).
"""
self.master = master
# Create the queue
self.queue = queue.Queue()
self.queue2 = queue.Queue()
self.running=True
# Set up the GUI part
self.gui = GuiPart(master, self.queue, self.queue2, self)
# Set up the thread to do asynchronous I/O
# More threads can also be created and used, if necessary
def start_thread1(self):
thread1=threading.Thread(target=self.worker_thread1)
thread1.start()
# how to check periodically if the thread is finished and when is finished run self.gui.processIncoming()
# if I run it straight away like this the self.gui.processIncoming() will run before the thread will finish and nothing will be plotted
if thread1.is_alive() == False:
self.gui.processIncoming()
def worker_thread1(self):
"""
This is where we handle the asynchronous I/O. For example, it may be
a 'select()'. One important thing to remember is that the thread has
to yield control pretty regularly, be it by select or otherwise.
"""
if self.running:
time.sleep(5) # I am using time.sleep(5) just to simulate a long-running process
for i in range(20):
msg = i
self.queue.put(msg)
for j in range(20):
msg2 = j
self.queue2.put(msg2)
root = tk.Tk()
root.title('Matplotlib threading')
client = ThreadedClient(root)
root.mainloop()
uj5u.com熱心網友回復:
如果我理解正確,你有一個 tkinter 程式,它有一個執行一些資料收集的執行緒,你需要從第二個執行緒中將資料回傳到 gui 中。您不能簡單地等待第二個執行緒完成,因為這會阻塞 tkinter 主回圈。
一種解決方案是創建一個回呼函式,將其傳遞給第二個執行緒,并在第二個執行緒的最后一步呼叫它。大多數 tkinter 物件不是執行緒安全的,因此如果您要在回呼函式中更新 GUI,則必須在主執行緒中運行回呼。為此,將回呼基于 tkinter 的after_idle函式。這會導致回呼發生在 tk 的事件回圈中,在主執行緒中,很像 tkinter 事件處理程式。
該程式執行此操作,并且與您的程式類似。我更改了一些小東西以使我的靜態型別檢查器 (pylint) 滿意。我不使用 matplotlib,所以我去掉了那個代碼。
重要的東西在start_thread1. 函式 f 被宣告并作為引數傳遞給執行緒。請注意,f 不會呼叫processIncoming,而是將其傳遞給 after_idle;指示 tk 主回圈執行實際呼叫。傳遞給 worker_thread1 的函式在執行緒的最后一步被呼叫。
最終結果是 processIncoming() 在作業執行緒完成時被觸發到主執行緒中。
from tkinter.constants import RIGHT
import tkinter as tk
import time
import threading
import queue
class GuiPart:
def __init__(self, master, queue1, queue2, client_instance):
self.queue1 = queue1
self.queue2 = queue2
self.x = []
self.y= []
# Set up the GUI
self.Button2 = tk.Button(master, text="Button2", padx=10,
pady=5, fg="white", bg="#263D42",
command=client_instance.start_thread1)
self.Button2.pack(side = RIGHT)
def processIncoming(self):
""" Handle all messages currently in the queue, if any. """
print(threading.current_thread())
while not self.queue1.empty():
msg = self.queue1.get_nowait()
self.x.append(msg)
print("X", msg)
while not self.queue2.empty():
msg2 = self.queue2.get_nowait()
self.y.append(msg2)
print("Y", msg2)
print("Make a plot now")
class ThreadedClient:
"""
Launch the main part of the GUI and the worker thread. periodic_call()
and end_application() could reside in the GUI part, but putting them
here means that you have all the thread controls in a single place.
"""
def __init__(self, master):
"""
Start the GUI and the asynchronous threads. We are in the main
(original) thread of the application, which will later be used by
the GUI as well. We spawn a new thread for the worker (I/O).
"""
self.master = master
# Create the queue
self.queue1 = queue.Queue()
self.queue2 = queue.Queue()
self.running=True
# Set up the GUI part
self.gui = GuiPart(master, self.queue1, self.queue2, self)
# Set up the thread to do asynchronous I/O
# More threads can also be created and used, if necessary
def start_thread1(self):
def f():
self.master.after_idle(self.gui.processIncoming)
thread1=threading.Thread(target=self.worker_thread1, args=(f, ))
thread1.start()
def worker_thread1(self, callback):
"""
This is where we handle the asynchronous I/O. For example, it may be
a 'select()'. One important thing to remember is that the thread has
to yield control pretty regularly, be it by select or otherwise.
"""
print(threading.current_thread())
if self.running:
time.sleep(1) # simulate a long-running process
for i in range(20):
msg = i
self.queue1.put(msg)
for j in range(20):
msg2 = j
self.queue2.put(msg2)
callback()
root = tk.Tk()
root.title('Matplotlib threading')
client = ThreadedClient(root)
root.mainloop()
轉載請註明出處,本文鏈接:https://www.uj5u.com/gongcheng/355682.html
