Skip to content
Snippets Groups Projects
Commit 8cbcf253 authored by iggy's avatar iggy
Browse files

menu: fundamentals and integration of demos; ui work; bugfixes

parent ecfca33f
No related branches found
No related tags found
No related merge requests found
......@@ -12,13 +12,11 @@ def xy_from_polar(r,deg):
) )
ctx = hardware.get_ctx()
ctx.text_align = ctx.CENTER
ctx.text_baseline = ctx.MIDDLE
popcorn = [9,7,9,5,0,5,-3,999]
synth = tinysynth(440,1)
synth.decay(25)
popcorn = [9,7,9,5,0,5,-3,999]
sequencer = None
handler = None
def on_step(data):
note = popcorn[data["step"]]
......@@ -39,8 +37,24 @@ def on_step(data):
ctx.move_to(x,y).rgb(0.5,0.5,0).text("{}".format(data["step"]))
hardware.display_update()
def handle_input(data={}):
sequencer.remove()
ev.remove()
def init():
ctx.text_align = ctx.CENTER
ctx.text_baseline = ctx.MIDDLE
event.Sequence(bpm=160, steps=8, action=on_step, loop=True)
synth = tinysynth(440,1)
synth.decay(25)
global sequencer
sequencer = event.Sequence(bpm=160, steps=8, action=on_step, loop=True)
global ev
ev=event.Event(name="sparabo",action=handle_input,
condition=lambda e: e["type"] =="button" and e["change"] and e["value"] == 2)
def run():
init();
print("run")
event.the_engine.eventloop()
......@@ -4,18 +4,6 @@ import time
import math
import event
def xy_from_polar(r,deg):
#rad = deg/180*math.pi
return( (
r * math.sin(deg), #x
r * math.cos(deg) #y
) )
def randrgb():
return ((random.random(),random.random(),random.random()))
WIDTH = 240
HEIGHT = 240
......@@ -27,22 +15,16 @@ BLUE = (0,0,1)
WHITE = (1,1,1)
GREY = (0.5,0.5,0.5)
# Get the global context (representing the whole screen)
global ctx
ctx = hardware.get_ctx()
#center the text horizontally and vertically
ctx.text_align = ctx.CENTER
ctx.text_baseline = ctx.MIDDLE
def xy_from_polar(r,deg):
#rad = deg/180*math.pi
#ctx.rgb() expects individual values for the channels, so unpack a list/tuple with *
#operations on ctx can be chained
#create a blue background
ctx.rgb(*BLUE).rectangle(-WIDTH/2,-HEIGHT/2,WIDTH,HEIGHT).fill()
return( (
r * math.sin(deg), #x
r * math.cos(deg) #y
) )
#Write some text
ctx.move_to(0,0).rgb(*WHITE).text("touch me :)")
hardware.display_update()
def randrgb():
return ((random.random(),random.random(),random.random()))
class Worm():
......@@ -101,33 +83,51 @@ class Worm():
self.mutate()
self._lastdist = dist
worms = []
for i in range(0):
worms.append(Worm())
def handle_input(data):
if data.get("type","")=="captouch" and data.get("to")==1:
print(data)
worms.append(Worm(data.get("index",0)*2*math.pi/10+math.pi ))
if len(worms)>10:
worms.pop(0)
def init(data={}):
# Get the global context (representing the whole screen)
ctx = hardware.get_ctx()
#center the text horizontally and vertically
ctx.text_align = ctx.CENTER
ctx.text_baseline = ctx.MIDDLE
#ctx.rgb() expects individual values for the channels, so unpack a list/tuple with *
#operations on ctx can be chained
#create a blue background
ctx.rgb(*BLUE).rectangle(-WIDTH/2,-HEIGHT/2,WIDTH,HEIGHT).fill()
#Write some text
ctx.move_to(0,0).rgb(*WHITE).text("touch me :)")
hardware.display_update()
global worms
worms = []
for i in range(0):
worms.append(Worm())
event.Event(name="worms_control",action=handle_input,
condition=lambda data: data.get("type","")=="captouch" and data.get("to")==1)
condition=lambda data: data.get("type","")=="captouch" and data.get("value")==1 and data["change"])
while True:
def loop(data={}):
for w in worms:
w.draw()
w.move()
hardware.display_update()
event.the_engine._eventloop_single()
time.sleep(0.001)
def run(data={}):
init()
event.the_engine.userloop = loop
event.the_engine.eventloop()
worms = []
ctx = hardware.get_ctx()
#Known problems:
#ctx.rotate(math.pi) turns the display black until powercycled
#ctx.clip() causes crashes
......
import hardware
import time
import math
import random
EVENTTYPE_TIMED = 1
......@@ -15,6 +16,8 @@ class Engine():
self.events_input = []
self.next_timed = None
self.last_input_state = None
self.userloop = None
self.is_running = False
def add(self,event):
if isinstance(event,EventTimed):
......@@ -24,8 +27,14 @@ class Engine():
def add_timed(self,event):
self.events_timed.append(event)
self.events_timed = sorted(self.events_timed, key = lambda event: event.deadline)
self._sort_timed()
def remove_timed(self,group_id):
self.events_timed = [event for event in self.events_timed if event.group_id==group_id]
self._sort_timed()
def _sort_timed(self):
self.events_timed = sorted(self.events_timed, key = lambda event: event.deadline)
def add_input(self,event):
self.events_input.append(event)
......@@ -62,11 +71,19 @@ class Engine():
diff.append({
"type" : input_state[key][1],
"index" : input_state[key][2],
"to" : input_state[key][0],
"value" : input_state[key][0],
"from" : self.last_input_state[key][0],
"ticks_ms": time.ticks_ms()
"ticks_ms": time.ticks_ms(),
"change": True
})
else:
diff.append({
"type" : input_state[key][1],
"index" : input_state[key][2],
"value" : input_state[key][0],
"ticks_ms": time.ticks_ms(),
"change": False
})
if diff:
#print(diff)
......@@ -80,19 +97,30 @@ class Engine():
self.last_input_state=input_state
def _handle_userloop(self):
if self.userloop:
self.userloop()
def _eventloop_single(self):
self._handle_timed()
self._handle_input()
self._handle_userloop()
def eventloop(self):
while True:
if self.is_running:
print ("eventloop already running")
return
else:
print("eventloop started")
self.is_running=True
while self.is_running:
self._eventloop_single()
time.sleep(0.005)
class Event():
def __init__(self,name="unknown",data={},action=None,condition=None):
def __init__(self,name="unknown",data={},action=None,condition=None,group_id=None):
#print (action)
self.name = name
self.eventtype = None
......@@ -101,18 +129,21 @@ class Event():
self.condition = condition
if not condition:
self.condition = lambda x: True
self.group_id=group_id
the_engine.add(self)
#print (data)
def trigger(self,triggerdata={}):
print ("triggered {} (with {})".format(self.name,triggerdata))
if not self.action is None:
triggerdata.update(self.data)
#print("trigger")
self.action(triggerdata)
def remove(self):
if self in the_engine.events_input:
the_engine.events_input.remove(self)
if self in the_engine.events_timed:
the_engine.events_timed.remove(self)
class EventTimed(Event):
def __init__(self,ms,name="timer", *args, **kwargs):
......@@ -129,6 +160,7 @@ class EventTimed(Event):
class Sequence():
def __init__(self,bpm=60,loop=True,steps=16,action=None):
self.group_id = random.randint(0,100000000)
self.bpm = bpm
if not action:
self.action = lambda data: print("step {}".format(data.get("step")))
......@@ -136,26 +168,13 @@ class Sequence():
self.action = action
stepsize_ms = int(60*1000/bpm)
for i in range(steps):
EventTimed(stepsize_ms*i,name="seq{}".format(i),action=self.action, data={'step':i})
EventTimed(stepsize_ms*i,name="seq{}".format(i),action=self.action, data={'step':i}, group_id=self.group_id)
if loop:
EventTimed(stepsize_ms*steps,name="loop",action=lambda data: Sequence(bpm=bpm,loop=loop,steps=steps,action=action))
EventTimed(stepsize_ms*steps,name="loop", group_id=self.group_id, action=lambda data: Sequence(bpm=bpm,loop=loop,steps=steps,action=action))
def remove(self):
the_engine.remove_timed(self.group_id)
global the_engine
the_engine = Engine()
\ No newline at end of file
EventTimed(200,name="bar",action=lambda data: print("GNANGNAGNA"))
EventTimed(100,name="foo")
Event(name="baz",
action=lambda data: print(data),
condition=lambda data: data.get('type')=="captouch"
)
#Sequence(action=axelf)
print (the_engine.events_timed)
#the_engine.eventloop()
import ui
import time
import hardware
import math
import event
menu_stack = []
active_menu = None
class Menu():
def __init__(self,name="menu",has_back=True):
self.name=name
self.items=[]
self.__index = 0
self.ui = ui.GroupRing(r=80)
self.ui.element_center = ui.Text(self.name)
self.icon = ui.Icon(label=name)
self.angle = 0
self.angle_step= 0.2
if has_back:
self.add(MenuItemBack())
def __repr__(self):
return "{} ({}): {}".format(self.name, self.__index, self.items)
def add(self, item):
self.items.append(item)
self.ui.add(item.ui)
def pop(self):
self.items.pop()
self.ui.children.pop()
def start(self):
print(self)
active_menu = self
render()
def scroll(self, n=0):
self.__index= (self.__index+n)%len(self.items)
return self.items[self.__index]
def rotate_by(self,angle):
self.rotate_to(self.angle+angle)
def rotate_to(self, angle):
self.angle = angle%(math.pi*2)
self.ui.angle_offset = self.angle
def rotate_steps(self, steps=1):
self.rotate_by(self.angle_step*steps)
def _get_hovered_index(self):
index = round(-self.angle/(math.pi*2)*len(self.items))
i = index%len(self.items)
return i
def get_hovered_item(self):
return self.items[self._get_hovered_index()]
def _get_angle_for_index(self,index):
return (math.pi*2/len(self.items)*(index)+self.angle)%(math.pi*2)
def _get_topness_for_index(self,index):
angle = self._get_angle_for_index(index)
dist = min(angle,math.pi*2-angle)
topness = 1-(dist/math.pi)
return topness
def draw(self):
hovered_index = self._get_hovered_index()
for i in range(len(self.items)):
item = self.items[i]
my_extra = abs(self._get_topness_for_index(i))*40
if i == hovered_index:
item.ui.has_highlight=True
my_extra+=20
else:
item.ui.has_highlight=False
item.ui.size=30+my_extra
self.ui.draw()
class MenuItem():
def __init__(self,name="item"):
self.name= name
self.action= None
self.ui = ui.Icon(label=name)
def __repr__(self):
return "item: {} (action: {})".format(self.name,"?")
def enter(self,data={}):
print("Enter MenuItem {}".format(self.name))
if self.action:
self.action(data)
class MenuItemSubmenu(MenuItem):
def __init__(self,submenu):
super().__init__(name=submenu.name)
self.ui = submenu.icon
self.target = submenu
def enter(self,data={}):
print("Enter Submenu {}".format(self.target.name))
menu_stack.append(active_menu)
set_active_menu(self.target)
class MenuItemBack(MenuItem):
def __init__(self):
super().__init__(name="<-")
def enter(self,data={}):
menu_back()
def on_scroll(d):
if active_menu is None:
return
if active_menu.angle_step<0.5:
active_menu.angle_step+=0.025
if d["value"] == -1:
active_menu.rotate_steps(-1)
elif d["value"] == 1:
active_menu.rotate_steps(1)
render()
def on_release(d):
if active_menu is None:
return
active_menu.angle_step = 0.2
render()
def on_enter(d):
if active_menu is None:
event.the_engine.userloop=None
menu_back()
return
else:
active_menu.get_hovered_item().enter()
event.Event(name="menu rotation",
condition=lambda e: e["type"] =="button" and not e["change"] and abs(e["value"])==1 ,
action=on_scroll
)
event.Event(name="menu rotation release",
condition=lambda e: e["type"] =="button" and e["change"] and e["value"] ==0,
action=on_release
)
event.Event(name="menu enter",
condition=lambda e: e["type"] =="button" and e["change"] and e["value"] == 2,
action=on_enter
)
def render():
print (active_menu)
if active_menu is None:
return
hardware.get_ctx().rectangle(-120,-120,240,240).rgb(0,0,0).fill()
active_menu.draw()
hardware.display_update()
def set_active_menu(menu):
global active_menu
active_menu = menu
def menu_back():
if not menu_stack:
return
previous = menu_stack.pop()
set_active_menu(previous)
\ No newline at end of file
import hardware
import random
import math
import time
class UIElement():
def __init__(self,origin=(0,0)):
self.children = []
self.origin = origin
self.ctx = hardware.get_ctx()
def draw(self, offset=(0,0)):
pos = (self.origin[0]+offset[0],self.origin[1]+offset[1])
self._draw(pos)
for child in self.children:
child.draw(pos)
def _draw(self,pos):
#print(pos)
pass
def add(self, child):
self.children.append(child)
class Text(UIElement):
def __init__(self,s="foo"):
self.s = s
super().__init__()
def _draw(self, pos):
self.ctx.text_align = self.ctx.CENTER
self.ctx.text_baseline = self.ctx.MIDDLE
self.ctx.rgb(1,1,1).move_to(pos[0],pos[1]).text(self.s)
class Icon(UIElement):
def __init__(self,label="?", size=60):
self.bg_r = random.random()
self.bg_g = random.random()
self.bg_b = random.random()
self.fg = 0
self.label=label
self.size = size
self.has_highlight = False
super().__init__()
def _draw(self,pos):
x = int(pos[0])
y = int(pos[1])
width = 55
height = 40
self.ctx.text_align = self.ctx.CENTER
self.ctx.text_baseline = self.ctx.MIDDLE
if self.has_highlight:
hs = self.size+5;
self.ctx.move_to(x,y-hs/2).rgb(1,1,1).round_rectangle(
x-hs/2,
y-hs/2,
hs,hs,hs//2
).fill()
self.ctx.move_to(x,y-self.size/2).rgb(self.bg_r,self.bg_g,self.bg_b).round_rectangle(
x-self.size/2,
y-self.size/2,
self.size,self.size,self.size//2
).fill()
self.ctx.rgb(1,1,1).move_to(x,y).text(self.label)
class GroupStackedVertical(UIElement):
pass
class GroupRing(UIElement):
def __init__(self,r=100,origin=(0,0),element_center=None):
self.r = r
self.angle_offset = 0
self.element_center=element_center
super().__init__(origin)
def draw(self, offset=(0,0)):
pos = (self.origin[0]+offset[0],self.origin[1]+offset[1])
self._draw(pos)
for index in range(len(self.children)):
child = self.children[index]
angle = 2*math.pi/len(self.children)*index+self.angle_offset
x = math.sin(angle)*self.r+pos[0]
y = -math.cos(angle)*self.r+pos[1]
child.draw(offset=(x,y))
def _draw(self,pos):
if self.element_center:
self.element_center._draw(pos)
def test():
group = UIElement((10,0))
group.add(UIElement((10,10)))
group.add(UIElement((20,20)))
#group.draw()
ring = GroupRing(r=80)
ctx = hardware.get_ctx()
for i in range(12):
ring.add(Icon(str(i)))
hardware.display_update()
while True:
ctx.rectangle(0,0,240,240).rgb(1,1,1).fill()
ring.draw()
hardware.display_update()
ring.angle_offset+=2*math.pi/60
time.sleep(0.01)
hardware.display_update()
#test()
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment