📄 buttons.py
字号:
# Module 'Buttons'# Import module 'rect' renamed as '_rect' to avoid exporting it on# 'from Buttons import *'#import rect_rect = rectdel 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): passclass Strut(NoReactivity, StrutAppearance, Define): passclass PushButton(TriggerReactivity, ButtonAppearance, Define): passclass CheckButton(CheckReactivity, CheckAppearance, Define): passclass RadioButton(RadioReactivity, RadioAppearance, Define): passclass ToggleButton(ToggleReactivity, ButtonAppearance, Define): pass
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -