Mercurial > code > home > repos > light9
view bin/gyrocontroller @ 271:97c08a1c4351
gyrocontroller: remap buttons, fix keep mode
Also, solo mode is now default, colors are brighter, numeric names for subs
are converted to subs with only that channel up, send zeroes when exiting
author | David McClosky <dmcc@bigasterisk.com> |
---|---|
date | Fri, 17 Jun 2005 04:23:07 +0000 |
parents | 60b6471fb0d2 |
children | 460bc5ebcaaf |
line wrap: on
line source
#!/usr/bin/env python # vi: syntax=python import run_local from light9.Submaster import Submasters, Submaster, combine_subdict from light9.subclient import SubClient import Tix as Tk # TODO: move to Utility? class circcycle: """Like itertools.cycle, but with a prev() method too. You lose all iterator benefits by using this, since it will store the whole sequence/iterator in memory. Also, do not run this on an infinite iterator, as it tuple'ifies the input.""" def __init__(self, sequence): self.sequence = tuple(sequence) self.index = None def __iter__(self): return self def change_index(self, delta): if self.index is None: if delta > 0: self.index = (-1 + delta) % len(self.sequence) elif delta < 0: self.index = delta % len(self.sequence) else: self.index = (self.index + delta) % len(self.sequence) def next(self): self.change_index(1) return self.sequence[self.index] def prev(self): self.change_index(-1) return self.sequence[self.index] class AbstractSimpleController(SubClient): """Simple controller with minimal input and output: Input is 4 directions and 3 buttons. Output is an integer and a color and maybe more. Left + B1/right + B1: prev/next sub Y-axis + B3: set current level B2: toggle keep/solo mode Triple-B2: clear kept levels""" def __init__(self, subnames): SubClient.__init__(self) self.subnames = subnames self.refresh() def get_sub(self, name): try: val = int(name) s = Submaster("Ch%d" % val, {val : 1.0}, temporary=True) return s except ValueError: return self.submasters.get_sub_by_name(name) def refresh(self): # reload subs from disk self.submasters = Submasters() self.all_subs = circcycle(self.subnames) self.current_sub = self.get_sub(self.all_subs.next()) self.current_level = 1.0 self.kept_levels = {} # subname : level [0..1] self.keep_solo_mode = 'solo' # either 'keep' or 'solo' def clear_kept_levels(self): self.kept_levels.clear() def next(self): if self.keep_solo_mode == 'keep': self.kept_levels[self.current_sub] = self.current_level self.current_sub = self.get_sub(self.all_subs.next()) def prev(self): if self.keep_solo_mode == 'keep': self.kept_levels[self.current_sub] = self.current_level self.current_sub = self.get_sub(self.all_subs.prev()) def toggle_keep_mode(self): if self.keep_solo_mode == 'keep': self.kept_levels[self.current_sub] = self.current_level self.keep_solo_mode = 'solo' else: self.keep_solo_mode = 'keep' def get_levels_as_sub(self): if self.keep_solo_mode == 'keep': # send all levels in self.kept_levels self.kept_levels[self.current_sub] = self.current_level levelsub = combine_subdict(self.kept_levels) else: levelsub = self.current_sub * self.current_level return levelsub class TkGyro(Tk.Canvas, AbstractSimpleController): def __init__(self, master, subnames): Tk.Canvas.__init__(self, master, bg='black', bd=0, highlightthickness=0, confine=None) AbstractSimpleController.__init__(self, subnames) height = int(self.winfo_screenheight()) width = int(self.winfo_screenwidth()) self.left = self.create_rectangle((0, 0, width / 2, height), tags='left', fill='black') self.right = self.create_rectangle((width / 2, 0, width, height), tags='right', fill='black') self.levelbar = self.create_rectangle(0, 0, width, 5, tags='level', fill='yellow', state='disabled', outline='') # the text is disabled so that it doesn't receive events self.modetext = self.create_text((width / 2, height / 2), font='Courier 200', fill='white', text=self.keep_solo_mode, state='disabled') self.flashtextafter = '' # current after timer for displaying text def setfill(item, color): self.itemconfig(item, fill=color) def setlevel(evt): if evt.state & 0x400 or evt.num == 3: y = (height - evt.y) / float(height) self.flash_text('<%d>' % (y * 100)) self.current_level = y self.coords(self.levelbar, 0, evt.y, width, evt.y + 5) self.send_levels() data = ((self.left, 'left', 'blue', self.prev), (self.right, 'right', 'red', self.next)) for item, tag, color, method in data: self.tag_bind(tag, '<Enter>', lambda evt, item=item, color=color: setfill(item, color)) self.tag_bind(tag, '<Leave>', lambda evt, item=item, color=color: setfill(item, 'black')) self.tag_bind(tag, '<ButtonPress-1>', lambda evt, item=item, color=color: setfill(item, 'green'), '+') self.tag_bind(tag, '<ButtonRelease-1>', lambda evt, item=item, color=color: setfill(item, color), '+') self.tag_bind(tag, '<Button-1>', lambda evt, method=method: method(), '+') # B2+drag sets current level, double-B2 resets kept levels self.tag_bind('all', '<Motion>', setlevel, '+') self.tag_bind('all', '<ButtonPress-3>', setlevel, '+') self.tag_bind('all', '<Triple-Button-2>', lambda evt: self.clear_kept_levels()) # B3 toggles between keep and solo mode self.tag_bind('all', '<Button-2>', lambda evt: self.toggle_keep_mode()) self.send_levels_loop() def toggle_keep_mode(self): AbstractSimpleController.toggle_keep_mode(self) self.show_current_mode() self.send_levels() def show_current_mode(self): if self.keep_solo_mode == 'keep': self.keep_solo_mode = 'keep' else: self.keep_solo_mode = '' self.itemconfig(self.modetext, text=self.keep_solo_mode) def clear_kept_levels(self): AbstractSimpleController.clear_kept_levels(self) self.flash_text('cleared') self.send_levels() def flash_text(self, text): self.itemconfig(self.modetext, text=text) self.after_cancel(self.flashtextafter) self.flashtextafter = self.after(2000, self.show_current_mode) def next(self): AbstractSimpleController.next(self) self.flash_text(self.current_sub.name) self.send_levels() def prev(self): AbstractSimpleController.prev(self) self.flash_text(self.current_sub.name) self.send_levels() if __name__ == "__main__": import sys, fileinput subnames = sys.argv[1:] if not subnames: subnames = [line.strip() for line in fileinput.input()] root = Tk.Tk() # these are hints to Fvwm2 if you add this to your .fvwm2rc: # Style "*NOTITLE*" NoTitle # Style "*NOBORDER*" BorderWidth 0, NoHandles # Style "*ONTOP*" StaysOnTop Sticky # hopefully, there's a better way to do this within Tk root.title("NOTITLE NOBORDER ONTOP") root.wm_geometry('%sx%s' % (root.winfo_screenwidth(), root.winfo_screenheight())) gyro = TkGyro(root, subnames) gyro.pack(fill='both', expand=1) def quit(event): gyro.send_zeroes() root.destroy() root.bind('<Key-q>', quit) root.maxsize(root.winfo_screenwidth(), root.winfo_screenheight()) Tk.mainloop()