Commit d7500fcb authored by Guido van Rossum's avatar Guido van Rossum

These directories renamed: tkinter -> lib-tk, stdwin -> lib-stdwin.

parent 045e688f
# Abstract classes for parents and children.
#
# Do not use as base class -- this is for documentation only.
#
# Note that the tree must be built top down (create the parent,
# then add the children).
#
# Also note that the creation methods are not standardized --
# these have extra parameters dependent on the widget type.
# For historical reasons, button creation methods are called
# define() while split creation methods are called create().
class AbstractParent:
#
# Upcalls from child to parent
#
def addchild(self, child): unimpl()
def delchild(self, child): unimpl()
#
def need_mouse(self, child): unimpl()
def no_mouse(self, child): unimpl()
#
def need_timer(self, child): unimpl()
def no_timer(self, child): unimpl()
#
# XXX need_kbd, no_kbd; focus???
#
def begindrawing(self): return unimpl()
def beginmeasuring(self): return unimpl()
def getwindow(self): return unimpl() # Only for very special cases
#
def change(self, area): unimpl()
def scroll(self, area, (dh, dv)): unimpl()
def settimer(self, itimer): unimpl()
class AbstractChild:
#
# Downcalls from parent to child
#
def destroy(self): unimpl()
#
def realize(self): return unimpl()
def getminsize(self, m, (width, height)): return unimpl()
def getbounds(self): return unimpl()
def setbounds(self, bounds): unimpl()
def draw(self, d, area): unimpl()
#
# Downcalls only made after certain upcalls
#
def mouse_down(self, detail): unimpl()
def mouse_move(self, detail): unimpl()
def mouse_up(self, detail): unimpl()
#
def timer(self): unimpl()
# A "Split" is a child that manages one or more children.
# (This terminology is due to DEC SRC, except for CSplits.)
# A child of a split may be another split, a button, a slider, etc.
# Certain upcalls and downcalls can be handled transparently, but
# for others (e.g., all geometry related calls) this is not possible.
class AbstractSplit(AbstractChild, AbstractParent):
pass
from TransParent import TransParent
class BoxParent(TransParent):
#
def create(self, parent, (dh, dv)):
self = TransParent.create(self, parent)
self.dh = dh
self.dv = dv
return self
#
def getminsize(self, m, (width, height)):
width = max(0, width - 2*self.dh)
height = max(0, height - 2*self.dv)
width, height = self.child.getminsize(m, (width, height))
return width + 2*self.dh, height + 2*self.dv
#
def setbounds(self, bounds):
(left, top), (right, bottom) = bounds
self.bounds = bounds
left = min(right, left + self.dh)
top = min(bottom, top + self.dv)
right = max(left, right - self.dh)
bottom = max(top, bottom - self.dv)
self.innerbounds = (left, top), (right, bottom)
self.child.setbounds(self.innerbounds)
#
def getbounds(self):
return self.bounds
#
def draw(self, d, area):
(left, top), (right, bottom) = self.bounds
left = left + 1
top = top + 1
right = right - 1
bottom = bottom - 1
d.box((left, top), (right, bottom))
TransParent.draw(self, d, area) # XXX clip to innerbounds?
#
# XXX should scroll clip to innerbounds???
# XXX currently the only user restricts itself to child's bounds
# Module 'Buttons'
# Import module 'rect' renamed as '_rect' to avoid exporting it on
# 'from Buttons import *'
#
import rect
_rect = rect
del rect
# Field indices in mouse event detail
#
_HV = 0
_CLICKS = 1
_BUTTON = 2
_MASK = 3
# LabelAppearance provides defaults for all appearance methods.
# selected state not visible
# disabled --> crossed out
# hilited --> inverted
#
class LabelAppearance:
#
# Initialization
#
def init_appearance(self):
self.bounds = _rect.empty
self.enabled = 1
self.hilited = 0
self.selected = 0
self.text = ''
#
# Size enquiry
#
def getminsize(self, m, (width, height)):
width = max(width, m.textwidth(self.text) + 6)
height = max(height, m.lineheight() + 6)
return width, height
#
def getbounds(self):
return self.bounds
#
# Changing the parameters
#
def settext(self, text):
self.text = text
if self.bounds <> _rect.empty:
self.recalctextpos()
self.redraw()
#
def setbounds(self, bounds):
self.bounds = bounds
if self.bounds <> _rect.empty:
self.recalc()
#
def realize(self):
pass
#
# Changing the state bits
#
def enable(self, flag):
if flag <> self.enabled:
self.enabled = flag
if self.bounds <> _rect.empty:
self.flipenable(self.parent.begindrawing())
#
def hilite(self, flag):
if flag <> self.hilited:
self.hilited = flag
if self.bounds <> _rect.empty:
self.fliphilite(self.parent.begindrawing())
#
def select(self, flag):
if flag <> self.selected:
self.selected = flag
if self.bounds <> _rect.empty:
self.redraw()
#
# Recalculate the box bounds and text position.
# This can be overridden by buttons that draw different boxes
# or want their text in a different position.
#
def recalc(self):
if self.bounds <> _rect.empty:
self.recalcbounds()
self.recalctextpos()
#
def recalcbounds(self):
self.hilitebounds = _rect.inset(self.bounds, (3, 3))
self.crossbounds = self.bounds
#
def recalctextpos(self):
(left, top), (right, bottom) = self.bounds
m = self.parent.beginmeasuring()
h = (left + right - m.textwidth(self.text)) / 2
v = (top + bottom - m.lineheight()) / 2
self.textpos = h, v
#
# Generic drawing interface.
# Do not override redraw() or draw() methods; override drawit() c.s.
#
def redraw(self):
if self.bounds <> _rect.empty:
d = self.parent.begindrawing()
d.erase(self.bounds)
self.draw(d, self.bounds)
#
def draw(self, d, area):
area = _rect.intersect([area, self.bounds])
if area == _rect.empty:
return
d.cliprect(area)
self.drawit(d)
d.noclip()
#
# The drawit() method is fairly generic but may be overridden.
#
def drawit(self, d):
self.drawpict(d)
if self.text:
d.text(self.textpos, self.text)
if not self.enabled:
self.flipenable(d)
if self.hilited:
self.fliphilite(d)
#
# Default drawing detail functions.
# Overriding these is normally sufficient to get different
# appearances.
#
def drawpict(self, d):
pass
#
def flipenable(self, d):
_xorcross(d, self.crossbounds)
#
def fliphilite(self, d):
d.invert(self.hilitebounds)
# A Strut is a label with no width of its own.
class StrutAppearance(LabelAppearance):
#
def getminsize(self, m, (width, height)):
height = max(height, m.lineheight() + 6)
return width, height
#
# ButtonAppearance displays a centered string in a box.
# selected --> bold border
# disabled --> crossed out
# hilited --> inverted
#
class ButtonAppearance(LabelAppearance):
#
def drawpict(self, d):
d.box(_rect.inset(self.bounds, (1, 1)))
if self.selected:
# Make a thicker box
d.box(self.bounds)
d.box(_rect.inset(self.bounds, (2, 2)))
d.box(_rect.inset(self.bounds, (3, 3)))
#
# CheckAppearance displays a small square box and a left-justified string.
# selected --> a cross appears in the box
# disabled --> whole button crossed out
# hilited --> box is inverted
#
class CheckAppearance(LabelAppearance):
#
def getminsize(self, m, (width, height)):
minwidth = m.textwidth(self.text) + 6
minheight = m.lineheight() + 6
width = max(width, minwidth + minheight + m.textwidth(' '))
height = max(height, minheight)
return width, height
#
def drawpict(self, d):
d.box(self.boxbounds)
if self.selected: _xorcross(d, self.boxbounds)
#
def recalcbounds(self):
LabelAppearance.recalcbounds(self)
(left, top), (right, bottom) = self.bounds
self.size = bottom - top - 4
self.boxbounds = (left+2, top+2), (left+2+self.size, bottom-2)
self.hilitebounds = self.boxbounds
#
def recalctextpos(self):
m = self.parent.beginmeasuring()
(left, top), (right, bottom) = self.boxbounds
h = right + m.textwidth(' ')
v = top + (self.size - m.lineheight()) / 2
self.textpos = h, v
#
# RadioAppearance displays a round indicator and a left-justified string.
# selected --> a dot appears in the indicator
# disabled --> whole button crossed out
# hilited --> indicator is inverted
#
class RadioAppearance(CheckAppearance):
#
def drawpict(self, d):
(left, top), (right, bottom) = self.boxbounds
radius = self.size / 2
center = left + radius, top + radius
d.circle(center, radius)
if self.selected:
d.fillcircle(center, radius*3/5)
#
# NoReactivity ignores mouse events.
#
class NoReactivity:
def init_reactivity(self): pass
# BaseReactivity defines hooks and asks for mouse events,
# but provides only dummy mouse event handlers.
# The trigger methods call the corresponding hooks set by the user.
# Hooks (and triggers) mean the following:
# down_hook called on some mouse-down events
# move_hook called on some mouse-move events
# up_hook called on mouse-up events
# on_hook called for buttons with on/off state, when it goes on
# hook called when a button 'fires' or a radiobutton goes on
# There are usually extra conditions, e.g., hooks are only called
# when the button is enabled, or active, or selected (on).
#
class BaseReactivity:
#
def init_reactivity(self):
self.down_hook = self.move_hook = self.up_hook = \
self.on_hook = self.off_hook = \
self.hook = self.active = 0
self.parent.need_mouse(self)
#
def mousetest(self, hv):
return _rect.pointinrect(hv, self.bounds)
#
def mouse_down(self, detail):
pass
#
def mouse_move(self, detail):
pass
#
def mouse_up(self, detail):
pass
#
def down_trigger(self):
if self.down_hook: self.down_hook(self)
#
def move_trigger(self):
if self.move_hook: self.move_hook(self)
#
def up_trigger(self):
if self.up_hook: self.up_hook(self)
#
def on_trigger(self):
if self.on_hook: self.on_hook(self)
#
def off_trigger(self):
if self.off_hook: self.off_hook(self)
#
def trigger(self):
if self.hook: self.hook(self)
# ToggleReactivity acts like a simple pushbutton.
# It toggles its hilite state on mouse down events.
#
class ToggleReactivity(BaseReactivity):
#
def mouse_down(self, detail):
if self.enabled and self.mousetest(detail[_HV]):
self.active = 1
self.hilite(not self.hilited)
self.down_trigger()
#
def mouse_move(self, detail):
if self.active:
self.move_trigger()
#
def mouse_up(self, detail):
if self.active:
self.up_trigger()
self.active = 0
#
def down_trigger(self):
if self.hilited:
self.on_trigger()
else:
self.off_trigger()
self.trigger()
#
# TriggerReactivity acts like a fancy pushbutton.
# It hilites itself while the mouse is down within its bounds.
#
class TriggerReactivity(BaseReactivity):
#
def mouse_down(self, detail):
if self.enabled and self.mousetest(detail[_HV]):
self.active = 1
self.hilite(1)
self.down_trigger()
#
def mouse_move(self, detail):
if self.active:
self.hilite(self.mousetest(detail[_HV]))
if self.hilited:
self.move_trigger()
#
def mouse_up(self, detail):
if self.active:
self.hilite(self.mousetest(detail[_HV]))
if self.hilited:
self.up_trigger()
self.trigger()
self.active = 0
self.hilite(0)
#
# CheckReactivity handles mouse events like TriggerReactivity,
# It overrides the up_trigger method to flip its selected state.
#
class CheckReactivity(TriggerReactivity):
#
def up_trigger(self):
self.select(not self.selected)
if self.selected:
self.on_trigger()
else:
self.off_trigger()
self.trigger()
# RadioReactivity turns itself on and the other buttons in its group
# off when its up_trigger method is called.
#
class RadioReactivity(TriggerReactivity):
#
def init_reactivity(self):
TriggerReactivity.init_reactivity(self)
self.group = []
#
def up_trigger(self):
for b in self.group:
if b <> self:
if b.selected:
b.select(0)
b.off_trigger()
self.select(1)
self.on_trigger()
self.trigger()
# Auxiliary class for 'define' method.
# Call the initializers in the right order.
#
class Define:
#
def define(self, parent):
self.parent = parent
parent.addchild(self)
self.init_appearance()
self.init_reactivity()
return self
#
def destroy(self):
self.parent = 0
#
def definetext(self, parent, text):
self = self.define(parent)
self.settext(text)
return self
# Subroutine to cross out a rectangle.
#
def _xorcross(d, bounds):
((left, top), (right, bottom)) = bounds
# This is s bit funny to make it look better
left = left + 2
right = right - 2
top = top + 2
bottom = bottom - 3
d.xorline(((left, top), (right, bottom)))
d.xorline((left, bottom), (right, top))
# Ready-made button classes.
#
class Label(NoReactivity, LabelAppearance, Define): pass
class Strut(NoReactivity, StrutAppearance, Define): pass
class PushButton(TriggerReactivity, ButtonAppearance, Define): pass
class CheckButton(CheckReactivity, CheckAppearance, Define): pass
class RadioButton(RadioReactivity, RadioAppearance, Define): pass
class ToggleButton(ToggleReactivity, ButtonAppearance, Define): pass
# A CSplit is a Clock-shaped split: the children are grouped in a circle.
# The numbering is a little different from a real clock: the 12 o'clock
# position is called 0, not 12. This is a little easier since Python
# usually counts from zero. (BTW, there needn't be exactly 12 children.)
from math import pi, sin, cos
from Split import Split
class CSplit(Split):
#
def getminsize(self, m, (width, height)):
# Since things look best if the children are spaced evenly
# along the circle (and often all children have the same
# size anyway) we compute the max child size and assume
# this is each child's size.
for child in self.children:
wi, he = child.getminsize(m, (0, 0))
width = max(width, wi)
height = max(height, he)
# In approximation, the diameter of the circle we need is
# (diameter of box) * (#children) / pi.
# We approximate pi by 3 (so we slightly overestimate
# our minimal size requirements -- not so bad).
# Because the boxes stick out of the circle we add the
# box size to each dimension.
# Because we really deal with ellipses, do everything
# separate in each dimension.
n = len(self.children)
return width + (width*n + 2)/3, height + (height*n + 2)/3
#
def getbounds(self):
return self.bounds
#
def setbounds(self, bounds):
self.bounds = bounds
# Place the children. This involves some math.
# Compute center positions for children as if they were
# ellipses with a diameter about 1/N times the
# circumference of the big ellipse.
# (There is some rounding involved to make it look
# reasonable for small and large N alike.)
# XXX One day Python will have automatic conversions...
n = len(self.children)
fn = float(n)
if n == 0: return
(left, top), (right, bottom) = bounds
width, height = right-left, bottom-top
child_width, child_height = width*3/(n+4), height*3/(n+4)
half_width, half_height = \
float(width-child_width)/2.0, \
float(height-child_height)/2.0
center_h, center_v = center = (left+right)/2, (top+bottom)/2
fch, fcv = float(center_h), float(center_v)
alpha = 2.0 * pi / fn
for i in range(n):
child = self.children[i]
fi = float(i)
fh, fv = \
fch + half_width*sin(fi*alpha), \
fcv - half_height*cos(fi*alpha)
left, top = \
int(fh) - child_width/2, \
int(fv) - child_height/2
right, bottom = \
left + child_width, \
top + child_height
child.setbounds(((left, top), (right, bottom)))
#
# DirList -- Directory Listing widget
# XXX Displays messy paths when following '..'
import os
import stdwin, rect
from stdwinevents import *
from Buttons import PushButton
from WindowParent import WindowParent
from HVSplit import HSplit, VSplit
class DirList(VSplit):
#
def create(self, parent, dirname):
self = VSplit.create(self, parent)
names = os.listdir(dirname)
for name in names:
if os.path.isdir(os.path.join(dirname, name)):
fullname = os.path.join(dirname, name)
btn = SubdirButton().definetext(self, fullname)
elif name[-3:] == '.py':
btn = ModuleButton().definetext(self, name)
else:
btn = FileButton().definetext(self, name)
return self
#
class DirListWindow(WindowParent):
#
def create(self, dirname):
self = WindowParent.create(self, dirname, (0, 0))
child = DirList().create(self, dirname)
self.realize()
return self
#
class SubdirButton(PushButton):
#
def drawpict(self, d):
PushButton.drawpict(self, d)
d.box(rect.inset(self.bounds, (3, 1)))
#
def up_trigger(self):
window = DirListWindow().create(self.text)
#
class FileButton(PushButton):
#
def up_trigger(self):
stdwin.fleep()
#
class ModuleButton(FileButton):
#
def drawpict(self, d):
PushButton.drawpict(self, d)
d.box(rect.inset(self.bounds, (1, 3)))
#
# A FormSplit lets you place its children exactly where you want them
# (including silly places!).
# It does no explicit geometry management except moving its children
# when it is moved.
# The interface to place children is as follows.
# Before you add a child, you may specify its (left, top) position
# relative to the FormSplit. If you don't specify a position for
# a child, it goes right below the previous child; the first child
# goes to (0, 0) by default.
# NB: This places data attributes named form_* on its children.
# XXX Yes, I know, there should be options to do all sorts of relative
# placement, but for now this will do.
from Split import Split
class FormSplit(Split):
#
def create(self, parent):
self.next_left = self.next_top = 0
self.last_child = None
return Split.create(self, parent)
#
def getminsize(self, m, sugg_size):
max_width, max_height = 0, 0
for c in self.children:
c.form_width, c.form_height = c.getminsize(m, (0, 0))
max_width = max(max_width, c.form_width + c.form_left)
max_height = max(max_height, \
c.form_height + c.form_top)
return max_width, max_height
#
def getbounds(self):
return self.bounds
#
def setbounds(self, bounds):
self.bounds = bounds
fleft, ftop = bounds[0]
for c in self.children:
left, top = c.form_left + fleft, c.form_top + ftop
right, bottom = left + c.form_width, top + c.form_height
c.setbounds(((left, top), (right, bottom)))
#
def placenext(self, left, top):
self.next_left = left
self.next_top = top
self.last_child = None
#
def addchild(self, child):
if self.last_child:
width, height = \
self.last_child.getminsize(self.beginmeasuring(), \
(0, 0))
self.next_top = self.next_top + height
child.form_left = self.next_left
child.form_top = self.next_top
Split.addchild(self, child)
self.last_child = child
#
# HVSplit contains generic code for HSplit and VSplit.
# HSplit and VSplit are specializations to either dimension.
# XXX This does not yet stretch/shrink children if there is too much
# XXX or too little space in the split dimension.
# XXX (NB There is no interface to ask children for stretch preferences.)
from Split import Split
class HVSplit(Split):
#
def create(self, parent, hv):
# hv is 0 for HSplit, 1 for VSplit
self = Split.create(self, parent)
self.hv = hv
return self
#
def getminsize(self, m, sugg_size):
hv, vh = self.hv, 1 - self.hv
size = [0, 0]
sugg_size = [sugg_size[0], sugg_size[1]]
sugg_size[hv] = 0
sugg_size = sugg_size[0], sugg_size[1] # Make a tuple
for c in self.children:
csize = c.getminsize(m, sugg_size)
if csize[vh] > size[vh]: size[vh] = csize[vh]
size[hv] = size[hv] + csize[hv]
return size[0], size[1]
#
def getbounds(self):
return self.bounds
#
def setbounds(self, bounds):
self.bounds = bounds
hv, vh = self.hv, 1 - self.hv
mf = self.parent.beginmeasuring
begin, end = bounds
sugg_size = end[0] - begin[0], end[1] - begin[1]
size = self.getminsize(mf(), sugg_size)
origin = [begin[0], begin[1]]
sugg_size = [sugg_size[0], sugg_size[1]] # Make a list
sugg_size[hv] = 0
sugg_size = sugg_size[0], sugg_size[1] # Make a tuple
for c in self.children:
size = c.getminsize(mf(), sugg_size)
corner = [0, 0]
corner[vh] = end[vh]
corner[hv] = origin[hv] + size[hv]
c.setbounds(((origin[0], origin[1]), \
(corner[0], corner[1])))
origin[hv] = corner[hv]
# XXX stretch
# XXX too-small
#
class HSplit(HVSplit):
def create(self, parent):
return HVSplit.create(self, parent, 0)
class VSplit(HVSplit):
def create(self, parent):
return HVSplit.create(self, parent, 1)
# Module 'Histogram'
from Buttons import *
# A Histogram displays a histogram of numeric data.
#
class HistogramAppearance(LabelAppearance, Define):
#
def define(self, parent):
Define.define(self, (parent, ''))
self.ydata = []
self.scale = (0, 100)
return self
#
def setdata(self, ydata, scale):
self.ydata = ydata
self.scale = scale # (min, max)
self.parent.change(self.bounds)
#
def drawpict(self, d):
(left, top), (right, bottom) = self.bounds
min, max = self.scale
size = max-min
width, height = right-left, bottom-top
ydata = self.ydata
npoints = len(ydata)
v1 = top + height # constant
h1 = left # changed in loop
for i in range(npoints):
h0 = h1
v0 = top + height - (ydata[i]-min)*height/size
h1 = left + (i+1) * width/npoints
d.paint((h0, v0), (h1, v1))
#
class Histogram(NoReactivity, HistogramAppearance): pass
# Module 'Sliders'
import stdwin
from stdwinevents import *
import rect
from Buttons import *
from HVSplit import HSplit
# Field indices in event detail
#
_HV = 0
_CLICKS = 1
_BUTTON = 2
_MASK = 3
# DragSlider is the simplest possible slider.
# It looks like a button but dragging the mouse left or right
# changes the controlled value.
# It does not support any of the triggers or hooks defined by Buttons,
# but defines its own setval_trigger and setval_hook.
#
class DragSliderReactivity(BaseReactivity):
#
def mouse_down(self, detail):
h, v = hv = detail[_HV]
if self.enabled and self.mousetest(hv):
self.anchor = h
self.oldval = self.val
self.active = 1
#
def mouse_move(self, detail):
if self.active:
h, v = detail[_HV]
self.setval(self.oldval + (h - self.anchor))
#
def mouse_up(self, detail):
if self.active:
h, v = detail[_HV]
self.setval(self.oldval + (h - self.anchor))
self.active = 0
#
class DragSliderAppearance(ButtonAppearance):
#
# INVARIANTS maintained by the setval method:
#
# self.min <= self.val <= self.max
# self.text = self.pretext + `self.val` + self.postext
#
# (Notice that unlike Python ranges, the end point belongs
# to the range.)
#
def init_appearance(self):
ButtonAppearance.init_appearance(self)
self.min = 0
self.val = 0
self.max = 100
self.hook = 0
self.pretext = self.postext = ''
self.recalctext()
#
# The 'get*' and 'set*' methods belong to the generic slider interface
#
def getval(self): return self.val
#
def sethook(self, hook):
self.hook = hook
#
def setminvalmax(self, min, val, max):
self.min = min
self.max = max
self.setval(val)
#
def settexts(self, pretext, postext):
self.pretext = pretext
self.postext = postext
self.recalctext()
#
def setval(self, val):
val = min(self.max, max(self.min, val))
if val <> self.val:
self.val = val
self.recalctext()
self.trigger()
#
def trigger(self):
if self.hook:
self.hook(self)
#
def recalctext(self):
self.settext(self.pretext + `self.val` + self.postext)
#
class DragSlider(DragSliderReactivity, DragSliderAppearance, Define):
def definetext(self, parent, text):
raise RuntimeError, 'DragSlider.definetext() not supported'
# Auxiliary class for PushButton incorporated in ComplexSlider
#
class _StepButton(PushButton):
def define(self, parent):
self = PushButton.define(self, parent)
self.step = 0
return self
def setstep(self, step):
self.step = step
def definetextstep(self, parent, text, step):
self = self.definetext(parent, text)
self.setstep(step)
return self
def init_reactivity(self):
PushButton.init_reactivity(self)
self.parent.need_timer(self)
def step_trigger(self):
self.parent.setval(self.parent.getval() + self.step)
def down_trigger(self):
self.step_trigger()
self.parent.settimer(5)
def timer(self):
if self.hilited:
self.step_trigger()
if self.active:
self.parent.settimer(1)
# A complex slider is an HSplit initialized to three buttons:
# one to step down, a dragslider, and one to step up.
#
class ComplexSlider(HSplit):
#
# Override Slider define() method
#
def define(self, parent):
self = self.create(parent) # HSplit
#
self.downbutton = _StepButton().definetextstep(self, '-', -1)
self.dragbutton = DragSlider().define(self)
self.upbutton = _StepButton().definetextstep(self, '+', 1)
#
return self
#
# Override HSplit methods
#
def getminsize(self, m, (width, height)):
w1, h1 = self.downbutton.getminsize(m, (0, height))
w3, h3 = self.upbutton.getminsize(m, (0, height))
w1 = max(w1, h1)
w3 = max(w3, h3)
w2, h2 = self.dragbutton.getminsize(m, (width-w1-w3, height))
return w1+w2+w3, max(h1, h2, h3)
#
def setbounds(self, bounds):
(left, top), (right, bottom) = self.bounds = bounds
size = bottom - top
self.downbutton.setbounds(((left, top), (left+size, bottom)))
self.dragbutton.setbounds(((left+size, top), \
(right-size, bottom)))
self.upbutton.setbounds(((right-size, top), (right, bottom)))
#
# Pass other Slider methods on to dragbutton
#
def getval(self): return self.dragbutton.getval()
def sethook(self, hook): self.dragbutton.sethook(hook)
def setminvalmax(self, args): self.dragbutton.setminvalmax(args)
def settexts(self, args): self.dragbutton.settexts(args)
def setval(self, val): self.dragbutton.setval(val)
def enable(self, flag):
self.downbutton.enable(flag)
self.dragbutton.enable(flag)
self.upbutton.enable(flag)
# Module 'Soundogram'
import audio
from Histogram import Histogram
class Soundogram(Histogram):
#
def define(self, win, chunk):
width, height = corner = win.getwinsize()
bounds = (0, 0), corner
self.chunk = chunk
self.step = (len(chunk)-1)/(width/2+1) + 1
ydata = _make_ydata(chunk, self.step)
return Histogram.define(self, (win, bounds, ydata, (0, 128)))
#
def setchunk(self, chunk):
self.chunk = chunk
self.recompute()
#
def recompute(self):
(left, top), (right, bottom) = self.bounds
width = right - left
self.step = (len(chunk)-1)/width + 1
ydata = _make_ydata(chunk, self.step)
self.setdata(ydata, (0, 128))
#
def _make_ydata(chunk, step):
ydata = []
for i in range(0, len(chunk), step):
piece = audio.chr2num(chunk[i:i+step])
mi, ma = min(piece), max(piece)
y = max(abs(mi), abs(ma))
ydata.append(y)
return ydata
# Generic Split implementation.
# Use as a base class for other splits.
# Derived classes should at least implement the methods that call
# unimpl() below: getminsize(), getbounds() and setbounds().
Error = 'Split.Error' # Exception
import rect
from stdwinevents import *
class Split:
#
# Calls from creator
# NB derived classes may add parameters to create()
#
def create(self, parent):
parent.addchild(self)
self.parent = parent
self.children = []
self.mouse_interest = []
self.keybd_interest = []
self.timer_interest = []
self.altdraw_interest = []
self.mouse_focus = None
self.keybd_focus = None
return self
#
# Downcalls from parent to child
#
def destroy(self):
self.parent = None
for child in self.children:
child.destroy()
del self.children[:]
del self.mouse_interest[:]
del self.keybd_interest[:]
del self.timer_interest[:]
del self.altdraw_interest[:]
self.mouse_focus = None
self.keybd_focus = None
#
def getminsize(self, m, (width, height)):
return unimpl() # Should ask children
def getbounds(self):
return unimpl()
def setbounds(self, bounds):
unimpl() # Should tell children
#
def realize(self):
for child in self.children:
child.realize()
#
def draw(self, d, detail):
# (Could avoid calls to children outside the area)
for child in self.children:
child.draw(d, detail)
#
def altdraw(self, detail):
for child in self.altdraw_interest:
child.altdraw(detail)
#
# Keyboard focus handling (used internally)
# XXX This is not enough if two levels of splits
# XXX surround text fields!
#
def set_keybd_focus(self, child):
if self.keybd_focus <> child:
if self.keybd_focus:
self.keybd_focus.deactivate()
self.keybd_focus = None
if child:
child.activate()
self.keybd_focus = child
def next_keybd_focus(self):
if not self.keybd_interest:
self.set_keybd_focus(None)
return
if self.keybd_focus in self.keybd_interest:
i = self.keybd_interest.index(self.keybd_focus)
i = (i+1) % len(self.keybd_interest)
else:
i = 0
self.set_keybd_focus(self.keybd_interest[i])
#
# Downcalls only made after certain upcalls
#
def mouse_down(self, detail):
if self.mouse_focus:
self.mouse_focus.mouse_down(detail)
return
p = detail[0]
for child in self.mouse_interest:
if rect.pointinrect(p, child.getbounds()):
self.mouse_focus = child
if child in self.keybd_interest:
self.set_keybd_focus(child)
child.mouse_down(detail)
def mouse_move(self, detail):
if self.mouse_focus:
self.mouse_focus.mouse_move(detail)
def mouse_up(self, detail):
if self.mouse_focus:
self.mouse_focus.mouse_up(detail)
self.mouse_focus = None
#
def activate(self):
if self.keybd_focus:
self.keybd_focus.activate()
else:
self.next_keybd_focus()
def deactivate(self):
if self.keybd_focus:
self.keybd_focus.deactivate()
#
def keybd(self, type, detail):
if not self.keybd_focus:
self.set_keybd_focus(self.keybd_interest[0])
if type == WE_COMMAND and detail == WC_TAB and \
len(self.keybd_interest) > 1:
self.next_keybd_focus()
return
self.keybd_focus.keybd(type, detail)
#
def timer(self):
for child in self.timer_interest:
child.timer()
#
# Upcalls from child to parent
#
def addchild(self, child):
if child in self.children:
raise Error, 'addchild: child already inlist'
self.children.append(child)
def delchild(self, child):
if child not in self.children:
raise Error, 'delchild: child not in list'
self.children.remove(child)
if child in self.mouse_interest:
self.mouse_interest.remove(child)
if child in self.keybd_interest:
self.keybd_interest.remove(child)
if child in self.timer_interest:
self.timer_interest.remove(child)
if child in self.altdraw_interest:
self.altdraw_interest.remove(child)
if child == self.mouse_focus:
self.mouse_focus = None
if child == self.keybd_focus:
self.keybd_focus = None
#
def need_mouse(self, child):
if child not in self.mouse_interest:
self.mouse_interest.append(child)
self.parent.need_mouse(self)
def no_mouse(self, child):
if child == self.mouse_focus:
self.mouse_focus = None
if child in self.mouse_interest:
self.mouse_interest.remove(child)
if not self.mouse_interest:
self.parent.no_mouse(self)
#
def need_keybd(self, child):
if child not in self.keybd_interest:
self.keybd_interest.append(child)
self.parent.need_keybd(self)
if not self.keybd_focus:
self.set_keybd_focus(child)
def no_keybd(self, child):
if child == self.keybd_focus:
self.keybd_focus = None # Don't call child.deactivate()
if child in self.keybd_interest:
self.keybd_interest.remove(child)
if not self.keybd_interest:
self.parent.no_keybd(self)
#
def need_timer(self, child):
if child not in self.timer_interest:
self.timer_interest.append(child)
self.parent.need_timer(self)
def no_timer(self, child):
if child in self.timer_interest:
self.timer_interest.remove(child)
if not self.timer_interest:
self.parent.no_timer(self)
#
def need_altdraw(self, child):
if child not in self.altdraw_interest:
self.altdraw_interest.append(child)
self.parent.need_altdraw(self)
def no_altdraw(self, child):
if child in self.altdraw_interest:
self.altdraw_interest.remove(child)
if not self.altdraw_interest:
self.parent.no_altdraw(self)
#
# The rest are transparent:
#
def begindrawing(self):
return self.parent.begindrawing()
def beginmeasuring(self):
return self.parent.beginmeasuring()
def getwindow(self):
return self.parent.getwindow()
#
def change(self, area):
self.parent.change(area)
def scroll(self, area, vector):
self.parent.scroll(area, vector)
def settimer(self, itimer):
self.parent.settimer(itimer)
# Module 'StripChart'
import rect
from Buttons import LabelAppearance, NoReactivity
# A StripChart doesn't really look like a label but it needs a base class.
# LabelAppearance allows it to be disabled and hilited.
class StripChart(LabelAppearance, NoReactivity):
#
def define(self, parent, scale):
self.parent = parent
parent.addchild(self)
self.init_appearance()
self.init_reactivity()
self.ydata = []
self.scale = scale
self.resetbounds()
return self
#
def destroy(self):
self.parent = 0
#
def setbounds(self, bounds):
LabelAppearance.setbounds(self, bounds)
self.resetbounds()
#
def resetbounds(self):
(left, top), (right, bottom) = self.bounds
self.width = right-left
self.height = bottom-top
excess = len(self.ydata) - self.width
if excess > 0:
del self.ydata[:excess]
elif excess < 0:
while len(self.ydata) < self.width:
self.ydata.insert(0, 0)
#
def append(self, y):
self.ydata.append(y)
excess = len(self.ydata) - self.width
if excess > 0:
del self.ydata[:excess]
if self.bounds <> rect.empty:
self.parent.scroll(self.bounds, (-excess, 0))
if self.bounds <> rect.empty:
(left, top), (right, bottom) = self.bounds
i = len(self.ydata)
area = (left+i-1, top), (left+i, bottom)
self.draw(self.parent.begindrawing(), area)
#
def draw(self, d, area):
area = rect.intersect([area, self.bounds])
if area == rect.empty:
return
d.cliprect(area)
d.erase(self.bounds)
(a_left, a_top), (a_right, a_bottom) = area
(left, top), (right, bottom) = self.bounds
height = bottom - top
i1 = a_left - left
i2 = a_right - left
for i in range(max(0, i1), min(len(self.ydata), i2)):
split = bottom-self.ydata[i]*height/self.scale
d.paint((left+i, split), (left+i+1, bottom))
if not self.enabled:
self.flipenable(d)
if self.hilited:
self.fliphilite(d)
d.noclip()
# Text editing widget
# NB: this always assumes fixed bounds.
# For auto-growing TextEdit windows, different code would be needed.
from stdwinevents import *
class TextEdit:
#
def create(self, parent, (cols, rows)):
parent.addchild(self)
self.parent = parent
self.cols = cols
self.rows = rows
self.text = ''
# Creation of the editor is done in realize()
self.editor = None
self.dh = self.dv = 0
return self
#
def createboxed(self, parent, (cols, rows), (dh, dv)):
self = self.create(parent, (cols, rows))
self.dh = max(0, dh)
self.dv = max(0, dv)
return self
#
def settext(self, text):
self.editor.settext(text)
#
def gettext(self):
return self.editor.gettext(text)
#
# Downcalls from parent to child
#
def destroy(self):
del self.parent
del self.editor
del self.window
#
def getminsize(self, m, (width, height)):
width = max(0, width - 2*self.dh)
height = max(0, height - 2*self.dv)
if width > 0 and self.editor:
(left, top), (right, bottom) = self.editor.getrect()
act_width, act_height = right - left, bottom - top
if width >= act_width:
width = width + 2*self.dh
height = max(height, act_height) + 2*self.dv
return width, height
width = max(width, self.cols*m.textwidth('in')/2) + 2*self.dh
height = max(height, self.rows*m.lineheight()) + 2*self.dv
return width, height
#
def setbounds(self, bounds):
self.bounds = bounds
if self.editor:
(left, top), (right, bottom) = bounds
left = left + self.dh
top = top + self.dv
right = right - self.dh
bottom = bottom - self.dv
self.editor.move((left, top), (right, bottom))
if self.dh and self.dv:
(left, top), (right, bottom) = bounds
left = left + 1
top = top + 1
right = right - 1
bottom = bottom - 1
bounds = (left, top), (right, bottom)
self.editor.setview(bounds)
#
def getbounds(self):
return self.bounds
#
def realize(self):
self.window = self.parent.getwindow()
(left, top), (right, bottom) = self.bounds
left = left + self.dh
top = top + self.dv
right = right - self.dh
bottom = bottom - self.dv
self.editor = \
self.window.textcreate((left, top), (right, bottom))
self.editor.setactive(0)
bounds = self.bounds
if self.dh and self.dv:
(left, top), (right, bottom) = bounds
left = left + 1
top = top + 1
right = right - 1
bottom = bottom - 1
bounds = (left, top), (right, bottom)
self.editor.setview(bounds)
self.editor.settext(self.text)
self.parent.need_mouse(self)
self.parent.need_keybd(self)
self.parent.need_altdraw(self)
#
def draw(self, d, area):
if self.dh and self.dv:
d.box(self.bounds)
#
def altdraw(self, area):
self.editor.draw(area)
#
# Event downcalls
#
def mouse_down(self, detail):
x = self.editor.event(WE_MOUSE_DOWN, self.window, detail)
#
def mouse_move(self, detail):
x = self.editor.event(WE_MOUSE_MOVE, self.window, detail)
#
def mouse_up(self, detail):
x = self.editor.event(WE_MOUSE_UP, self.window, detail)
#
def keybd(self, type, detail):
x = self.editor.event(type, self.window, detail)
#
def activate(self):
self.editor.setfocus(0, 30000)
self.editor.setactive(1)
#
def deactivate(self):
self.editor.setactive(0)
#
# A class that sits transparently between a parent and one child.
# First create the parent, then this thing, then the child.
# Use this as a base class for objects that are almost transparent.
# Don't use as a base class for parents with multiple children.
Error = 'TransParent.Error' # Exception
class ManageOneChild:
#
# Upcalls shared with other single-child parents
#
def addchild(self, child):
if self.child:
raise Error, 'addchild: one child only'
if not child:
raise Error, 'addchild: bad child'
self.child = child
#
def delchild(self, child):
if not self.child:
raise Error, 'delchild: no child'
if child <> self.child:
raise Error, 'delchild: not my child'
self.child = 0
class TransParent(ManageOneChild):
#
# Calls from creator
# NB derived classes may add parameters to create()
#
def create(self, parent):
parent.addchild(self)
self.parent = parent
self.child = None # No child yet
return self
#
# Downcalls from parent to child
#
def destroy(self):
del self.parent
if self.child: self.child.destroy()
del self.child
#
def getminsize(self, args):
if not self.child:
m, size = args
return size
else:
return self.child.getminsize(args)
def getbounds(self, bounds):
if not self.child:
raise Error, 'getbounds w/o child'
else:
return self.child.getbounds()
def setbounds(self, bounds):
if not self.child:
raise Error, 'setbounds w/o child'
else:
self.child.setbounds(bounds)
def realize(self):
if self.child:
self.child.realize()
def draw(self, d, area):
if self.child:
self.child.draw(d, area)
def altdraw(self, area):
if self.child:
self.child.altdraw(area)
#
# Downcalls only made after certain upcalls
#
def mouse_down(self, detail):
if self.child: self.child.mouse_down(detail)
def mouse_move(self, detail):
if self.child: self.child.mouse_move(detail)
def mouse_up(self, detail):
if self.child: self.child.mouse_up(detail)
#
def keybd(self, type_detail):
self.child.keybd(type_detail)
def activate(self):
self.child.activate()
def deactivate(self):
self.child.deactivate()
#
def timer(self):
if self.child: self.child.timer()
#
# Upcalls from child to parent
#
def need_mouse(self, child):
self.parent.need_mouse(self)
def no_mouse(self, child):
self.parent.no_mouse(self)
#
def need_timer(self, child):
self.parent.need_timer(self)
def no_timer(self, child):
self.parent.no_timer(self)
#
def need_altdraw(self, child):
self.parent.need_altdraw(self)
def no_altdraw(self, child):
self.parent.no_altdraw(self)
#
def need_keybd(self, child):
self.parent.need_keybd(self)
def no_keybd(self, child):
self.parent.no_keybd(self)
#
def begindrawing(self):
return self.parent.begindrawing()
def beginmeasuring(self):
return self.parent.beginmeasuring()
def getwindow(self):
return self.parent.getwindow()
#
def change(self, area):
self.parent.change(area)
def scroll(self, area, vector):
self.parent.scroll(area, vector)
def settimer(self, itimer):
self.parent.settimer(itimer)
# Module 'VUMeter'
import audio
from StripChart import StripChart
K = 1024
Rates = [0, 32*K, 16*K, 8*K]
class VUMeter(StripChart):
#
# Override define() and timer() methods
#
def define(self, parent):
self = StripChart.define(self, (parent, 128))
self.parent.need_timer(self)
self.sampling = 0
self.rate = 3
self.enable(0)
return self
#
def timer(self):
if self.sampling:
chunk = audio.wait_recording()
self.sampling = 0
nums = audio.chr2num(chunk)
ampl = max(abs(min(nums)), abs(max(nums)))
self.append(ampl)
if self.enabled and not self.sampling:
audio.setrate(self.rate)
size = Rates[self.rate]/10
size = size/48*48
audio.start_recording(size)
self.sampling = 1
if self.sampling:
self.parent.settimer(1)
#
# New methods: start() and stop()
#
def stop(self):
if self.sampling:
chunk = audio.stop_recording()
self.sampling = 0
self.enable(0)
#
def start(self):
self.enable(1)
self.timer()
# A 'WindowParent' is the only module that uses real stdwin functionality.
# It is the root of the tree.
# It should have exactly one child when realized.
#
# There is also an alternative interface to "mainloop" here.
import stdwin
from stdwinevents import *
import mainloop
from TransParent import ManageOneChild
Error = 'WindowParent.Error' # Exception
class WindowParent(ManageOneChild):
#
def create(self, title, size):
self.title = title
self.size = size # (width, height)
self._reset()
self.close_hook = WindowParent.delayed_destroy
return self
#
def _reset(self):
self.child = None
self.win = None
self.itimer = 0
self.do_mouse = 0
self.do_keybd = 0
self.do_timer = 0
self.do_altdraw = 0
self.pending_destroy = 0
self.close_hook = None
self.menu_hook = None
#
def destroy(self):
mainloop.unregister(self.win)
if self.child: self.child.destroy()
self._reset()
#
def delayed_destroy(self):
# This interface to be used by 'Close' buttons etc.;
# destroying a window from within a button hook
# is not a good idea...
self.pending_destroy = 1
#
def close_trigger(self):
if self.close_hook: self.close_hook(self)
#
def menu_trigger(self, menu, item):
if self.menu_hook:
self.menu_hook(self, menu, item)
#
def need_mouse(self, child): self.do_mouse = 1
def no_mouse(self, child): self.do_mouse = 0
#
def need_keybd(self, child):
self.do_keybd = 1
self.child.activate()
def no_keybd(self, child):
self.do_keybd = 0
self.child.deactivate()
#
def need_timer(self, child): self.do_timer = 1
def no_timer(self, child): self.do_timer = 0
#
def need_altdraw(self, child): self.do_altdraw = 1
def no_altdraw(self, child): self.do_altdraw = 0
#
def realize(self):
if self.win:
raise Error, 'realize(): called twice'
if not self.child:
raise Error, 'realize(): no child'
# Compute suggested size
self.size = self.child.getminsize(self.beginmeasuring(), \
self.size)
save_defsize = stdwin.getdefwinsize()
scrwidth, scrheight = stdwin.getscrsize()
width, height = self.size
if width > scrwidth:
width = scrwidth * 2/3
if height > scrheight:
height = scrheight * 2/3
stdwin.setdefwinsize(width, height)
self.hbar, self.vbar = stdwin.getdefscrollbars()
self.win = stdwin.open(self.title)
stdwin.setdefwinsize(save_defsize)
self.win.setdocsize(self.size)
if self.itimer:
self.win.settimer(self.itimer)
width, height = self.win.getwinsize()
if self.hbar:
width = self.size[0]
if self.vbar:
height = self.size[1]
self.child.setbounds(((0, 0), (width, height)))
self.child.realize()
self.win.dispatch = self.dispatch
mainloop.register(self.win)
#
def fixup(self):
# XXX This could share code with realize() above
self.size = self.child.getminsize(self.beginmeasuring(), \
self.win.getwinsize())
self.win.setdocsize(self.size)
width, height = self.win.getwinsize()
if self.hbar:
width = self.size[0]
if self.vbar:
height = self.size[1]
self.child.setbounds(((0, 0), (width, height)))
# Force a redraw of the entire window:
self.win.change((0, 0), self.size)
#
def beginmeasuring(self):
# Return something with which a child can measure text
if self.win:
return self.win.begindrawing()
else:
return stdwin
#
def begindrawing(self):
if self.win:
return self.win.begindrawing()
else:
raise Error, 'begindrawing(): not realized yet'
#
def getwindow(self):
if self.win:
return self.win
else:
raise Error, 'getwindow(): not realized yet'
#
def change(self, area):
if self.win:
self.win.change(area)
#
def scroll(self, area, vector):
if self.win:
self.win.scroll(area, vector)
#
def settimer(self, itimer):
if self.win:
self.win.settimer(itimer)
else:
self.itimer = itimer
#
# Only call dispatch once we are realized
#
def dispatch(self, (type, win, detail)):
if type == WE_DRAW:
d = self.win.begindrawing()
self.child.draw(d, detail)
del d
if self.do_altdraw: self.child.altdraw(detail)
elif type == WE_MOUSE_DOWN:
if self.do_mouse: self.child.mouse_down(detail)
elif type == WE_MOUSE_MOVE:
if self.do_mouse: self.child.mouse_move(detail)
elif type == WE_MOUSE_UP:
if self.do_mouse: self.child.mouse_up(detail)
elif type in (WE_CHAR, WE_COMMAND):
if self.do_keybd: self.child.keybd(type, detail)
elif type == WE_TIMER:
if self.do_timer: self.child.timer()
elif type == WE_SIZE:
self.fixup()
elif type == WE_CLOSE:
self.close_trigger()
elif type == WE_MENU:
self.menu_trigger(detail)
if self.pending_destroy:
self.destroy()
#
def MainLoop():
mainloop.mainloop()
def Dispatch(event):
mainloop.dispatch(event)
# Interface used by WindowSched:
def CountWindows():
return mainloop.countwindows()
def AnyWindow():
return mainloop.anywindow()
# Combine a real-time scheduling queue and stdwin event handling.
# Keeps times in milliseconds.
import stdwin, stdwinq
from stdwinevents import WE_TIMER
import mainloop
import sched
import time
# Delay function called by the scheduler when it has nothing to do.
# Return immediately when something is done, or when the delay is up.
#
def delayfunc(msecs):
msecs = int(msecs)
#
# Check for immediate stdwin event
#
event = stdwinq.pollevent()
if event:
mainloop.dispatch(event)
return
#
# Use sleep for very short delays or if there are no windows
#
if msecs < 100 or mainloop.countwindows() == 0:
if msecs > 0:
time.sleep(msecs * 0.001)
return
#
# Post a timer event on an arbitrary window and wait for it
#
window = mainloop.anywindow()
window.settimer(msecs/100)
event = stdwinq.getevent()
window.settimer(0)
if event[0] <> WE_TIMER:
mainloop.dispatch(event)
def millitimer():
return time.time() * 1000
q = sched.scheduler(millitimer, delayfunc)
# Export functions enter, enterabs and cancel just like a scheduler
#
enter = q.enter
enterabs = q.enterabs
cancel = q.cancel
# Emptiness check must check both queues
#
def empty():
return q.empty() and mainloop.countwindows() == 0
# Run until there is nothing left to do
#
def run():
while not empty():
if q.empty():
mainloop.dispatch(stdwinq.getevent())
else:
q.run()
# Module 'anywin'
# Open a file or directory in a window
import dirwin
import filewin
import os
def open(name):
print 'opening', name, '...'
if os.path.isdir(name):
w = dirwin.open(name)
else:
w = filewin.open(name)
return w
# basewin.py
import stdwin
import mainloop
from stdwinevents import *
class BaseWindow:
def __init__(self, title):
self.win = stdwin.open(title)
self.win.dispatch = self.dispatch
mainloop.register(self.win)
# def reopen(self):
# title = self.win.gettitle()
# winpos = self.win.getwinpos()
# winsize = self.win.getwinsize()
# origin = self.win.getorigin()
# docsize = self.win.getdocsize()
# mainloop.unregister(self.win)
# del self.win.dispatch
# self.win.close()
# stdwin.setdefwinpos(winpos)
# stdwin.setdefwinsize(winsize)
# self.win = stdwin.open(title)
# stdwin.setdefwinpos(0, 0)
# stdwin.setdefwinsize(0, 0)
# self.win.setdocsize(docsize)
# self.win.setorigin(origin)
# self.win.dispatch = self.dispatch
# mainloop.register(self.win)
def popup(self):
if self.win is not stdwin.getactive():
self.win.setactive()
def close(self):
mainloop.unregister(self.win)
del self.win.dispatch
self.win.close()
def dispatch(self, event):
type, win, detail = event
if type == WE_CHAR:
self.char(detail)
elif type == WE_COMMAND:
self.command(detail)
elif type == WE_MOUSE_DOWN:
self.mouse_down(detail)
elif type == WE_MOUSE_MOVE:
self.mouse_move(detail)
elif type == WE_MOUSE_UP:
self.mouse_up(detail)
elif type == WE_DRAW:
self.draw(detail)
elif type == WE_CLOSE:
self.close()
def no_op(self, detail):
pass
char = command = mouse_down = mouse_move = mouse_up = draw = no_op
def refreshall(self):
self.win.change((-10, 0), (10000, 30000))
# Module 'dirwin'
# Directory windows, a subclass of listwin
import os
import gwin
import listwin
import anywin
import dircache
def action(w, string, i, detail):
(h, v), clicks, button, mask = detail
if clicks == 2:
name = os.path.join(w.name, string)
try:
w2 = anywin.open(name)
w2.parent = w
except os.error, why:
stdwin.message('Can\'t open ' + name + ': ' + why[1])
def open(name):
name = os.path.join(name, '')
list = dircache.opendir(name)[:]
list.sort()
dircache.annotate(name, list)
w = listwin.open(name, list)
w.name = name
w.action = action
return w
# Module 'filewin'
# File windows, a subclass of textwin (which is a subclass of gwin)
import textwin
import __builtin__
# FILE WINDOW
def open_readonly(fn): # Open a file window
fp = __builtin__.open(fn, 'r')
w = textwin.open_readonly(fn, fp.read())
w.fn = fn
return w
def open(fn): # Open a file window
fp = __builtin__.open(fn, 'r')
w = textwin.open(fn, fp.read())
w.fn = fn
return w
# A class to help applications that do fancy text formatting.
# You create an instance each time you must redraw the window.
# Set the initial left, top and right coordinates;
# then feed it words, font changes and vertical movements.
#
# This class should eventually be extended to support much fancier
# formatting, along the lines of TeX; for now, a very simple model
# is sufficient.
#
class formatter:
#
# Initialize a formatter instance.
# Pass the window's drawing object, and left, top, right
# coordinates of the drawing space as arguments.
#
def __init__(self, d, left, top, right):
self.d = d # Drawing object
self.left = left # Left margin
self.right = right # Right margin
self.v = top # Top of current line
self.center = 0
self.justify = 1
self.setfont('') # Default font
self._reset() # Prepare for new line
#
# Reset for start of fresh line.
#
def _reset(self):
self.boxes = [] # Boxes and glue still to be output
self.sum_width = 0 # Total width of boxes
self.sum_space = 0 # Total space between boxes
self.sum_stretch = 0 # Total stretch for space between boxes
self.max_ascent = 0 # Max ascent of current line
self.max_descent = 0 # Max descent of current line
self.avail_width = self.right - self.left
self.hang_indent = 0
#
# Set the current font, and compute some values from it.
#
def setfont(self, font):
self.font = font
self.d.setfont(font)
self.font_space = self.d.textwidth(' ')
self.font_ascent = self.d.baseline()
self.font_descent = self.d.lineheight() - self.font_ascent
#
# Add a word to the list of boxes; first flush if line is full.
# Space and stretch factors are expressed in fractions
# of the current font's space width.
# (Two variations: one without, one with explicit stretch factor.)
#
def addword(self, word, spacefactor):
self.addwordstretch(word, spacefactor, spacefactor)
#
def addwordstretch(self, word, spacefactor, stretchfactor):
width = self.d.textwidth(word)
if width > self.avail_width:
self._flush(1)
space = int(float(self.font_space) * float(spacefactor))
stretch = int(float(self.font_space) * float(stretchfactor))
box = (self.font, word, width, space, stretch)
self.boxes.append(box)
self.sum_width = self.sum_width + width
self.sum_space = self.sum_space + space
self.sum_stretch = self.sum_stretch + stretch
self.max_ascent = max(self.font_ascent, self.max_ascent)
self.max_descent = max(self.font_descent, self.max_descent)
self.avail_width = self.avail_width - width - space
#
# Flush current line and start a new one.
# Flushing twice is harmless (i.e. does not introduce a blank line).
# (Two versions: the internal one has a parameter for justification.)
#
def flush(self):
self._flush(0)
#
def _flush(self, justify):
if not self.boxes:
return
#
# Compute amount of stretch needed.
#
if justify and self.justify or self.center:
#
# Compute extra space to fill;
# this is avail_width plus glue from last box.
# Also compute available stretch.
#
last_box = self.boxes[len(self.boxes)-1]
font, word, width, space, stretch = last_box
tot_extra = self.avail_width + space
tot_stretch = self.sum_stretch - stretch
else:
tot_extra = tot_stretch = 0
#
# Output the boxes.
#
baseline = self.v + self.max_ascent
h = self.left + self.hang_indent
if self.center:
h = h + tot_extra / 2
tot_extra = tot_stretch = 0
for font, word, width, space, stretch in self.boxes:
self.d.setfont(font)
v = baseline - self.d.baseline()
self.d.text((h, v), word)
h = h + width + space
if tot_extra > 0 and tot_stretch > 0:
extra = stretch * tot_extra / tot_stretch
h = h + extra
tot_extra = tot_extra - extra
tot_stretch = tot_stretch - stretch
#
# Prepare for next line.
#
self.v = baseline + self.max_descent
self.d.setfont(self.font)
self._reset()
#
# Add vertical space; first flush.
# Vertical space is expressed in fractions of the current
# font's line height.
#
def vspace(self, lines):
self.vspacepixels(int(lines * self.d.lineheight()))
#
# Add vertical space given in pixels.
#
def vspacepixels(self, dv):
self.flush()
self.v = self.v + dv
#
# Set temporary (hanging) indent, for paragraph start.
# First flush.
#
def tempindent(self, space):
self.flush()
hang = int(float(self.font_space) * float(space))
self.hang_indent = hang
self.avail_width = self.avail_width - hang
#
# Add (permanent) left indentation. First flush.
#
def addleftindent(self, space):
self.flush()
self.left = self.left \
+ int(float(self.font_space) * float(space))
self._reset()
#
# Test procedure
#
def test():
import stdwin, stdwinq
from stdwinevents import *
try:
import mac
# Mac font assignments:
font1 = 'times', '', 12
font2 = 'times', 'b', 14
except ImportError:
# X11R4 font assignments
font1 = '*times-medium-r-*-120-*'
font2 = '*times-bold-r-*-140-*'
words = \
['The','quick','brown','fox','jumps','over','the','lazy','dog.']
words = words * 2
stage = 0
stages = [(0,0,'ragged'), (1,0,'justified'), (0,1,'centered')]
justify, center, title = stages[stage]
stdwin.setdefwinsize(300,200)
w = stdwin.open(title)
winsize = w.getwinsize()
while 1:
type, window, detail = stdwinq.getevent()
if type == WE_CLOSE:
break
elif type == WE_SIZE:
newsize = w.getwinsize()
if newsize <> winsize:
w.change((0,0), winsize)
winsize = newsize
w.change((0,0), winsize)
elif type == WE_MOUSE_DOWN:
stage = (stage + 1) % len(stages)
justify, center, title = stages[stage]
w.settitle(title)
w.change((0, 0), (1000, 1000))
elif type == WE_DRAW:
width, height = winsize
f = formatter(w.begindrawing(), 0, 0, width)
f.center = center
f.justify = justify
if not center:
f.tempindent(5)
for font in font1, font2, font1:
f.setfont(font)
for word in words:
space = 1 + (word[-1:] == '.')
f.addword(word, space)
if center and space > 1:
f.flush()
f.flush()
height = f.v
del f
w.setdocsize(0, height)
# Module 'gwin'
# Generic stdwin windows
# This is used as a base class from which to derive other window types.
# XXX DON'T USE THIS CODE ANY MORE! It is ages old!
import stdwin, stdwinq
from stdwinevents import *
from mainloop import mainloop, register, unregister, windows
# Open a window
def open(title): # Open a generic window
w = stdwin.open(title)
stdwin.setdefwinsize(0, 0)
# Set default event handlers
w.draw = nop
w.char = nop
w.mdown = nop
w.mmove = nop
w.mup = nop
w.m2down = m2down
w.m2up = m2up
w.size = nop
w.move = nop
w.activate = w.deactivate = nop
w.timer = nop
# default command handlers
w.close = close
w.tab = tab
w.enter = enter
w.backspace = backspace
w.arrow = arrow
w.kleft = w.kup = w.kright = w.kdown = nop
w.dispatch = treatevent
register(w)
return w
def treatevent(e): # Handle a stdwin event
type, w, detail = e
if type == WE_DRAW:
w.draw(w, detail)
elif type == WE_MENU:
m, item = detail
m.action[item](w, m, item)
elif type == WE_COMMAND:
treatcommand(w, detail)
elif type == WE_CHAR:
w.char(w, detail)
elif type == WE_MOUSE_DOWN:
if detail[1] > 1: w.m2down(w, detail)
else: w.mdown(w, detail)
elif type == WE_MOUSE_MOVE:
w.mmove(w, detail)
elif type == WE_MOUSE_UP:
if detail[1] > 1: w.m2up(w, detail)
else: w.mup(w, detail)
elif type == WE_SIZE:
w.size(w, w.getwinsize())
elif type == WE_ACTIVATE:
w.activate(w)
elif type == WE_DEACTIVATE:
w.deactivate(w)
elif type == WE_MOVE:
w.move(w)
elif type == WE_TIMER:
w.timer(w)
elif type == WE_CLOSE:
w.close(w)
def treatcommand(w, type): # Handle a we_command event
if type == WC_CLOSE:
w.close(w)
elif type == WC_RETURN:
w.enter(w)
elif type == WC_TAB:
w.tab(w)
elif type == WC_BACKSPACE:
w.backspace(w)
elif type in (WC_LEFT, WC_UP, WC_RIGHT, WC_DOWN):
w.arrow(w, type)
# Methods
def close(w): # Close method
unregister(w)
del w.close # Delete our close function
w.close() # Call the close method
def arrow(w, detail): # Arrow key method
if detail == WC_LEFT:
w.kleft(w)
elif detail == WC_UP:
w.kup(w)
elif detail == WC_RIGHT:
w.kright(w)
elif detail == WC_DOWN:
w.kdown(w)
# Trivial methods
def tab(w): w.char(w, '\t')
def enter(w): w.char(w, '\n') # 'return' is a Python reserved word
def backspace(w): w.char(w, '\b')
def m2down(w, detail): w.mdown(w, detail)
def m2up(w, detail): w.mup(w, detail)
def nop(*args): pass
# Module 'listwin'
# List windows, a subclass of gwin
import gwin
import stdwin
def maxlinewidth(a): # Compute maximum textwidth of lines in a sequence
max = 0
for line in a:
width = stdwin.textwidth(line)
if width > max: max = width
return max
def action(w, string, i, detail): # Default item selection method
pass
def mup(w, detail): # Mouse up method
(h, v), clicks, button, mask = detail
i = divmod(v, w.lineheight)[0]
if 0 <= i < len(w.data):
w.action(w, w.data[i], i, detail)
def draw(w, ((left, top), (right, bottom))): # Text window draw method
data = w.data
d = w.begindrawing()
lh = w.lineheight
itop = top/lh
ibot = (bottom-1)/lh + 1
if itop < 0: itop = 0
if ibot > len(data): ibot = len(data)
for i in range(itop, ibot): d.text((0, i*lh), data[i])
def open(title, data): # Display a list of texts in a window
lineheight = stdwin.lineheight()
h, v = maxlinewidth(data), len(data)*lineheight
h0, v0 = h + stdwin.textwidth(' '), v + lineheight
if h0 > stdwin.textwidth(' ')*80: h0 = 0
if v0 > stdwin.lineheight()*24: v0 = 0
stdwin.setdefwinsize(h0, v0)
w = gwin.open(title)
w.setdocsize(h, v)
w.lineheight = lineheight
w.data = data
w.draw = draw
w.action = action
w.mup = mup
return w
# Standard main loop for *all* STDWIN applications.
# This requires that applications:
# - register their windows on creation and unregister them when closed
# - have a 'dispatch' function as a window member
import stdwin, stdwinq
from stdwinevents import *
# List of windows known to the main loop.
#
windows = []
# Last window that ever received an event
#
last_window = None
# Function to register a window.
#
def register(win):
# First test the dispatch function by passing it a null event --
# this catches registration of unconforming windows.
win.dispatch((WE_NULL, win, None))
if win not in windows:
windows.append(win)
# Function to unregister a window.
# It is not an error to unregister an already unregistered window
# (this is useful for cleanup actions).
#
def unregister(win):
global last_window
if win == last_window:
last_window = None
if win in windows:
windows.remove(win) # Not in 0.9.1
# 0.9.1 solution:
#for i in range(len(windows)):
# if windows[i] = win:
# del windows[i]
# break
# Interfaces used by WindowSched.
#
def countwindows():
return len(windows)
#
def anywindow():
if windows:
return windows[0]
else:
return None
# NEW: register any number of file descriptors
#
fdlist = []
select_args = None
select_handlers = None
#
def registerfd(fd, mode, handler):
if mode not in ('r', 'w', 'x'):
raise ValueError, 'mode must be r, w or x'
if type(fd) <> type(0):
fd = fd.fileno() # If this fails it's not a proper select arg
for i in range(len(fdlist)):
if fdlist[i][:2] == (fd, mode):
raise ValueError, \
'(fd, mode) combination already registered'
fdlist.append((fd, mode, handler))
make_select_args()
#
def unregisterfd(fd, *args):
if type(fd) <> type(0):
fd = fd.fileno() # If this fails it's not a proper select arg
args = (fd,) + args
n = len(args)
for i in range(len(fdlist)):
if fdlist[i][:n] == args:
del fdlist[i]
make_select_args()
#
def make_select_args():
global select_args, select_handlers
rlist, wlist, xlist = [], [], []
rhandlers, whandlers, xhandlers = {}, {}, {}
for fd, mode, handler in fdlist:
if mode == 'r':
rlist.append(fd)
rhandlers[`fd`] = handler
if mode == 'w':
wlist.append(fd)
whandlers[`fd`] = handler
if mode == 'x':
xlist.append(fd)
xhandlers[`fd`] = handler
if rlist or wlist or xlist:
select_args = rlist, wlist, xlist
select_handlers = rhandlers, whandlers, xhandlers
else:
select_args = None
select_handlers = None
#
def do_select():
import select
reply = apply(select.select, select_args)
for mode in 0, 1, 2:
list = reply[mode]
for fd in list:
handler = select_handlers[mode][`fd`]
handler(fd, 'rwx'[mode])
# Event processing main loop.
# Return when there are no windows left, or when an unhandled
# exception occurs. (It is safe to restart the main loop after
# an unsuccessful exit.)
# Python's stdwin.getevent() turns WE_COMMAND/WC_CANCEL events
# into KeyboardInterrupt exceptions; these are turned back in events.
#
recursion_level = 0 # Hack to make it reentrant
def mainloop():
global recursion_level
recursion_level = recursion_level + 1
try:
stdwin_select_handler() # Process events already in queue
while 1:
if windows and not fdlist:
while windows and not fdlist:
try:
event = stdwinq.getevent()
except KeyboardInterrupt:
event = (WE_COMMAND, \
None, WC_CANCEL)
dispatch(event)
elif windows and fdlist:
fd = stdwin.fileno()
if recursion_level == 1:
registerfd(fd, 'r', stdwin_select_handler)
try:
while windows:
do_select()
stdwin_select_handler()
finally:
if recursion_level == 1:
unregisterfd(fd)
elif fdlist:
while fdlist and not windows:
do_select()
else:
break
finally:
recursion_level = recursion_level - 1
# Check for events without ever blocking
#
def check():
stdwin_select_handler()
# XXX Should check for socket stuff as well
# Handle stdwin events until none are left
#
def stdwin_select_handler(*args):
while 1:
try:
event = stdwinq.pollevent()
except KeyboardInterrupt:
event = (WE_COMMAND, None, WC_CANCEL)
if event is None:
break
dispatch(event)
# Run a modal dialog loop for a window. The dialog window must have
# been registered first. This prohibits most events (except size/draw
# events) to other windows. The modal dialog loop ends when the
# dialog window unregisters itself.
#
passthrough = WE_SIZE, WE_DRAW
beeping = WE_MOUSE_DOWN, WE_COMMAND, WE_CHAR, WE_KEY, WE_CLOSE, WE_MENU
#
def modaldialog(window):
if window not in windows:
raise ValueError, 'modaldialog window not registered'
while window in windows:
try:
event = stdwinq.getevent()
except KeyboardInterrupt:
event = WE_COMMAND, None, WC_CANCEL
etype, ewindow, edetail = event
if etype not in passthrough and ewindow <> window:
if etype in beeping:
stdwin.fleep()
continue
dispatch(event)
# Dispatch a single event.
# Events for the no window in particular are sent to the active window
# or to the last window that received an event (these hacks are for the
# WE_LOST_SEL event, which is directed to no particular window).
# Windows not in the windows list don't get their events:
# events for such windows are silently ignored.
#
def dispatch(event):
global last_window
if event[1] == None:
active = stdwin.getactive()
if active: last_window = active
else:
last_window = event[1]
if last_window in windows:
last_window.dispatch(event)
# Dialog base class
#
class Dialog:
#
def __init__(self, title):
self.window = stdwin.open(title)
self.window.dispatch = self.dispatch
register(self.window)
#
def close(self):
unregister(self.window)
del self.window.dispatch
self.window.close()
#
def dispatch(self, event):
etype, ewindow, edetail = event
if etype == WE_CLOSE:
self.close()
# Standard modal dialogs
# XXX implemented using stdwin dialogs for now
#
def askstr(prompt, default):
return stdwin.askstr(prompt, default)
#
def askync(prompt, yesorno):
return stdwin.askync(prompt, yesorno)
#
def askfile(prompt, default, new):
return stdwin.askfile(prompt, default, new)
#
def message(msg):
stdwin.message(msg)
# Module 'rect'.
#
# Operations on rectangles.
# There is some normalization: all results return the object 'empty'
# if their result would contain no points.
# Exception.
#
error = 'rect.error'
# The empty rectangle.
#
empty = (0, 0), (0, 0)
# Check if a rectangle is empty.
#
def is_empty(r):
(left, top), (right, bottom) = r
return left >= right or top >= bottom
# Compute the intersection or two or more rectangles.
# This works with a list or tuple argument.
#
def intersect(list):
if not list: raise error, 'intersect called with empty list'
if is_empty(list[0]): return empty
(left, top), (right, bottom) = list[0]
for rect in list[1:]:
if is_empty(rect):
return empty
(l, t), (r, b) = rect
if left < l: left = l
if top < t: top = t
if right > r: right = r
if bottom > b: bottom = b
if is_empty(((left, top), (right, bottom))):
return empty
return (left, top), (right, bottom)
# Compute the smallest rectangle containing all given rectangles.
# This works with a list or tuple argument.
#
def union(list):
(left, top), (right, bottom) = list[0]
for (l, t), (r, b) in list[1:]:
if not is_empty(((l, t), (r, b))):
if l < left: left = l
if t < top: top = t
if r > right: right = r
if b > bottom: bottom = b
res = (left, top), (right, bottom)
if is_empty(res):
return empty
return res
# Check if a point is in a rectangle.
#
def pointinrect((h, v), ((left, top), (right, bottom))):
return left <= h < right and top <= v < bottom
# Return a rectangle that is dh, dv inside another
#
def inset(((left, top), (right, bottom)), (dh, dv)):
left = left + dh
top = top + dv
right = right - dh
bottom = bottom - dv
r = (left, top), (right, bottom)
if is_empty(r):
return empty
else:
return r
# Conversions between rectangles and 'geometry tuples',
# given as origin (h, v) and dimensions (width, height).
#
def rect2geom((left, top), (right, bottom)):
return (left, top), (right-left, bottom-top)
def geom2rect((h, v), (width, height)):
return (h, v), (h+width, v+height)
# srcwin.py -- a source listing window
import stdwin
from stdwinevents import *
import basewin
WIDTH = 40
MAXHEIGHT = 24
class TextWindow(basewin.BaseWindow):
def __init__(self, title, contents):
self.contents = contents
self.linecount = countlines(self.contents)
#
self.lineheight = lh = stdwin.lineheight()
self.leftmargin = self.getmargin()
self.top = 0
self.rightmargin = 30000 # Infinity
self.bottom = lh * self.linecount
#
width = WIDTH*stdwin.textwidth('0')
height = lh*min(MAXHEIGHT, self.linecount)
stdwin.setdefwinsize(width, height)
basewin.BaseWindow.__init__(self, title)
#
self.win.setdocsize(0, self.bottom)
self.initeditor()
def initeditor(self):
r = (self.leftmargin, self.top), (self.rightmargin, self.bottom)
self.editor = self.win.textcreate(r)
self.editor.settext(self.contents)
def closeeditor(self):
self.editor.close()
# def reopen(self):
# self.closeeditor()
# basewin.BaseWindow.reopen(self)
# self.initeditor()
# Override the following two methods to format line numbers differently
def getmark(self, lineno):
return `lineno`
def getmargin(self):
return stdwin.textwidth(`self.linecount + 1` + ' ')
# Event dispatcher, called from mainloop.mainloop()
def dispatch(self, event):
if event[0] == WE_NULL: return # Dummy tested by mainloop
if event[0] == WE_DRAW or not self.editor.event(event):
basewin.BaseWindow.dispatch(self, event)
# Event handlers
def close(self):
self.closeeditor()
basewin.BaseWindow.close(self)
def draw(self, detail):
dummy = self.editor.draw(detail)
# Draw line numbers
(left, top), (right, bottom) = detail
topline = top/self.lineheight
botline = bottom/self.lineheight + 1
botline = min(self.linecount, botline)
d = self.win.begindrawing()
try:
h, v = 0, self.lineheight * topline
for lineno in range(topline+1, botline+1):
d.text((h, v), self.getmark(lineno))
v = v + self.lineheight
finally:
d.close()
# Calls from outside
def changemark(self, lineno): # redraw the mark for a line
left = 0
top = (lineno-1) * self.lineheight
right = self.leftmargin
bottom = lineno * self.lineheight
d = self.win.begindrawing()
try:
d.erase((left, top), (right, bottom))
d.text((left, top), self.getmark(lineno))
finally:
d.close()
def showline(self, lineno): # scroll to make a line visible
left = 0
top = (lineno-1) * self.lineheight
right = self.leftmargin
bottom = lineno * self.lineheight
self.win.show((left, top), (right, bottom))
# Subroutine to count the number of lines in a string
def countlines(text):
n = 0
for c in text:
if c == '\n': n = n+1
if text and text[-1] != '\n': n = n+1 # Partial last line
return n
class SourceWindow(TextWindow):
def __init__(self, filename):
self.filename = filename
f = open(self.filename, 'r')
contents = f.read()
f.close()
TextWindow.__init__(self, self.filename, contents)
# ------------------------------ testing ------------------------------
TESTFILE = 'srcwin.py'
def test():
import mainloop
sw = SourceWindow(TESTFILE)
mainloop.mainloop()
# Module 'stdwinevents' -- Constants for stdwin event types
#
# Suggested usage:
# from stdwinevents import *
# The function stdwin.getevent() returns a tuple containing:
# (type, window, detail)
# where detail may be <no value> or a value depending on type, see below:
# Values for type:
WE_NULL = 0 # not reported -- means 'no event' internally
WE_ACTIVATE = 1 # detail is None
WE_CHAR = 2 # detail is the character
WE_COMMAND = 3 # detail is one of the WC_* constants below
WE_MOUSE_DOWN = 4 # detail is ((h, v), clicks, button, mask)
WE_MOUSE_MOVE = 5 # ditto
WE_MOUSE_UP = 6 # ditto
WE_MENU = 7 # detail is (menu, item)
WE_SIZE = 8 # detail is (width, height)
WE_MOVE = 9 # not reported -- reserved for future use
WE_DRAW = 10 # detail is ((left, top), (right, bottom))
WE_TIMER = 11 # detail is None
WE_DEACTIVATE = 12 # detail is None
WE_EXTERN = 13 # detail is None
WE_KEY = 14 # detail is ???
WE_LOST_SEL = 15 # detail is selection number
WE_CLOSE = 16 # detail is None
# Values for detail when type is WE_COMMAND:
WC_CLOSE = 1 # obsolete; now reported as WE_CLOSE
WC_LEFT = 2 # left arrow key
WC_RIGHT = 3 # right arrow key
WC_UP = 4 # up arrow key
WC_DOWN = 5 # down arrow key
WC_CANCEL = 6 # not reported -- turned into KeyboardInterrupt
WC_BACKSPACE = 7 # backspace key
WC_TAB = 8 # tab key
WC_RETURN = 9 # return or enter key
# Selection numbers
WS_CLIPBOARD = 0
WS_PRIMARY = 1
WS_SECONDARY = 2
# Modifier masks in key and mouse events
WM_SHIFT = (1 << 0)
WM_LOCK = (1 << 1)
WM_CONTROL = (1 << 2)
WM_META = (1 << 3)
WM_OPTION = (1 << 4)
WM_NUM = (1 << 5)
WM_BUTTON1 = (1 << 8)
WM_BUTTON2 = (1 << 9)
WM_BUTTON3 = (1 << 10)
WM_BUTTON4 = (1 << 11)
WM_BUTTON5 = (1 << 12)
# Replacements for getevent() and pollevent(),
# and new functions ungetevent() and sync().
# Every library module should ideally use this instead of
# stdwin.{get,poll}event(), so applications can use the services
# of ungetevent() and sync().
import stdwin
# Events read ahead are stored in this queue.
#
queue = []
# Replacement for getevent().
#
def getevent():
if queue:
event = queue[0]
del queue[0]
return event
else:
return stdwin.getevent()
# Replacement for pollevent().
#
def pollevent():
if queue:
return getevent()
else:
return stdwin.pollevent()
# Push an event back in the queue.
#
def ungetevent(event):
queue.insert(0, event)
# Synchronize the display. It turns out that this is the way to
# force STDWIN to call XSync(), which some (esoteric) applications need.
# (This is stronger than just flushing -- it actually waits for a
# positive response from the X server on the last command issued.)
#
def sync():
while 1:
event = stdwin.pollevent()
if not event: break
queue.append(event)
# Module 'tablewin'
# Display a table, with per-item actions:
# A1 | A2 | A3 | .... | AN
# B1 | B2 | B3 | .... | BN
# C1 | C2 | C3 | .... | CN
# .. | .. | .. | .... | ..
# Z1 | Z2 | Z3 | .... | ZN
# Not all columns need to have the same length.
# The data structure is a list of columns;
# each column is a list of items.
# Each item is a pair of a string and an action procedure.
# The first item may be a column title.
import stdwin
import gwin
from stdwinevents import *
def open(title, data): # Public function to open a table window
#
# Set geometry parameters (one day, these may be changeable)
#
margin = stdwin.textwidth(' ')
lineheight = stdwin.lineheight()
#
# Geometry calculations
#
colstarts = [0]
totwidth = 0
maxrows = 0
for coldata in data:
# Height calculations
rows = len(coldata)
if rows > maxrows: maxrows = rows
# Width calculations
width = colwidth(coldata) + margin
totwidth = totwidth + width
colstarts.append(totwidth)
#
# Calculate document and window height
#
docwidth, docheight = totwidth, maxrows*lineheight
winwidth, winheight = docwidth, docheight
if winwidth > stdwin.textwidth('n')*100: winwidth = 0
if winheight > stdwin.lineheight()*30: winheight = 0
#
# Create the window
#
stdwin.setdefwinsize(winwidth, winheight)
w = gwin.open(title)
#
# Set properties and override methods
#
w.data = data
w.margin = margin
w.lineheight = lineheight
w.colstarts = colstarts
w.totwidth = totwidth
w.maxrows = maxrows
w.selection = (-1, -1)
w.lastselection = (-1, -1)
w.selshown = 0
w.setdocsize(docwidth, docheight)
w.draw = draw
w.mup = mup
w.arrow = arrow
#
# Return
#
return w
def update(w, data): # Change the data
#
# Hide selection
#
hidesel(w, w.begindrawing())
#
# Get old geometry parameters
#
margin = w.margin
lineheight = w.lineheight
#
# Geometry calculations
#
colstarts = [0]
totwidth = 0
maxrows = 0
for coldata in data:
# Height calculations
rows = len(coldata)
if rows > maxrows: maxrows = rows
# Width calculations
width = colwidth(coldata) + margin
totwidth = totwidth + width
colstarts.append(totwidth)
#
# Calculate document and window height
#
docwidth, docheight = totwidth, maxrows*lineheight
#
# Set changed properties and change window size
#
w.data = data
w.colstarts = colstarts
w.totwidth = totwidth
w.maxrows = maxrows
w.change((0, 0), (10000, 10000))
w.setdocsize(docwidth, docheight)
w.change((0, 0), (docwidth, docheight))
#
# Show selection, or forget it if out of range
#
showsel(w, w.begindrawing())
if not w.selshown: w.selection = (-1, -1)
def colwidth(coldata): # Subroutine to calculate column width
maxwidth = 0
for string, action in coldata:
width = stdwin.textwidth(string)
if width > maxwidth: maxwidth = width
return maxwidth
def draw(w, ((left, top), (right, bottom))): # Draw method
ileft = whichcol(w, left)
iright = whichcol(w, right-1) + 1
if iright > len(w.data): iright = len(w.data)
itop = divmod(top, w.lineheight)[0]
if itop < 0: itop = 0
ibottom, remainder = divmod(bottom, w.lineheight)
if remainder: ibottom = ibottom + 1
d = w.begindrawing()
if ileft <= w.selection[0] < iright:
if itop <= w.selection[1] < ibottom:
hidesel(w, d)
d.erase((left, top), (right, bottom))
for i in range(ileft, iright):
col = w.data[i]
jbottom = len(col)
if ibottom < jbottom: jbottom = ibottom
h = w.colstarts[i]
v = itop * w.lineheight
for j in range(itop, jbottom):
string, action = col[j]
d.text((h, v), string)
v = v + w.lineheight
showsel(w, d)
def mup(w, detail): # Mouse up method
(h, v), nclicks, button, mask = detail
icol = whichcol(w, h)
if 0 <= icol < len(w.data):
irow = divmod(v, w.lineheight)[0]
col = w.data[icol]
if 0 <= irow < len(col):
string, action = col[irow]
action(w, string, (icol, irow), detail)
def whichcol(w, h): # Return column number (may be >= len(w.data))
for icol in range(0, len(w.data)):
if h < w.colstarts[icol+1]:
return icol
return len(w.data)
def arrow(w, type):
if type == WC_LEFT:
incr = -1, 0
elif type == WC_UP:
incr = 0, -1
elif type == WC_RIGHT:
incr = 1, 0
elif type == WC_DOWN:
incr = 0, 1
else:
return
icol, irow = w.lastselection
icol = icol + incr[0]
if icol < 0: icol = len(w.data)-1
if icol >= len(w.data): icol = 0
if 0 <= icol < len(w.data):
irow = irow + incr[1]
if irow < 0: irow = len(w.data[icol]) - 1
if irow >= len(w.data[icol]): irow = 0
else:
irow = 0
if 0 <= icol < len(w.data) and 0 <= irow < len(w.data[icol]):
w.lastselection = icol, irow
string, action = w.data[icol][irow]
detail = (0, 0), 1, 1, 1
action(w, string, (icol, irow), detail)
# Selection management
# TO DO: allow multiple selected entries
def select(w, selection): # Public function to set the item selection
d = w.begindrawing()
hidesel(w, d)
w.selection = selection
showsel(w, d)
if w.selshown: lastselection = selection
def hidesel(w, d): # Hide the selection, if shown
if w.selshown: invertsel(w, d)
def showsel(w, d): # Show the selection, if hidden
if not w.selshown: invertsel(w, d)
def invertsel(w, d): # Invert the selection, if valid
icol, irow = w.selection
if 0 <= icol < len(w.data) and 0 <= irow < len(w.data[icol]):
left = w.colstarts[icol]
right = w.colstarts[icol+1]
top = irow * w.lineheight
bottom = (irow+1) * w.lineheight
d.invert((left, top), (right, bottom))
w.selshown = (not w.selshown)
# Demonstration
def demo_action(w, string, (icol, irow), detail): # Action function for demo
select(w, (irow, icol))
def demo(): # Demonstration
da = demo_action # shorthand
col0 = [('a1', da), ('bbb1', da), ('c1', da)]
col1 = [('a2', da), ('bbb2', da)]
col2 = [('a3', da), ('b3', da), ('c3', da), ('d4', da), ('d5', da)]
col3 = []
for i in range(1, 31): col3.append(('xxx' + `i`, da))
data = [col0, col1, col2, col3]
w = open('tablewin.demo', data)
gwin.mainloop()
return w
# Module 'textwin'
# Text windows, a subclass of gwin
import stdwin
import gwin
from stdwinevents import *
def fixsize(w):
docwidth, docheight = w.text.getrect()[1]
winheight = w.getwinsize()[1]
if winheight > docheight: docheight = winheight
w.setdocsize(0, docheight)
fixeditmenu(w)
def cut(w, m, id):
s = w.text.getfocustext()
if s:
stdwin.setcutbuffer(0, s)
w.text.replace('')
fixsize(w)
def copy(w, m, id):
s = w.text.getfocustext()
if s:
stdwin.setcutbuffer(0, s)
fixeditmenu(w)
def paste(w, m, id):
w.text.replace(stdwin.getcutbuffer(0))
fixsize(w)
def addeditmenu(w):
m = w.editmenu = w.menucreate('Edit')
m.action = []
m.additem('Cut', 'X')
m.action.append(cut)
m.additem('Copy', 'C')
m.action.append(copy)
m.additem('Paste', 'V')
m.action.append(paste)
def fixeditmenu(w):
m = w.editmenu
f = w.text.getfocus()
can_copy = (f[0] < f[1])
m.enable(1, can_copy)
if not w.readonly:
m.enable(0, can_copy)
m.enable(2, (stdwin.getcutbuffer(0) <> ''))
def draw(w, area): # Draw method
w.text.draw(area)
def size(w, newsize): # Size method
w.text.move((0, 0), newsize)
fixsize(w)
def close(w): # Close method
del w.text # Break circular ref
gwin.close(w)
def char(w, c): # Char method
w.text.replace(c)
fixsize(w)
def backspace(w): # Backspace method
void = w.text.event(WE_COMMAND, w, WC_BACKSPACE)
fixsize(w)
def arrow(w, detail): # Arrow method
w.text.arrow(detail)
fixeditmenu(w)
def mdown(w, detail): # Mouse down method
void = w.text.event(WE_MOUSE_DOWN, w, detail)
fixeditmenu(w)
def mmove(w, detail): # Mouse move method
void = w.text.event(WE_MOUSE_MOVE, w, detail)
def mup(w, detail): # Mouse up method
void = w.text.event(WE_MOUSE_UP, w, detail)
fixeditmenu(w)
def activate(w): # Activate method
fixeditmenu(w)
def open(title, str): # Display a string in a window
w = gwin.open(title)
w.readonly = 0
w.text = w.textcreate((0, 0), w.getwinsize())
w.text.replace(str)
w.text.setfocus(0, 0)
addeditmenu(w)
fixsize(w)
w.draw = draw
w.size = size
w.close = close
w.mdown = mdown
w.mmove = mmove
w.mup = mup
w.char = char
w.backspace = backspace
w.arrow = arrow
w.activate = activate
return w
def open_readonly(title, str): # Same with char input disabled
w = open(title, str)
w.readonly = 1
w.char = w.backspace = gwin.nop
# Disable Cut and Paste menu item; leave Copy alone
w.editmenu.enable(0, 0)
w.editmenu.enable(2, 0)
return w
# wdb.py -- a window-based Python debugger
# XXX To do:
# - don't fall out of bottom frame
import stdwin
from stdwinevents import *
import sys
import basewin
import bdb
import repr
WIDTH = 40
HEIGHT = 8
WdbDone = 'wdb.WdbDone' # Exception to continue execution
class Wdb(bdb.Bdb, basewin.BaseWindow): # Window debugger
def __init__(self):
self.sourcewindows = {}
self.framewindows = {}
bdb.Bdb.__init__(self)
width = WIDTH*stdwin.textwidth('0')
height = HEIGHT*stdwin.lineheight()
stdwin.setdefwinsize(width, height)
basewin.BaseWindow.__init__(self, '--Stack--')
self.closed = 0
def reset(self):
if self.closed: raise RuntimeError, 'already closed'
bdb.Bdb.reset(self)
self.forget()
def forget(self):
self.lineno = None
self.stack = []
self.curindex = 0
self.curframe = None
for fn in self.sourcewindows.keys():
self.sourcewindows[fn].resetlineno()
def setup(self, f, t):
self.forget()
self.stack, self.curindex = self.get_stack(f, t)
self.curframe = self.stack[self.curindex][0]
# Build a list of current frames
cfl = []
for f, i in self.stack: cfl.append(f)
# Remove deactivated frame windows
for name in self.framewindows.keys():
fw = self.framewindows[name]
if fw.frame not in cfl: fw.close()
else: fw.refreshframe()
# Refresh the stack window
self.refreshstack()
# Override Bdb methods (except user_call, for now)
def user_line(self, frame):
# This function is called when we stop or break at this line
self.interaction(frame, None)
def user_return(self, frame, return_value):
# This function is called when a return trap is set here
frame.f_locals['__return__'] = return_value
self.settitle('--Return--')
self.interaction(frame, None)
if not self.closed:
self.settitle('--Stack--')
def user_exception(self, frame, (exc_type, exc_value, exc_traceback)):
# This function is called if an exception occurs,
# but only if we are to stop at or just below this level
frame.f_locals['__exception__'] = exc_type, exc_value
if type(exc_type) == type(''):
exc_type_name = exc_type
else: exc_type_name = exc_type.__name__
self.settitle(exc_type_name + ': ' + repr.repr(exc_value))
stdwin.fleep()
self.interaction(frame, exc_traceback)
if not self.closed:
self.settitle('--Stack--')
# Change the title
def settitle(self, title):
self.savetitle = self.win.gettitle()
self.win.settitle(title)
# General interaction function
def interaction(self, frame, traceback):
import mainloop
self.popup()
self.setup(frame, traceback)
try:
mainloop.mainloop()
except WdbDone:
pass
self.forget()
# Functions whose name is do_X for some character X
# are callable directly from the keyboard.
def do_up(self):
if self.curindex == 0:
stdwin.fleep()
else:
self.curindex = self.curindex - 1
self.curframe = self.stack[self.curindex][0]
self.refreshstack()
do_u = do_up
def do_down(self):
if self.curindex + 1 == len(self.stack):
stdwin.fleep()
else:
self.curindex = self.curindex + 1
self.curframe = self.stack[self.curindex][0]
self.refreshstack()
do_d = do_down
def do_step(self):
self.set_step()
raise WdbDone
do_s = do_step
def do_next(self):
self.set_next(self.curframe)
raise WdbDone
do_n = do_next
def do_return(self):
self.set_return(self.curframe)
raise WdbDone
do_r = do_return
def do_continue(self):
self.set_continue()
raise WdbDone
do_c = do_cont = do_continue
def do_quit(self):
self.close()
raise WdbDone
do_q = do_quit
def do_list(self):
fn = self.curframe.f_code.co_filename
if not self.sourcewindows.has_key(fn):
import wdbsrcwin
try:
self.sourcewindows[fn] = wdbsrcwin. \
DebuggerSourceWindow(self, fn)
except IOError:
stdwin.fleep()
return
w = self.sourcewindows[fn]
lineno = self.stack[self.curindex][1]
w.setlineno(lineno)
w.popup()
do_l = do_list
def do_frame(self):
name = 'locals' + `self.curframe`[16:-1]
if self.framewindows.has_key(name):
self.framewindows[name].popup()
else:
import wdbframewin
self.framewindows[name] = \
wdbframewin.FrameWindow(self, \
self.curframe, \
self.curframe.f_locals, name)
do_f = do_frame
def do_globalframe(self):
name = 'globals' + `self.curframe`[16:-1]
if self.framewindows.has_key(name):
self.framewindows[name].popup()
else:
import wdbframewin
self.framewindows[name] = \
wdbframewin.FrameWindow(self, \
self.curframe, \
self.curframe.f_globals, name)
do_g = do_globalframe
# Link between the debugger and the window
def refreshstack(self):
height = stdwin.lineheight() * (1 + len(self.stack))
self.win.setdocsize((0, height))
self.refreshall() # XXX be more subtle later
# Also pass the information on to the source windows
filename = self.curframe.f_code.co_filename
lineno = self.curframe.f_lineno
for fn in self.sourcewindows.keys():
w = self.sourcewindows[fn]
if fn == filename:
w.setlineno(lineno)
else:
w.resetlineno()
# The remaining methods override BaseWindow methods
def close(self):
if not self.closed:
basewin.BaseWindow.close(self)
self.closed = 1
for key in self.sourcewindows.keys():
self.sourcewindows[key].close()
for key in self.framewindows.keys():
self.framewindows[key].close()
self.set_quit()
def char(self, detail):
try:
func = eval('self.do_' + detail)
except (AttributeError, SyntaxError):
stdwin.fleep()
return
func()
def command(self, detail):
if detail == WC_UP:
self.do_up()
elif detail == WC_DOWN:
self.do_down()
def mouse_down(self, detail):
(h, v), clicks, button, mask = detail
i = v / stdwin.lineheight()
if 0 <= i < len(self.stack):
if i != self.curindex:
self.curindex = i
self.curframe = self.stack[self.curindex][0]
self.refreshstack()
elif clicks == 2:
self.do_frame()
else:
stdwin.fleep()
def draw(self, detail):
import linecache, string
d = self.win.begindrawing()
try:
h, v = 0, 0
for f, lineno in self.stack:
fn = f.f_code.co_filename
if f is self.curframe:
s = '> '
else:
s = ' '
s = s + fn + '(' + `lineno` + ')'
s = s + f.f_code.co_name
if f.f_locals.has_key('__args__'):
args = f.f_locals['__args__']
if args is not None:
s = s + repr.repr(args)
if f.f_locals.has_key('__return__'):
rv = f.f_locals['__return__']
s = s + '->'
s = s + repr.repr(rv)
line = linecache.getline(fn, lineno)
if line: s = s + ': ' + string.strip(line)
d.text((h, v), s)
v = v + d.lineheight()
finally:
d.close()
# Simplified interface
def run(statement, globals=None, locals=None):
x = Wdb()
try: x.run(statement, globals, locals)
finally: x.close()
def runeval(expression, globals=None, locals=None):
x = Wdb()
try: return x.runeval(expression, globals, locals)
finally: x.close()
def runctx(statement, globals, locals):
# B/W compatibility
run(statement, globals, locals)
def runcall(*args):
x = Wdb()
try: return apply(x.runcall, args)
finally: x.close()
def set_trace():
Wdb().set_trace()
# Post-Mortem interface
def post_mortem(traceback):
x = Wdb()
x.reset()
x.interaction(None, traceback)
def pm():
import sys
post_mortem(sys.last_traceback)
# Main program for testing
TESTCMD = 'import x; x.main()'
def test():
run(TESTCMD)
# wdbframewin.py -- frame window for wdb.py
# XXX To do:
# - display function name in window title
# - execute arbitrary statements instead of just evaluating expressions
# - allow setting variables by editing their values
import stdwin
from stdwinevents import *
import basewin
import sys
WIDTH = 40
MINHEIGHT = 8
MAXHEIGHT = 16
class FrameWindow(basewin.BaseWindow):
def __init__(self, debugger, frame, dict, name):
self.debugger = debugger
self.frame = frame # Not used except for identity tests
self.dict = dict
self.name = name
nl = max(MINHEIGHT, len(self.dict) + 5)
nl = min(nl, MAXHEIGHT)
width = WIDTH*stdwin.textwidth('0')
height = nl*stdwin.lineheight()
stdwin.setdefwinsize(width, height)
basewin.BaseWindow.__init__(
self, '--Frame ' + name + '--')
# XXX Should use current function name
self.initeditor()
self.displaylist = ['>>>', '', '-'*WIDTH]
self.refreshframe()
def initeditor(self):
r = (stdwin.textwidth('>>> '), 0), (30000, stdwin.lineheight())
self.editor = self.win.textcreate(r)
def closeeditor(self):
self.editor.close()
def dispatch(self, event):
type, win, detail = event
if type == WE_NULL: return # Dummy tested by mainloop
if type in (WE_DRAW, WE_COMMAND) \
or not self.editor.event(event):
basewin.BaseWindow.dispatch(self, event)
def close(self):
del self.debugger.framewindows[self.name]
del self.debugger, self.dict
self.closeeditor()
basewin.BaseWindow.close(self)
def command(self, detail):
if detail == WC_RETURN:
self.re_eval()
else:
dummy = self.editor.event(WE_COMMAND, \
self.win, detail)
def mouse_down(self, detail):
(h, v), clicks, button, mask = detail
if clicks != 2:
return
i = v / stdwin.lineheight()
if 5 <= i < len(self.displaylist):
import string
name = string.splitfields(self.displaylist[i],' = ')[0]
if not self.dict.has_key(name):
stdwin.fleep()
return
value = self.dict[name]
if not hasattr(value, '__dict__'):
stdwin.fleep()
return
name = 'instance ' + `value`
if self.debugger.framewindows.has_key(name):
self.debugger.framewindows[name].popup()
else:
self.debugger.framewindows[name] = \
FrameWindow(self.debugger,
self.frame, value.__dict__,
name)
return
stdwin.fleep()
def re_eval(self):
import string, repr
expr = string.strip(self.editor.gettext())
if expr == '':
output = ''
else:
globals = self.frame.f_globals
globals['__privileged__'] = 1
locals = self.dict
try:
value = eval(expr, globals, locals)
output = repr.repr(value)
except:
if type(sys.exc_type) == type(''):
exc_type_name = sys.exc_type
else: exc_type_name = sys.exc_type.__name__
output = exc_type_name + ': ' + `sys.exc_value`
self.displaylist[1] = output
lh = stdwin.lineheight()
r = (-10, 0), (30000, 2*lh)
self.win.change(r)
self.editor.setfocus(0, len(expr))
def draw(self, detail):
(left, top), (right, bottom) = detail
dummy = self.editor.draw(detail)
d = self.win.begindrawing()
try:
lh = d.lineheight()
h, v = 0, 0
for line in self.displaylist:
if v+lh > top and v < bottom:
d.text((h, v), line)
v = v + lh
finally:
d.close()
def refreshframe(self):
import repr
del self.displaylist[3:]
self.re_eval()
names = self.dict.keys()
for key, label in ('__args__', 'Args: '), \
('__return__', 'Return: '):
if self.dict.has_key(key):
names.remove(key)
value = self.dict[key]
label = label + repr.repr(value)
self.displaylist.append(label)
names.sort()
for name in names:
value = self.dict[name]
line = name + ' = ' + repr.repr(value)
self.displaylist.append(line)
self.win.setdocsize(0, \
stdwin.lineheight() * len(self.displaylist))
self.refreshall() # XXX Be more subtle later
# wdbsrcwin.py -- source window for wdb
import stdwin
from stdwinevents import *
import srcwin
class DebuggerSourceWindow(srcwin.SourceWindow):
def __init__(self, debugger, filename):
self.debugger = debugger
self.curlineno = 0
self.focus = 0
srcwin.SourceWindow.__init__(self, filename)
def close(self):
del self.debugger.sourcewindows[self.filename]
del self.debugger
srcwin.SourceWindow.close(self)
def dispatch(self, event):
type, win, detail = event
if type == WE_CHAR:
self.char(detail)
elif type == WE_COMMAND:
self.command(detail)
elif type == WE_MOUSE_DOWN:
self.mouse_down(detail)
else:
srcwin.SourceWindow.dispatch(self, event)
def char(self, detail):
self.debugger.char(detail)
def command(self, detail):
self.debugger.command(detail)
def mouse_down(self, detail):
(h, v), clicks, button, mask = detail
if h >= self.leftmargin:
srcwin.SourceWindow.dispatch(self, \
(WE_MOUSE_DOWN, self.win, detail))
return
lineno = v/self.lineheight + 1
if 1 <= lineno <= self.linecount:
if self.debugger.get_break(self.filename, lineno):
f = self.debugger.clear_break
else:
f = self.debugger.set_break
err = f(self.filename, lineno)
if err: stdwin.message(err)
else: self.changemark(lineno)
else:
stdwin.fleep()
def getmark(self, lineno):
s = `lineno`
if lineno == self.focus:
s = '[' + s + ']'
else:
s = ' ' + s + ' '
if lineno == self.curlineno:
s = s + '->'
else:
s = s + ' '
br = self.debugger.breaks
if br.has_key(self.filename) and lineno in br[self.filename]:
s = s + 'B'
else:
s = s + ' '
return s
def getmargin(self):
return stdwin.textwidth('[' + `self.linecount+1` + ']->B ')
def setlineno(self, newlineno):
if newlineno != self.curlineno:
oldlineno = self.curlineno
self.curlineno = newlineno
self.changemark(oldlineno)
self.changemark(newlineno)
if newlineno != 0:
self.showline(newlineno)
def resetlineno(self):
self.setlineno(0)
def setfocus(self, newfocus):
if newfocus != self.focus:
oldfocus = self.focus
self.focus = newfocus
self.changemark(oldfocus)
self.changemark(newfocus)
if newfocus != 0:
self.showline(newfocus)
def resetfocus(self):
self.setfocus(0)
# XXX Should get rid of focus stuff again
# This module exports classes for the various canvas item types
from Tkinter import Canvas, _cnfmerge, _flatten
class CanvasItem:
def __init__(self, canvas, itemType, *args, **kw):
self.canvas = canvas
self.id = canvas._create(itemType, args, kw)
if not hasattr(canvas, 'items'):
canvas.items = {}
canvas.items[self.id] = self
def __str__(self):
return str(self.id)
def __repr__(self):
return '<%s, id=%d>' % (self.__class__.__name__, self.id)
def delete(self):
del self.canvas.items[self.id]
self.canvas.delete(self.id)
def __getitem__(self, key):
v = self.canvas.tk.split(self.canvas.tk.call(
self.canvas._w, 'itemconfigure',
self.id, '-' + key))
return v[4]
cget = __getitem__
def __setitem__(self, key, value):
self.canvas.itemconfig(self.id, {key: value})
def keys(self):
if not hasattr(self, '_keys'):
self._keys = map(lambda x, tk=self.canvas.tk:
tk.splitlist(x)[0][1:],
self.canvas.tk.splitlist(
self.canvas._do(
'itemconfigure',
(self.id,))))
return self._keys
def has_key(self, key):
return key in self.keys()
def addtag(self, tag, option='withtag'):
self.canvas.addtag(tag, option, self.id)
def bbox(self):
x1, y1, x2, y2 = self.canvas.bbox(self.id)
return (x1, y1), (x2, y2)
def bind(self, sequence=None, command=None):
return self.canvas.tag_bind(self.id, sequence, command)
def unbind(self, sequence):
self.canvas.tag_bind(self.id, sequence, '')
def config(self, cnf={}, **kw):
return self.canvas.itemconfig(self.id, _cnfmerge((cnf, kw)))
def coords(self, pts = ()):
flat = ()
for x, y in pts: flat = flat + (x, y)
return apply(self.canvas.coords, (self.id,) + flat)
def dchars(self, first, last=None):
self.canvas.dchars(self.id, first, last)
def dtag(self, ttd):
self.canvas.dtag(self.id, ttd)
def focus(self):
self.canvas.focus(self.id)
def gettags(self):
return self.canvas.gettags(self.id)
def icursor(self, index):
self.canvas.icursor(self.id, index)
def index(self, index):
return self.canvas.index(self.id, index)
def insert(self, beforethis, string):
self.canvas.insert(self.id, beforethis, string)
def lower(self, belowthis=None):
self.canvas.lower(self.id, belowthis)
def move(self, xamount, yamount):
self.canvas.move(self.id, xamount, yamount)
def tkraise(self, abovethis=None):
self.canvas.tkraise(self.id, abovethis)
raise_ = tkraise # BW compat
def scale(self, xorigin, yorigin, xscale, yscale):
self.canvas.scale(self.id, xorigin, yorigin, xscale, yscale)
def type(self):
return self.canvas.type(self.id)
class Arc(CanvasItem):
def __init__(self, canvas, *args, **kw):
apply(CanvasItem.__init__, (self, canvas, 'arc') + args, kw)
class Bitmap(CanvasItem):
def __init__(self, canvas, *args, **kw):
apply(CanvasItem.__init__, (self, canvas, 'bitmap') + args, kw)
class ImageItem(CanvasItem):
def __init__(self, canvas, *args, **kw):
apply(CanvasItem.__init__, (self, canvas, 'image') + args, kw)
class Line(CanvasItem):
def __init__(self, canvas, *args, **kw):
apply(CanvasItem.__init__, (self, canvas, 'line') + args, kw)
class Oval(CanvasItem):
def __init__(self, canvas, *args, **kw):
apply(CanvasItem.__init__, (self, canvas, 'oval') + args, kw)
class Polygon(CanvasItem):
def __init__(self, canvas, *args, **kw):
apply(CanvasItem.__init__, (self, canvas, 'polygon') + args, kw)
class Rectangle(CanvasItem):
def __init__(self, canvas, *args, **kw):
apply(CanvasItem.__init__, (self, canvas, 'rectangle') + args, kw)
# XXX "Text" is taken by the Text widget...
class CanvasText(CanvasItem):
def __init__(self, canvas, *args, **kw):
apply(CanvasItem.__init__, (self, canvas, 'text') + args, kw)
class Window(CanvasItem):
def __init__(self, canvas, *args, **kw):
apply(CanvasItem.__init__, (self, canvas, 'window') + args, kw)
class Group:
def __init__(self, canvas, tag=None):
if not tag:
tag = 'Group%d' % id(self)
self.tag = self.id = tag
self.canvas = canvas
self.canvas.dtag(self.tag)
def str(self):
return self.tag
__str__ = str
def _do(self, cmd, *args):
return self.canvas._do(cmd, (self.tag,) + _flatten(args))
def addtag_above(self, tagOrId):
self._do('addtag', 'above', tagOrId)
def addtag_all(self):
self._do('addtag', 'all')
def addtag_below(self, tagOrId):
self._do('addtag', 'below', tagOrId)
def addtag_closest(self, x, y, halo=None, start=None):
self._do('addtag', 'closest', x, y, halo, start)
def addtag_enclosed(self, x1, y1, x2, y2):
self._do('addtag', 'enclosed', x1, y1, x2, y2)
def addtag_overlapping(self, x1, y1, x2, y2):
self._do('addtag', 'overlapping', x1, y1, x2, y2)
def addtag_withtag(self, tagOrId):
self._do('addtag', 'withtag', tagOrId)
def bbox(self):
return self._getints(self._do('bbox'))
def bind(self, sequence=None, command=None):
return self.canvas.tag_bind(self.id, sequence, command)
def unbind(self, sequence):
self.canvas.tag_bind(self.id, sequence, '')
def coords(self, *pts):
return self._do('coords', pts)
def dchars(self, first, last=None):
self._do('dchars', first, last)
def delete(self):
self._do('delete')
def dtag(self, tagToDelete=None):
self._do('dtag', tagToDelete)
def focus(self):
self._do('focus')
def gettags(self):
return self.canvas.tk.splitlist(self._do('gettags', self.tag))
def icursor(self, index):
return self._do('icursor', index)
def index(self, index):
return self.canvas.tk.getint(self._do('index', index))
def insert(self, beforeThis, string):
self._do('insert', beforeThis, string)
def config(self, cnf={}, **kw):
return self.canvas.itemconfigure(self.tag, _cnfmerge((cnf,kw)))
def lower(self, belowThis=None):
self._do('lower', belowThis)
def move(self, xAmount, yAmount):
self._do('move', xAmount, yAmount)
def tkraise(self, aboveThis=None):
self._do('raise', aboveThis)
lift = tkraise
def scale(self, xOrigin, yOrigin, xScale, yScale):
self._do('scale', xOrigin, yOrigin, xScale, yScale)
def select_adjust(self, index):
self.canvas._do('select', ('adjust', self.tag, index))
def select_from(self, index):
self.canvas._do('select', ('from', self.tag, index))
def select_to(self, index):
self.canvas._do('select', ('to', self.tag, index))
def type(self):
return self._do('type')
# Dialog.py -- Tkinter interface to the tk_dialog script.
from Tkinter import *
from Tkinter import _cnfmerge
if TkVersion <= 3.6:
DIALOG_ICON = 'warning'
else:
DIALOG_ICON = 'questhead'
class Dialog(Widget):
def __init__(self, master=None, cnf={}, **kw):
cnf = _cnfmerge((cnf, kw))
self.widgetName = '__dialog__'
Widget._setup(self, master, cnf)
self.num = self.tk.getint(
apply(self.tk.call,
('tk_dialog', self._w,
cnf['title'], cnf['text'],
cnf['bitmap'], cnf['default'])
+ cnf['strings']))
try: Widget.destroy(self)
except TclError: pass
def destroy(self): pass
def _test():
d = Dialog(None, {'title': 'File Modified',
'text':
'File "Python.h" has been modified'
' since the last time it was saved.'
' Do you want to save it before'
' exiting the application.',
'bitmap': DIALOG_ICON,
'default': 0,
'strings': ('Save File',
'Discard Changes',
'Return to Editor')})
print d.num
if __name__ == '__main__':
t = Button(None, {'text': 'Test',
'command': _test,
Pack: {}})
q = Button(None, {'text': 'Quit',
'command': t.quit,
Pack: {}})
t.mainloop()
"""File selection dialog classes.
Classes:
- FileDialog
- LoadFileDialog
- SaveFileDialog
"""
from Tkinter import *
from Dialog import Dialog
import os
import fnmatch
dialogstates = {}
class FileDialog:
"""Standard file selection dialog -- no checks on selected file.
Usage:
d = FileDialog(master)
file = d.go(dir_or_file, pattern, default, key)
if file is None: ...canceled...
else: ...open file...
All arguments to go() are optional.
The 'key' argument specifies a key in the global dictionary
'dialogstates', which keeps track of the values for the directory
and pattern arguments, overriding the values passed in (it does
not keep track of the default argument!). If no key is specified,
the dialog keeps no memory of previous state. Note that memory is
kept even when the dialog is cancelled. (All this emulates the
behavior of the Macintosh file selection dialogs.)
"""
title = "File Selection Dialog"
def __init__(self, master, title=None):
if title is None: title = self.title
self.master = master
self.directory = None
self.top = Toplevel(master)
self.top.title(title)
self.top.iconname(title)
self.botframe = Frame(self.top)
self.botframe.pack(side=BOTTOM, fill=X)
self.selection = Entry(self.top)
self.selection.pack(side=BOTTOM, fill=X)
self.selection.bind('<Return>', self.ok_event)
self.filter = Entry(self.top)
self.filter.pack(side=TOP, fill=X)
self.filter.bind('<Return>', self.filter_command)
self.midframe = Frame(self.top)
self.midframe.pack(expand=YES, fill=BOTH)
self.filesbar = Scrollbar(self.midframe)
self.filesbar.pack(side=RIGHT, fill=Y)
self.files = Listbox(self.midframe, exportselection=0,
yscrollcommand=(self.filesbar, 'set'))
self.files.pack(side=RIGHT, expand=YES, fill=BOTH)
btags = self.files.bindtags()
self.files.bindtags(btags[1:] + btags[:1])
self.files.bind('<ButtonRelease-1>', self.files_select_event)
self.files.bind('<Double-ButtonRelease-1>', self.files_double_event)
self.filesbar.config(command=(self.files, 'yview'))
self.dirsbar = Scrollbar(self.midframe)
self.dirsbar.pack(side=LEFT, fill=Y)
self.dirs = Listbox(self.midframe, exportselection=0,
yscrollcommand=(self.dirsbar, 'set'))
self.dirs.pack(side=LEFT, expand=YES, fill=BOTH)
self.dirsbar.config(command=(self.dirs, 'yview'))
btags = self.dirs.bindtags()
self.dirs.bindtags(btags[1:] + btags[:1])
self.dirs.bind('<ButtonRelease-1>', self.dirs_select_event)
self.dirs.bind('<Double-ButtonRelease-1>', self.dirs_double_event)
self.ok_button = Button(self.botframe,
text="OK",
command=self.ok_command)
self.ok_button.pack(side=LEFT)
self.filter_button = Button(self.botframe,
text="Filter",
command=self.filter_command)
self.filter_button.pack(side=LEFT, expand=YES)
self.cancel_button = Button(self.botframe,
text="Cancel",
command=self.cancel_command)
self.cancel_button.pack(side=RIGHT)
self.top.protocol('WM_DELETE_WINDOW', self.cancel_command)
# XXX Are the following okay for a general audience?
self.top.bind('<Alt-w>', self.cancel_command)
self.top.bind('<Alt-W>', self.cancel_command)
def go(self, dir_or_file=os.curdir, pattern="*", default="", key=None):
if key and dialogstates.has_key(key):
self.directory, pattern = dialogstates[key]
else:
dir_or_file = os.path.expanduser(dir_or_file)
if os.path.isdir(dir_or_file):
self.directory = dir_or_file
else:
self.directory, default = os.path.split(dir_or_file)
self.set_filter(self.directory, pattern)
self.set_selection(default)
self.filter_command()
self.selection.focus_set()
self.top.grab_set()
self.how = None
self.master.mainloop() # Exited by self.quit(how)
if key: dialogstates[key] = self.get_filter()
self.top.destroy()
return self.how
def quit(self, how=None):
self.how = how
self.master.quit() # Exit mainloop()
def dirs_double_event(self, event):
self.filter_command()
def dirs_select_event(self, event):
dir, pat = self.get_filter()
subdir = self.dirs.get('active')
dir = os.path.normpath(os.path.join(self.directory, subdir))
self.set_filter(dir, pat)
def files_double_event(self, event):
self.ok_command()
def files_select_event(self, event):
file = self.files.get('active')
self.set_selection(file)
def ok_event(self, event):
self.ok_command()
def ok_command(self):
self.quit(self.get_selection())
def filter_command(self, event=None):
dir, pat = self.get_filter()
try:
names = os.listdir(dir)
except os.error:
self.master.bell()
return
self.directory = dir
self.set_filter(dir, pat)
names.sort()
subdirs = [os.pardir]
matchingfiles = []
for name in names:
fullname = os.path.join(dir, name)
if os.path.isdir(fullname):
subdirs.append(name)
elif fnmatch.fnmatch(name, pat):
matchingfiles.append(name)
self.dirs.delete(0, END)
for name in subdirs:
self.dirs.insert(END, name)
self.files.delete(0, END)
for name in matchingfiles:
self.files.insert(END, name)
head, tail = os.path.split(self.get_selection())
if tail == os.curdir: tail = ''
self.set_selection(tail)
def get_filter(self):
filter = self.filter.get()
filter = os.path.expanduser(filter)
if filter[-1:] == os.sep or os.path.isdir(filter):
filter = os.path.join(filter, "*")
return os.path.split(filter)
def get_selection(self):
file = self.selection.get()
file = os.path.expanduser(file)
return file
def cancel_command(self, event=None):
self.quit()
def set_filter(self, dir, pat):
if not os.path.isabs(dir):
try:
pwd = os.getcwd()
except os.error:
pwd = None
if pwd:
dir = os.path.join(pwd, dir)
dir = os.path.normpath(dir)
self.filter.delete(0, END)
self.filter.insert(END, os.path.join(dir or os.curdir, pat or "*"))
def set_selection(self, file):
self.selection.delete(0, END)
self.selection.insert(END, os.path.join(self.directory, file))
class LoadFileDialog(FileDialog):
"""File selection dialog which checks that the file exists."""
title = "Load File Selection Dialog"
def ok_command(self):
file = self.get_selection()
if not os.path.isfile(file):
self.master.bell()
else:
self.quit(file)
class SaveFileDialog(FileDialog):
"""File selection dialog which checks that the file may be created."""
title = "Save File Selection Dialog"
def ok_command(self):
file = self.get_selection()
if os.path.exists(file):
if os.path.isdir(file):
self.master.bell()
return
d = Dialog(self.top,
title="Overwrite Existing File Question",
text="Overwrite existing file %s?" % `file`,
bitmap='questhead',
default=1,
strings=("Yes", "Cancel"))
if d.num != 0:
return
else:
head, tail = os.path.split(file)
if not os.path.isdir(head):
self.master.bell()
return
self.quit(file)
def test():
"""Simple test program."""
root = Tk()
root.withdraw()
fd = LoadFileDialog(root)
loadfile = fd.go(key="test")
fd = SaveFileDialog(root)
savefile = fd.go(key="test")
print loadfile, savefile
if __name__ == '__main__':
test()
# A ScrolledText widget feels like a text widget but also has a
# vertical scroll bar on its right. (Later, options may be added to
# add a horizontal bar as well, to make the bars disappear
# automatically when not needed, to move them to the other side of the
# window, etc.)
#
# Configuration options are passed to the Text widget.
# A Frame widget is inserted between the master and the text, to hold
# the Scrollbar widget.
# Most methods calls are inherited from the Text widget; Pack methods
# are redirected to the Frame widget however.
from Tkinter import *
from Tkinter import _cnfmerge
class ScrolledText(Text):
def __init__(self, master=None, cnf={}, **kw):
if kw:
cnf = _cnfmerge((cnf, kw))
fcnf = {}
for k in cnf.keys():
if type(k) == ClassType or k == 'name':
fcnf[k] = cnf[k]
del cnf[k]
self.frame = apply(Frame, (master,), fcnf)
self.vbar = Scrollbar(self.frame, name='vbar')
self.vbar.pack(side=RIGHT, fill=Y)
cnf['name'] = 'text'
apply(Text.__init__, (self, self.frame), cnf)
self.pack(side=LEFT, fill=BOTH, expand=1)
self['yscrollcommand'] = self.vbar.set
self.vbar['command'] = self.yview
# Copy Pack methods of self.frame -- hack!
for m in Pack.__dict__.keys():
if m[0] != '_' and m != 'config':
setattr(self, m, getattr(self.frame, m))
"""A simple but flexible modal dialog box."""
from Tkinter import *
class SimpleDialog:
def __init__(self, master,
text='', buttons=[], default=None, cancel=None,
title=None, class_=None):
if class_:
self.root = Toplevel(master, class_=class_)
else:
self.root = Toplevel(master)
if title:
self.root.title(title)
self.root.iconname(title)
self.message = Message(self.root, text=text, aspect=400)
self.message.pack(expand=1, fill=BOTH)
self.frame = Frame(self.root)
self.frame.pack()
self.num = default
self.cancel = cancel
self.default = default
self.root.bind('<Return>', self.return_event)
for num in range(len(buttons)):
s = buttons[num]
b = Button(self.frame, text=s,
command=(lambda self=self, num=num: self.done(num)))
if num == default:
b.config(relief=RIDGE, borderwidth=8)
b.pack(side=LEFT, fill=BOTH, expand=1)
self.root.protocol('WM_DELETE_WINDOW', self.wm_delete_window)
self._set_transient(master)
def _set_transient(self, master, relx=0.5, rely=0.3):
widget = self.root
widget.withdraw() # Remain invisible while we figure out the geometry
widget.transient(master)
widget.update_idletasks() # Actualize geometry information
if master.winfo_ismapped():
m_width = master.winfo_width()
m_height = master.winfo_height()
m_x = master.winfo_rootx()
m_y = master.winfo_rooty()
else:
m_width = master.winfo_screenwidth()
m_height = master.winfo_screenheight()
m_x = m_y = 0
w_width = widget.winfo_reqwidth()
w_height = widget.winfo_reqheight()
x = m_x + (m_width - w_width) * relx
y = m_y + (m_height - w_height) * rely
widget.geometry("+%d+%d" % (x, y))
widget.deiconify() # Become visible at the desired location
def go(self):
self.root.grab_set()
self.root.mainloop()
self.root.destroy()
return self.num
def return_event(self, event):
if self.default is None:
self.root.bell()
else:
self.done(self.default)
def wm_delete_window(self):
if self.cancel is None:
self.root.bell()
else:
self.done(self.cancel)
def done(self, num):
self.num = num
self.root.quit()
def test():
root = Tk()
def doit(root=root):
d = SimpleDialog(root,
text="This is a test dialog. "
"Would this have been an actual dialog, "
"the buttons below would have been glowing "
"in soft pink light.\n"
"Do you believe this?",
buttons=["Yes", "No", "Cancel"],
default=0,
cancel=2,
title="Test Dialog")
print d.go()
t = Button(root, text='Test', command=doit)
t.pack()
q = Button(root, text='Quit', command=t.quit)
q.pack()
t.mainloop()
if __name__ == '__main__':
test()
# Symbolic constants for Tk
# Booleans
NO=FALSE=OFF=0
YES=TRUE=ON=1
# -anchor
N='n'
S='s'
W='w'
E='e'
NW='nw'
SW='sw'
NE='ne'
SE='se'
CENTER='center'
# -fill
NONE='none'
X='x'
Y='y'
BOTH='both'
# -side
LEFT='left'
TOP='top'
RIGHT='right'
BOTTOM='bottom'
# -relief
RAISED='raised'
SUNKEN='sunken'
FLAT='flat'
RIDGE='ridge'
GROOVE='groove'
# -orient
HORIZONTAL='horizontal'
VERTICAL='vertical'
# -tabs
NUMERIC='numeric'
# -wrap
CHAR='char'
WORD='word'
# -align
BASELINE='baseline'
# Special tags, marks and insert positions
SEL='sel'
SEL_FIRST='sel.first'
SEL_LAST='sel.last'
END='end'
INSERT='insert'
CURRENT='current'
ANCHOR='anchor'
ALL='all' # e.g. Canvas.delete(ALL)
# Text widget and button states
NORMAL='normal'
DISABLED='disabled'
ACTIVE='active'
# Menu item types
CASCADE='cascade'
CHECKBUTTON='checkbutton'
COMMAND='command'
RADIOBUTTON='radiobutton'
SEPARATOR='separator'
# Selection modes for list boxes
SINGLE='single'
BROWSE='browse'
MULTIPLE='multiple'
EXTENDED='extended'
# Tkinter.py -- Tk/Tcl widget wrappers
__version__ = "$Revision$"
import _tkinter # If this fails your Python is not configured for Tk
tkinter = _tkinter # b/w compat for export
TclError = _tkinter.TclError
from types import *
from Tkconstants import *
import string; _string = string; del string
TkVersion = _string.atof(_tkinter.TK_VERSION)
TclVersion = _string.atof(_tkinter.TCL_VERSION)
READABLE = _tkinter.READABLE
WRITABLE = _tkinter.WRITABLE
EXCEPTION = _tkinter.EXCEPTION
# These are not always defined, e.g. not on Win32 with Tk 8.0 :-(
try: _tkinter.createfilehandler
except AttributeError: _tkinter.createfilehandler = None
try: _tkinter.deletefilehandler
except AttributeError: _tkinter.deletefilehandler = None
def _flatten(tuple):
res = ()
for item in tuple:
if type(item) in (TupleType, ListType):
res = res + _flatten(item)
elif item is not None:
res = res + (item,)
return res
def _cnfmerge(cnfs):
if type(cnfs) is DictionaryType:
return cnfs
elif type(cnfs) in (NoneType, StringType):
return cnfs
else:
cnf = {}
for c in _flatten(cnfs):
try:
cnf.update(c)
except (AttributeError, TypeError), msg:
print "_cnfmerge: fallback due to:", msg
for k, v in c.items():
cnf[k] = v
return cnf
class Event:
pass
_default_root = None
def _tkerror(err):
pass
def _exit(code='0'):
raise SystemExit, code
_varnum = 0
class Variable:
_default = ""
def __init__(self, master=None):
global _default_root
global _varnum
if master:
self._tk = master.tk
else:
self._tk = _default_root.tk
self._name = 'PY_VAR' + `_varnum`
_varnum = _varnum + 1
self.set(self._default)
def __del__(self):
self._tk.globalunsetvar(self._name)
def __str__(self):
return self._name
def set(self, value):
return self._tk.globalsetvar(self._name, value)
class StringVar(Variable):
_default = ""
def __init__(self, master=None):
Variable.__init__(self, master)
def get(self):
return self._tk.globalgetvar(self._name)
class IntVar(Variable):
_default = 0
def __init__(self, master=None):
Variable.__init__(self, master)
def get(self):
return self._tk.getint(self._tk.globalgetvar(self._name))
class DoubleVar(Variable):
_default = 0.0
def __init__(self, master=None):
Variable.__init__(self, master)
def get(self):
return self._tk.getdouble(self._tk.globalgetvar(self._name))
class BooleanVar(Variable):
_default = "false"
def __init__(self, master=None):
Variable.__init__(self, master)
def get(self):
return self._tk.getboolean(self._tk.globalgetvar(self._name))
def mainloop(n=0):
_default_root.tk.mainloop(n)
def getint(s):
return _default_root.tk.getint(s)
def getdouble(s):
return _default_root.tk.getdouble(s)
def getboolean(s):
return _default_root.tk.getboolean(s)
class Misc:
_tclCommands = None
def destroy(self):
if self._tclCommands is not None:
for name in self._tclCommands:
#print '- Tkinter: deleted command', name
self.tk.deletecommand(name)
self._tclCommands = None
def deletecommand(self, name):
#print '- Tkinter: deleted command', name
self.tk.deletecommand(name)
index = self._tclCommands.index(name)
del self._tclCommands[index]
def tk_strictMotif(self, boolean=None):
return self.tk.getboolean(self.tk.call(
'set', 'tk_strictMotif', boolean))
def tk_bisque(self):
self.tk.call('tk_bisque')
def tk_setPalette(self, *args, **kw):
apply(self.tk.call, ('tk_setPalette',)
+ _flatten(args) + _flatten(kw.items()))
def tk_menuBar(self, *args):
pass # obsolete since Tk 4.0
def wait_variable(self, name='PY_VAR'):
self.tk.call('tkwait', 'variable', name)
waitvar = wait_variable # XXX b/w compat
def wait_window(self, window=None):
if window == None:
window = self
self.tk.call('tkwait', 'window', window._w)
def wait_visibility(self, window=None):
if window == None:
window = self
self.tk.call('tkwait', 'visibility', window._w)
def setvar(self, name='PY_VAR', value='1'):
self.tk.setvar(name, value)
def getvar(self, name='PY_VAR'):
return self.tk.getvar(name)
def getint(self, s):
return self.tk.getint(s)
def getdouble(self, s):
return self.tk.getdouble(s)
def getboolean(self, s):
return self.tk.getboolean(s)
def focus_set(self):
self.tk.call('focus', self._w)
focus = focus_set # XXX b/w compat?
def focus_force(self):
self.tk.call('focus', '-force', self._w)
def focus_get(self):
name = self.tk.call('focus')
if name == 'none' or not name: return None
return self._nametowidget(name)
def focus_displayof(self):
name = self.tk.call('focus', '-displayof', self._w)
if name == 'none' or not name: return None
return self._nametowidget(name)
def focus_lastfor(self):
name = self.tk.call('focus', '-lastfor', self._w)
if name == 'none' or not name: return None
return self._nametowidget(name)
def tk_focusFollowsMouse(self):
self.tk.call('tk_focusFollowsMouse')
def tk_focusNext(self):
name = self.tk.call('tk_focusNext', self._w)
if not name: return None
return self._nametowidget(name)
def tk_focusPrev(self):
name = self.tk.call('tk_focusPrev', self._w)
if not name: return None
return self._nametowidget(name)
def after(self, ms, func=None, *args):
if not func:
# I'd rather use time.sleep(ms*0.001)
self.tk.call('after', ms)
else:
# XXX Disgusting hack to clean up after calling func
tmp = []
def callit(func=func, args=args, self=self, tmp=tmp):
try:
apply(func, args)
finally:
self.deletecommand(tmp[0])
name = self._register(callit)
tmp.append(name)
return self.tk.call('after', ms, name)
def after_idle(self, func, *args):
return apply(self.after, ('idle', func) + args)
def after_cancel(self, id):
self.tk.call('after', 'cancel', id)
def bell(self, displayof=0):
apply(self.tk.call, ('bell',) + self._displayof(displayof))
# Clipboard handling:
def clipboard_clear(self, **kw):
if not kw.has_key('displayof'): kw['displayof'] = self._w
apply(self.tk.call,
('clipboard', 'clear') + self._options(kw))
def clipboard_append(self, string, **kw):
if not kw.has_key('displayof'): kw['displayof'] = self._w
apply(self.tk.call,
('clipboard', 'append') + self._options(kw)
+ ('--', string))
# XXX grab current w/o window argument
def grab_current(self):
name = self.tk.call('grab', 'current', self._w)
if not name: return None
return self._nametowidget(name)
def grab_release(self):
self.tk.call('grab', 'release', self._w)
def grab_set(self):
self.tk.call('grab', 'set', self._w)
def grab_set_global(self):
self.tk.call('grab', 'set', '-global', self._w)
def grab_status(self):
status = self.tk.call('grab', 'status', self._w)
if status == 'none': status = None
return status
def lower(self, belowThis=None):
self.tk.call('lower', self._w, belowThis)
def option_add(self, pattern, value, priority = None):
self.tk.call('option', 'add', pattern, value, priority)
def option_clear(self):
self.tk.call('option', 'clear')
def option_get(self, name, className):
return self.tk.call('option', 'get', self._w, name, className)
def option_readfile(self, fileName, priority = None):
self.tk.call('option', 'readfile', fileName, priority)
def selection_clear(self, **kw):
if not kw.has_key('displayof'): kw['displayof'] = self._w
apply(self.tk.call, ('selection', 'clear') + self._options(kw))
def selection_get(self, **kw):
if not kw.has_key('displayof'): kw['displayof'] = self._w
return apply(self.tk.call,
('selection', 'get') + self._options(kw))
def selection_handle(self, command, **kw):
name = self._register(command)
apply(self.tk.call,
('selection', 'handle') + self._options(kw)
+ (self._w, name))
def selection_own(self, **kw):
"Become owner of X selection."
apply(self.tk.call,
('selection', 'own') + self._options(kw) + (self._w,))
def selection_own_get(self, **kw):
"Find owner of X selection."
if not kw.has_key('displayof'): kw['displayof'] = self._w
name = apply(self.tk.call,
('selection', 'own') + self._options(kw))
if not name: return None
return self._nametowidget(name)
def send(self, interp, cmd, *args):
return apply(self.tk.call, ('send', interp, cmd) + args)
def lower(self, belowThis=None):
self.tk.call('lower', self._w, belowThis)
def tkraise(self, aboveThis=None):
self.tk.call('raise', self._w, aboveThis)
lift = tkraise
def colormodel(self, value=None):
return self.tk.call('tk', 'colormodel', self._w, value)
def winfo_atom(self, name, displayof=0):
args = ('winfo', 'atom') + self._displayof(displayof) + (name,)
return self.tk.getint(apply(self.tk.call, args))
def winfo_atomname(self, id, displayof=0):
args = ('winfo', 'atomname') \
+ self._displayof(displayof) + (id,)
return apply(self.tk.call, args)
def winfo_cells(self):
return self.tk.getint(
self.tk.call('winfo', 'cells', self._w))
def winfo_children(self):
return map(self._nametowidget,
self.tk.splitlist(self.tk.call(
'winfo', 'children', self._w)))
def winfo_class(self):
return self.tk.call('winfo', 'class', self._w)
def winfo_colormapfull(self):
return self.tk.getboolean(
self.tk.call('winfo', 'colormapfull', self._w))
def winfo_containing(self, rootX, rootY, displayof=0):
args = ('winfo', 'containing') \
+ self._displayof(displayof) + (rootX, rootY)
name = apply(self.tk.call, args)
if not name: return None
return self._nametowidget(name)
def winfo_depth(self):
return self.tk.getint(self.tk.call('winfo', 'depth', self._w))
def winfo_exists(self):
return self.tk.getint(
self.tk.call('winfo', 'exists', self._w))
def winfo_fpixels(self, number):
return self.tk.getdouble(self.tk.call(
'winfo', 'fpixels', self._w, number))
def winfo_geometry(self):
return self.tk.call('winfo', 'geometry', self._w)
def winfo_height(self):
return self.tk.getint(
self.tk.call('winfo', 'height', self._w))
def winfo_id(self):
return self.tk.getint(
self.tk.call('winfo', 'id', self._w))
def winfo_interps(self, displayof=0):
args = ('winfo', 'interps') + self._displayof(displayof)
return self.tk.splitlist(apply(self.tk.call, args))
def winfo_ismapped(self):
return self.tk.getint(
self.tk.call('winfo', 'ismapped', self._w))
def winfo_manager(self):
return self.tk.call('winfo', 'manager', self._w)
def winfo_name(self):
return self.tk.call('winfo', 'name', self._w)
def winfo_parent(self):
return self.tk.call('winfo', 'parent', self._w)
def winfo_pathname(self, id, displayof=0):
args = ('winfo', 'pathname') \
+ self._displayof(displayof) + (id,)
return apply(self.tk.call, args)
def winfo_pixels(self, number):
return self.tk.getint(
self.tk.call('winfo', 'pixels', self._w, number))
def winfo_pointerx(self):
return self.tk.getint(
self.tk.call('winfo', 'pointerx', self._w))
def winfo_pointerxy(self):
return self._getints(
self.tk.call('winfo', 'pointerxy', self._w))
def winfo_pointery(self):
return self.tk.getint(
self.tk.call('winfo', 'pointery', self._w))
def winfo_reqheight(self):
return self.tk.getint(
self.tk.call('winfo', 'reqheight', self._w))
def winfo_reqwidth(self):
return self.tk.getint(
self.tk.call('winfo', 'reqwidth', self._w))
def winfo_rgb(self, color):
return self._getints(
self.tk.call('winfo', 'rgb', self._w, color))
def winfo_rootx(self):
return self.tk.getint(
self.tk.call('winfo', 'rootx', self._w))
def winfo_rooty(self):
return self.tk.getint(
self.tk.call('winfo', 'rooty', self._w))
def winfo_screen(self):
return self.tk.call('winfo', 'screen', self._w)
def winfo_screencells(self):
return self.tk.getint(
self.tk.call('winfo', 'screencells', self._w))
def winfo_screendepth(self):
return self.tk.getint(
self.tk.call('winfo', 'screendepth', self._w))
def winfo_screenheight(self):
return self.tk.getint(
self.tk.call('winfo', 'screenheight', self._w))
def winfo_screenmmheight(self):
return self.tk.getint(
self.tk.call('winfo', 'screenmmheight', self._w))
def winfo_screenmmwidth(self):
return self.tk.getint(
self.tk.call('winfo', 'screenmmwidth', self._w))
def winfo_screenvisual(self):
return self.tk.call('winfo', 'screenvisual', self._w)
def winfo_screenwidth(self):
return self.tk.getint(
self.tk.call('winfo', 'screenwidth', self._w))
def winfo_server(self):
return self.tk.call('winfo', 'server', self._w)
def winfo_toplevel(self):
return self._nametowidget(self.tk.call(
'winfo', 'toplevel', self._w))
def winfo_viewable(self):
return self.tk.getint(
self.tk.call('winfo', 'viewable', self._w))
def winfo_visual(self):
return self.tk.call('winfo', 'visual', self._w)
def winfo_visualid(self):
return self.tk.call('winfo', 'visualid', self._w)
def winfo_visualsavailable(self, includeids=0):
data = self.tk.split(
self.tk.call('winfo', 'visualsavailable', self._w,
includeids and 'includeids' or None))
def parseitem(x, self=self):
return x[:1] + tuple(map(self.tk.getint, x[1:]))
return map(parseitem, data)
def winfo_vrootheight(self):
return self.tk.getint(
self.tk.call('winfo', 'vrootheight', self._w))
def winfo_vrootwidth(self):
return self.tk.getint(
self.tk.call('winfo', 'vrootwidth', self._w))
def winfo_vrootx(self):
return self.tk.getint(
self.tk.call('winfo', 'vrootx', self._w))
def winfo_vrooty(self):
return self.tk.getint(
self.tk.call('winfo', 'vrooty', self._w))
def winfo_width(self):
return self.tk.getint(
self.tk.call('winfo', 'width', self._w))
def winfo_x(self):
return self.tk.getint(
self.tk.call('winfo', 'x', self._w))
def winfo_y(self):
return self.tk.getint(
self.tk.call('winfo', 'y', self._w))
def update(self):
self.tk.call('update')
def update_idletasks(self):
self.tk.call('update', 'idletasks')
def bindtags(self, tagList=None):
if tagList is None:
return self.tk.splitlist(
self.tk.call('bindtags', self._w))
else:
self.tk.call('bindtags', self._w, tagList)
def _bind(self, what, sequence, func, add):
if func:
cmd = ("%sset _tkinter_break [%s %s]\n"
'if {"$_tkinter_break" == "break"} break\n') \
% (add and '+' or '',
self._register(func, self._substitute),
_string.join(self._subst_format))
apply(self.tk.call, what + (sequence, cmd))
elif func == '':
apply(self.tk.call, what + (sequence, func))
else:
return apply(self.tk.call, what + (sequence,))
def bind(self, sequence=None, func=None, add=None):
return self._bind(('bind', self._w), sequence, func, add)
def unbind(self, sequence):
self.tk.call('bind', self._w, sequence, '')
def bind_all(self, sequence=None, func=None, add=None):
return self._bind(('bind', 'all'), sequence, func, add)
def unbind_all(self, sequence):
self.tk.call('bind', 'all' , sequence, '')
def bind_class(self, className, sequence=None, func=None, add=None):
self._bind(('bind', className), sequence, func, add)
def unbind_class(self, className, sequence):
self.tk.call('bind', className , sequence, '')
def mainloop(self, n=0):
self.tk.mainloop(n)
def quit(self):
self.tk.quit()
def _getints(self, string):
if not string: return None
return tuple(map(self.tk.getint, self.tk.splitlist(string)))
def _getdoubles(self, string):
if not string: return None
return tuple(map(self.tk.getdouble, self.tk.splitlist(string)))
def _getboolean(self, string):
if string:
return self.tk.getboolean(string)
def _displayof(self, displayof):
if displayof:
return ('-displayof', displayof)
if displayof is None:
return ('-displayof', self._w)
return ()
def _options(self, cnf, kw = None):
if kw:
cnf = _cnfmerge((cnf, kw))
else:
cnf = _cnfmerge(cnf)
res = ()
for k, v in cnf.items():
if v is not None:
if k[-1] == '_': k = k[:-1]
if callable(v):
v = self._register(v)
res = res + ('-'+k, v)
return res
def _nametowidget(self, name):
w = self
if name[0] == '.':
w = w._root()
name = name[1:]
find = _string.find
while name:
i = find(name, '.')
if i >= 0:
name, tail = name[:i], name[i+1:]
else:
tail = ''
w = w.children[name]
name = tail
return w
def _register(self, func, subst=None):
f = CallWrapper(func, subst, self).__call__
name = `id(f)`
try:
func = func.im_func
except AttributeError:
pass
try:
name = name + func.__name__
except AttributeError:
pass
self.tk.createcommand(name, f)
if self._tclCommands is None:
self._tclCommands = []
self._tclCommands.append(name)
#print '+ Tkinter created command', name
return name
register = _register
def _root(self):
w = self
while w.master: w = w.master
return w
_subst_format = ('%#', '%b', '%f', '%h', '%k',
'%s', '%t', '%w', '%x', '%y',
'%A', '%E', '%K', '%N', '%W', '%T', '%X', '%Y')
def _substitute(self, *args):
tk = self.tk
if len(args) != len(self._subst_format): return args
nsign, b, f, h, k, s, t, w, x, y, A, E, K, N, W, T, X, Y = args
# Missing: (a, c, d, m, o, v, B, R)
e = Event()
e.serial = tk.getint(nsign)
e.num = tk.getint(b)
try: e.focus = tk.getboolean(f)
except TclError: pass
e.height = tk.getint(h)
e.keycode = tk.getint(k)
# For Visibility events, event state is a string and
# not an integer:
try:
e.state = tk.getint(s)
except TclError:
e.state = s
e.time = tk.getint(t)
e.width = tk.getint(w)
e.x = tk.getint(x)
e.y = tk.getint(y)
e.char = A
try: e.send_event = tk.getboolean(E)
except TclError: pass
e.keysym = K
e.keysym_num = tk.getint(N)
e.type = T
e.widget = self._nametowidget(W)
e.x_root = tk.getint(X)
e.y_root = tk.getint(Y)
return (e,)
def _report_exception(self):
import sys
exc, val, tb = sys.exc_type, sys.exc_value, sys.exc_traceback
root = self._root()
root.report_callback_exception(exc, val, tb)
class CallWrapper:
def __init__(self, func, subst, widget):
self.func = func
self.subst = subst
self.widget = widget
def __call__(self, *args):
try:
if self.subst:
args = apply(self.subst, args)
return apply(self.func, args)
except SystemExit, msg:
raise SystemExit, msg
except:
self.widget._report_exception()
class Wm:
def aspect(self,
minNumer=None, minDenom=None,
maxNumer=None, maxDenom=None):
return self._getints(
self.tk.call('wm', 'aspect', self._w,
minNumer, minDenom,
maxNumer, maxDenom))
def client(self, name=None):
return self.tk.call('wm', 'client', self._w, name)
def colormapwindows(self, *wlist):
args = ('wm', 'colormapwindows', self._w) + _flatten(wlist)
return map(self._nametowidget, apply(self.tk.call, args))
def command(self, value=None):
return self.tk.call('wm', 'command', self._w, value)
def deiconify(self):
return self.tk.call('wm', 'deiconify', self._w)
def focusmodel(self, model=None):
return self.tk.call('wm', 'focusmodel', self._w, model)
def frame(self):
return self.tk.call('wm', 'frame', self._w)
def geometry(self, newGeometry=None):
return self.tk.call('wm', 'geometry', self._w, newGeometry)
def grid(self,
baseWidht=None, baseHeight=None,
widthInc=None, heightInc=None):
return self._getints(self.tk.call(
'wm', 'grid', self._w,
baseWidht, baseHeight, widthInc, heightInc))
def group(self, pathName=None):
return self.tk.call('wm', 'group', self._w, pathName)
def iconbitmap(self, bitmap=None):
return self.tk.call('wm', 'iconbitmap', self._w, bitmap)
def iconify(self):
return self.tk.call('wm', 'iconify', self._w)
def iconmask(self, bitmap=None):
return self.tk.call('wm', 'iconmask', self._w, bitmap)
def iconname(self, newName=None):
return self.tk.call('wm', 'iconname', self._w, newName)
def iconposition(self, x=None, y=None):
return self._getints(self.tk.call(
'wm', 'iconposition', self._w, x, y))
def iconwindow(self, pathName=None):
return self.tk.call('wm', 'iconwindow', self._w, pathName)
def maxsize(self, width=None, height=None):
return self._getints(self.tk.call(
'wm', 'maxsize', self._w, width, height))
def minsize(self, width=None, height=None):
return self._getints(self.tk.call(
'wm', 'minsize', self._w, width, height))
def overrideredirect(self, boolean=None):
return self._getboolean(self.tk.call(
'wm', 'overrideredirect', self._w, boolean))
def positionfrom(self, who=None):
return self.tk.call('wm', 'positionfrom', self._w, who)
def protocol(self, name=None, func=None):
if callable(func):
command = self._register(func)
else:
command = func
return self.tk.call(
'wm', 'protocol', self._w, name, command)
def resizable(self, width=None, height=None):
return self.tk.call('wm', 'resizable', self._w, width, height)
def sizefrom(self, who=None):
return self.tk.call('wm', 'sizefrom', self._w, who)
def state(self):
return self.tk.call('wm', 'state', self._w)
def title(self, string=None):
return self.tk.call('wm', 'title', self._w, string)
def transient(self, master=None):
return self.tk.call('wm', 'transient', self._w, master)
def withdraw(self):
return self.tk.call('wm', 'withdraw', self._w)
class Tk(Misc, Wm):
_w = '.'
def __init__(self, screenName=None, baseName=None, className='Tk'):
global _default_root
self.master = None
self.children = {}
if baseName is None:
import sys, os
baseName = os.path.basename(sys.argv[0])
baseName, ext = os.path.splitext(baseName)
if ext not in ('.py', 'pyc'): baseName = baseName + ext
self.tk = _tkinter.create(screenName, baseName, className)
try:
# Disable event scanning except for Command-Period
import MacOS
try:
MacOS.SchedParams(1, 0)
except AttributeError:
# pre-1.5, use old routine
MacOS.EnableAppswitch(0)
except ImportError:
pass
else:
# Work around nasty MacTk bug
self.update()
# Version sanity checks
tk_version = self.tk.getvar('tk_version')
if tk_version != _tkinter.TK_VERSION:
raise RuntimeError, \
"tk.h version (%s) doesn't match libtk.a version (%s)" \
% (_tkinter.TK_VERSION, tk_version)
tcl_version = self.tk.getvar('tcl_version')
if tcl_version != _tkinter.TCL_VERSION:
raise RuntimeError, \
"tcl.h version (%s) doesn't match libtcl.a version (%s)" \
% (_tkinter.TCL_VERSION, tcl_version)
if TkVersion < 4.0:
raise RuntimeError, \
"Tk 4.0 or higher is required; found Tk %s" \
% str(TkVersion)
self.tk.createcommand('tkerror', _tkerror)
self.tk.createcommand('exit', _exit)
self.readprofile(baseName, className)
if not _default_root:
_default_root = self
def destroy(self):
for c in self.children.values(): c.destroy()
self.tk.call('destroy', self._w)
Misc.destroy(self)
global _default_root
if _default_root is self:
_default_root = None
def __str__(self):
return self._w
def readprofile(self, baseName, className):
import os
if os.environ.has_key('HOME'): home = os.environ['HOME']
else: home = os.curdir
class_tcl = os.path.join(home, '.%s.tcl' % className)
class_py = os.path.join(home, '.%s.py' % className)
base_tcl = os.path.join(home, '.%s.tcl' % baseName)
base_py = os.path.join(home, '.%s.py' % baseName)
dir = {'self': self}
exec 'from Tkinter import *' in dir
if os.path.isfile(class_tcl):
print 'source', `class_tcl`
self.tk.call('source', class_tcl)
if os.path.isfile(class_py):
print 'execfile', `class_py`
execfile(class_py, dir)
if os.path.isfile(base_tcl):
print 'source', `base_tcl`
self.tk.call('source', base_tcl)
if os.path.isfile(base_py):
print 'execfile', `base_py`
execfile(base_py, dir)
def report_callback_exception(self, exc, val, tb):
import traceback
print "Exception in Tkinter callback"
traceback.print_exception(exc, val, tb)
class Pack:
def config(self, cnf={}, **kw):
apply(self.tk.call,
('pack', 'configure', self._w)
+ self._options(cnf, kw))
configure = config
pack = config
def __setitem__(self, key, value):
Pack.config({key: value})
def forget(self):
self.tk.call('pack', 'forget', self._w)
pack_forget = forget
def info(self):
words = self.tk.splitlist(
self.tk.call('pack', 'info', self._w))
dict = {}
for i in range(0, len(words), 2):
key = words[i][1:]
value = words[i+1]
if value[:1] == '.':
value = self._nametowidget(value)
dict[key] = value
return dict
pack_info = info
_noarg_ = ['_noarg_']
def propagate(self, flag=_noarg_):
if flag is Pack._noarg_:
return self._getboolean(self.tk.call(
'pack', 'propagate', self._w))
else:
self.tk.call('pack', 'propagate', self._w, flag)
pack_propagate = propagate
def slaves(self):
return map(self._nametowidget,
self.tk.splitlist(
self.tk.call('pack', 'slaves', self._w)))
pack_slaves = slaves
class Place:
def config(self, cnf={}, **kw):
for k in ['in_']:
if kw.has_key(k):
kw[k[:-1]] = kw[k]
del kw[k]
apply(self.tk.call,
('place', 'configure', self._w)
+ self._options(cnf, kw))
configure = config
place = config
def __setitem__(self, key, value):
Place.config({key: value})
def forget(self):
self.tk.call('place', 'forget', self._w)
place_forget = forget
def info(self):
words = self.tk.splitlist(
self.tk.call('place', 'info', self._w))
dict = {}
for i in range(0, len(words), 2):
key = words[i][1:]
value = words[i+1]
if value[:1] == '.':
value = self._nametowidget(value)
dict[key] = value
return dict
place_info = info
def slaves(self):
return map(self._nametowidget,
self.tk.splitlist(
self.tk.call(
'place', 'slaves', self._w)))
place_slaves = slaves
class Grid:
# Thanks to Masazumi Yoshikawa (yosikawa@isi.edu)
def config(self, cnf={}, **kw):
apply(self.tk.call,
('grid', 'configure', self._w)
+ self._options(cnf, kw))
grid = config
def __setitem__(self, key, value):
Grid.config({key: value})
def bbox(self, column, row):
return self._getints(
self.tk.call(
'grid', 'bbox', self._w, column, row)) or None
grid_bbox = bbox
def columnconfigure(self, index, cnf={}, **kw):
if type(cnf) is not DictionaryType and not kw:
options = self._options({cnf: None})
else:
options = self._options(cnf, kw)
res = apply(self.tk.call,
('grid', 'columnconfigure', self._w, index)
+ options)
if options == ('-minsize', None):
return self.tk.getint(res) or None
elif options == ('-weight', None):
return self.tk.getdouble(res) or None
def forget(self):
self.tk.call('grid', 'forget', self._w)
grid_forget = forget
def info(self):
words = self.tk.splitlist(
self.tk.call('grid', 'info', self._w))
dict = {}
for i in range(0, len(words), 2):
key = words[i][1:]
value = words[i+1]
if value[:1] == '.':
value = self._nametowidget(value)
dict[key] = value
return dict
grid_info = info
def location(self, x, y):
return self._getints(
self.tk.call(
'grid', 'location', self._w, x, y)) or None
_noarg_ = ['_noarg_']
def propagate(self, flag=_noarg_):
if flag is Grid._noarg_:
return self._getboolean(self.tk.call(
'grid', 'propagate', self._w))
else:
self.tk.call('grid', 'propagate', self._w, flag)
grid_propagate = propagate
def rowconfigure(self, index, cnf={}, **kw):
if type(cnf) is not DictionaryType and not kw:
options = self._options({cnf: None})
else:
options = self._options(cnf, kw)
res = apply(self.tk.call,
('grid', 'rowconfigure', self._w, index)
+ options)
if options == ('-minsize', None):
return self.tk.getint(res) or None
elif options == ('-weight', None):
return self.tk.getdouble(res) or None
def size(self):
return self._getints(
self.tk.call('grid', 'size', self._w)) or None
def slaves(self, *args):
return map(self._nametowidget,
self.tk.splitlist(
apply(self.tk.call,
('grid', 'slaves', self._w) + args)))
grid_slaves = slaves
class Widget(Misc, Pack, Place, Grid):
def _setup(self, master, cnf):
global _default_root
if not master:
if not _default_root:
_default_root = Tk()
master = _default_root
if not _default_root:
_default_root = master
self.master = master
self.tk = master.tk
name = None
if cnf.has_key('name'):
name = cnf['name']
del cnf['name']
if not name:
name = `id(self)`
self._name = name
if master._w=='.':
self._w = '.' + name
else:
self._w = master._w + '.' + name
self.children = {}
if self.master.children.has_key(self._name):
self.master.children[self._name].destroy()
self.master.children[self._name] = self
def __init__(self, master, widgetName, cnf={}, kw={}, extra=()):
if kw:
cnf = _cnfmerge((cnf, kw))
self.widgetName = widgetName
Widget._setup(self, master, cnf)
classes = []
for k in cnf.keys():
if type(k) is ClassType:
classes.append((k, cnf[k]))
del cnf[k]
apply(self.tk.call,
(widgetName, self._w) + extra + self._options(cnf))
for k, v in classes:
k.config(self, v)
def config(self, cnf=None, **kw):
# XXX ought to generalize this so tag_config etc. can use it
if kw:
cnf = _cnfmerge((cnf, kw))
elif cnf:
cnf = _cnfmerge(cnf)
if cnf is None:
cnf = {}
for x in self.tk.split(
self.tk.call(self._w, 'configure')):
cnf[x[0][1:]] = (x[0][1:],) + x[1:]
return cnf
if type(cnf) is StringType:
x = self.tk.split(self.tk.call(
self._w, 'configure', '-'+cnf))
return (x[0][1:],) + x[1:]
apply(self.tk.call, (self._w, 'configure')
+ self._options(cnf))
configure = config
def cget(self, key):
return self.tk.call(self._w, 'cget', '-' + key)
__getitem__ = cget
def __setitem__(self, key, value):
Widget.config(self, {key: value})
def keys(self):
return map(lambda x: x[0][1:],
self.tk.split(self.tk.call(self._w, 'configure')))
def __str__(self):
return self._w
def destroy(self):
for c in self.children.values(): c.destroy()
if self.master.children.has_key(self._name):
del self.master.children[self._name]
self.tk.call('destroy', self._w)
Misc.destroy(self)
def _do(self, name, args=()):
return apply(self.tk.call, (self._w, name) + args)
class Toplevel(Widget, Wm):
def __init__(self, master=None, cnf={}, **kw):
if kw:
cnf = _cnfmerge((cnf, kw))
extra = ()
for wmkey in ['screen', 'class_', 'class', 'visual',
'colormap']:
if cnf.has_key(wmkey):
val = cnf[wmkey]
# TBD: a hack needed because some keys
# are not valid as keyword arguments
if wmkey[-1] == '_': opt = '-'+wmkey[:-1]
else: opt = '-'+wmkey
extra = extra + (opt, val)
del cnf[wmkey]
Widget.__init__(self, master, 'toplevel', cnf, {}, extra)
root = self._root()
self.iconname(root.iconname())
self.title(root.title())
class Button(Widget):
def __init__(self, master=None, cnf={}, **kw):
Widget.__init__(self, master, 'button', cnf, kw)
def tkButtonEnter(self, *dummy):
self.tk.call('tkButtonEnter', self._w)
def tkButtonLeave(self, *dummy):
self.tk.call('tkButtonLeave', self._w)
def tkButtonDown(self, *dummy):
self.tk.call('tkButtonDown', self._w)
def tkButtonUp(self, *dummy):
self.tk.call('tkButtonUp', self._w)
def tkButtonInvoke(self, *dummy):
self.tk.call('tkButtonInvoke', self._w)
def flash(self):
self.tk.call(self._w, 'flash')
def invoke(self):
self.tk.call(self._w, 'invoke')
# Indices:
# XXX I don't like these -- take them away
def AtEnd():
return 'end'
def AtInsert(*args):
s = 'insert'
for a in args:
if a: s = s + (' ' + a)
return s
def AtSelFirst():
return 'sel.first'
def AtSelLast():
return 'sel.last'
def At(x, y=None):
if y is None:
return '@' + `x`
else:
return '@' + `x` + ',' + `y`
class Canvas(Widget):
def __init__(self, master=None, cnf={}, **kw):
Widget.__init__(self, master, 'canvas', cnf, kw)
def addtag(self, *args):
self._do('addtag', args)
def addtag_above(self, newtag, tagOrId):
self.addtag(newtag, 'above', tagOrId)
def addtag_all(self, newtag):
self.addtag(newtag, 'all')
def addtag_below(self, newtag, tagOrId):
self.addtag(newtag, 'below', tagOrId)
def addtag_closest(self, newtag, x, y, halo=None, start=None):
self.addtag(newtag, 'closest', x, y, halo, start)
def addtag_enclosed(self, newtag, x1, y1, x2, y2):
self.addtag(newtag, 'enclosed', x1, y1, x2, y2)
def addtag_overlapping(self, newtag, x1, y1, x2, y2):
self.addtag(newtag, 'overlapping', x1, y1, x2, y2)
def addtag_withtag(self, newtag, tagOrId):
self.addtag(newtag, 'withtag', tagOrId)
def bbox(self, *args):
return self._getints(self._do('bbox', args)) or None
def tag_unbind(self, tagOrId, sequence):
self.tk.call(self._w, 'bind', tagOrId, sequence, '')
def tag_bind(self, tagOrId, sequence=None, func=None, add=None):
return self._bind((self._w, 'bind', tagOrId),
sequence, func, add)
def canvasx(self, screenx, gridspacing=None):
return self.tk.getdouble(self.tk.call(
self._w, 'canvasx', screenx, gridspacing))
def canvasy(self, screeny, gridspacing=None):
return self.tk.getdouble(self.tk.call(
self._w, 'canvasy', screeny, gridspacing))
def coords(self, *args):
return map(self.tk.getdouble,
self.tk.splitlist(self._do('coords', args)))
def _create(self, itemType, args, kw): # Args: (val, val, ..., cnf={})
args = _flatten(args)
cnf = args[-1]
if type(cnf) in (DictionaryType, TupleType):
args = args[:-1]
else:
cnf = {}
return self.tk.getint(apply(
self.tk.call,
(self._w, 'create', itemType)
+ args + self._options(cnf, kw)))
def create_arc(self, *args, **kw):
return self._create('arc', args, kw)
def create_bitmap(self, *args, **kw):
return self._create('bitmap', args, kw)
def create_image(self, *args, **kw):
return self._create('image', args, kw)
def create_line(self, *args, **kw):
return self._create('line', args, kw)
def create_oval(self, *args, **kw):
return self._create('oval', args, kw)
def create_polygon(self, *args, **kw):
return self._create('polygon', args, kw)
def create_rectangle(self, *args, **kw):
return self._create('rectangle', args, kw)
def create_text(self, *args, **kw):
return self._create('text', args, kw)
def create_window(self, *args, **kw):
return self._create('window', args, kw)
def dchars(self, *args):
self._do('dchars', args)
def delete(self, *args):
self._do('delete', args)
def dtag(self, *args):
self._do('dtag', args)
def find(self, *args):
return self._getints(self._do('find', args)) or ()
def find_above(self, tagOrId):
return self.find('above', tagOrId)
def find_all(self):
return self.find('all')
def find_below(self, tagOrId):
return self.find('below', tagOrId)
def find_closest(self, x, y, halo=None, start=None):
return self.find('closest', x, y, halo, start)
def find_enclosed(self, x1, y1, x2, y2):
return self.find('enclosed', x1, y1, x2, y2)
def find_overlapping(self, x1, y1, x2, y2):
return self.find('overlapping', x1, y1, x2, y2)
def find_withtag(self, tagOrId):
return self.find('withtag', tagOrId)
def focus(self, *args):
return self._do('focus', args)
def gettags(self, *args):
return self.tk.splitlist(self._do('gettags', args))
def icursor(self, *args):
self._do('icursor', args)
def index(self, *args):
return self.tk.getint(self._do('index', args))
def insert(self, *args):
self._do('insert', args)
def itemcget(self, tagOrId, option):
return self._do('itemcget', (tagOrId, '-'+option))
def itemconfig(self, tagOrId, cnf=None, **kw):
if cnf is None and not kw:
cnf = {}
for x in self.tk.split(
self._do('itemconfigure', (tagOrId,))):
cnf[x[0][1:]] = (x[0][1:],) + x[1:]
return cnf
if type(cnf) == StringType and not kw:
x = self.tk.split(self._do('itemconfigure',
(tagOrId, '-'+cnf,)))
return (x[0][1:],) + x[1:]
self._do('itemconfigure', (tagOrId,)
+ self._options(cnf, kw))
itemconfigure = itemconfig
def lower(self, *args):
self._do('lower', args)
def move(self, *args):
self._do('move', args)
def postscript(self, cnf={}, **kw):
return self._do('postscript', self._options(cnf, kw))
def tkraise(self, *args):
self._do('raise', args)
lift = tkraise
def scale(self, *args):
self._do('scale', args)
def scan_mark(self, x, y):
self.tk.call(self._w, 'scan', 'mark', x, y)
def scan_dragto(self, x, y):
self.tk.call(self._w, 'scan', 'dragto', x, y)
def select_adjust(self, tagOrId, index):
self.tk.call(self._w, 'select', 'adjust', tagOrId, index)
def select_clear(self):
self.tk.call(self._w, 'select', 'clear')
def select_from(self, tagOrId, index):
self.tk.call(self._w, 'select', 'set', tagOrId, index)
def select_item(self):
self.tk.call(self._w, 'select', 'item')
def select_to(self, tagOrId, index):
self.tk.call(self._w, 'select', 'to', tagOrId, index)
def type(self, tagOrId):
return self.tk.call(self._w, 'type', tagOrId) or None
def xview(self, *args):
if not args:
return self._getdoubles(self.tk.call(self._w, 'xview'))
apply(self.tk.call, (self._w, 'xview')+args)
def yview(self, *args):
if not args:
return self._getdoubles(self.tk.call(self._w, 'yview'))
apply(self.tk.call, (self._w, 'yview')+args)
class Checkbutton(Widget):
def __init__(self, master=None, cnf={}, **kw):
Widget.__init__(self, master, 'checkbutton', cnf, kw)
def deselect(self):
self.tk.call(self._w, 'deselect')
def flash(self):
self.tk.call(self._w, 'flash')
def invoke(self):
self.tk.call(self._w, 'invoke')
def select(self):
self.tk.call(self._w, 'select')
def toggle(self):
self.tk.call(self._w, 'toggle')
class Entry(Widget):
def __init__(self, master=None, cnf={}, **kw):
Widget.__init__(self, master, 'entry', cnf, kw)
def delete(self, first, last=None):
self.tk.call(self._w, 'delete', first, last)
def get(self):
return self.tk.call(self._w, 'get')
def icursor(self, index):
self.tk.call(self._w, 'icursor', index)
def index(self, index):
return self.tk.getint(self.tk.call(
self._w, 'index', index))
def insert(self, index, string):
self.tk.call(self._w, 'insert', index, string)
def scan_mark(self, x):
self.tk.call(self._w, 'scan', 'mark', x)
def scan_dragto(self, x):
self.tk.call(self._w, 'scan', 'dragto', x)
def selection_adjust(self, index):
self.tk.call(self._w, 'selection', 'adjust', index)
select_adjust = selection_adjust
def selection_clear(self):
self.tk.call(self._w, 'selection', 'clear')
select_clear = selection_clear
def selection_from(self, index):
self.tk.call(self._w, 'selection', 'from', index)
select_from = selection_from
def selection_present(self):
return self.tk.getboolean(
self.tk.call(self._w, 'selection', 'present'))
select_present = selection_present
def selection_range(self, start, end):
self.tk.call(self._w, 'selection', 'range', start, end)
select_range = selection_range
def selection_to(self, index):
self.tk.call(self._w, 'selection', 'to', index)
select_to = selection_to
def xview(self, index):
self.tk.call(self._w, 'xview', index)
def xview_moveto(self, fraction):
self.tk.call(self._w, 'xview', 'moveto', fraction)
def xview_scroll(self, number, what):
self.tk.call(self._w, 'xview', 'scroll', number, what)
class Frame(Widget):
def __init__(self, master=None, cnf={}, **kw):
cnf = _cnfmerge((cnf, kw))
extra = ()
if cnf.has_key('class_'):
extra = ('-class', cnf['class_'])
del cnf['class_']
elif cnf.has_key('class'):
extra = ('-class', cnf['class'])
del cnf['class']
Widget.__init__(self, master, 'frame', cnf, {}, extra)
class Label(Widget):
def __init__(self, master=None, cnf={}, **kw):
Widget.__init__(self, master, 'label', cnf, kw)
class Listbox(Widget):
def __init__(self, master=None, cnf={}, **kw):
Widget.__init__(self, master, 'listbox', cnf, kw)
def activate(self, index):
self.tk.call(self._w, 'activate', index)
def bbox(self, *args):
return self._getints(self._do('bbox', args)) or None
def curselection(self):
# XXX Ought to apply self._getints()...
return self.tk.splitlist(self.tk.call(
self._w, 'curselection'))
def delete(self, first, last=None):
self.tk.call(self._w, 'delete', first, last)
def get(self, first, last=None):
if last:
return self.tk.splitlist(self.tk.call(
self._w, 'get', first, last))
else:
return self.tk.call(self._w, 'get', first)
def insert(self, index, *elements):
apply(self.tk.call,
(self._w, 'insert', index) + elements)
def nearest(self, y):
return self.tk.getint(self.tk.call(
self._w, 'nearest', y))
def scan_mark(self, x, y):
self.tk.call(self._w, 'scan', 'mark', x, y)
def scan_dragto(self, x, y):
self.tk.call(self._w, 'scan', 'dragto', x, y)
def see(self, index):
self.tk.call(self._w, 'see', index)
def index(self, index):
i = self.tk.call(self._w, 'index', index)
if i == 'none': return None
return self.tk.getint(i)
def select_anchor(self, index):
self.tk.call(self._w, 'selection', 'anchor', index)
selection_anchor = select_anchor
def select_clear(self, first, last=None):
self.tk.call(self._w,
'selection', 'clear', first, last)
selection_clear = select_clear
def select_includes(self, index):
return self.tk.getboolean(self.tk.call(
self._w, 'selection', 'includes', index))
selection_includes = select_includes
def select_set(self, first, last=None):
self.tk.call(self._w, 'selection', 'set', first, last)
selection_set = select_set
def size(self):
return self.tk.getint(self.tk.call(self._w, 'size'))
def xview(self, *what):
if not what:
return self._getdoubles(self.tk.call(self._w, 'xview'))
apply(self.tk.call, (self._w, 'xview')+what)
def yview(self, *what):
if not what:
return self._getdoubles(self.tk.call(self._w, 'yview'))
apply(self.tk.call, (self._w, 'yview')+what)
class Menu(Widget):
def __init__(self, master=None, cnf={}, **kw):
Widget.__init__(self, master, 'menu', cnf, kw)
def tk_bindForTraversal(self):
pass # obsolete since Tk 4.0
def tk_mbPost(self):
self.tk.call('tk_mbPost', self._w)
def tk_mbUnpost(self):
self.tk.call('tk_mbUnpost')
def tk_traverseToMenu(self, char):
self.tk.call('tk_traverseToMenu', self._w, char)
def tk_traverseWithinMenu(self, char):
self.tk.call('tk_traverseWithinMenu', self._w, char)
def tk_getMenuButtons(self):
return self.tk.call('tk_getMenuButtons', self._w)
def tk_nextMenu(self, count):
self.tk.call('tk_nextMenu', count)
def tk_nextMenuEntry(self, count):
self.tk.call('tk_nextMenuEntry', count)
def tk_invokeMenu(self):
self.tk.call('tk_invokeMenu', self._w)
def tk_firstMenu(self):
self.tk.call('tk_firstMenu', self._w)
def tk_mbButtonDown(self):
self.tk.call('tk_mbButtonDown', self._w)
def tk_popup(self, x, y, entry=""):
self.tk.call('tk_popup', self._w, x, y, entry)
def activate(self, index):
self.tk.call(self._w, 'activate', index)
def add(self, itemType, cnf={}, **kw):
apply(self.tk.call, (self._w, 'add', itemType)
+ self._options(cnf, kw))
def add_cascade(self, cnf={}, **kw):
self.add('cascade', cnf or kw)
def add_checkbutton(self, cnf={}, **kw):
self.add('checkbutton', cnf or kw)
def add_command(self, cnf={}, **kw):
self.add('command', cnf or kw)
def add_radiobutton(self, cnf={}, **kw):
self.add('radiobutton', cnf or kw)
def add_separator(self, cnf={}, **kw):
self.add('separator', cnf or kw)
def insert(self, index, itemType, cnf={}, **kw):
apply(self.tk.call, (self._w, 'insert', index, itemType)
+ self._options(cnf, kw))
def insert_cascade(self, index, cnf={}, **kw):
self.insert(index, 'cascade', cnf or kw)
def insert_checkbutton(self, index, cnf={}, **kw):
self.insert(index, 'checkbutton', cnf or kw)
def insert_command(self, index, cnf={}, **kw):
self.insert(index, 'command', cnf or kw)
def insert_radiobutton(self, index, cnf={}, **kw):
self.insert(index, 'radiobutton', cnf or kw)
def insert_separator(self, index, cnf={}, **kw):
self.insert(index, 'separator', cnf or kw)
def delete(self, index1, index2=None):
self.tk.call(self._w, 'delete', index1, index2)
def entryconfig(self, index, cnf=None, **kw):
if cnf is None and not kw:
cnf = {}
for x in self.tk.split(apply(self.tk.call,
(self._w, 'entryconfigure', index))):
cnf[x[0][1:]] = (x[0][1:],) + x[1:]
return cnf
if type(cnf) == StringType and not kw:
x = self.tk.split(apply(self.tk.call,
(self._w, 'entryconfigure', index, '-'+cnf)))
return (x[0][1:],) + x[1:]
apply(self.tk.call, (self._w, 'entryconfigure', index)
+ self._options(cnf, kw))
entryconfigure = entryconfig
def index(self, index):
i = self.tk.call(self._w, 'index', index)
if i == 'none': return None
return self.tk.getint(i)
def invoke(self, index):
return self.tk.call(self._w, 'invoke', index)
def post(self, x, y):
self.tk.call(self._w, 'post', x, y)
def unpost(self):
self.tk.call(self._w, 'unpost')
def yposition(self, index):
return self.tk.getint(self.tk.call(
self._w, 'yposition', index))
class Menubutton(Widget):
def __init__(self, master=None, cnf={}, **kw):
Widget.__init__(self, master, 'menubutton', cnf, kw)
class Message(Widget):
def __init__(self, master=None, cnf={}, **kw):
Widget.__init__(self, master, 'message', cnf, kw)
class Radiobutton(Widget):
def __init__(self, master=None, cnf={}, **kw):
Widget.__init__(self, master, 'radiobutton', cnf, kw)
def deselect(self):
self.tk.call(self._w, 'deselect')
def flash(self):
self.tk.call(self._w, 'flash')
def invoke(self):
self.tk.call(self._w, 'invoke')
def select(self):
self.tk.call(self._w, 'select')
class Scale(Widget):
def __init__(self, master=None, cnf={}, **kw):
Widget.__init__(self, master, 'scale', cnf, kw)
def get(self):
value = self.tk.call(self._w, 'get')
try:
return self.tk.getint(value)
except TclError:
return self.tk.getdouble(value)
def set(self, value):
self.tk.call(self._w, 'set', value)
class Scrollbar(Widget):
def __init__(self, master=None, cnf={}, **kw):
Widget.__init__(self, master, 'scrollbar', cnf, kw)
def activate(self, index):
self.tk.call(self._w, 'activate', index)
def delta(self, deltax, deltay):
return self.getdouble(self.tk.call(
self._w, 'delta', deltax, deltay))
def fraction(self, x, y):
return self.getdouble(self.tk.call(
self._w, 'fraction', x, y))
def identify(self, x, y):
return self.tk.call(self._w, 'identify', x, y)
def get(self):
return self._getdoubles(self.tk.call(self._w, 'get'))
def set(self, *args):
apply(self.tk.call, (self._w, 'set')+args)
class Text(Widget):
def __init__(self, master=None, cnf={}, **kw):
Widget.__init__(self, master, 'text', cnf, kw)
def bbox(self, *args):
return self._getints(self._do('bbox', args)) or None
def tk_textSelectTo(self, index):
self.tk.call('tk_textSelectTo', self._w, index)
def tk_textBackspace(self):
self.tk.call('tk_textBackspace', self._w)
def tk_textIndexCloser(self, a, b, c):
self.tk.call('tk_textIndexCloser', self._w, a, b, c)
def tk_textResetAnchor(self, index):
self.tk.call('tk_textResetAnchor', self._w, index)
def compare(self, index1, op, index2):
return self.tk.getboolean(self.tk.call(
self._w, 'compare', index1, op, index2))
def debug(self, boolean=None):
return self.tk.getboolean(self.tk.call(
self._w, 'debug', boolean))
def delete(self, index1, index2=None):
self.tk.call(self._w, 'delete', index1, index2)
def dlineinfo(self, index):
return self._getints(self.tk.call(self._w, 'dlineinfo', index))
def get(self, index1, index2=None):
return self.tk.call(self._w, 'get', index1, index2)
def index(self, index):
return self.tk.call(self._w, 'index', index)
def insert(self, index, chars, *args):
apply(self.tk.call, (self._w, 'insert', index, chars)+args)
def mark_gravity(self, markName, direction=None):
return apply(self.tk.call,
(self._w, 'mark', 'gravity', markName, direction))
def mark_names(self):
return self.tk.splitlist(self.tk.call(
self._w, 'mark', 'names'))
def mark_set(self, markName, index):
self.tk.call(self._w, 'mark', 'set', markName, index)
def mark_unset(self, *markNames):
apply(self.tk.call, (self._w, 'mark', 'unset') + markNames)
def scan_mark(self, x, y):
self.tk.call(self._w, 'scan', 'mark', x, y)
def scan_dragto(self, x, y):
self.tk.call(self._w, 'scan', 'dragto', x, y)
def search(self, pattern, index, stopindex=None,
forwards=None, backwards=None, exact=None,
regexp=None, nocase=None, count=None):
args = [self._w, 'search']
if forwards: args.append('-forwards')
if backwards: args.append('-backwards')
if exact: args.append('-exact')
if regexp: args.append('-regexp')
if nocase: args.append('-nocase')
if count: args.append('-count'); args.append(count)
if pattern[0] == '-': args.append('--')
args.append(pattern)
args.append(index)
if stopindex: args.append(stopindex)
return apply(self.tk.call, tuple(args))
def see(self, index):
self.tk.call(self._w, 'see', index)
def tag_add(self, tagName, index1, index2=None):
self.tk.call(
self._w, 'tag', 'add', tagName, index1, index2)
def tag_unbind(self, tagName, sequence):
self.tk.call(self._w, 'tag', 'bind', tagName, sequence, '')
def tag_bind(self, tagName, sequence, func, add=None):
return self._bind((self._w, 'tag', 'bind', tagName),
sequence, func, add)
def tag_cget(self, tagName, option):
if option[:1] != '-':
option = '-' + option
if option[-1:] == '_':
option = option[:-1]
return self.tk.call(self._w, 'tag', 'cget', tagName, option)
def tag_config(self, tagName, cnf={}, **kw):
if type(cnf) == StringType:
x = self.tk.split(self.tk.call(
self._w, 'tag', 'configure', tagName, '-'+cnf))
return (x[0][1:],) + x[1:]
apply(self.tk.call,
(self._w, 'tag', 'configure', tagName)
+ self._options(cnf, kw))
tag_configure = tag_config
def tag_delete(self, *tagNames):
apply(self.tk.call, (self._w, 'tag', 'delete') + tagNames)
def tag_lower(self, tagName, belowThis=None):
self.tk.call(self._w, 'tag', 'lower', tagName, belowThis)
def tag_names(self, index=None):
return self.tk.splitlist(
self.tk.call(self._w, 'tag', 'names', index))
def tag_nextrange(self, tagName, index1, index2=None):
return self.tk.splitlist(self.tk.call(
self._w, 'tag', 'nextrange', tagName, index1, index2))
def tag_raise(self, tagName, aboveThis=None):
self.tk.call(
self._w, 'tag', 'raise', tagName, aboveThis)
def tag_ranges(self, tagName):
return self.tk.splitlist(self.tk.call(
self._w, 'tag', 'ranges', tagName))
def tag_remove(self, tagName, index1, index2=None):
self.tk.call(
self._w, 'tag', 'remove', tagName, index1, index2)
def window_cget(self, index, option):
return self.tk.call(self._w, 'window', 'cget', index, option)
def window_config(self, index, cnf={}, **kw):
if type(cnf) == StringType:
x = self.tk.split(self.tk.call(
self._w, 'window', 'configure',
index, '-'+cnf))
return (x[0][1:],) + x[1:]
apply(self.tk.call,
(self._w, 'window', 'configure', index)
+ self._options(cnf, kw))
window_configure = window_config
def window_create(self, index, cnf={}, **kw):
apply(self.tk.call,
(self._w, 'window', 'create', index)
+ self._options(cnf, kw))
def window_names(self):
return self.tk.splitlist(
self.tk.call(self._w, 'window', 'names'))
def xview(self, *what):
if not what:
return self._getdoubles(self.tk.call(self._w, 'xview'))
apply(self.tk.call, (self._w, 'xview')+what)
def yview(self, *what):
if not what:
return self._getdoubles(self.tk.call(self._w, 'yview'))
apply(self.tk.call, (self._w, 'yview')+what)
def yview_pickplace(self, *what):
apply(self.tk.call, (self._w, 'yview', '-pickplace')+what)
class _setit:
def __init__(self, var, value):
self.__value = value
self.__var = var
def __call__(self, *args):
self.__var.set(self.__value)
class OptionMenu(Menubutton):
def __init__(self, master, variable, value, *values):
kw = {"borderwidth": 2, "textvariable": variable,
"indicatoron": 1, "relief": RAISED, "anchor": "c",
"highlightthickness": 2}
Widget.__init__(self, master, "menubutton", kw)
self.widgetName = 'tk_optionMenu'
menu = self.__menu = Menu(self, name="menu", tearoff=0)
self.menuname = menu._w
menu.add_command(label=value, command=_setit(variable, value))
for v in values:
menu.add_command(label=v, command=_setit(variable, v))
self["menu"] = menu
def __getitem__(self, name):
if name == 'menu':
return self.__menu
return Widget.__getitem__(self, name)
def destroy(self):
Menubutton.destroy(self)
self.__menu = None
class Image:
def __init__(self, imgtype, name=None, cnf={}, **kw):
self.name = None
master = _default_root
if not master: raise RuntimeError, 'Too early to create image'
self.tk = master.tk
if not name:
name = `id(self)`
# The following is needed for systems where id(x)
# can return a negative number, such as Linux/m68k:
if name[0] == '-': name = '_' + name[1:]
if kw and cnf: cnf = _cnfmerge((cnf, kw))
elif kw: cnf = kw
options = ()
for k, v in cnf.items():
if callable(v):
v = self._register(v)
options = options + ('-'+k, v)
apply(self.tk.call,
('image', 'create', imgtype, name,) + options)
self.name = name
def __str__(self): return self.name
def __del__(self):
if self.name:
self.tk.call('image', 'delete', self.name)
def __setitem__(self, key, value):
self.tk.call(self.name, 'configure', '-'+key, value)
def __getitem__(self, key):
return self.tk.call(self.name, 'configure', '-'+key)
def config(self, **kw):
res = ()
for k, v in _cnfmerge(kw).items():
if v is not None:
if k[-1] == '_': k = k[:-1]
if callable(v):
v = self._register(v)
res = res + ('-'+k, v)
apply(self.tk.call, (self.name, 'config') + res)
configure = config
def height(self):
return self.tk.getint(
self.tk.call('image', 'height', self.name))
def type(self):
return self.tk.call('image', 'type', self.name)
def width(self):
return self.tk.getint(
self.tk.call('image', 'width', self.name))
class PhotoImage(Image):
def __init__(self, name=None, cnf={}, **kw):
apply(Image.__init__, (self, 'photo', name, cnf), kw)
def blank(self):
self.tk.call(self.name, 'blank')
def cget(self, option):
return self.tk.call(self.name, 'cget', '-' + option)
# XXX config
def __getitem__(self, key):
return self.tk.call(self.name, 'cget', '-' + key)
def copy(self):
destImage = PhotoImage()
self.tk.call(destImage, 'copy', self.name)
return destImage
def zoom(self,x,y=''):
destImage = PhotoImage()
if y=='': y=x
self.tk.call(destImage, 'copy', self.name, '-zoom',x,y)
return destImage
def subsample(self,x,y=''):
destImage = PhotoImage()
if y=='': y=x
self.tk.call(destImage, 'copy', self.name, '-subsample',x,y)
return destImage
def get(self, x, y):
return self.tk.call(self.name, 'get', x, y)
def put(self, data, to=None):
args = (self.name, 'put', data)
if to:
args = args + to
apply(self.tk.call, args)
# XXX read
def write(self, filename, format=None, from_coords=None):
args = (self.name, 'write', filename)
if format:
args = args + ('-format', format)
if from_coords:
args = args + ('-from',) + tuple(from_coords)
apply(self.tk.call, args)
class BitmapImage(Image):
def __init__(self, name=None, cnf={}, **kw):
apply(Image.__init__, (self, 'bitmap', name, cnf), kw)
def image_names(): return _default_root.tk.call('image', 'names')
def image_types(): return _default_root.tk.call('image', 'types')
######################################################################
# Extensions:
class Studbutton(Button):
def __init__(self, master=None, cnf={}, **kw):
Widget.__init__(self, master, 'studbutton', cnf, kw)
self.bind('<Any-Enter>', self.tkButtonEnter)
self.bind('<Any-Leave>', self.tkButtonLeave)
self.bind('<1>', self.tkButtonDown)
self.bind('<ButtonRelease-1>', self.tkButtonUp)
class Tributton(Button):
def __init__(self, master=None, cnf={}, **kw):
Widget.__init__(self, master, 'tributton', cnf, kw)
self.bind('<Any-Enter>', self.tkButtonEnter)
self.bind('<Any-Leave>', self.tkButtonLeave)
self.bind('<1>', self.tkButtonDown)
self.bind('<ButtonRelease-1>', self.tkButtonUp)
self['fg'] = self['bg']
self['activebackground'] = self['bg']
######################################################################
# Test:
def _test():
root = Tk()
label = Label(root, text="Proof-of-existence test for Tk")
label.pack()
test = Button(root, text="Click me!",
command=lambda root=root: root.test.config(
text="[%s]" % root.test['text']))
test.pack()
root.test = test
quit = Button(root, text="QUIT", command=root.destroy)
quit.pack()
root.tkraise()
root.mainloop()
if __name__ == '__main__':
_test()
# Emacs cruft
# Local Variables:
# py-indent-offset: 8
# End:
#
# Instant Python
# $Id$
#
# tk common colour chooser dialogue
#
# this module provides an interface to the native color dialogue
# available in Tk 4.2 and newer.
#
# written by Fredrik Lundh, May 1997
#
#
# options (all have default values):
#
# - initialcolor: colour to mark as selected when dialog is displayed
# (given as an RGB triplet or a Tk color string)
#
# - parent: which window to place the dialog on top of
#
# - title: dialog title
#
# FIXME: as of Tk 8.0a2, the Unix colour picker is really ugly, and
# doesn't seem to work properly on true colour displays. maybe we
# should use the instant python version instead?
from tkCommonDialog import Dialog
#
# color chooser class
class Chooser(Dialog):
"Ask for a color"
command = "tk_chooseColor"
def _fixoptions(self):
try:
# make sure initialcolor is a tk color string
color = self.options["initialcolor"]
if type(color) == type(()):
# assume an RGB triplet
self.options["initialcolor"] = "%02x%02x%02x" % color
except KeyError:
pass
def _fixresult(self, widget, result):
# to simplify application code, the color chooser returns
# an RGB tuple together with the Tk color string
if not result:
return None, None # cancelled
r, g, b = widget.winfo_rgb(result)
return (r/256, g/256, b/256), result
#
# convenience stuff
def askcolor(color = None, **options):
"Ask for a color"
return apply(Chooser, (), options).show()
# --------------------------------------------------------------------
# test stuff
if __name__ == "__main__":
print "color", askcolor()
#
# Instant Python
# $Id$
#
# base class for tk common dialogues
#
# this module provides a base class for accessing the common
# dialogues available in Tk 4.2 and newer. use tkFileDialog,
# tkColorChooser, and tkMessageBox to access the individual
# dialogs.
#
# written by Fredrik Lundh, May 1997
#
from Tkinter import *
import os
class Dialog:
command = None
def __init__(self, master=None, **options):
# FIXME: should this be placed on the module level instead?
if TkVersion < 4.2:
raise TclError, "this module requires Tk 4.2 or newer"
self.master = master
self.options = options
def _fixoptions(self):
pass # hook
def _fixresult(self, widget, result):
return result # hook
def show(self, **options):
# update instance options
for k, v in options.items():
self.options[k] = v
self._fixoptions()
# we need a dummy widget to properly process the options
# (at least as long as we use Tkinter 1.63)
w = Frame(self.master)
try:
s = apply(w.tk.call, (self.command,) + w._options(self.options))
s = self._fixresult(w, s)
finally:
try:
# get rid of the widget
w.destroy()
except:
pass
return s
#
# Instant Python
# $Id$
#
# tk common file dialogues
#
# this module provides interfaces to the native file dialogues
# available in Tk 4.2 and newer.
#
# written by Fredrik Lundh, May 1997.
#
#
# options (all have default values):
#
# - defaultextension: added to filename if not explicitly given
#
# - filetypes: sequence of (label, pattern) tuples. the same pattern
# may occur with several patterns. use "*" as pattern to indicate
# all files.
#
# - initialdir: initial directory. preserved by dialog instance.
#
# - initialfile: initial file (ignored by the open dialog). preserved
# by dialog instance.
#
# - parent: which window to place the dialog on top of
#
# - title: dialog title
#
from tkCommonDialog import Dialog
class _Dialog(Dialog):
def _fixoptions(self):
try:
# make sure "filetypes" is a tuple
self.options["filetypes"] = tuple(self.options["filetypes"])
except KeyError:
pass
def _fixresult(self, widget, result):
if result:
# keep directory and filename until next time
import os
path, file = os.path.split(result)
self.options["initialdir"] = path
self.options["initialfile"] = file
self.filename = result # compatibility
return result
#
# file dialogs
class Open(_Dialog):
"Ask for a filename to open"
command = "tk_getOpenFile"
class SaveAs(_Dialog):
"Ask for a filename to save as"
command = "tk_getSaveFile"
#
# convenience stuff
def askopenfilename(**options):
"Ask for a filename to open"
return apply(Open, (), options).show()
def asksaveasfilename(**options):
"Ask for a filename to save as"
return apply(SaveAs, (), options).show()
# FIXME: are the following two perhaps a bit too convenient?
def askopenfile(mode = "r", **options):
"Ask for a filename to open, and returned the opened file"
filename = apply(Open, (), options).show()
if filename:
return open(filename, mode)
return None
def asksaveasfile(mode = "w", **options):
"Ask for a filename to save as, and returned the opened file"
filename = apply(SaveAs, (), options).show()
if filename:
return open(filename, mode)
return None
# --------------------------------------------------------------------
# test stuff
if __name__ == "__main__":
print "open", askopenfilename(filetypes=[("all filez", "*")])
print "saveas", asksaveasfilename()
#
# Instant Python
# $Id$
#
# tk common message boxes
#
# this module provides an interface to the native message boxes
# available in Tk 4.2 and newer.
#
# written by Fredrik Lundh, May 1997
#
#
# options (all have default values):
#
# - default: which button to make default (one of the reply codes)
#
# - icon: which icon to display (see below)
#
# - message: the message to display
#
# - parent: which window to place the dialog on top of
#
# - title: dialog title
#
# - type: dialog type; that is, which buttons to display (see below)
#
from tkCommonDialog import Dialog
#
# constants
# icons
ERROR = "error"
INFO = "info"
QUESTION = "question"
WARNING = "warning"
# types
ABORTRETRYIGNORE = "abortretryignore"
OK = "ok"
OKCANCEL = "okcancel"
RETRYCANCEL = "retrycancel"
YESNO = "yesno"
YESNOCANCEL = "yesnocancel"
# replies
ABORT = "abort"
RETRY = "retry"
IGNORE = "ignore"
OK = "ok"
CANCEL = "cancel"
YES = "yes"
NO = "no"
#
# message dialog class
class Message(Dialog):
"A message box"
command = "tk_messageBox"
#
# convenience stuff
def _show(title=None, message=None, icon=None, type=None, **options):
if icon: options["icon"] = icon
if type: options["type"] = type
if title: options["title"] = title
if message: options["message"] = message
return apply(Message, (), options).show()
def showinfo(title=None, message=None, **options):
"Show an info message"
return apply(_show, (title, message, INFO, OK), options)
def showwarning(title=None, message=None, **options):
"Show a warning message"
return apply(_show, (title, message, WARNING, OK), options)
def showerror(title=None, message=None, **options):
"Show an error message"
return apply(_show, (title, message, ERROR, OK), options)
def askquestion(title=None, message=None, **options):
"Ask a question"
return apply(_show, (title, message, QUESTION, YESNO), options)
def askokcancel(title=None, message=None, **options):
"Ask if operation should proceed; return true if the answer is ok"
s = apply(_show, (title, message, QUESTION, OKCANCEL), options)
return s == OK
def askyesno(title=None, message=None, **options):
"Ask a question; return true if the answer is yes"
s = apply(_show, (title, message, QUESTION, YESNO), options)
return s == YES
def askretrycancel(title=None, message=None, **options):
"Ask if operation should be retried; return true if the answer is yes"
s = apply(_show, (title, message, WARNING, RETRYCANCEL), options)
return s == RETRY
# --------------------------------------------------------------------
# test stuff
if __name__ == "__main__":
print "info", showinfo("Spam", "Egg Information")
print "warning", showwarning("Spam", "Egg Warning")
print "error", showerror("Spam", "Egg Alert")
print "question", askquestion("Spam", "Question?")
print "proceed", askokcancel("Spam", "Proceed?")
print "yes/no", askyesno("Spam", "Got it?")
print "try again", askretrycancel("Spam", "Try again?")
#
# An Introduction to Tkinter
# tkSimpleDialog.py
#
# Copyright (c) 1997 by Fredrik Lundh
#
# fredrik@pythonware.com
# http://www.pythonware.com
#
# --------------------------------------------------------------------
# dialog base class
from Tkinter import *
import os
class Dialog(Toplevel):
def __init__(self, parent, title = None):
Toplevel.__init__(self, parent)
self.transient(parent)
if title:
self.title(title)
self.parent = parent
self.result = None
body = Frame(self)
self.initial_focus = self.body(body)
body.pack(padx=5, pady=5)
self.buttonbox()
self.grab_set()
if not self.initial_focus:
self.initial_focus = self
self.protocol("WM_DELETE_WINDOW", self.cancel)
self.geometry("+%d+%d" % (parent.winfo_rootx()+50,
parent.winfo_rooty()+50))
self.initial_focus.focus_set()
self.wait_window(self)
#
# construction hooks
def body(self, master):
# create dialog body. return widget that should have
# initial focus. this method should be overridden
pass
def buttonbox(self):
# add standard button box. override if you don't want the
# standard buttons
box = Frame(self)
w = Button(box, text="OK", width=10, command=self.ok, default=ACTIVE)
w.pack(side=LEFT, padx=5, pady=5)
w = Button(box, text="Cancel", width=10, command=self.cancel)
w.pack(side=LEFT, padx=5, pady=5)
self.bind("<Return>", self.ok)
self.bind("<Escape>", self.cancel)
box.pack()
#
# standard button semantics
def ok(self, event=None):
if not self.validate():
self.initial_focus.focus_set() # put focus back
return
self.withdraw()
self.update_idletasks()
self.apply()
self.cancel()
def cancel(self, event=None):
# put focus back to the parent window
self.parent.focus_set()
self.destroy()
#
# command hooks
def validate(self):
return 1 # override
def apply(self):
pass # override
# --------------------------------------------------------------------
# convenience dialogues
import string
class _QueryDialog(Dialog):
def __init__(self, title, prompt,
initialvalue=None,
minvalue = None, maxvalue = None,
parent = None):
from Tkinter import _default_root
if not parent:
parent = _default_root
self.prompt = prompt
self.minvalue = minvalue
self.maxvalue = maxvalue
self.initialvalue = initialvalue
Dialog.__init__(self, parent, title)
def body(self, master):
w = Label(master, text=self.prompt, justify=LEFT)
w.grid(row=0, padx=5, sticky=W)
self.entry = Entry(master, name="entry")
self.entry.grid(row=1, padx=5, sticky=W+E)
if self.initialvalue:
self.entry.insert(0, self.initialvalue)
self.entry.select_range(0, END)
return self.entry
def validate(self):
import tkMessageBox
try:
result = self.getresult()
except ValueError:
tkMessageBox.showwarning(
"Illegal value",
self.errormessage + "\nPlease try again",
parent = self
)
return 0
if self.minvalue is not None and result < self.minvalue:
tkMessageBox.showwarning(
"Too small",
"The allowed minimum value is %s. "
"Please try again." % self.minvalue,
parent = self
)
return 0
if self.maxvalue is not None and result > self.maxvalue:
tkMessageBox.showwarning(
"Too large",
"The allowed maximum value is %s. "
"Please try again." % self.maxvalue,
parent = self
)
return 0
self.result = result
return 1
class _QueryInteger(_QueryDialog):
errormessage = "Not an integer."
def getresult(self):
return string.atoi(self.entry.get())
def askinteger(title, prompt, **kw):
d = apply(_QueryInteger, (title, prompt), kw)
return d.result
class _QueryFloat(_QueryDialog):
errormessage = "Not a floating point value."
def getresult(self):
return string.atof(self.entry.get())
def askfloat(title, prompt, **kw):
d = apply(_QueryFloat, (title, prompt), kw)
return d.result
class _QueryString(_QueryDialog):
def getresult(self):
return self.entry.get()
def askstring(title, prompt, **kw):
d = apply(_QueryString, (title, prompt), kw)
return d.result
if __name__ == "__main__":
root = Tk()
root.update()
print askinteger("Spam", "Egg count", initialvalue=12*12)
print askfloat("Spam", "Egg weight\n(in tons)", minvalue=1, maxvalue=100)
print askstring("Spam", "Egg label")
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment