问题
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:
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:
- Running Functions as Threads in Python
- How to connect a progress bar to a function?
- linking tkinter progress bar to function
来源:https://stackoverflow.com/questions/42772952/display-the-progress-of-execution-of-a-function-using-a-progress-bar