![](/img/trans.png)
[英]Create progress Bar using concurrent.futures in ThreadPoolexecutor process
[英]Using Tkinter With Concurrent.Futures / ThreadPoolExecutor Class
我一直在嘗試將兩者結合起來,但 ThreadPool 不會連接到 GUI。 甚至有可能將兩者聯系在一起嗎?
我只是以此為例來學習如何使用 ThreadPooling。 我希望每個名字都打印在它自己的線程上。 最終我將使用 Treeview 從它們自己的獨立線程收集數據,但不確定是否可以與 ThreadPoolExecutor 一起使用。
這是我嘗試過但無濟於事的方法:
from tkinter import Tk, Button, Listbox
from concurrent.futures import ThreadPoolExecutor
class MainWindow(Tk):
def __init__(self):
super().__init__()
self.lb1 = Listbox(self, width=26, cursor='hand2')
self.lb1.pack(side='left', fill='y', padx=20, pady=20)
self.b1 = Button(self, text='START', bg='green', fg='white', cursor='hand2', command=self.start)
self.b1.pack(side='left')
self.lb1.insert('end', 'Aaron')
self.lb1.insert('end', 'Billy')
self.lb1.insert('end', 'Chris')
self.lb1.insert('end', 'David')
self.lb1.insert('end', 'Edward')
self.lb1.insert('end', 'Frank')
self.lb1.insert('end', 'George')
self.lb1.insert('end', 'Howard')
self.lb1.insert('end', 'Ian')
self.lb1.insert('end', 'Johnny')
def worker1(self):
for i in range(self.lb1.size()):
print(self.lb1.get(i))
def start(self):
with ThreadPoolExecutor(max_workers=2) as executor:
executor.submit(self.worker1)
if __name__ == '__main__':
app = MainWindow()
app.title('Main Window')
app.configure(bg='#333333')
#center the Main Window:
w = 500 # Width
h = 420 # Height
screen_width = app.winfo_screenwidth() # Width of the screen
screen_height = app.winfo_screenheight() # Height of the screen
# Calculate Starting X and Y coordinates for Window
x = (screen_width / 2) - (w / 2)
y = (screen_height / 2) - (h / 2)
app.geometry('%dx%d+%d+%d' % (w, h, x, y))
#app.maxsize(1400, 620)
app.mainloop()
有沒有辦法讓他們一起工作?
線程池是一個高級任務分配概念,它必須以一種“事件循環”的方式管理工作線程和主線程之間的任務分配和同步。
問題是 tkinter 在您的主線程上運行着自己的事件循環,阻止它運行您的“線程池”循環是錯誤的,因為 GUI 將變得無響應。
tkinter本身從線程訪問是不安全的,可能會崩潰,所以你運行上面代碼的方式是不使用線程直接運行到主線程,但是如果你想使用線程可以直接使用threading
模塊.
def start(self):
thread = threading.Thread(target=self.worker1, daemon=True)
thread.start()
上面的代碼不是線程安全的,tkinter 解釋器可能會崩潰,跨線程執行函數的正確方法是在主線程中對 GUI 進行所有查詢,然后再將其傳遞給子線程。
def start(self):
labels = [self.lb1.get(i) for i in range(self.lb1.size())]
thread = threading.Thread(target=self.print_func,args=(labels,), daemon=True)
thread.start()
def print_func(self, labels):
for item in labels:
print(item)
上面的代碼是線程安全的,因為所有的 GUI 查詢都是在主線程中完成的,子線程只對遠離 tkinter 的數據進行操作,並且是在 tkinter 中使用線程的推薦方式。
為了將線程發出的命令返回到 GUI 主線程,您應該使用自定義信號和隊列進行通信。
MainWindow.bind("<<my_custom_signal>>",some_callback_function) # called in init
MainWindow.event_generate("<<my_custom_signal>>") # called in child thread
雖然可以直接從子線程向 GUI 發出命令,但不建議這樣做,因為它不是線程安全的,應用程序可能會崩潰或出現意外行為。
如果你必須運行一個線程池,你可以從一個子線程而不是你的主線程運行一個線程池,但是它不應該直接與你的 GUI 交互,你將只讓你的主線程管理 tkinter 事件循環和子線程管理線程池循環,管理更多的子線程。
def start(self):
labels = [self.lb1.get(i) for i in range(self.lb1.size())]
thread = threading.Thread(target=self.print_func, args=(labels,), daemon=True)
thread.start()
def print_func(self, labels): # called in child thread
with ThreadPoolExecutor(max_workers=2) as executor: # create child of child
tasks = []
for label in labels:
task = executor.submit(print, label) # just print in the child threadpool
tasks.append(task)
for task in tasks:
task.result()
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.