"ask" module in Tk

Bill Janssen (janssen@parc.xerox.com)
Mon, 13 Jun 1994 20:53:40 PDT

Here's the full ask module, with ask.ync(), ask.string(), ask.message()
(bit of a misnomer there), and ask.file(). I modified the args to
ask.file() a bit:

ask.file (PROMPT, DEFAULT, optional EXISTS?) -- prompts for a file name
with PROMPT, showing DEFAULT as the default name. If EXISTS? has the
value "ask.New", must be name of non-existent file. If EXISTS? has the
value "ask.Existing", must be the name of an existing file. If EXISTS?
has the value "ask.Maybe" (the default), may be either. If the user
cancels the prompt, the exception "KeyboardInterrupt" is raised.

Note that it uses my (assuredly temporary) variant of Tkinter.py,
herewith also appended.

# ask.py
#

KeyboardInterrupt = 'KeyboardInterrupt'

import Tkinter2

returnvalue = None

No = 0
Yes = 1
Maybe = 2
New=1
Existing=0

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.EmacsEntry (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

def message (s):

global returnvalue

returnvalue = None

tk = Tkinter2.Tk()
top = Tkinter2.Frame(tk)
Tkinter2.Pack.config(top)

msg = Tkinter2.Message(top, {'text': s, 'width': '3i',
'packing': { 'side': 'top', 'expand': 1,
'fill': 'both', 'padx': '3m', 'pady': '3m'}})

accept = Tkinter2.Button(top, {'text': 'OK',
'command': lambda w=top: _do_str_action(w),
'packing': {'side': 'left',
'padx': 4, 'pady': 4,
'expand': 'true'}})
top.mainloop()

def _flash (w):
w.tk.call('wm', 'withdraw', w)
w.tk.call('wm', 'deiconify', w)

def _do_file_action (w, new, v=None):
global returnvalue
import posixpath
if v:
filename = v.get()
if (new == Yes and posixpath.exists(filename)):
_flash(w.toplevel())
return
elif (new == No and not posixpath.exists(filename)):
_flash(w.toplevel())
return
else:
returnvalue = filename
Tkinter2.Tk.__del__(w)
else:
Tkinter2.Tk.__del__(w)

def file (prompt, response='', new=Maybe):

import posixpath

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.EmacsEntry (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, n=new: _do_file_action(w, n, b))
val.takefocus()

buttonpanel = Tkinter2.Frame (top, {'packing': {'side': 'top', 'expand':'true'}})

accept = Tkinter2.Button(buttonpanel, {'text': 'Accept',
'command': lambda w=top, b=val, n=new: _do_file_action(w, n, b),
'packing': {'side': 'left',
'padx': 4, 'pady': 4,
'expand': 'true'}})
cancel = Tkinter2.Button(buttonpanel, {'text': 'Cancel',
'command': lambda w=top, n=new: _do_file_action(w, n),
'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):
master = None
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
Widget.__setitem__(self, 'background', 'lightgray')
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 toplevel(self):
m = self
while hasattr(m, 'master') and m.master:
m = m.master
return m
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)
def activate (self):
self.cmd('activate')
def deactivate (self):
self.cmd('deactivate')
def flash (self):
self.cmd('flash')
def invoke (self):
self.cmd('invoke')

class Canvas(Widget):
def __init__(self, master=None, cnf={}):
self.items=[]
Widget.__init__(self, master, 'canvas', cnf)

class CanvasItem:
def __init__(self, master, cnf={}):
self.master = master
self.callbacks = None
for k in cnf.keys():
CanvasItem.__setitem__(self, k, cnf[k])
def __getitem__(self, key):
v = self.master.tk.split(self.master.tk.call(self.master.pathName,
'itemconfigure',
str(self.id),
'-' + 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.master.tk.createcommand(`id(value)`, value)
self.master.tk.call(self.master.pathName, 'itemconfigure',
str(self.id), '-' + key, id(value))
else:
self.master.tk.call(self.master.pathName, 'itemconfigure',
str(self.id), '-' + key, value)

class Arc(CanvasItem):
def __init__(self, master, pt1, pt2, cnf={}):
self.id = master.cmd('create', 'arc', str(pt1[0]), str(pt1[1]), str(pt2[0]), str(pt2[1]))
CanvasItem.__init__(self, master, cnf)

class Line(CanvasItem):
def __init__(self, master, pts, cnf={}):
pts = reduce(lambda a, b: a+b, map(lambda pt: (str(pt[0]), str(pt[1])), pts))
self.id = apply(master.tk.call, (master.pathName, 'create', 'line') + pts)
CanvasItem.__init__(self, master, cnf)

class Polygon(CanvasItem):
def __init__(self, master, pts, cnf={}):
pts = reduce(lambda a, b: a+b, map(lambda pt: (str(pt[0]), str(pt[1])), pts))
self.id = apply(master.tk.call, (master.pathName, 'create', 'polygon') + pts)
CanvasItem.__init__(self, master, cnf)

class Curve(CanvasItem):
def __init__(self, master, pts, cnf={}):
pts = reduce(lambda a, b: a+b, map(lambda pt: (str(pt[0]), str(pt[1])), pts))
self.id = apply(master.tk.call, (master.pathName, 'create', 'polygon') + pts)
cnf['smooth'] = 'true'
CanvasItem.__init__(self, master, cnf)

class Oval(CanvasItem):
def __init__(self, master, pt1, pt2, cnf={}):
self.id = master.cmd('create', 'oval', str(pt1[0]), str(pt1[1]), str(pt2[0]), str(pt2[1]))
CanvasItem.__init__(self, master, cnf)

class Rectangle(CanvasItem):
def __init__(self, master, pt1, pt2, cnf={}):
self.id = master.cmd('create', 'rectangle', str(pt1[0]), str(pt1[1]), str(pt2[0]), str(pt2[1]))
CanvasItem.__init__(self, master, cnf)

class String(CanvasItem):
def __init__(self, master, where, cnf={}):
self.id = master.cmd('create', 'text', str(where[0]), str(where[1]))
CanvasItem.__init__(self, master, cnf)

class Window(CanvasItem):
def __init__(self, master, where, cnf={}):
self.id = master.cmd('create', 'window', str(where[0]), str(where[1]))
CanvasItem.__init__(self, master, 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 delete(self, index1, index2 = None):
if index2 is None:
self.cmd('delete', index1)
else:
self.cmd('delete', index1, index2)
def get(self):
return self.cmd('get')
def insert(self, index, chars):
self.cmd('insert', index, chars)

class EmacsEntry(Entry):
def __init__(self, master=None, cmd={}):
Entry.__init__(self, master, cmd)
self.killed = ''
self.bind('<Control-a>', self.beginning_of_line)
self.bind('<Control-e>', self.end_of_line)
self.bind('<Control-d>', self.delete_char)
self.bind('<Control-f>', self.forward_char)
self.bind('<Right>', self.forward_char)
self.bind('<Control-b>', self.backward_char)
self.bind('<Left>', self.backward_char)
self.bind('<Control-k>', self.kill_line)
def beginning_of_line(self):
self.cmd('icursor', '0')
def end_of_line(self):
self.cmd('icursor', 'end')
def delete_char(self):
self.delete('insert')
def forward_char(self):
import string
insertchar = string.atoi(self.cmd('index', 'insert'))
self.cmd('icursor', str(insertchar+1))
def backward_char(self):
import string
insertchar = string.atoi(self.cmd('index', 'insert'))
self.cmd('icursor', str(insertchar-1))
def kill_line(self):
self.delete('insert', 'end')

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)

class EmacsText(Text):

def __init__(self, master=None, cmd={}):
Text.__init__(self, master, cmd)
self.killed = ''
self.bind('<Control-a>', self.beginning_of_line)
self.bind('<Control-e>', self.end_of_line)
self.bind('<Control-d>', self.delete_char)
self.bind('<Control-f>', self.forward_char)
self.bind('<Right>', self.forward_char)
self.bind('<Control-b>', self.backward_char)
self.bind('<Left>', self.backward_char)
self.bind('<Control-k>', self.kill_line)
self.bind('<Control-y>', self.yank)
def beginning_of_line(self):
self.mark('set', 'insert', 'insert linestart')
def end_of_line(self):
self.mark('set', 'insert', 'insert lineend')
def delete_char(self):
self.delete('insert')
def forward_char(self):
self.mark('set', 'insert', 'insert + 1 char')
def backward_char(self):
self.mark('set', 'insert', 'insert - 1 char')
def kill_line(self):
self.killed = self.get('insert', 'insert lineend')
self.delete('insert', 'insert lineend')
def yank(self):
self.insert('insert', self.killed)
# end of Tkinter2.py