diff --git a/Widgets/Fadable.py b/Widgets/Fadable.py new file mode 100644 --- /dev/null +++ b/Widgets/Fadable.py @@ -0,0 +1,143 @@ +# taken from SnackMix -- now that's reusable code +from Tix import * +import time + +class Fadable: + """Fading mixin: must mix in with a Tk widget (or something that has + 'after' at least) This is currently used by VolumeBox and MixerTk. + It's probably too specialized to be used elsewhere, but could possibly + work with an Entry or a Meter, I guess. + + var is a Tk variable that should be used to set and get the levels. + If use_fades is true, it will use fades to move between levels. + If key_bindings is true, it will install these keybindings: + + Press a number to fade to that amount (e.g. '5' = 50%). Also, + '`' (grave) will fade to 0 and '0' will fade to 100%. + + If mouse_bindings is true, the following mouse bindings will be + installed: Right clicking toggles muting. The mouse wheel will + raise or lower the volume. Shift-mouse wheeling will cause a more + precise volume adjustment. Control-mouse wheeling will cause a + longer fade.""" + def __init__(self, var, wheel_step=5, use_fades=1, key_bindings=1, + mouse_bindings=1): + self.use_fades = use_fades # whether increase and decrease should fade + self.wheel_step = wheel_step # amount that increase and descrease should + # change volume (by default) + + self.fade_start_level = 0 + self.fade_end_level = 0 + self.fade_start_time = 0 + self.fade_length = 1 + self.fade_step_time = 10 + self.fade_var = var + self.fading = 0 # whether a fade is in progress + + if key_bindings: + for k in range(1, 10): + self.bind("" % k, + lambda evt, k=k: self.fade(k / 10.0)) + self.bind("", lambda evt: self.fade(100)) + self.bind("", lambda evt: self.fade(0)) + + # up / down arrows + self.bind("", lambda evt: self.increase()) + self.bind("", lambda evt: self.decrease()) + + if mouse_bindings: + # right mouse button toggles muting + # self.bind('<3>', lambda evt: self.toggle_mute()) + # "NOT ANY MORE!" - homer + + # mouse wheel + self.bind('<4>', lambda evt: self.increase()) + self.bind('<5>', lambda evt: self.decrease()) + + # modified mouse wheel + self.bind('', lambda evt: self.increase(multiplier=0.2)) + self.bind('', lambda evt: self.decrease(multiplier=0.2)) + self.bind('', lambda evt: self.increase(length=1)) + self.bind('', lambda evt: self.decrease(length=1)) + + self.last_level = 0 # used for muting + def fade(self, value, length=0.5, step_time=10): + """Fade to value in length seconds with steps every step_time + seconds""" + self.fade_start_time = time.time() + self.fade_length = length + + self.fade_start_level = self.fade_var.get() + self.fade_end_level = value + + self.fade_step_time = step_time + if not self.fading: + self.fading = 1 + self.do_fade() + def do_fade(self): + """Actually performs the fade for Fadable.fade. Shouldn't be called + directly.""" + now = time.time() + elapsed = now - self.fade_start_time + complete = elapsed / self.fade_length + complete = min(1.0, complete) + diff = self.fade_end_level - self.fade_start_level + newlevel = (complete * diff) + self.fade_start_level + self.fade_var.set(newlevel) + if complete < 1: + self.after(self.fade_step_time, self.do_fade) + else: + self.fading = 0 + def increase(self, multiplier=1, length=0.3): + """Increases the volume by multiplier * wheel_step. If use_fades is + true, it do this as a fade over length time.""" + amount = self.wheel_step * multiplier + if self.fading: + newlevel = self.fade_end_level + amount + else: + newlevel = self.fade_var.get() + amount + newlevel = min(100, newlevel) + self.set_volume(newlevel, length) + def decrease(self, multiplier=1, length=0.3): + """Descreases the volume by multiplier * wheel_step. If use_fades + is true, it do this as a fade over length time.""" + amount = self.wheel_step * multiplier + if self.fading: + newlevel = self.fade_end_level - amount + else: + newlevel = self.fade_var.get() - amount + newlevel = max(0, newlevel) + self.set_volume(newlevel, length) + def set_volume(self, newlevel, length=0.3): + """Sets the volume to newlevel, performing a fade of length if + use_fades is true.""" + if self.use_fades: + self.fade(newlevel, length=length) + else: + self.fade_var.set(newlevel) + def toggle_mute(self): + """Toggles whether the volume is being muted.""" + curlevel = self.fade_var.get() + if curlevel: + newlevel = 0 + self.last_level = curlevel + self['bg'] = 'red' # TODO: let them choose these colors + else: + newlevel = self.last_level + self['bg'] = 'lightGray' + + self.fade_var.set(newlevel) + +if __name__ == "__main__": + class SubScale(Scale, Fadable): + def __init__(self, master, *args, **kw): + self.scale_var = DoubleVar() + kw['variable'] = self.scale_var + Scale.__init__(self, master, *args, **kw) + Fadable.__init__(self, var=self.scale_var) + + root = Tk() + root.tk_focusFollowsMouse() + ss = SubScale(root, from_=100, to_=0,) + ss.pack() + mainloop()