Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 336f281

Browse files
committed
Initial revision
1 parent 2a9096b commit 336f281

4 files changed

Lines changed: 942 additions & 0 deletions

File tree

Lib/lib-stdwin/Buttons.py

Lines changed: 397 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,397 @@
1+
# Module 'Buttons' -- see README
2+
#
3+
# Module functionality is now split in two parts:
4+
# - 'appearance' defines what it looks like
5+
# - 'reactivity' defines how it acts to mouse events
6+
7+
8+
# Import module 'rect' renamed as '_rect'
9+
#
10+
import rect
11+
_rect = rect
12+
del rect
13+
14+
15+
# Field indices in mouse event detail
16+
#
17+
_HV = 0
18+
_CLICKS = 1
19+
_BUTTON = 2
20+
_MASK = 3
21+
22+
23+
# BaseAppearance provides defaults for all appearance methods.
24+
# In fact it looks like a label.
25+
#
26+
class BaseAppearance():
27+
#
28+
# Initialization
29+
#
30+
def init_appearance(self, (win, bounds)):
31+
win.change(bounds)
32+
self.win = win
33+
self.bounds = bounds
34+
self.enabled = 1
35+
self.hilited = 0
36+
self.selected = 0
37+
self.text = ''
38+
#
39+
# Changing the parameters
40+
#
41+
def settext(self, text):
42+
self.text = text
43+
self.redraw()
44+
#
45+
def setbounds(self, bounds):
46+
# This elays drawing until after all buttons are moved
47+
self.win.change(self.bounds)
48+
self.bounds = bounds
49+
self.win.change(bounds)
50+
#
51+
# Changing the state bits
52+
#
53+
def enable(self, flag):
54+
if flag <> self.enabled:
55+
self.enabled = flag
56+
self.flipenable(self.win.begindrawing())
57+
#
58+
def hilite(self, flag):
59+
if flag <> self.hilited:
60+
self.hilited = flag
61+
self.fliphilite(self.win.begindrawing())
62+
#
63+
def select(self, flag):
64+
if flag <> self.selected:
65+
self.selected = flag
66+
self.redraw()
67+
#
68+
# Generic drawing mechanism.
69+
# There should be no reason to override redraw() or draw() methods.
70+
#
71+
def redraw(self):
72+
self.draw(self.win.begindrawing(), self.bounds)
73+
#
74+
def draw(self, (d, area)):
75+
area = _rect.intersect(area, self.bounds)
76+
if area = _rect.empty:
77+
return
78+
d.cliprect(area)
79+
d.erase(self.bounds)
80+
self.drawit(d)
81+
d.noclip()
82+
#
83+
# The drawit() method is fairly generic but may be overridden.
84+
#
85+
def drawit(self, d):
86+
self.drawpict(d) # Box, circle etc.; also 'selected'
87+
if self.text:
88+
hv = self.textpos(d)
89+
d.text(hv, self.text)
90+
if not self.enabled:
91+
self.flipenable(d)
92+
if self.hilited:
93+
self.fliphilite(d)
94+
#
95+
# Default drawing detail functions.
96+
# Overriding these is normally sufficient to get different
97+
# appearances.
98+
# No picture; centered text; enable crosses out; hilite inverts.
99+
#
100+
def drawpict(self, d):
101+
pass
102+
#
103+
def textpos(self, d):
104+
# XXX shouldn't this be done once by init/settext()?
105+
(left, top), (right, bottom) = self.bounds
106+
h = (left + right - d.textwidth(self.text)) / 2
107+
v = (top + bottom - d.lineheight()) / 2
108+
return h, v
109+
#
110+
def flipenable(self, d):
111+
_xorcross(d, self.bounds)
112+
#
113+
def fliphilite(self, d):
114+
d.invert(_rect.inset(self.bounds, (3, 3)))
115+
116+
117+
# Subroutine to cross out a rectangle.
118+
#
119+
def _xorcross(d, bounds):
120+
((left, top), (right, bottom)) = bounds
121+
left = left + 2
122+
right = right - 2
123+
top = top + 2
124+
bottom = bottom - 3
125+
d.xorline(((left, top), (right, bottom)))
126+
d.xorline((left, bottom), (right, top))
127+
128+
129+
# LabelAppearance displays a centered string.
130+
# selected --> underlined
131+
# disabled --> crossed out
132+
# hilited --> inverted
133+
#
134+
class LabelAppearance() = BaseAppearance():
135+
#
136+
def drawpict(self, d):
137+
if self.selected:
138+
# Underline it
139+
d.line((left+1, bottom-1), (right-1, bottom-1))
140+
#
141+
if not self.enabled: self._crossout(d)
142+
if self.hilited: self._invert(d)
143+
#
144+
145+
146+
# ButtonAppearance displays a centered string in a box.
147+
# selected --> bold border
148+
# disabled --> crossed out
149+
# hilited --> inverted
150+
#
151+
class ButtonAppearance() = BaseAppearance():
152+
#
153+
def drawpict(self, d):
154+
d.box(_rect.inset(self.bounds, (1, 1)))
155+
if self.selected:
156+
# Make a thicker box
157+
d.box(self.bounds)
158+
d.box(_rect.inset(self.bounds, (2, 2)))
159+
d.box(_rect.inset(self.bounds, (3, 3)))
160+
#
161+
162+
163+
# CheckAppearance displays a small square box and a left-justified string.
164+
# selected --> a cross appears in the box
165+
# disabled --> whole button crossed out
166+
# hilited --> box is inverted
167+
#
168+
class CheckAppearance() = BaseAppearance():
169+
#
170+
def drawpict(self, d):
171+
(left, top), (right, bottom) = self.bounds
172+
size = bottom - top
173+
boxbounds = (left, top), (left+size, bottom)
174+
d.box(boxbounds)
175+
if self.selected: _xorcross(d, boxbounds)
176+
#
177+
def textpos(self, d):
178+
(left, top), (right, bottom) = self.bounds
179+
size = bottom - top
180+
h = left + size + d.textwidth(' ')
181+
v = top + (size - d.lineheight()) / 2
182+
return h, v
183+
#
184+
def fliphilite(self, d):
185+
(left, top), (right, bottom) = self.bounds
186+
size = bottom - top
187+
boxbounds = (left, top), (left+size, bottom)
188+
d.invert(boxbounds)
189+
#
190+
191+
192+
# RadioAppearance displays a round indicator and a left-justified string.
193+
# selected --> a dot appears in the indicator
194+
# disabled --> whole button crossed out
195+
# hilited --> indicator is inverted
196+
#
197+
class RadioAppearance() = BaseAppearance():
198+
#
199+
def drawpict(self, d):
200+
(left, top), (right, bottom) = self.bounds
201+
size = bottom - top
202+
radius = size / 2
203+
h, v = left + radius, top + radius
204+
d.circle((h, v), radius - 1)
205+
if self.selected:
206+
some = radius/3
207+
d.paint((h-some, v-some), (h+some, v+some))
208+
#
209+
def textpos(self, d):
210+
(left, top), (right, bottom) = self.bounds
211+
size = bottom - top
212+
h = left + size + d.textwidth(' ')
213+
v = top + (size - d.lineheight()) / 2
214+
return h, v
215+
#
216+
def fliphilite(self, d):
217+
(left, top), (right, bottom) = self.bounds
218+
size = bottom - top
219+
d.invert((left, top), (left + size, bottom))
220+
#
221+
222+
223+
# NoReactivity ignores mouse and timer events.
224+
# The trigger methods call the corresponding hooks set by the user.
225+
# Hooks (and triggers) mean the following:
226+
# down_hook called on some mouse-down events
227+
# active_hook called on some mouse-move events
228+
# up_hook called on mouse-up events
229+
# on_hook called for buttons with on/off state, when it goes on
230+
# timer_hook called on timer events
231+
# hook called when a button 'fires' or a radiobutton goes on
232+
# There are usually extra conditions, e.g., hooks are only called
233+
# when the button is enabled, or active, or selected (on).
234+
#
235+
class NoReactivity():
236+
#
237+
def init_reactivity(self):
238+
self.down_hook = self.active_hook = self.up_hook = \
239+
self.on_hook = self.off_hook = self.timer_hook = \
240+
self.hook = self.active = 0
241+
#
242+
def mousetest(self, hv):
243+
return _rect.pointinrect(hv, self.bounds)
244+
#
245+
def mouse_down(self, detail):
246+
pass
247+
#
248+
def mouse_move(self, detail):
249+
pass
250+
#
251+
def mouse_up(self, detail):
252+
pass
253+
#
254+
def timer(self):
255+
pass
256+
#
257+
def down_trigger(self):
258+
if self.down_hook: self.down_hook(self)
259+
#
260+
def active_trigger(self):
261+
if self.active_hook: self.active_hook(self)
262+
#
263+
def up_trigger(self):
264+
if self.up_hook: self.up_hook(self)
265+
#
266+
def on_trigger(self):
267+
if self.on_hook: self.on_hook(self)
268+
#
269+
def off_trigger(self):
270+
if self.off_hook: self.off_hook(self)
271+
#
272+
def timer_trigger(self):
273+
if self.timer_hook: self.timer_hook(self)
274+
#
275+
def trigger(self):
276+
if self.hook: self.hook(self)
277+
278+
279+
# ToggleReactivity acts like a simple pushbutton.
280+
# It toggles its hilite state on mouse down events.
281+
# Its timer_trigger method is called for all timer events while hilited.
282+
#
283+
class ToggleReactivity() = NoReactivity():
284+
#
285+
def mouse_down(self, detail):
286+
if self.enabled and self.mousetest(detail[_HV]):
287+
self.active = 1
288+
self.hilite(not self.hilited)
289+
self.down_trigger()
290+
#
291+
def mouse_move(self, detail):
292+
if self.active:
293+
self.active_trigger()
294+
#
295+
def mouse_up(self, detail):
296+
if self.active:
297+
self.up_trigger()
298+
self.active = 0
299+
#
300+
def timer(self):
301+
if self.hilited:
302+
self.timer_trigger()
303+
#
304+
def down_trigger(self):
305+
if self.hilited:
306+
self.on_trigger()
307+
else:
308+
self.off_trigger()
309+
self.trigger()
310+
#
311+
312+
313+
# TriggerReactivity acts like a fancy pushbutton.
314+
# It hilites itself while the mouse is down within its bounds.
315+
#
316+
class TriggerReactivity() = NoReactivity():
317+
#
318+
def mouse_down(self, detail):
319+
if self.enabled and self.mousetest(detail[_HV]):
320+
self.active = 1
321+
self.hilite(1)
322+
self.down_trigger()
323+
#
324+
def mouse_move(self, detail):
325+
if self.active:
326+
self.hilite(self.mousetest(detail[_HV]))
327+
if self.hilited:
328+
self.active_trigger()
329+
#
330+
def mouse_up(self, detail):
331+
if self.active:
332+
self.hilite(self.mousetest(detail[_HV]))
333+
if self.hilited:
334+
self.up_trigger()
335+
self.trigger()
336+
self.active = 0
337+
self.hilite(0)
338+
#
339+
def timer(self):
340+
if self.active and self.hilited:
341+
self.active_trigger()
342+
#
343+
344+
345+
# CheckReactivity handles mouse events like TriggerReactivity,
346+
# It overrides the up_trigger method to flip its selected state.
347+
#
348+
class CheckReactivity() = TriggerReactivity():
349+
#
350+
def up_trigger(self):
351+
self.select(not self.selected)
352+
if self.selected:
353+
self.on_trigger()
354+
else:
355+
self.off_trigger()
356+
self.trigger()
357+
358+
359+
# RadioReactivity turns itself on and the other buttons in its group
360+
# off when its up_trigger method is called.
361+
#
362+
class RadioReactivity() = TriggerReactivity():
363+
#
364+
def init_reactivity(self):
365+
TriggerReactivity.init_reactivity(self)
366+
self.group = []
367+
#
368+
def up_trigger(self):
369+
for b in self.group:
370+
if b <> self:
371+
if b.selected:
372+
b.select(0)
373+
b.off_trigger()
374+
self.select(1)
375+
self.on_trigger()
376+
self.trigger()
377+
378+
379+
# Auxiliary class for 'define' method.
380+
#
381+
class Define():
382+
#
383+
def define(self, (win, bounds, text)):
384+
self.init_appearance(win, bounds)
385+
self.text = text
386+
self.init_reactivity()
387+
return self
388+
389+
390+
# Ready-made button classes
391+
#
392+
class BaseButton() = NoReactivity(), BaseAppearance(), Define(): pass
393+
class Label() = NoReactivity(), LabelAppearance(), Define(): pass
394+
class ClassicButton() = TriggerReactivity(), ButtonAppearance(), Define(): pass
395+
class CheckButton() = CheckReactivity(), CheckAppearance(), Define(): pass
396+
class RadioButton() = RadioReactivity(), RadioAppearance(), Define(): pass
397+
class Toggle() = ToggleReactivity(), ButtonAppearance(), Define(): pass

0 commit comments

Comments
 (0)