mirror of https://github.com/python/cpython
These directories renamed: tkinter -> lib-tk, stdwin -> lib-stdwin.
This commit is contained in:
parent
045e688f6f
commit
d7500fcbb4
|
@ -1,63 +0,0 @@
|
|||
# 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
|
|
@ -1,40 +0,0 @@
|
|||
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
|
|
@ -1,411 +0,0 @@
|
|||
# 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
|
|
@ -1,69 +0,0 @@
|
|||
# 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)))
|
||||
#
|
|
@ -1,58 +0,0 @@
|
|||
# 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)))
|
||||
#
|
|
@ -1,58 +0,0 @@
|
|||
# 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
|
||||
#
|
|
@ -1,62 +0,0 @@
|
|||
# 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)
|
|
@ -1,36 +0,0 @@
|
|||
# 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
|
|
@ -1,174 +0,0 @@
|
|||
# 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)
|
|
@ -1,36 +0,0 @@
|
|||
# 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
|
|
@ -1,211 +0,0 @@
|
|||
# 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)
|
|
@ -1,70 +0,0 @@
|
|||
# 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()
|
|
@ -1,126 +0,0 @@
|
|||
# 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)
|
||||
#
|
|
@ -1,123 +0,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)
|
|
@ -1,47 +0,0 @@
|
|||
# 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()
|
|
@ -1,189 +0,0 @@
|
|||
# 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()
|
|
@ -1,62 +0,0 @@
|
|||
# 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()
|
|
@ -1,14 +0,0 @@
|
|||
# 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
|
|
@ -1,64 +0,0 @@
|
|||
# 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))
|
|
@ -1,29 +0,0 @@
|
|||
# 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
|
|
@ -1,20 +0,0 @@
|
|||
# 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
|
|
@ -1,207 +0,0 @@
|
|||
# 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)
|
|
@ -1,110 +0,0 @@
|
|||
# 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
|
|
@ -1,47 +0,0 @@
|
|||
# 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
|
|
@ -1,256 +0,0 @@
|
|||
# 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)
|
|
@ -1,89 +0,0 @@
|
|||
# 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)
|
|
@ -1,129 +0,0 @@
|
|||
# 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()
|
|
@ -1,61 +0,0 @@
|
|||
# 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)
|
|
@ -1,53 +0,0 @@
|
|||
# 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)
|
|
@ -1,236 +0,0 @@
|
|||
# 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
|
|
@ -1,117 +0,0 @@
|
|||
# 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
|
|
@ -1,316 +0,0 @@
|
|||
# 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)
|
|
@ -1,146 +0,0 @@
|
|||
# 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
|
|
@ -1,100 +0,0 @@
|
|||
# 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
|
|
@ -1,185 +0,0 @@
|
|||
# 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')
|
|
@ -1,49 +0,0 @@
|
|||
# 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()
|
|
@ -1,269 +0,0 @@
|
|||
"""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()
|
|
@ -1,37 +0,0 @@
|
|||
# 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))
|
|
@ -1,103 +0,0 @@
|
|||
"""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()
|
|
@ -1,77 +0,0 @@
|
|||
# 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'
|
File diff suppressed because it is too large
Load Diff
|
@ -1,73 +0,0 @@
|
|||
#
|
||||
# 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()
|
||||
|
|
@ -1,64 +0,0 @@
|
|||
#
|
||||
# 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
|
||||
|
|
@ -1,106 +0,0 @@
|
|||
#
|
||||
# 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()
|
|
@ -1,120 +0,0 @@
|
|||
#
|
||||
# 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?")
|
|
@ -1,220 +0,0 @@
|
|||
#
|
||||
# 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")
|
||||
|
Loading…
Reference in New Issue