从python 3中的multiprocess.process更新tk Progress Bar

时间:2011-09-27 22:15:10

标签: multithreading python-3.x progress-bar tkinter multiprocessing

我已经成功创建了一个线程的线程示例,它可以随时更新Progressbar。然而,到目前为止,我还没有采用多处理方式做同样的事情。 我开始怀疑是否有可能以这种方式使用tkinter。有没有人这样做过?

我在OS X 10.7上运行。我从周围看到不同的操作系统可能表现得非常不同,特别是对于多处理和tkinter。

我尝试过一个通过命名空间和event.wait以及event.set直接与小部件对话的制作人。我与生产者谈论消费者做了同样的事情,消费者是与小部件对话的方法或功能。所有这些事情都成功运行,但不要直观地更新小部件。虽然我在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()

2 个答案:

答案 0 :(得分:2)

做类似的事情,我最终不得不使用线程和进程的组合 - GUI前端有两个线程:一个用于tkinter,一个从multiprocessing.Queue读取并调用gui.update() - 然后后端进程会将更新写入该队列

答案 1 :(得分: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()