Re: Tkinter plans

Bill Janssen (janssen@parc.xerox.com)
Sun, 12 Jun 1994 18:52:09 PDT

Excerpts from ext.python: 12-Jun-94 Re: Tkinter plans "Steven
Miale"@cs.indian (331)

> 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