]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
*** empty log message ***
authorGuido van Rossum <guido@python.org>
Mon, 5 Nov 1990 19:44:31 +0000 (19:44 +0000)
committerGuido van Rossum <guido@python.org>
Mon, 5 Nov 1990 19:44:31 +0000 (19:44 +0000)
Lib/lib-stdwin/Buttons.py
Lib/lib-stdwin/Histogram.py
Lib/lib-stdwin/Sliders.py
Lib/lib-stdwin/StripChart.py
Lib/lib-stdwin/VUMeter.py
Lib/stdwin/Buttons.py
Lib/stdwin/Histogram.py
Lib/stdwin/Sliders.py
Lib/stdwin/StripChart.py
Lib/stdwin/VUMeter.py

index 5395f881907ccbce4f2402b4ea7541350c40f3ab..3ad56050545ffacadaad72d636f0a45fb998b124 100644 (file)
@@ -1,10 +1,8 @@
 # Module 'Buttons'
 
 
-from Resize import *
-
-
-# Import module 'rect' renamed as '_rect'
+# Import module 'rect' renamed as '_rect' to avoid exporting it on
+# 'from Buttons import *'
 #
 import rect
 _rect = rect
@@ -28,61 +26,69 @@ class LabelAppearance():
        #
        # Initialization
        #
-       def init_appearance(self, (win, bounds)):
-               self.win = win
-               self.bounds = bounds
+       def init_appearance(self):
+               self.bounds = _rect.empty
                self.enabled = 1
                self.hilited = 0
                self.selected = 0
                self.text = ''
-               self.limbo = 1
-               self.recalc()
-               self.win.change(self.bounds)
-               # While the limbo flag is set, redraw calls are ignored.
-               # It is cleared by the first draw event.
-               # This is intended to avoid duplicate drawing during
-               # initialization.
+       #
+       # Size enquiry
+       #
+       def minsize(self, m):
+               try:
+                       self.text = self.text
+               except NameError:
+                       self.text = ''
+               return m.textwidth(self.text) + 6, m.lineheight() + 6
+       #
+       def getbounds(self):
+               return self.bounds
        #
        # Changing the parameters
        #
        def settext(self, text):
                self.text = text
-               self.recalctextpos()
-               self.redraw()
+               if self.bounds <> _rect.empty:
+                       self.recalctextpos()
+                       self.redraw()
        #
        def setbounds(self, bounds):
-               # This delays drawing until after all buttons are moved
-               self.win.change(self.bounds)
+               if self.bounds <> _rect.empty:
+                       self.parent.change(self.bounds)
                self.bounds = bounds
-               self.recalc()
-               self.win.change(bounds)
+               if self.bounds <> _rect.empty:
+                       self.recalc()
+                       self.parent.change(bounds)
        #
        # Changing the state bits
        #
        def enable(self, flag):
                if flag <> self.enabled:
                        self.enabled = flag
-                       if not self.limbo:
-                               self.flipenable(self.win.begindrawing())
+                       if self.bounds <> _rect.empty:
+                               self.flipenable(self.parent.begindrawing())
        #
        def hilite(self, flag):
                if flag <> self.hilited:
                        self.hilited = flag
-                       if not self.limbo:
-                               self.fliphilite(self.win.begindrawing())
+                       if self.bounds <> _rect.empty:
+                               self.fliphilite(self.parent.begindrawing())
        #
        def select(self, flag):
                if flag <> self.selected:
                        self.selected = flag
-                       self.redraw()
+                       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):
-               self.recalcbounds()
-               self.recalctextpos()
+               if self.bounds <> _rect.empty:
+                       self.recalcbounds()
+                       self.recalctextpos()
        #
        def recalcbounds(self):
                self.hilitebounds = _rect.inset(self.bounds, (3, 3))
@@ -90,20 +96,19 @@ class LabelAppearance():
        #
        def recalctextpos(self):
                (left, top), (right, bottom) = self.bounds
-               d = self.win.begindrawing()
-               h = (left + right - d.textwidth(self.text)) / 2
-               v = (top + bottom - d.lineheight()) / 2
+               m = self.parent.beginmeasuring()
+               h = (left + right - m.textwidth(self.text)) / 2
+               v = (top + bottom - m.lineheight()) / 2
                self.textpos = h, v
        #
-       # Generic drawing mechanism.
+       # Generic drawing interface.
        # Do not override redraw() or draw() methods; override drawit() c.s.
        #
        def redraw(self):
-               if not self.limbo:
-                       self.draw(self.win.begindrawing(), self.bounds)
+               if self.bounds <> _rect.empty:
+                       self.draw(self.parent.begindrawing(), self.bounds)
        #
        def draw(self, (d, area)):
-               self.limbo = 0
                area = _rect.intersect(area, self.bounds)
                if area = _rect.empty:
                        return
@@ -161,6 +166,10 @@ class ButtonAppearance() = LabelAppearance():
 #
 class CheckAppearance() = LabelAppearance():
        #
+       def minsize(self, m):
+               width, height = m.textwidth(self.text) + 6, m.lineheight() + 6
+               return width + height + m.textwidth(' '), height
+       #
        def drawpict(self, d):
                d.box(self.boxbounds)
                if self.selected: _xorcross(d, self.boxbounds)
@@ -173,10 +182,10 @@ class CheckAppearance() = LabelAppearance():
                self.hilitebounds = self.boxbounds
        #
        def recalctextpos(self):
-               d = self.win.begindrawing()
+               m = self.parent.beginmeasuring()
                (left, top), (right, bottom) = self.boxbounds
-               h = right + d.textwidth(' ')
-               v = top + (self.size - d.lineheight()) / 2
+               h = right + m.textwidth(' ')
+               v = top + (self.size - m.lineheight()) / 2
                self.textpos = h, v
        #
 
@@ -199,24 +208,31 @@ class RadioAppearance() = CheckAppearance():
        #
 
 
-# NoReactivity ignores mouse and timer events.
+# 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
-# timer_hook   called on timer events
 # 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 NoReactivity():
+class BaseReactivity():
        #
        def init_reactivity(self):
                self.down_hook = self.move_hook = self.up_hook = \
-                 self.on_hook = self.off_hook = self.timer_hook = \
-                 self.hook = self.active = 0
+                       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)
@@ -230,9 +246,6 @@ class NoReactivity():
        def mouse_up(self, detail):
                pass
        #
-       def timer(self):
-               pass
-       #
        def down_trigger(self):
                if self.down_hook: self.down_hook(self)
        #
@@ -248,18 +261,14 @@ class NoReactivity():
        def off_trigger(self):
                if self.off_hook: self.off_hook(self)
        #
-       def timer_trigger(self):
-               if self.timer_hook: self.timer_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.
-# Its timer_trigger method is called for all timer events while hilited.
 #
-class ToggleReactivity() = NoReactivity():
+class ToggleReactivity() = BaseReactivity():
        #
        def mouse_down(self, detail):
                if self.enabled and self.mousetest(detail[_HV]):
@@ -276,10 +285,6 @@ class ToggleReactivity() = NoReactivity():
                        self.up_trigger()
                        self.active = 0
        #
-       def timer(self):
-               if self.hilited:
-                       self.timer_trigger()
-       #
        def down_trigger(self):
                if self.hilited:
                        self.on_trigger()
@@ -292,7 +297,7 @@ class ToggleReactivity() = NoReactivity():
 # TriggerReactivity acts like a fancy pushbutton.
 # It hilites itself while the mouse is down within its bounds.
 #
-class TriggerReactivity() = NoReactivity():
+class TriggerReactivity() = BaseReactivity():
        #
        def mouse_down(self, detail):
                if self.enabled and self.mousetest(detail[_HV]):
@@ -315,10 +320,6 @@ class TriggerReactivity() = NoReactivity():
                        self.active = 0
                        self.hilite(0)
        #
-       def timer(self):
-               if self.active and self.hilited:
-                       self.timer_trigger()
-       #
 
 
 # CheckReactivity handles mouse events like TriggerReactivity,
@@ -356,13 +357,22 @@ class RadioReactivity() = TriggerReactivity():
 
 
 # Auxiliary class for 'define' method.
+# Call the initializers in the right order.
 #
-class Define() = NoResize():
+class Define():
        #
-       def define(self, (win, bounds, text)):
-               self.init_appearance(win, bounds)
+       def define(self, parent):
+               self.parent = parent
+               parent.addchild(self)
+               self.init_appearance()
                self.init_reactivity()
-               self.init_resize()
+               return self
+       #
+       def destroy(self):
+               self.parent = 0
+       #
+       def definetext(self, (parent, text)):
+               self = self.define(parent)
                self.settext(text)
                return self
 
@@ -380,11 +390,10 @@ def _xorcross(d, bounds):
        d.xorline((left, bottom), (right, top))
 
 
-# Ready-made button classes
+# Ready-made button classes.
 #
-class BaseButton() = NoReactivity(), LabelAppearance(), Define(): pass
 class Label() = NoReactivity(), LabelAppearance(), Define(): pass
-class ClassicButton() = TriggerReactivity(), ButtonAppearance(), Define(): pass
+class PushButton() = TriggerReactivity(), ButtonAppearance(), Define(): pass
 class CheckButton() = CheckReactivity(), CheckAppearance(), Define(): pass
 class RadioButton() = RadioReactivity(), RadioAppearance(), Define(): pass
-class Toggle() = ToggleReactivity(), ButtonAppearance(), Define(): pass
+class ToggleButton() = ToggleReactivity(), ButtonAppearance(), Define(): pass
index f4692725f067320055bf58df64d786346eee72c9..505bbaffcdccf9e3d3b5d8dd2d294598121e3d8a 100644 (file)
@@ -1,41 +1,36 @@
 # Module 'Histogram'
 
 from Buttons import *
-from Resize import Resize
-
 
 # A Histogram displays a histogram of numeric data.
-# It reacts to resize events by resizing itself,
-# leaving the same amount of space around the borders.
-# (This is geometry management, and should really be implemented
-# by a different group of classes, but for now this hack is OK.)
 #
-class HistogramAppearance() = Resize(), LabelAppearance():
+class HistogramAppearance() = LabelAppearance(), Define():
        #
-       def define(self, (win, bounds, ydata, scale)):
-               self.init_appearance(win, bounds)
-               self.init_resize()
-               self.ydata = ydata
-               self.scale = scale # (min, max)
+       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.win.change(self.bounds)
+               self.parent.change(self.bounds)
        #
-       def drawit(self, d):
-               ydata = self.ydata
+       def drawpict(self, d):
                (left, top), (right, bottom) = self.bounds
                min, max = self.scale
                size = max-min
                width, height = right-left, bottom-top
-               for i in range(len(ydata)):
-                       h0 = left + i * width/len(ydata)
-                       h1 = left + (i+1) * width/len(ydata)
-                       v0 = top + height - (self.ydata[i]-min)*height/size
-                       v1 = top + height
+               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() = HistogramAppearance(), NoReactivity(): pass
+class Histogram() = NoReactivity(), HistogramAppearance(): pass
index b142ef4f74095fce8a2eca2ed25d3ff1bd9de3e2..f655e77b678ba30f2a4f4abcb3acf691c1f68aa6 100644 (file)
@@ -1,13 +1,11 @@
 # Module 'Sliders'
-#
-# XXX Should split caller interface, appearance and reactivity better
 
 
 import stdwin
 from stdwinevents import *
 import rect
 from Buttons import *
-from Resize import *
+from HVSplit import HSplit
 
 
 # Field indices in event detail
@@ -18,11 +16,13 @@ _BUTTON = 2
 _MASK = 3
 
 
-# A dragslider is the simplest possible slider.
+# 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() = NoReactivity():
+class DragSliderReactivity() = BaseReactivity():
        #
        def mouse_down(self, detail):
                h, v = hv = detail[_HV]
@@ -43,136 +43,133 @@ class DragSliderReactivity() = NoReactivity():
                        self.active = 0
        #
 
-class DragSliderAppearance() = NoResize(), ButtonAppearance():
-       #
-       def define(self, (win, bounds)):
-               self.min = 0
-               self.val = -1 # Changed by next setval call
-               self.max = 100
-               self.setval_hook = 0
-               self.pretext = self.postext = ''
-               self = ClassicButton.define(self, (win, bounds, ''))
-               self.setval(50)
-               return self
+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 in Python ranges, the end point belongs
+       # (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.setval_trigger()
-                       # (The trigger may change val, pretext and postext)
-                       self.settext(self.pretext + `self.val` + self.postext)
+                       self.recalctext()
+                       self.trigger()
+       #
+       def trigger(self):
+               if self.hook:
+                       self.hook(self)
        #
-       def setval_trigger(self):
-               if self.setval_hook:
-                       self.setval_hook(self)
+       def recalctext(self):
+               self.settext(self.pretext + `self.val` + self.postext)
        #
 
-class DragSlider() = DragSliderReactivity(), DragSliderAppearance(): pass
+class DragSlider() = DragSliderReactivity(), DragSliderAppearance(), Define():
+       def definetext(self, (parent, text)):
+               raise RuntimeError, 'DragSlider.definetext() not supported'
 
 
-# Auxiliary class for DragSlider incorporated in ComplexSlider
+# Auxiliary class for PushButton incorporated in ComplexSlider
 #
-class _SubDragSlider() = DragSlider():
-       def define(self, (win, bounds, parent)):
-               self.parent = parent
-               return DragSlider.define(self, (win, bounds))
-       def setval_trigger(self):
-               self.parent.val = self.val
-               self.parent.setval_trigger()
-
-# Auxiliary class for ClassicButton incorporated in ComplexSlider
-#
-class _SubClassicButton() = ClassicButton():
-       def define(self, (win, bounds, text, step, parent)):
-               self.parent = parent
+class _StepButton() = PushButton():
+       def define(self, parent):
+               self = PushButton.define(self, parent)
+               self.step = 0
+               return self
+       def setstep(self, step):
                self.step = step
-               return ClassicButton.define(self, (win, bounds, text))
+       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.parent.setval(self.parent.val + self.step)
-               self.delay = 5
-               self.win.settimer(self.delay)
-       def move_trigger(self):
-               self.win.settimer(self.delay)
-       def timer_trigger(self):
-               self.delay = 1
-               self.parent.setval(self.parent.val + self.step)
-               self.win.settimer(self.delay)
+               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 a wrapper around three buttons:
-# One to step down, a dragslider, and one to step up.
+
+# A complex slider is an HSplit initialized to three buttons:
+# one to step down, a dragslider, and one to step up.
 #
-class ComplexSlider() = NoResize(), LabelAppearance(), NoReactivity():
+class ComplexSlider() = HSplit():
        #
-       def define(self, (win, bounds)):
-               #
-               self.win = win
-               self.bounds = bounds
-               self.setval_hook = 0
-               #
-               (left, top), (right, bottom) = bounds
-               size = bottom - top
-               #
-               downbox = (left, top), (left+size, bottom)
-               sliderbox = (left+size, top), (right-size, bottom)
-               upbox = (right-size, top), (right, bottom)
-               #
-               self.downbutton = \
-                       _SubClassicButton().define(win, downbox, '-', -1, self)
-               #
-               self.sliderbutton = \
-                       _SubDragSlider().define(win, sliderbox, self)
-               #
-               self.upbutton = \
-                       _SubClassicButton().define(win, upbox, '+', 1, self)
-               #
-               self.min = self.sliderbutton.min
-               self.val = self.sliderbutton.val
-               self.max = self.sliderbutton.max
-               self.pretext = self.sliderbutton.pretext
-               self.postext = self.sliderbutton.postext
+       # Override Slider define() method
+       #
+       def define(self, parent):
+               self = self.create(parent) # HSplit
                #
-               self.children = \
-                       [self.downbutton, self.sliderbutton, self.upbutton]
+               self.downbutton = _StepButton().definetextstep(self, '-', -1)
+               self.dragbutton = DragSlider().define(self)
+               self.upbutton = _StepButton().definetextstep(self, '+', 1)
                #
                return self
        #
-       def mouse_down(self, detail):
-               for b in self.children:
-                       b.mouse_down(detail)
+       # Override HSplit methods
        #
-       def mouse_move(self, detail):
-               for b in self.children:
-                       b.mouse_move(detail)
-       #
-       def mouse_up(self, detail):
-               for b in self.children:
-                       b.mouse_up(detail)
-       #
-       def timer(self):
-               for b in self.children:
-                       b.timer()
-       #
-       def draw(self, area):
-               for b in self.children:
-                       b.draw(area)
-       #
-       def setval(self, val):
-               self.sliderbutton.min = self.min
-               self.sliderbutton.max = self.max
-               self.sliderbutton.pretext = self.pretext
-               self.sliderbutton.postext = self.postext
-               self.sliderbutton.setval(val)
-       #
-       def setval_trigger(self):
-               if self.setval_hook:
-                       self.setval_hook(self)
+       def minsize(self, m):
+               w1, h1 = self.downbutton.minsize(m)
+               w2, h2 = self.dragbutton.minsize(m)
+               w3, h3 = self.upbutton.minsize(m)
+               height = max(h1, h2, h3)
+               w1 = max(w1, height)
+               w3 = max(w3, height)
+               return w1+w2+w3, height
        #
+       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)
index 2021cc4f9d009c38247af7ab32fe1010a8f788df..ac767c3e110f94659214de56443a69f84ec88666 100644 (file)
@@ -1,17 +1,18 @@
 # Module 'StripChart'
 
-
 import rect
-from Buttons import *
-from Resize import *
+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(), NoResize():
+class StripChart() = LabelAppearance(), NoReactivity():
        #
-       def define(self, (win, bounds, scale)):
-               self.init_appearance(win, bounds)
+       def define(self, (parent, scale)):
+               self.parent = parent
+               parent.addchild(self)
+               self.init_appearance()
                self.init_reactivity()
-               self.init_resize()
                self.ydata = []
                self.scale = scale
                self.resetbounds()
@@ -37,18 +38,18 @@ class StripChart() = LabelAppearance(), NoReactivity(), NoResize():
                excess = len(self.ydata) - self.width
                if excess > 0:
                        del self.ydata[:excess]
-                       if not self.limbo:
-                               self.win.scroll(self.bounds, (-excess, 0))
-               if not self.limbo:
+                       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.win.begindrawing(), area)
+                       self.draw(self.parent.begindrawing(), area)
        #
        def draw(self, (d, area)):
-               self.limbo = 0
                area = rect.intersect(area, self.bounds)
                if area = rect.empty:
+                       print 'mt'
                        return
                d.cliprect(area)
                d.erase(self.bounds)
index 65c018b08ca643ec4d72d1f5880d5ca5d0fed433..59fc36f184eb1fae206daf0b391348dcad150859 100644 (file)
@@ -10,8 +10,9 @@ class VUMeter() = StripChart():
        #
        # Override define() and timer() methods
        #
-       def define(self, (win, bounds)):
-               self = StripChart.define(self, (win, bounds, 128))
+       def define(self, parent):
+               self = StripChart.define(self, (parent, 128))
+               self.parent.need_timer(self)
                self.sampling = 0
                self.rate = 3
                self.enable(0)
@@ -31,7 +32,7 @@ class VUMeter() = StripChart():
                        audio.start_recording(size)
                        self.sampling = 1
                if self.sampling:
-                       self.win.settimer(1)
+                       self.parent.settimer(1)
        #
        # New methods: start() and stop()
        #
index 5395f881907ccbce4f2402b4ea7541350c40f3ab..3ad56050545ffacadaad72d636f0a45fb998b124 100755 (executable)
@@ -1,10 +1,8 @@
 # Module 'Buttons'
 
 
-from Resize import *
-
-
-# Import module 'rect' renamed as '_rect'
+# Import module 'rect' renamed as '_rect' to avoid exporting it on
+# 'from Buttons import *'
 #
 import rect
 _rect = rect
@@ -28,61 +26,69 @@ class LabelAppearance():
        #
        # Initialization
        #
-       def init_appearance(self, (win, bounds)):
-               self.win = win
-               self.bounds = bounds
+       def init_appearance(self):
+               self.bounds = _rect.empty
                self.enabled = 1
                self.hilited = 0
                self.selected = 0
                self.text = ''
-               self.limbo = 1
-               self.recalc()
-               self.win.change(self.bounds)
-               # While the limbo flag is set, redraw calls are ignored.
-               # It is cleared by the first draw event.
-               # This is intended to avoid duplicate drawing during
-               # initialization.
+       #
+       # Size enquiry
+       #
+       def minsize(self, m):
+               try:
+                       self.text = self.text
+               except NameError:
+                       self.text = ''
+               return m.textwidth(self.text) + 6, m.lineheight() + 6
+       #
+       def getbounds(self):
+               return self.bounds
        #
        # Changing the parameters
        #
        def settext(self, text):
                self.text = text
-               self.recalctextpos()
-               self.redraw()
+               if self.bounds <> _rect.empty:
+                       self.recalctextpos()
+                       self.redraw()
        #
        def setbounds(self, bounds):
-               # This delays drawing until after all buttons are moved
-               self.win.change(self.bounds)
+               if self.bounds <> _rect.empty:
+                       self.parent.change(self.bounds)
                self.bounds = bounds
-               self.recalc()
-               self.win.change(bounds)
+               if self.bounds <> _rect.empty:
+                       self.recalc()
+                       self.parent.change(bounds)
        #
        # Changing the state bits
        #
        def enable(self, flag):
                if flag <> self.enabled:
                        self.enabled = flag
-                       if not self.limbo:
-                               self.flipenable(self.win.begindrawing())
+                       if self.bounds <> _rect.empty:
+                               self.flipenable(self.parent.begindrawing())
        #
        def hilite(self, flag):
                if flag <> self.hilited:
                        self.hilited = flag
-                       if not self.limbo:
-                               self.fliphilite(self.win.begindrawing())
+                       if self.bounds <> _rect.empty:
+                               self.fliphilite(self.parent.begindrawing())
        #
        def select(self, flag):
                if flag <> self.selected:
                        self.selected = flag
-                       self.redraw()
+                       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):
-               self.recalcbounds()
-               self.recalctextpos()
+               if self.bounds <> _rect.empty:
+                       self.recalcbounds()
+                       self.recalctextpos()
        #
        def recalcbounds(self):
                self.hilitebounds = _rect.inset(self.bounds, (3, 3))
@@ -90,20 +96,19 @@ class LabelAppearance():
        #
        def recalctextpos(self):
                (left, top), (right, bottom) = self.bounds
-               d = self.win.begindrawing()
-               h = (left + right - d.textwidth(self.text)) / 2
-               v = (top + bottom - d.lineheight()) / 2
+               m = self.parent.beginmeasuring()
+               h = (left + right - m.textwidth(self.text)) / 2
+               v = (top + bottom - m.lineheight()) / 2
                self.textpos = h, v
        #
-       # Generic drawing mechanism.
+       # Generic drawing interface.
        # Do not override redraw() or draw() methods; override drawit() c.s.
        #
        def redraw(self):
-               if not self.limbo:
-                       self.draw(self.win.begindrawing(), self.bounds)
+               if self.bounds <> _rect.empty:
+                       self.draw(self.parent.begindrawing(), self.bounds)
        #
        def draw(self, (d, area)):
-               self.limbo = 0
                area = _rect.intersect(area, self.bounds)
                if area = _rect.empty:
                        return
@@ -161,6 +166,10 @@ class ButtonAppearance() = LabelAppearance():
 #
 class CheckAppearance() = LabelAppearance():
        #
+       def minsize(self, m):
+               width, height = m.textwidth(self.text) + 6, m.lineheight() + 6
+               return width + height + m.textwidth(' '), height
+       #
        def drawpict(self, d):
                d.box(self.boxbounds)
                if self.selected: _xorcross(d, self.boxbounds)
@@ -173,10 +182,10 @@ class CheckAppearance() = LabelAppearance():
                self.hilitebounds = self.boxbounds
        #
        def recalctextpos(self):
-               d = self.win.begindrawing()
+               m = self.parent.beginmeasuring()
                (left, top), (right, bottom) = self.boxbounds
-               h = right + d.textwidth(' ')
-               v = top + (self.size - d.lineheight()) / 2
+               h = right + m.textwidth(' ')
+               v = top + (self.size - m.lineheight()) / 2
                self.textpos = h, v
        #
 
@@ -199,24 +208,31 @@ class RadioAppearance() = CheckAppearance():
        #
 
 
-# NoReactivity ignores mouse and timer events.
+# 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
-# timer_hook   called on timer events
 # 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 NoReactivity():
+class BaseReactivity():
        #
        def init_reactivity(self):
                self.down_hook = self.move_hook = self.up_hook = \
-                 self.on_hook = self.off_hook = self.timer_hook = \
-                 self.hook = self.active = 0
+                       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)
@@ -230,9 +246,6 @@ class NoReactivity():
        def mouse_up(self, detail):
                pass
        #
-       def timer(self):
-               pass
-       #
        def down_trigger(self):
                if self.down_hook: self.down_hook(self)
        #
@@ -248,18 +261,14 @@ class NoReactivity():
        def off_trigger(self):
                if self.off_hook: self.off_hook(self)
        #
-       def timer_trigger(self):
-               if self.timer_hook: self.timer_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.
-# Its timer_trigger method is called for all timer events while hilited.
 #
-class ToggleReactivity() = NoReactivity():
+class ToggleReactivity() = BaseReactivity():
        #
        def mouse_down(self, detail):
                if self.enabled and self.mousetest(detail[_HV]):
@@ -276,10 +285,6 @@ class ToggleReactivity() = NoReactivity():
                        self.up_trigger()
                        self.active = 0
        #
-       def timer(self):
-               if self.hilited:
-                       self.timer_trigger()
-       #
        def down_trigger(self):
                if self.hilited:
                        self.on_trigger()
@@ -292,7 +297,7 @@ class ToggleReactivity() = NoReactivity():
 # TriggerReactivity acts like a fancy pushbutton.
 # It hilites itself while the mouse is down within its bounds.
 #
-class TriggerReactivity() = NoReactivity():
+class TriggerReactivity() = BaseReactivity():
        #
        def mouse_down(self, detail):
                if self.enabled and self.mousetest(detail[_HV]):
@@ -315,10 +320,6 @@ class TriggerReactivity() = NoReactivity():
                        self.active = 0
                        self.hilite(0)
        #
-       def timer(self):
-               if self.active and self.hilited:
-                       self.timer_trigger()
-       #
 
 
 # CheckReactivity handles mouse events like TriggerReactivity,
@@ -356,13 +357,22 @@ class RadioReactivity() = TriggerReactivity():
 
 
 # Auxiliary class for 'define' method.
+# Call the initializers in the right order.
 #
-class Define() = NoResize():
+class Define():
        #
-       def define(self, (win, bounds, text)):
-               self.init_appearance(win, bounds)
+       def define(self, parent):
+               self.parent = parent
+               parent.addchild(self)
+               self.init_appearance()
                self.init_reactivity()
-               self.init_resize()
+               return self
+       #
+       def destroy(self):
+               self.parent = 0
+       #
+       def definetext(self, (parent, text)):
+               self = self.define(parent)
                self.settext(text)
                return self
 
@@ -380,11 +390,10 @@ def _xorcross(d, bounds):
        d.xorline((left, bottom), (right, top))
 
 
-# Ready-made button classes
+# Ready-made button classes.
 #
-class BaseButton() = NoReactivity(), LabelAppearance(), Define(): pass
 class Label() = NoReactivity(), LabelAppearance(), Define(): pass
-class ClassicButton() = TriggerReactivity(), ButtonAppearance(), Define(): pass
+class PushButton() = TriggerReactivity(), ButtonAppearance(), Define(): pass
 class CheckButton() = CheckReactivity(), CheckAppearance(), Define(): pass
 class RadioButton() = RadioReactivity(), RadioAppearance(), Define(): pass
-class Toggle() = ToggleReactivity(), ButtonAppearance(), Define(): pass
+class ToggleButton() = ToggleReactivity(), ButtonAppearance(), Define(): pass
index f4692725f067320055bf58df64d786346eee72c9..505bbaffcdccf9e3d3b5d8dd2d294598121e3d8a 100755 (executable)
@@ -1,41 +1,36 @@
 # Module 'Histogram'
 
 from Buttons import *
-from Resize import Resize
-
 
 # A Histogram displays a histogram of numeric data.
-# It reacts to resize events by resizing itself,
-# leaving the same amount of space around the borders.
-# (This is geometry management, and should really be implemented
-# by a different group of classes, but for now this hack is OK.)
 #
-class HistogramAppearance() = Resize(), LabelAppearance():
+class HistogramAppearance() = LabelAppearance(), Define():
        #
-       def define(self, (win, bounds, ydata, scale)):
-               self.init_appearance(win, bounds)
-               self.init_resize()
-               self.ydata = ydata
-               self.scale = scale # (min, max)
+       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.win.change(self.bounds)
+               self.parent.change(self.bounds)
        #
-       def drawit(self, d):
-               ydata = self.ydata
+       def drawpict(self, d):
                (left, top), (right, bottom) = self.bounds
                min, max = self.scale
                size = max-min
                width, height = right-left, bottom-top
-               for i in range(len(ydata)):
-                       h0 = left + i * width/len(ydata)
-                       h1 = left + (i+1) * width/len(ydata)
-                       v0 = top + height - (self.ydata[i]-min)*height/size
-                       v1 = top + height
+               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() = HistogramAppearance(), NoReactivity(): pass
+class Histogram() = NoReactivity(), HistogramAppearance(): pass
index b142ef4f74095fce8a2eca2ed25d3ff1bd9de3e2..f655e77b678ba30f2a4f4abcb3acf691c1f68aa6 100755 (executable)
@@ -1,13 +1,11 @@
 # Module 'Sliders'
-#
-# XXX Should split caller interface, appearance and reactivity better
 
 
 import stdwin
 from stdwinevents import *
 import rect
 from Buttons import *
-from Resize import *
+from HVSplit import HSplit
 
 
 # Field indices in event detail
@@ -18,11 +16,13 @@ _BUTTON = 2
 _MASK = 3
 
 
-# A dragslider is the simplest possible slider.
+# 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() = NoReactivity():
+class DragSliderReactivity() = BaseReactivity():
        #
        def mouse_down(self, detail):
                h, v = hv = detail[_HV]
@@ -43,136 +43,133 @@ class DragSliderReactivity() = NoReactivity():
                        self.active = 0
        #
 
-class DragSliderAppearance() = NoResize(), ButtonAppearance():
-       #
-       def define(self, (win, bounds)):
-               self.min = 0
-               self.val = -1 # Changed by next setval call
-               self.max = 100
-               self.setval_hook = 0
-               self.pretext = self.postext = ''
-               self = ClassicButton.define(self, (win, bounds, ''))
-               self.setval(50)
-               return self
+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 in Python ranges, the end point belongs
+       # (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.setval_trigger()
-                       # (The trigger may change val, pretext and postext)
-                       self.settext(self.pretext + `self.val` + self.postext)
+                       self.recalctext()
+                       self.trigger()
+       #
+       def trigger(self):
+               if self.hook:
+                       self.hook(self)
        #
-       def setval_trigger(self):
-               if self.setval_hook:
-                       self.setval_hook(self)
+       def recalctext(self):
+               self.settext(self.pretext + `self.val` + self.postext)
        #
 
-class DragSlider() = DragSliderReactivity(), DragSliderAppearance(): pass
+class DragSlider() = DragSliderReactivity(), DragSliderAppearance(), Define():
+       def definetext(self, (parent, text)):
+               raise RuntimeError, 'DragSlider.definetext() not supported'
 
 
-# Auxiliary class for DragSlider incorporated in ComplexSlider
+# Auxiliary class for PushButton incorporated in ComplexSlider
 #
-class _SubDragSlider() = DragSlider():
-       def define(self, (win, bounds, parent)):
-               self.parent = parent
-               return DragSlider.define(self, (win, bounds))
-       def setval_trigger(self):
-               self.parent.val = self.val
-               self.parent.setval_trigger()
-
-# Auxiliary class for ClassicButton incorporated in ComplexSlider
-#
-class _SubClassicButton() = ClassicButton():
-       def define(self, (win, bounds, text, step, parent)):
-               self.parent = parent
+class _StepButton() = PushButton():
+       def define(self, parent):
+               self = PushButton.define(self, parent)
+               self.step = 0
+               return self
+       def setstep(self, step):
                self.step = step
-               return ClassicButton.define(self, (win, bounds, text))
+       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.parent.setval(self.parent.val + self.step)
-               self.delay = 5
-               self.win.settimer(self.delay)
-       def move_trigger(self):
-               self.win.settimer(self.delay)
-       def timer_trigger(self):
-               self.delay = 1
-               self.parent.setval(self.parent.val + self.step)
-               self.win.settimer(self.delay)
+               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 a wrapper around three buttons:
-# One to step down, a dragslider, and one to step up.
+
+# A complex slider is an HSplit initialized to three buttons:
+# one to step down, a dragslider, and one to step up.
 #
-class ComplexSlider() = NoResize(), LabelAppearance(), NoReactivity():
+class ComplexSlider() = HSplit():
        #
-       def define(self, (win, bounds)):
-               #
-               self.win = win
-               self.bounds = bounds
-               self.setval_hook = 0
-               #
-               (left, top), (right, bottom) = bounds
-               size = bottom - top
-               #
-               downbox = (left, top), (left+size, bottom)
-               sliderbox = (left+size, top), (right-size, bottom)
-               upbox = (right-size, top), (right, bottom)
-               #
-               self.downbutton = \
-                       _SubClassicButton().define(win, downbox, '-', -1, self)
-               #
-               self.sliderbutton = \
-                       _SubDragSlider().define(win, sliderbox, self)
-               #
-               self.upbutton = \
-                       _SubClassicButton().define(win, upbox, '+', 1, self)
-               #
-               self.min = self.sliderbutton.min
-               self.val = self.sliderbutton.val
-               self.max = self.sliderbutton.max
-               self.pretext = self.sliderbutton.pretext
-               self.postext = self.sliderbutton.postext
+       # Override Slider define() method
+       #
+       def define(self, parent):
+               self = self.create(parent) # HSplit
                #
-               self.children = \
-                       [self.downbutton, self.sliderbutton, self.upbutton]
+               self.downbutton = _StepButton().definetextstep(self, '-', -1)
+               self.dragbutton = DragSlider().define(self)
+               self.upbutton = _StepButton().definetextstep(self, '+', 1)
                #
                return self
        #
-       def mouse_down(self, detail):
-               for b in self.children:
-                       b.mouse_down(detail)
+       # Override HSplit methods
        #
-       def mouse_move(self, detail):
-               for b in self.children:
-                       b.mouse_move(detail)
-       #
-       def mouse_up(self, detail):
-               for b in self.children:
-                       b.mouse_up(detail)
-       #
-       def timer(self):
-               for b in self.children:
-                       b.timer()
-       #
-       def draw(self, area):
-               for b in self.children:
-                       b.draw(area)
-       #
-       def setval(self, val):
-               self.sliderbutton.min = self.min
-               self.sliderbutton.max = self.max
-               self.sliderbutton.pretext = self.pretext
-               self.sliderbutton.postext = self.postext
-               self.sliderbutton.setval(val)
-       #
-       def setval_trigger(self):
-               if self.setval_hook:
-                       self.setval_hook(self)
+       def minsize(self, m):
+               w1, h1 = self.downbutton.minsize(m)
+               w2, h2 = self.dragbutton.minsize(m)
+               w3, h3 = self.upbutton.minsize(m)
+               height = max(h1, h2, h3)
+               w1 = max(w1, height)
+               w3 = max(w3, height)
+               return w1+w2+w3, height
        #
+       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)
index 2021cc4f9d009c38247af7ab32fe1010a8f788df..ac767c3e110f94659214de56443a69f84ec88666 100755 (executable)
@@ -1,17 +1,18 @@
 # Module 'StripChart'
 
-
 import rect
-from Buttons import *
-from Resize import *
+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(), NoResize():
+class StripChart() = LabelAppearance(), NoReactivity():
        #
-       def define(self, (win, bounds, scale)):
-               self.init_appearance(win, bounds)
+       def define(self, (parent, scale)):
+               self.parent = parent
+               parent.addchild(self)
+               self.init_appearance()
                self.init_reactivity()
-               self.init_resize()
                self.ydata = []
                self.scale = scale
                self.resetbounds()
@@ -37,18 +38,18 @@ class StripChart() = LabelAppearance(), NoReactivity(), NoResize():
                excess = len(self.ydata) - self.width
                if excess > 0:
                        del self.ydata[:excess]
-                       if not self.limbo:
-                               self.win.scroll(self.bounds, (-excess, 0))
-               if not self.limbo:
+                       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.win.begindrawing(), area)
+                       self.draw(self.parent.begindrawing(), area)
        #
        def draw(self, (d, area)):
-               self.limbo = 0
                area = rect.intersect(area, self.bounds)
                if area = rect.empty:
+                       print 'mt'
                        return
                d.cliprect(area)
                d.erase(self.bounds)
index 65c018b08ca643ec4d72d1f5880d5ca5d0fed433..59fc36f184eb1fae206daf0b391348dcad150859 100755 (executable)
@@ -10,8 +10,9 @@ class VUMeter() = StripChart():
        #
        # Override define() and timer() methods
        #
-       def define(self, (win, bounds)):
-               self = StripChart.define(self, (win, bounds, 128))
+       def define(self, parent):
+               self = StripChart.define(self, (parent, 128))
+               self.parent.need_timer(self)
                self.sampling = 0
                self.rate = 3
                self.enable(0)
@@ -31,7 +32,7 @@ class VUMeter() = StripChart():
                        audio.start_recording(size)
                        self.sampling = 1
                if self.sampling:
-                       self.win.settimer(1)
+                       self.parent.settimer(1)
        #
        # New methods: start() and stop()
        #