使用进度条显示函数的执行进度

时间:2017-03-13 20:36:20

标签: python function tkinter progress-bar execution-time

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

我有一个计算两个整数之和的函数。我想使用tkinter进度条显示此sum函数的执行状态。

这是我的方法,但它在执行sum函数后显示进度条,我想在sum函数执行时显示进度条,并且应该根据函数的执行时间。

我找不到符合我要求的答案。如果有人可以帮助我,那就太好了。

1 个答案:

答案 0 :(得分:1)

你的问题很有意思,但你接近是完全错误的。 它首先执行sum,因为GUI尚未到达mainloop

因此,在GUI到达mainloop之后,它开始等待按钮按下事件(因为GUI编程的事件驱动性质)。换句话说:如果你要求回调tkinter,你就不能在mainloop之前调用函数。

另一个问题 - tkinter是单线程的,因此gui只能在函数完成运行时自行更新。因此,如果你在函数中启动一个循环,那么gui也没有回调函数,但是你可以从循环中调用函数,并在每次迭代时更新gui! (例如,自生成事件的“循环”,由方法generate_eventafter组成。)

为了上帝的缘故,如果函数没有完全执行,进度条如何知道函数的执行时间?如果有人知道,请发表评论..

但是如果你敢,你可以开始玩多线程和队列了!

在我的示例中,进度条与函数的执行并行更新,这要归功于执行函数的单独线程,以及函数“响应”所依赖的队列,在此基础上进度条正在更新!

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

链接: