> Another suggestion: when an error occurs, all we see is a dialog box
> giving the id of the function. Being able to see where the error took
> place would be nice.
Here's a module "ask", which implements askync() (as ask.ync()) and
askstr() (as ask.string()) from the stdwin module, but with Tk. (I'm
still working on ask.file().) It uses my own (I'm sure temporary)
version of Tkinter.py, Tkinter2.py. All python functions called from
Tcl are wrapped in something that tells you where the error occurred,
and prevents the Tk dialog box from coming up.
# ask.py
KeyboardInterrupt = 'KeyboardInterrupt'
import Tkinter2
returnvalue = None
No = 0
Yes = 1
def _do_ync_action (i, w):
global returnvalue
returnvalue = i
Tkinter2.Tk.__del__(w)
def ync (prompt, default=-1):
def make_button (parent, top, default, text, value):
if (value == default):
f = Tkinter2.Frame(parent, {'borderwidth': 4, 'relief': 'ridge',
'packing': {'fill': 'both', 'side': 'left', 'expand': 'true'}})
b = Tkinter2.Button(f, {'text': text,
'command': lambda v=value, win=top: _do_ync_action(v, win),
'packing': {'side': 'left', 'fill': 'both',
'padx': 0, 'pady': 0, 'expand': 'true'}})
else:
b = Tkinter2.Button(parent, {'text': text,
'command': lambda v=value, win=top: _do_ync_action(v, win),
'packing': {'side': 'left', 'fill': 'both',
'padx': 4, 'pady': 4, 'expand': 'true'}})
return b
tk = Tkinter2.Tk()
top = Tkinter2.Frame(tk)
tk['title'] = 'askync'
tk['iconname'] = 'askync'
Tkinter2.Pack.config(top)
msg = Tkinter2.Message(top, {'width': '3i', 'text': prompt,
'packing': { 'side': 'top', 'expand': 1,
'fill': 'both',
'padx': '3m', 'pady': '3m'}})
button_panel = Tkinter2.Frame (top, {'borderwidth': 4,
'packing': {'side': 'top', 'fill': 'both'}})
buttons = []
buttons.append(make_button(button_panel, top, default, 'No', 0))
buttons.append(make_button(button_panel, top, default, 'Yes', 1))
buttons.append(make_button(button_panel, top, default, 'Cancel', 2))
if default >= 0:
top.bind('<Return>',
lambda b=buttons[default], i=default, w=top:
(b.cmd('flash'), _do_ync_action(i, w)))
top.takefocus()
top.tk.mainloop()
if returnvalue == 2: raise KeyboardInterrupt
else: return (returnvalue)
def _do_str_action (w, val=None):
global returnvalue
if val:
returnvalue = val.get()
Tkinter2.Tk.__del__(w)
def string (prompt, response=''):
global returnvalue
returnvalue = None
tk = Tkinter2.Tk()
top = Tkinter2.Frame(tk)
Tkinter2.Pack.config(top)
msg = Tkinter2.Message(top, {'text': prompt, 'width': '3i',
'packing': { 'side': 'top', 'expand': 1,
'fill': 'both', 'padx': '3m', 'pady': '3m'}})
val = Tkinter2.Entry (top, {'relief': 'sunken',
'packing': { 'side': 'top', 'fill': 'both',
'padx': 4, 'pady': 4, 'expand': 1}})
val.insert (0, response)
val.bind('<Return>', lambda b=val, w=top: _do_str_action(w, b))
val.takefocus()
buttonpanel = Tkinter2.Frame (top, {'packing': {'side': 'top', 'expand':'true'}})
accept = Tkinter2.Button(buttonpanel, {'text': 'Accept',
'command': lambda w=top, b=val: _do_str_action(w, b),
'packing': {'side': 'left',
'padx': 4, 'pady': 4,
'expand': 'true'}})
cancel = Tkinter2.Button(buttonpanel, {'text': 'Cancel',
'command': lambda w=top: _do_str_action(w),
'packing': {'side': 'left',
'padx': 4, 'pady': 4,
'expand': 'true'}})
top.mainloop()
if returnvalue == None:
raise KeyboardInterrupt
else: return returnvalue
# end of ask.py
# Tkinter2.py
#$Id: Tkinter2.py,v 1.1 1994/06/09 05:44:28 janssen Exp janssen $
import tkinter
class Wm:
# XXX This doesn't work for wm commands receiving more than one
# XXX argument or none at all (e.g. aspect, [de]iconify)
def __getitem__(self, key):
return self.tk.call('wm', key, self.pathName)
def __setitem__(self, key, value):
self.tk.call('wm', key, self.pathName, value)
def config(self, cnf={}):
for k in cnf.keys():
Wm.__setitem__(self, k, cnf[k])
wm = config
def _print_traceback (t):
if t:
return ' %s, line %s\n%s' % (t.tb_frame.f_code, t.tb_lineno, _print_traceback (t.tb_next))
else:
return ''
def _call_safely(f):
import sys
try:
val = f()
if val == None:
return ''
else: return val
except:
try:
errmsg = 'On callback to %s, exception %s (value %s) signalled.\nStack is:\n%s\n' % (f, sys.exc_type, sys.exc_value,
_print_traceback(sys.exc_traceback.tb_next))
sys.stderr.write(errmsg)
except:
print 'recursive exception (%s, %s) signalled in Tkinter.py:_call_safely.' % (sys.exc_type, sys.exc_value)
return ''
class Tk(Wm):
pathName = '.'
def __init__(self, screenName=None, baseName=None,
className='Tk'):
if baseName is None:
import sys, os
baseName = os.path.basename(sys.argv[0])
if baseName[-3:] == '.py': baseName = baseName[:-3]
self.tk = tkinter.create(screenName, baseName, className)
def __del__(self):
self.tk.call('destroy', '.')
def __str__(self):
return '.'
def mainloop(self):
self.tk.mainloop()
def after(self, ms, func=None, *args):
if not func:
self.tk.call('after', ms)
else:
name = `id(func)`
self.tk.createcommand(name, func)
apply(self.tk.call, ('after', ms, name) + args)
def focus(self, *args):
return apply(self.tk.call, ('focus',) + args)
def grab(self, *args): # XXX '-global' option!
return self.tk.split(apply(self.tk.call, ('grab',) + args))
def lower(self, *args):
return apply(self.tk.call, ('lower',) + args)
def selection(self, *args):
return apply(self.tk.call, ('selection',) + args)
def send(self, *args):
return apply(self.tk.call, ('send',) + args)
def colormodel(self, *value):
return apply(self.tk.call,
('tk', 'colormodel', self.pathName) + value)
def winfo(self, *args):
return apply(self.tk.call, ('winfo',) + args)
def update(self, *args):
return apply(self.tk.call, ('update',) + args)
def bind(self, sequence=None, func=None, substitution=()):
if sequence == None:
return self.tk.splitlist(
self.tk.call('bind', self.pathName))
if not self.callbacks:
self.callbacks = []
callback = lambda f=func: _call_safely(f)
self.callbacks.insert(0, callback)
self.tk.createcommand(`id(callback)`, callback)
self.tk.call('bind', self.pathName, sequence,
(`id(callback)`,) + substitution)
def cmd(self, *args):
return apply(self.tk.call, args)
class Pack:
def __setitem__(self, key, value):
self.tk.call('pack', 'configure',
self.pathName, '-' + key, value)
def config(self, cnf={}):
if cnf == {}:
self.tk.call('pack', 'configure', self.pathName)
else:
for k in cnf.keys():
Pack.__setitem__(self, k, cnf[k])
pack = config
def cmd(self, option, *args):
return apply(self.tk.call,
('pack', option, self.pathName) + args)
class Place:
def __setitem__(self, key, value):
self.tk.call('place', 'configure',
self.pathName, '-' + key, value)
def config(self, cnf={}):
if cnf == {}:
self.tk.call('place', 'configure', self.pathName)
else:
for k in cnf.keys():
Pack.__setitem__(self, k, cnf[k])
place = config
def cmd(self, option, *args):
return apply(self.tk.call,
('pack', option, self.pathName) + args)
class Widget(Pack, Place, Tk):
def __init__(self, master, widgetName, cnf={}, extra=()):
if not master:
master = Tk()
self.master = master # preserve parent from GC
self.tk = master.tk
self.callbacks = None
if master.pathName=='.':
self.pathName = '.' + `id(self)`
else:
self.pathName = master.pathName + '.' + `id(self)`
self.widgetName = widgetName
apply(self.tk.call, (widgetName, self.pathName) + extra)
if (cnf.has_key('packing')):
packing = cnf['packing']
del cnf['packing']
else:
packing = None
for k in cnf.keys():
Widget.__setitem__(self, k, cnf[k])
if packing:
self.pack(packing)
def __getitem__(self, key):
v = self.tk.split(self.tk.call(self.pathName,
'configure',
'-' + key))
return v[4]
def __setitem__(self, key, value):
if type(value) in (type(Widget.__init__), type(_call_safely)):
if (type(value) == type(_call_safely)):
if not self.callbacks:
self.callbacks = []
callback = lambda f=value: _call_safely(f)
self.callbacks.insert(0, callback)
value = callback
self.tk.createcommand(`id(value)`, value)
self.tk.call(self.pathName, 'configure',
'-' + key, id(value))
else:
self.tk.call(self.pathName, 'configure',
'-' + key, value)
def config(self, cnf={}):
for k in cnf.keys():
Widget.__setitem__(self, k, cnf[k])
def cmd(self, option, *args):
return apply(self.tk.call, (self.pathName, option) + args)
def takefocus(self):
self.tk.call('focus', self)
def __str__(self):
return self.pathName
def __del__(self):
self.tk.call('destroy', self.pathName)
destroy = __del__
class Toplevel(Widget):
def __init__(self, master=None, cnf={}):
extra = ()
if cnf.has_key('screen'):
extra = ('-screen', cnf['screen'])
del cnf['screen']
if cnf.has_key('class'):
extra = extra + ('-class', cnf['class'])
del cnf['class']
Widget.__init__(self, master, 'toplevel', cnf, extra)
self.wm({'iconname': self.tk.call('wm', 'title', '.')})
self.wm({'title': self.tk.call('wm', 'title', '.')})
for k in cnf.keys():
Toplevel.__setitem__(k, cnf[k])
class Button(Widget):
def __init__(self, master=None, cnf={}):
Widget.__init__(self, master, 'button', cnf)
class Canvas(Widget):
def __init__(self, master=None, cnf={}):
Widget.__init__(self, master, 'canvas', cnf)
class Checkbutton(Widget):
def __init__(self, master=None, cnf={}):
Widget.__init__(self, master, 'checkbutton', cnf)
class Entry(Widget):
def __init__(self, master=None, cnf={}):
Widget.__init__(self, master, 'entry', cnf)
def get (self):
return (self.cmd ('get'))
def insert (self, position, chars):
return (self.cmd ('insert', str(position), chars))
class Frame(Widget):
def __init__(self, master=None, cnf={}):
Widget.__init__(self, master, 'frame', cnf)
def tk_menuBar(self, *args):
apply(self.tk.call, ('tk_menuBar', self.pathName) + args)
class Label(Widget):
def __init__(self, master=None, cnf={}):
Widget.__init__(self, master, 'label', cnf)
class Listbox(Widget):
def __init__(self, master=None, cnf={}):
Widget.__init__(self, master, 'listbox', cnf)
class Menu(Widget):
def __init__(self, master=None, cnf={}):
Widget.__init__(self, master, 'menu', cnf)
def activate(self, index):
self.cmd('activate', index)
def add(self, itemtype, cnf = {}):
args = ()
for k, v in cnf.items():
if type(v) in (type(_call_safely), type(Menu.__init__)):
if (type(v) == type(_call_safely)):
if not self.callbacks:
self.callbacks = []
callback = lambda f=v: _call_safely(f)
self.callbacks.insert(0, callback)
v = callback
self.tk.createcommand(`id(v)`, v)
v = `id(v)`
args = args + ('-'+k, v)
return apply(self.cmd, ('add', itemtype) + args)
def delete(self, index1, index2 = None):
if index2 is None: self.cmd('delete', index1)
else: self.cmd('delete', index1, index2)
def disable(self, index): # XXX obsolete
self.cmd('disable', index)
def enable(self, index): # XXX obsolete
self.cmd('enable', index)
def entryconfigure(self, index, cnf = {}):
args = ()
for k, v in cnf.items():
args = args + ('-'+k, v)
return apply(self.cmd, ('entryconfigure', type) + args)
def index(self, index):
return self.cmd('index', index)
def invoke(self, index):
return self.cmd('invoke', index)
def post(self, x, y):
return self.cmd('post', x, y)
def unpost(self):
self.cmd('unpost')
def yposition(self, index):
return self.cmd('yposition', index)
class Menubutton(Widget):
def __init__(self, master=None, cnf={}):
Widget.__init__(self, master, 'menubutton', cnf)
class Message(Widget):
def __init__(self, master=None, cnf={}):
Widget.__init__(self, master, 'message', cnf)
class Radiobutton(Widget):
def __init__(self, master=None, cnf={}):
Widget.__init__(self, master, 'radiobutton', cnf)
class Scale(Widget):
def __init__(self, master=None, cnf={}):
Widget.__init__(self, master, 'scale', cnf)
class Scrollbar(Widget):
def __init__(self, master=None, cnf={}):
Widget.__init__(self, master, 'scrollbar', cnf)
class Text(Widget):
def __init__(self, master=None, cnf={}):
Widget.__init__(self, master, 'text', cnf)
def compare(self, index1, op, index2):
return self.cmd('compare', index1, op, index2)
def debug(self, flag = None):
if flag is None: return self.cmd('debug')
else: self.cmd('debug', not not flag)
def delete(self, index1, index2 = None):
if index2 is None: self.cmd('delete', index1)
else: self.cmd('delete', index1, index2)
def get(self, index1, index2 = None):
if index2 is None: return self.cmd('get', index1)
else: return self.cmd('get', index1, index2)
def index(self, index):
return self.cmd('index', index)
def insert(self, index, chars):
self.cmd('insert', index, chars)
def mark(self, option, *args):
apply(self.cmd, ('mark', option) + args)
def scan(self, option, *args):
apply(self.cmd, ('scan', option) + args)
def tag(self, option, *args):
apply(self.cmd, ('tag', option) + args)
def tag_configure(self, option, cnf = {}):
args = ()
for k, v in cnf.items():
args = args + ('-'+k, v)
apply(self.cmd, ('tag', 'configure', option) + args)
def yview(self, *args):
# Should check for yview ?-pickplace? what
apply(self.cmd, ('yview',) + args)
# end of Tkinter2.py