Display the progress of execution of a function us

2019-06-10 04:35发布

import sys
import ttk
from Tkinter import *
from timeit import default_timer as timer


def sum(a, b):
    for i in range(10):
        c = a + b
        print "Sum", c
        time.sleep(5)
    return c


mGui = Tk()
mGui.title('Progress')
mpb = ttk.Progressbar(mGui,orient ="horizontal", length = 200, mode ="determinate")
mpb.pack()
mpb.start()
mpb["maximum"] = 100


Start_Timer=timer()
sum(3,4)
Stop_Timer=timer()
Execution_Time=Stop_Timer-Start_Timer
mpb["value"] = Execution_Time
mGui.mainloop()

I have a function which calculates the sum of two integers. I want to display the status of the execution of this sum function using tkinter progress bar.

This is my approach, but it displays progress bar after executing the sum function, and I want to display the progress bar while the sum function is executing, and the progress should be indicated based on the execution time of the function.

I didn't find answers which satisfy my requirement. It would be great if someone could help me with this.

1条回答
戒情不戒烟
2楼-- · 2019-06-10 05:15

You question is interesting, but you approach is totally wrong. It executes your sum first, because the GUI has not reached mainloop.

So after GUI reaches mainloop it start to wait for events (because of event-driven nature of GUI programming) like button presses. In other words: you can't call a function until mainloop if you ask for callbacks to tkinter.

Another problem - tkinter is single threaded so the gui can only update itself when the function finishes running. So if you start a loop in function there's no callbacks to gui either, but you can call function from a loop, and update gui on each iteration! (e.g. "Loop" of self generated events, consisting of the methods generate_event or after.)

And for god's sake how progressbar can know execution time of a function if function isn't completely executed? If anybody knows that, please comment..

But if you dare, you can start playing with multithreading and queues!

In my example, the progress bar is updated in parallel with the execution of the function, thanks to the separate thread in which the function is executed, and the queue to which the "responses" of the function fall, on the basis of which the progress bar is updating!

Tested it with python 3.5:

try:
    import Tkinter as tk              # Python 2
    import ttk
    import Queue as queue
except ImportError:
    import tkinter as tk              # Python 3
    import tkinter.ttk as ttk
    import queue

import threading
import time


class ThreadFunc(threading.Thread):
    def __init__(self, loop_time=1.0 / 60):
        super(ThreadFunc, self).__init__()
        self.queue = queue.Queue()
        self.timeout = loop_time
        self.parent = None
        self.stop_on_complete = None
        self.running = False
        self._stop = threading.Event()

    def start_thread(self, parent, stop_on_complete=False):
        # thread can wait for functions if not stop_on_complete
        self.parent = parent
        self.stop_on_complete = stop_on_complete
        self.running = True
        self.start()

    def put_function(self, function, *args, **kwargs):
        # put another function in queue
        self.queue.put((function, args, kwargs))

    def run(self):
        print('### STARTED ###')
        while self.running:
            try:
                function, args, kwargs = self.queue.get(timeout=self.timeout)
                print('### RUNNING ###')
                function(*args, **kwargs)
            except queue.Empty:
                if self.stop_on_complete:
                    self.stop()
                else:
                    self.idle()

    def stop(self):
        print('### STOPPED ###')
        self.running = False
        self._stop.set()

    @staticmethod
    def idle():
        print('### IDLE ###')


class App(tk.Tk):
    def __init__(self, *args, **kwargs):
        tk.Tk.__init__(self, *args, **kwargs)
        self.resizable(width=False, height=False)
        self.minsize(width=400, height=25)
        self.wm_title('Another SO Example with progressbar')
        self.queue = queue.Queue()
        self.thread = None
        self.in_work = False

        self.mpb_frame = tk.Frame(self)
        self.mpb = ttk.Progressbar(self.mpb_frame, orient='horizontal', length=400, mode='determinate')
        self.mpb.pack()
        self.mpb_frame.pack()

        self.mpb.bind('<Map>', self.start_example)

    def start_example(self, event=None):
        if self.in_work:
            return
        self.in_work = True
        self.spawn_thread(sum_func, 4, 3, self.queue)

    def spawn_thread(self, command, *args):
        # spawn a thread
        self.thread = ThreadFunc()
        self.thread.start_thread(self, True)
        self.thread.put_function(command, *args)
        self.periodic_call()

    def periodic_call(self):
        # check if our thread is running and if so - update progressbar
        self.check_queue()
        try:
            self.thread.is_alive()
            self.after(100, self.periodic_call)
        except TypeError:
            self.in_work = False
            self.quit()

    def check_queue(self):
        # "transfer" messages to mpb-progressbar steps (10 iteration over you sum)
        while self.queue.qsize():
            try:
                self.queue.get(0)
                self.mpb.step(10)
            except queue.Empty:
                pass


def sum_func(a, b, queue_local):
    # your sum function
    for i in range(10):
        c = a + b
        time.sleep(1)
        queue_local.put(c)
        print('Sum', c)


app = App()
app.mainloop()

Links:

查看更多
登录 后发表回答