Files
@ 5322639d61e9
Branch filter:
Location: light9/bin/gyrocontroller
5322639d61e9
7.9 KiB
text/plain
refactoring and little fixes in curvecalc and keyboardcomposer
font change in KC, cleanup of CC's main section, maybe some little
fixes that i can't remember because darcs doesn't show the changes
at the same time i'm writing this message
font change in KC, cleanup of CC's main section, maybe some little
fixes that i can't remember because darcs doesn't show the changes
at the same time i'm writing this message
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 | #!/usr/bin/env python
# vi: syntax=python
import run_local
from light9.Submaster import Submasters, Submaster, combine_subdict
from light9.subclient import SubClient
import light9.Patch as Patch
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):
if name in self.submasters.get_all_sub_names():
return self.submasters.get_sub_by_name(name)
try:
val = int(name)
s = Submaster("#%d" % val, {val : 1.0}, temporary=True)
return s
except ValueError:
pass
try:
subnum = Patch.get_dmx_channel(name)
s = Submaster("'%s'" % name, {subnum : 1.0}, temporary=True)
return s
except ValueError:
pass
# make an error sub
return Submaster('%s!?' % 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()
|