Обновление тк ProgressBar из мультипроцесса.процесс в python3 - PullRequest
4 голосов
/ 28 сентября 2011

Я успешно создал пример потока, который может обновлять индикатор выполнения по мере его поступления.Однако то же самое с многопроцессорной обработкой до сих пор ускользало от меня.Я начинаю задаваться вопросом, возможно ли использовать tkinter таким образом.Кто-нибудь делал это?

Я работаю на OS X 10.7.Оглядываясь вокруг, я знаю, что разные ОС могут вести себя очень по-разному, особенно с многопроцессорностью и tkinter.

Я пробовал продюсера, который общается напрямую с виджетом через пространства имен и event.wait, а также event.set.Я проделал то же самое с производителем, разговаривающим с потребителем, который является либо методом, либо функцией, которая общается с виджетом.Все эти вещи успешно выполняются, но не обновляют виджет визуально.Хотя я сделал get () в IntVar, к которому привязан виджет, и увидел, что он меняется, как при использовании widget.step () и / или widget.set ().Я даже пытался запустить отдельный экземпляр tk () внутри подпроцесса.Ничто не обновляет Progressbar.

Вот одна из самых простых версий.Подпроцесс - это метод объекта, который является оболочкой для виджета Progressbar.Графический интерфейс tk работает как основной процесс.Я также нахожу немного странным, что виджет не разрушается в конце цикла, что, вероятно, является подсказкой, которую я не понимаю.

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 ]

2 голосов
/ 07 октября 2011

Делая что-то похожее, я вынужден был использовать комбинацию потоков и процессов - во внешнем интерфейсе GUI было два потока: один для tkinter и один для чтения из многопроцессорной обработки. Quue и вызов gui.update () внутренние процессы будут записывать обновления в эту очередь

0 голосов
/ 26 июня 2013

Это может быть странный подход, но он работает для меня.Скопируйте и вставьте этот код в файл и запустите его, чтобы увидеть результат.Он готов к запуску.

У меня нет терпения, чтобы объяснить мой код прямо сейчас, я мог бы изменить его в другой день.

О, и это в 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()
...