4

我已經成功創建了一個線程的線程示例,它可以隨時更新Progressbar。然而,用多處理技術做同樣的事情到目前爲止還是沒有。 我開始懷疑是否有可能以這種方式使用tkinter。有沒有人做過這個?從python3中的multiprocess.proccess更新tk ProgressBar

我在OS X 10.7上運行。我從四處看看,發現不同的操作系統可能會有很大的不同,尤其是多處理和tkinter。

我試過了一個生產者,通過命名空間和event.wait,以及event.set直接與widget對話。我和一個製作人與一個消費者交談的方式做了同樣的事情,這個消費者或者是一個與該Widget交談的方法或者功能。所有這些都成功運行,但不要直觀地更新小部件。儘管我在IntVar上完成了一個get(),但是在使用widget.step()和/或widget.set()時,這個小部件會被綁定並看到它發生了變化。我甚至嘗試在子進程中運行一個單獨的tk()實例。沒有更新進度條。

這是一個更簡單的版本。子進程是一個對象的方法,它是Progressbar小部件的包裝器。 tk GUI作爲主流程運行。我也覺得這個小部件在循環結束時並沒有被破壞,這可能是一個線索,我不理解其含義。

import multiprocessing 
from tkinter import * 
from tkinter import ttk 
import time 

root = Tk() 

class main_window: 

    def __init__(self): 
     self.dialog_count = 0 

     self.parent = root 
     self.parent.title('multiprocessing progess bar') 

     frame = ttk.Labelframe(self.parent) 
     frame.pack(pady=10, padx=10) 

     btn = ttk.Button(frame, text="Cancel") 
     btn.bind("<Button-1>", self.cancel) 
     btn.grid(row=0, column=1, pady=10) 

     btn = ttk.Button(frame, text="progress_bar") 
     btn.bind("<Button-1>", self.pbar) 
     btn.grid(row=0, column=2, pady=10) 

     self.parent.mainloop() 

    def pbar(self, event): 

     name="producer %d" % self.dialog_count 
     self.dialog_count += 1 

     pbar = pbar_dialog(self.parent, title=name) 

     event = multiprocessing.Event() 
     p = multiprocessing.Process(target=pbar.consumer, args=(None, event)) 

     p.start() 



    def cancel(self, event): 
     self.parent.destroy() 



class pbar_dialog: 

    toplevel=None 
    pbar_count = 0 

    def __init__(self, parent, ns=None, event=None, title=None, max=100): 
     self.ns = ns 
     self.pbar_value = IntVar() 
     self.max = max 

     pbar_dialog.pbar_count += 1 
     self.pbar_value.set(0) 


     if not pbar_dialog.toplevel: 
      pbar_dialog.toplevel= Toplevel(parent) 

     self.frame = ttk.Labelframe(pbar_dialog.toplevel, text=title) 
     #self.frame.pack() 
     self.pbar = ttk.Progressbar(self.frame, length=300, variable=self.pbar_value) 
     self.pbar.grid(row=0, column=1, columnspan=2, padx=5, pady=5) 

     btn = ttk.Button(self.frame, text="Cancel") 
     btn.bind("<Button-1>", self.cancel) 
     btn.grid(row=0, column=3, pady=10) 
     self.frame.pack() 


    def set(self,value): 
     self.pbar_value.set(value) 

    def step(self,increment=1): 
     self.pbar.step(increment) 
     print ("Current", self.pbar_value.get()) 

    def cancel(self, event): 
     self.destroy() 

    def destroy(self): 
     self.frame.destroy() 
     pbar_dialog.pbar_count -= 1 
     if pbar_dialog.pbar_count == 0: 
      pbar_dialog.toplevel.destroy() 

    def consumer(self, ns, event): 
     for i in range(21): 
      #event.wait(2) 
      self.step(5) 
      #self.set(i) 
      print("Consumer", i) 
     self.destroy() 



if __name__ == '__main__': 
    main_window() 

相比之下,這裏是完美的線程版本。

import threading 
from tkinter import * 
from tkinter import ttk 
import time 

root = Tk() 

class main_window: 

    def __init__(self): 
     self.dialog_count = 0 

     self.parent = root 
     self.parent.title('multiprocessing progess bar') 

     frame = ttk.Labelframe(self.parent) 
     frame.pack(pady=10, padx=10) 

     btn = ttk.Button(frame, text="Cancel") 
     btn.bind("<Button-1>", self.cancel) 
     btn.grid(row=0, column=1, pady=10) 

     btn = ttk.Button(frame, text="progress_bar") 
     btn.bind("<Button-1>", self.pbar) 
     btn.grid(row=0, column=2, pady=10) 

     self.parent.mainloop() 


    def producer(self, pbar): 
     i=0 
     while i < 101: 
      time.sleep(1) 
      pbar.step(1) 
      i += 1 
     pbar.destroy() 


    def pbar(self, event): 

     name="producer %d" % self.dialog_count 
     self.dialog_count += 1 

     pbar = pbar_dialog(self.parent, title=name) 

     p = threading.Thread(name=name, target=self.producer, args=(pbar,)) 

     p.start() 

     #p.join() 


    def cancel(self, event): 
     self.parent.destroy() 



class pbar_dialog: 

    toplevel=None 
    pbar_count = 0 

    def __init__(self, parent, ns=None, event=None, title=None, max=100): 
     self.ns = ns 
     self.pbar_value = IntVar() 
     self.title = title 
     self.max = max 

     pbar_dialog.pbar_count += 1 

     if not pbar_dialog.toplevel: 
      pbar_dialog.toplevel= Toplevel(parent) 

     self.frame = ttk.Labelframe(pbar_dialog.toplevel, text=title) 
     #self.frame.pack() 
     self.pbar = ttk.Progressbar(self.frame, length=300, variable=self.pbar_value) 
     self.pbar.grid(row=0, column=1, columnspan=2, padx=5, pady=5) 

     btn = ttk.Button(self.frame, text="Cancel") 
     btn.bind("<Button-1>", self.cancel) 
     btn.grid(row=0, column=3, pady=10) 
     self.frame.pack() 

     self.set(0) 

    def set(self,value): 
     self.pbar_value.set(value) 

    def step(self,increment=1): 
     self.pbar.step(increment) 

    def cancel(self, event): 
     self.destroy() 

    def destroy(self): 
     self.frame.destroy() 
     pbar_dialog.pbar_count -= 1 
     if pbar_dialog.pbar_count == 0: 
      pbar_dialog.toplevel.destroy() 
      pbar_dialog.toplevel = None 

    def automatic(self, ns, event): 
     for i in range(1,100): 
      self.step() 

if __name__ == '__main__': 
    main_window() 
+0

謝謝你提出這個問題。這是一個令人難以置信的重要想法,在簡單的方式上不能很好地解決stackoverflow。 – chase

回答

2

做類似的事情,我最後不得不使用線程和進程的組合 - 包括GUI前端有兩個線程:一個用於Tkinter的,並從multiprocessing.Queue並調用gui.update一個閱讀() - 那麼後端進程會將更新寫入該隊列

+0

這是一個不錯的解決方案@Shish。在後端使用多處理功能,在前端使用線程。 – user967953

+0

我也有這種類型的設置,但我仍然收到此問題描述的錯誤。在互聯網上有沒有例子給你提供了一個如何設置它的好主意? – chase

0

這可能是一種奇怪的方法,但它適用於我。將此代碼複製並粘貼到文件並運行以查看結果。它已準備好運行。

我沒有足夠的耐心來解釋我的代碼,我可能會在另一天進行編輯。

哦,這是在Python 2.7我開始編程兩個月前,所以我不知道如果差異是相關的。

# -*- coding: utf-8 -*- 
# threadsandprocesses.py 

# Importing modules 
import time 
import threading 
import multiprocessing 
import Tkinter as tki 
import ttk 


class Master(object): 
    def __init__(self): 
     self.mainw = tki.Tk() 
     self.mainw.protocol("WM_DELETE_WINDOW", self.myclose) 
     self.mainw.title("Progressbar") 
     self.mainw.geometry('300x100+300+300') 
     self.main = tki.Frame(self.mainw) 
     self.RunButton = ttk.Button(self.main, text='Run', 
            command=self.dostuff) 
     self.EntryBox = ttk.Entry(self.main) 
     self.EntryBox.insert(0, "Enter a number") 
     self.progress = ttk.Progressbar(self.main, 
             mode='determinate', value=0) 
     self.main.pack(fill=tki.BOTH, expand=tki.YES) 
     self.progress.pack(expand=tki.YES) 
     self.EntryBox.pack(expand=tki.YES) 
     self.RunButton.pack() 
     print "The Master was created" 

    def dostuff(self): 
     print "The Master does no work himself" 
     data = range(int(self.EntryBox.get())) 
     S = Slave(self, data) 
     print "The Master created a Slave to do his stuff" 
     print "The Slave gets told to start his work" 
     S.start() 

    def myclose(self): 
     self.mainw.destroy() 
     return 

    def nextstep(self): 
     print "Good job, Slave, I see the result is" 
     print Master.results.get() 


class Slave(threading.Thread): 
    def __init__(self, guest, data): 
     print "This is the Slave." 
     print "Nowdays, Work is outsourced!" 
     self.data = data 
     self.guest = guest 
     threading.Thread.__init__(self) 

    def run(self): 
     print "The Slave is outsourcing his work to Calcualte inc." 
     time.sleep(1) 
     Outsourcing = Calculate() 
     Results = Outsourcing.run(self.guest, self.data) 
     return Results 


# unwrapping outside a class 
def calc(arg, **kwarg): 
    return Calculate.calculate(*arg, **kwarg) 


class Calculate(object): 

    def run(self, guest, data): 
     print"This is Calculate inc. ... how can I help you?" 
     time.sleep(1) 
     maximum = int(guest.EntryBox.get()) 
     guest.progress.configure(maximum=maximum, value=0) 
     manager = multiprocessing.Manager() 
     queue = manager.Queue() 
     lock = manager.Lock() 
     print "Things are setup and good to go" 
     # Counting the number of available CPUs in System 
     pool_size = multiprocessing.cpu_count() 
     print "Your system has %d CPUs" % (pool_size) 
     # Creating a pool of processes with the maximal number of CPUs possible 
     pool = multiprocessing.Pool(processes=pool_size) 
     Master.results = pool.map_async(calc, (zip([self]*len(data), [lock]*len(data), 
             [queue]*len(data), data))) 
     for job in range(1, maximum+1): 
      queue.get() # this is an abuse I think, but works for me 
      guest.progress.configure(value=job) 
     # Properly close and end all processes, once we're done 
     pool.close() 
     pool.join() 
     print "All done" 
     guest.nextstep() 
     return 

    def calculate(self, lock, queue, indata): 
     lock.acquire() 
     print 'Reading values and starting work' 
     lock.release() 
     time.sleep(3) # some work 
     results = indata # The works results 
     lock.acquire() 
     print 'Done' 
     lock.release() 
     queue.put("Finished!") 
     return results 

if __name__ == '__main__': 
    TheMaster = Master() 
    TheMaster.mainw.mainloop()