多线程 – 从python3中的multiprocess.proccess更新tk ProgressBar

我已经成功创建了一个线程的线程示例,它可以随时更新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()

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

点赞