Updating a tk ProgressBar from a multiprocess.proccess in python3
Question: I have successfully created a threading example of a thread which can update a
Progressbar as it goes. However doing the same thing with multiprocessing has
so far eluded me. I'm beginning to wonder if it is possible to use tkinter in
this way. Has anyone done this?

I am running on OS X 10.7. I know from looking around that different OS's may
behave very differently, especially with multiprocessing and tkinter.

I have tried a producer which talks directly to the widget, through both
namespaces and event.wait, and event.set. I have done the same thing with a
producer talking to a consumer which is either a method or function which
talks to the widget. All of these things successfully run, but do not update
the widget visually. Although I have done a get() on the IntVar the widget is
bound to and seen it change, both when using widget.step() and/or
widget.set(). I have even tried running a separate tk() instance inside the
sub process. Nothing updates the Progressbar.

Here is one of the simpler versions. The sub process is a method on an object
that is a wrapper for the Progressbar widget. The tk GUI runs as the main
process. I also find it a little odd that the widget does not get destroyed at
the end of the loop, which is probably a clue I'm not understanding the
implications of.

    
    
    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()
    

**For contrast, here is the threading version which works perfectly.**

    
    
    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()
    


Answer: Doing something similar, I ended up having to use a combination of threads and
processes - the GUI front end had two threads: one for tkinter, and one
reading from a multiprocessing.Queue and calling gui.update() - then the back-
end processes would write updates into that Queue

