Skip to content

Instantly share code, notes, and snippets.

@mikofski
Last active December 17, 2015 14:08
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mikofski/5621839 to your computer and use it in GitHub Desktop.
Save mikofski/5621839 to your computer and use it in GitHub Desktop.
async Tk waitbox widget
"""
An example of a combobox that adds user entries using key binding to return
"""
from Tkinter import *
from ttk import *
def on_combobox_selected(event):
new_value = event.widget.get()
is_in_list = event.widget.current()
if is_in_list < 0:
event.widget['value'] += (new_value, )
if __name__ == '__main__':
root = Tk()
master = Frame(root)
cbox = Combobox(master, values=['this','that'])
cbox.pack(fill=BOTH)
master.pack(fill=BOTH)
cbox.bind('<Return>', on_combobox_selected)
"""
An example of a ttk Progressbar that is way better than ticking hourglass in waitbox_widget.
Combine this with waitbox_widget as the process on the main thread while the async task
completes and it would be a nice addition. It even has a builtin timer!
"""
from Tkinter import *
from ttk import *
if __name__ == '__main__':
root = Tk()
master = Frame(root)
master.pack(fill=BOTH) # fill the Tk root window with the Frame
Label(master, text="Please wait ...").pack(fill=BOTH) # a custom message, fill the Frame
# by default widgets are stacked vertically
progbar = Progressbar(master, orient=HORIZONTAL, length='3i', mode='determinate', value=0)
# save progbar, you'll need it to change the value
progbar.pack(fill=BOTH) # fill the Frame with the progressbar
"""
An example of a Tix Meter widget that is only slightly nicer than the ticking hourglass
in waitbox_widget. It pales in comparison to ttk's Progressbar.
"""
import Tix
if __name__ == '__main__':
root = Tix.Tk()
master = Tix.Frame(root)
progmeter = Tix.Meter(master, fillcolor='blue', text='progress', value=10)
progmeter.pack(fill='both')
master.pack(fill=BOTH)
import logging
from Tkinter import Frame, Toplevel, IntVar, BOTH
from threading import Thread
from Queue import Queue
# basic logging
logging.basicConfig(level=logging.DEBUG,
format='[%(levelname)s] (%(threadName)-10s) %(message)s')
class WaitWidget(Frame):
"""
A wait widget that shows something is happening.
:parameter queue: A queue into which results and other info are placed.
:parameter master: A Toplevel Tk window.
:returns obj: A WaitWidget object.
"""
def __init__(self, queue, master):
self.queue = queue # the queue
Frame.__init__(self, master) # call the Frame constructor
self.pack(fill=BOTH) # fill the Toplevel Tk window with the frame
self.focus_set() # get focus
self.grab_set() # make the window modal
master.resizable(False, False) # not resizable
master.title("") # no title
# don't let user close window using X - instead call timer
master.protocol("WM_DELETE_WINDOW", self.timer)
# Customize with whatever info is put in the queue, EG: with "progress"
# create a integer variable in master called "wait"
self.wait = IntVar(master, 0, "wait")
# put an hourglass in the widget and display how long we've been waiting
Label(master, bitmap="hourglass").pack(fill=BOTH) # customize with a photoimage,
Label(master, text="Please wait ...").pack(fill=BOTH) # a custom message
Label(master, textvariable=self.wait).pack(fill=BOTH) # and custom output
# start timer
self.timer() # customize with a callback
def timer(self):
"""
A callback that counts milliseconds until `self.queue` has somthing in it
"""
wait = self.wait.get() + 1 # increment 100[ms]
# customize with a different action based on queue contents
# For example:
# >>> result = self.queue.get()
# >>> if type(result) == int:
# >>> self.wait.set(result)
# >>> else:
# >>> self.queue.put(result)
# >>> self.quit()
if not self.queue.empty():
# when queue is filled, quit loop and print elapsed time
print 'elapsed time = %2.1f [s]' % (wait * 0.10)
self.quit()
self.wait.set(wait)
# loop over this callback every 100[ms] until queue is filled
# this does **not** block the main thread, yay!
self.after(100, self.timer)
def setqueue(original_function, queue):
"""
Create a new function `queuefun` that calculates the results from the
`original_function` and puts them in a queue.
:parameter original_function: The function from which the results are added to \
the queue.
:parameter queue: The queue to which the results of the original_function are \
added.
:returns queuefun: The new function.
"""
def queuefun(*args, **kwargs):
"""
Call `original_function` with args & kwargs and put the results in `queue`.
NOTE: this is function *call*, not a function object!
This is equivalent to:
>>> results = original_function(*args, **kwargs) # calc results
>>> queue.put(results) # put results in queue
>>> results = queue.get() # get results from queue
"""
logging.debug('Starting')
queue.put(original_function(*args, **kwargs))
logging.debug('Exiting')
return queuefun
def waitbox(original_function):
"""
Create a new function that adds a waitbox widget to original_function.
:parameter original_function: A funciton to wrap with a waitbox waitWidget
"""
def new_function(*args, **kwargs):
"""
Create a queue, create a queuefun from original_function and make the
new queuefun the target of a thread, passing the thread target
original_function's args and kwargs. Then instantiate a new Tk Toplevel
Frame called master and a new waitWidget with the queue and master.
Start master's mainloop which exits when the original_function's
results are fed to the queue. Destroy master and return the
original_function's results.
"""
queue = Queue.Queue() # create a queue
queuefun = setqueue(original_function, queue) # create a queuefun function
thread = Thread(target=queuefun, args=args, kwargs=kwargs) # create thread
thread.start() # start the thread
master = Toplevel() # make a Toplevel Tk window
waitBox = WaitWidget(queue, master) # create a WaitWidget object
waitBox.mainloop() # start its mainloop and wait for it to end
master.destroy() # kill the WaitWidget
return queue.get() # return the final contents of the queue
return new_function
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment