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

fixes after rebase, intro of application api, debugging and fixes in ui

debugging ctx crash

ui: debugging stuff out, fixes

ui fixes
parent e164ade0
Branches
No related tags found
No related merge requests found
import ui
import event
import menu
STATE_OFF = 0
STATE_INIT = 10
STATE_BACKGROUND = 200
STATE_FOREGROUND = 300
STATE_ERROR = 500
class Application():
def __init__(self,title="badge23 app", author="someone@earth"):
self.title = title
self.author = author
self.state = STATE_OFF
self.has_background = False
self.has_foreground = True
self._events_background = []
self._events_foreground = []
self.ui = ui.Viewport()
self.engine = event.the_engine
self.add_event(event.Event(
name="exit",
action=self.exit,
condition=lambda e: e["type"]=="button" and e.get("from")==2 and e["change"]
))
self.ui.add(ui.Icon(label=self.title))
def __repr__(self):
return "App "+self.title
def init(self):
print("INIT")
self.state = STATE_INIT
print("before on_init")
self.on_init()
print("after on_init")
if self.has_background:
if self._events_background:
self._set_events(self._events_background,True)
engine.register_service_loop(self.main_always,True)
def run(self):
print ("RUN from",self.state)
if self.state == STATE_OFF:
print("go init")
self.init()
if self.has_foreground:
self._to_foreground()
elif self.has_background:
self._to_background()
print ("App {} has no foreground, running in background".format(self.title))
else:
print("App has neither foreground nor background, not doing anything")
#start the eventloop if it is not already running
event.the_engine.eventloop()
def exit(self,data={}):
print("EXIT")
self.on_exit()
if self.state == STATE_FOREGROUND:
self._to_background()
def kill(self):
#disable all events
engine.register_service_loop(self.main_always,False)
engine.register_main_loop(self.main_forground,False)
self._set_events(self._events_background,False)
self._set_events(self._events_forground,False)
self.state = STATE_OFF
def add_event(self,event,is_background=False):
if not is_background:
self._events_foreground.append(event)
else:
self._events_background.append(event)
def is_foreground(self):
return self.state == STATE_FOREGROUND
def _to_foreground(self):
print ("to foreground", self)
if not self.has_foreground:
#TODO log
return
if self._events_background:
self._set_events(self_events_background,False)
self.state = STATE_FOREGROUND
if self._events_foreground:
self._set_events(self._events_foreground,True)
#TODO should this really happen here??
menu.menu_disable()
if self.ui:
self.ui.draw()
self.on_foreground()
self.engine.register_main_loop(self.main_foreground,True)
def _to_background(self):
self.state = STATE_BACKGROUND
if self._events_foreground:
print("baz")
self._set_events(self._events_foreground,False)
self.engine.register_main_loop(self.main_foreground,False)
if self.has_background:
self.state = STATE_BACKGROUND
self.on_background()
def _set_events(self,events,enabled=True):
for e in events:
e.set_enabled(enabled)
def on_init(self):
print("nothing to init")
pass
def on_foreground(self):
pass
def on_background(self):
pass
def on_exit(self):
pass
def on_kill(self):
pass
def main_foreground(self):
#print("nothing")
pass
def main_always(self):
pass
def main(self):
self.main_foreground()
...@@ -26,3 +26,8 @@ def run(): ...@@ -26,3 +26,8 @@ def run():
def foreground(): def foreground():
pass pass
from application import Application
app = Application("cap touch")
app.main_foreground = run
...@@ -2,6 +2,7 @@ import menu ...@@ -2,6 +2,7 @@ import menu
import event import event
import hardware import hardware
import control import control
import application
import demo_worms,demo_sparabo,cap_touch_demo, melodic_demo, harmonic_demo import demo_worms,demo_sparabo,cap_touch_demo, melodic_demo, harmonic_demo
import menu_settings,menu_tinysynth import menu_settings,menu_tinysynth
...@@ -9,67 +10,14 @@ import menu_settings,menu_tinysynth ...@@ -9,67 +10,14 @@ import menu_settings,menu_tinysynth
import time import time
hardware.captouch_autocalib() hardware.captouch_autocalib()
hardware.set_global_volume_dB(0)
def start_worms(action):
menu.menu_stack.append(menu.active_menu)
menu.active_menu=None
demo_worms.run()
def start_sparabo(action):
menu.menu_stack.append(menu.active_menu)
menu.active_menu=None
demo_sparabo.run()
def start_captouch(action):
armed = False
while True:
cap_touch_demo.run()
time.sleep_ms(10)
if hardware.get_button(1) == 0:
armed = True
if armed and hardware.get_button(1) == 2:
break
def start_melodic(action):
armed = False
melodic_demo.init()
hardware.set_global_volume_dB(20)
hardware.display_fill(0)
while True:
melodic_demo.run()
time.sleep_ms(10)
if hardware.get_button(1) == 0:
armed = True
if armed and hardware.get_button(1) == 2:
break
def start_harmonic(action):
armed = False
harmonic_demo.init()
hardware.set_global_volume_dB(20)
hardware.display_fill(0)
while True:
harmonic_demo.run()
time.sleep_ms(10)
if hardware.get_button(1) == 0:
armed = True
if armed and hardware.get_button(1) == 2:
break
menu_demo = menu.Menu("demo") menu_demo = menu.Menu("demo")
item_worms = menu.MenuItem("worms")
item_worms.action = start_worms
menu_demo.add(item_worms)
item_abo = menu.MenuItem("abo")
item_abo.action = start_sparabo
menu_demo.add(item_abo)
item_cap = menu.MenuItem("captouch") for app_module in [demo_worms,demo_sparabo,cap_touch_demo,melodic_demo,harmonic_demo]:
item_cap.action = start_captouch menu_demo.add(menu.MenuItemApp(app_module.app))
menu_demo.add(item_cap)
menu_demo.add(menu.MenuItem("melodic", action=start_melodic))
menu_demo.add(menu.MenuItem("harmonic", action=start_harmonic))
testmenu = menu.Menu("test") testmenu = menu.Menu("test")
......
#python
import math
#badge23
import event import event
import hardware import hardware
from synth import tinysynth from synth import tinysynth
import math import application
import ui
def xy_from_polar(r,deg):
#rad = deg/180*math.pi
return( (
r * math.sin(deg), #x
r * math.cos(deg) #y
) )
ctx = hardware.get_ctx()
popcorn = [9,7,9,5,0,5,-3,999] popcorn = [9,7,9,5,0,5,-3,999]
synth = tinysynth(440,1)
sequencer = None
handler = None
def on_step(data): def on_step(data):
ctx = app.ui.ctx
synth = app.synth
note = popcorn[data["step"]] note = popcorn[data["step"]]
if note != 999: if note != 999:
synth.tone(note) synth.tone(note)
synth.start() synth.start()
if not app.is_foreground(): return
ctx.text_align = ctx.CENTER
ctx.text_baseline = ctx.MIDDLE
ctx.rgb(1,1,0).rectangle(-120,-120,240,240).fill() ctx.rgb(1,1,0).rectangle(-120,-120,240,240).fill()
(x,y) = xy_from_polar(90,-2*math.pi/8*data["step"]+math.pi) (x,y) = ui.xy_from_polar(90,-2*math.pi/8*data["step"]+math.pi)
size=180 size=180
ctx.rgb(0.8,0.8,0) ctx.rgb(0.8,0.8,0)
ctx.round_rectangle( ctx.round_rectangle(
...@@ -35,32 +34,25 @@ def on_step(data): ...@@ -35,32 +34,25 @@ def on_step(data):
size,size,size//2 size,size,size//2
).fill() ).fill()
ctx.move_to(x,y).rgb(0.5,0.5,0).text("{}".format(data["step"])) ctx.move_to(x,y).rgb(0.5,0.5,0).text("{}".format(data["step"]))
hardware.display_update()
def handle_input(data={}):
print("removed")
class AppSparabo(application.Application):
def on_init(self):
hardware.set_global_volume_dB(0)
sequencer.remove() self.synth = tinysynth(440,1)
ev.remove() self.synth.decay(25)
#TODO this is a bad hack! print ("here")
event.the_engine.events_timed=[] self.sequencer = event.Sequence(bpm=160, steps=8, action=on_step, loop=True)
self.sequencer.start()
if self.sequencer.repeat_event:
self.add_event(self.sequencer.repeat_event)
def on_foreground(self):
self.sequencer.start()
def init(): def on_exit(self):
ctx.text_align = ctx.CENTER self.sequencer.stop()
ctx.text_baseline = ctx.MIDDLE
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(): app = AppSparabo("sequencer")
init(); \ No newline at end of file
print("run")
event.the_engine.eventloop()
import hardware #python
import random import random
import time import time
import math import math
#badge23
import event import event
import application
import ui
def xy_from_polar(r,deg): # Subclass Application
#rad = deg/180*math.pi class AppWorms(application.Application):
return ( def on_init(self):
r * math.sin(deg), #x print("on init")
r * math.cos(deg) #y
) # TODO(q3k): factor out frame limiter
self.last_render = None
self.target_fps = 30
self.target_delta = 1000 / self.target_fps
self.frame_slack = None
self.last_report = None
self.worms = []
for i in range(0):
worms.append(Worm())
def on_foreground(self):
print("on foreground")
ctx = app.ui.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(*ui.BLUE).rectangle(-ui.WIDTH/2,-ui.HEIGHT/2,ui.WIDTH,ui.HEIGHT).fill()
#Write some text
ctx.move_to(0,0).rgb(*ui.WHITE).text("touch me :)")
def main_foreground(self):
now = time.ticks_ms()
if self.last_render is not None:
delta = now - self.last_render
if self.frame_slack is None:
self.frame_slack = self.target_delta - delta
if delta < self.target_delta:
return
if self.last_report is None or (now - self.last_report) > 1000:
fps = 1000/delta
print(f'fps: {fps:.3}, frame budget slack: {self.frame_slack:.3}ms')
self.last_report = now
def randrgb(): # Simulation is currently locked to FPS.
return ((random.random(),random.random(),random.random()))
for w in self.worms:
w.draw()
w.move()
self.last_render = now
WIDTH = 240 app = AppWorms("worms")
HEIGHT = 240
#Define a few RGB (0.0 to 1.0) colors def handle_input(data):
BLACK = (0,0,0) worms = app.worms
RED = (1,0,0) worms.append(Worm(data.get("index",0)*2*math.pi/10+math.pi ))
GREEN = (0,1,0) if len(worms)>10:
BLUE = (0,0,1) worms.pop(0)
WHITE = (1,1,1)
GREY = (0.5,0.5,0.5)
# The global context (representing the whole screen) app.add_event(event.Event(
ctx = None name="worms_control",
action=handle_input,
condition=lambda data: data.get("type","")=="captouch" and data.get("value")==1 and data["change"],
)
)
worms = [] app.add_event(event.Event(
name="worms_exit",
action=app.exit,
condition=lambda e: e["type"]=="button" and e.get("from")==2 and e["change"]
))
class Worm(): class Worm():
def __init__(self,direction=None): def __init__(self,direction=None):
self.color = randrgb() self.color = ui.randrgb()
if direction: if direction:
self.direction = direction self.direction = direction
else: else:
self.direction = random.random()*math.pi*2 self.direction = random.random()*math.pi*2
self.size = 50 self.size = 50
self.speed = self.size/5 self.speed = self.size/5
(x,y) = xy_from_polar(100, self.direction) (x,y) = ui.xy_from_polar(100, self.direction)
self.x = x self.x = x
self.y= y self.y= y
#(self.dx,self.dy) = xy_from_polar(1,self.direction) #(self.dx,self.dy) = xy_from_polar(1,self.direction)
self._lastdist = 0.0 self._lastdist = 0.0
def draw(self): def draw(self):
ctx.rgb(*self.color) app.ui.ctx.rgb(*self.color)
ctx.round_rectangle( app.ui.ctx.round_rectangle(
self.x-self.size/2, self.x-self.size/2,
self.y-self.size/2, self.y-self.size/2,
self.size,self.size,self.size//2 self.size,self.size,self.size//2
...@@ -74,7 +126,7 @@ class Worm(): ...@@ -74,7 +126,7 @@ class Worm():
self.direction += (random.random()-0.5)*math.pi/4 self.direction += (random.random()-0.5)*math.pi/4
(dx,dy) = xy_from_polar(self.speed,self.direction) (dx,dy) = ui.xy_from_polar(self.speed,self.direction)
self.x+=dx self.x+=dx
self.y+=dy self.y+=dy
...@@ -87,83 +139,10 @@ class Worm(): ...@@ -87,83 +139,10 @@ class Worm():
self.mutate() self.mutate()
self._lastdist = dist self._lastdist = dist
# To run standalone:
def handle_input(data): #app.run()
worms.append(Worm(data.get("index",0)*2*math.pi/10+math.pi )) #app.engine.eventloop()
if len(worms)>10:
worms.pop(0)
def init(data={}):
# Get the global context (representing the whole screen)
ctx = hardware.get_ctx()
# TODO(q3k): factor out frame limiter
last_render = None
target_fps = 30
target_delta = 1000 / target_fps
frame_slack = None
last_report = None
def run():
global last_render
global last_report
global frame_slack
now = time.ticks_ms()
if last_render is not None:
delta = now - last_render
if frame_slack is None:
frame_slack = target_delta - delta
if delta < target_delta:
return
if last_report is None or (now - last_report) > 1000:
fps = 1000/delta
print(f'fps: {fps:.3}, frame budget slack: {frame_slack:.3}ms')
last_report = now
# Simulation is currently locked to FPS.
global worms
for w in worms:
w.draw()
w.move()
hardware.display_update()
last_render = now
def foreground():
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("value")==1 and data["change"])
def loop(data={}):
for w in worms:
w.draw()
w.move()
hardware.display_update()
def run(data={}):
init()
event.the_engine.userloop = loop
event.the_engine.eventloop()
#Known problems: #Known problems:
#ctx.rotate(math.pi) turns the display black until powercycled #ctx.rotate(math.pi) turns the display black until powercycled
#ctx.clip() causes crashes #ctx.clip() causes crashes
\ No newline at end of file
...@@ -37,12 +37,23 @@ class Engine(): ...@@ -37,12 +37,23 @@ class Engine():
self.remove_timed(group_id) self.remove_timed(group_id)
def remove_timed(self,group_id): def remove_timed(self,group_id):
#print("before:",len(self.events_timed))
self.events_timed = [event for event in self.events_timed if event.group_id!=group_id] self.events_timed = [event for event in self.events_timed if event.group_id!=group_id]
self._sort_timed() self._sort_timed()
#print("after",len(self.events_timed))
def remove_input(self,group_id): def remove_input(self,group_id):
self.events_input = [event for event in self.events_input if event.group_id!=group_id] self.events_input = [event for event in self.events_input if event.group_id!=group_id]
def register_main_loop(self,loop,enable=True):
if enable:
#print ("new userloop",loop)
#loop()
self.userloop = loop
elif self.userloop == loop:
#print ("removed userloop")
self.userloop = None
def _sort_timed(self): def _sort_timed(self):
self.events_timed = sorted(self.events_timed, key = lambda event: event.deadline) self.events_timed = sorted(self.events_timed, key = lambda event: event.deadline)
...@@ -102,7 +113,7 @@ class Engine(): ...@@ -102,7 +113,7 @@ class Engine():
entry["change"] = False entry["change"] = False
#find and trigger the events q #find and trigger the events q
triggered_events = list(filter(lambda e: e.condition(entry),self.events_input)) triggered_events = list(filter(lambda e: e.enabled and e.condition(entry),self.events_input))
#print (triggered_events) #print (triggered_events)
#map(lambda e: e.trigger(d), triggered_events) #map(lambda e: e.trigger(d), triggered_events)
for e in triggered_events: for e in triggered_events:
...@@ -111,7 +122,8 @@ class Engine(): ...@@ -111,7 +122,8 @@ class Engine():
self.last_input_state=input_state self.last_input_state=input_state
def _handle_userloop(self): def _handle_userloop(self):
if self.userloop: if not self.userloop is None:
#print("userloop",self.userloop)
self.userloop() self.userloop()
def _eventloop_single(self): def _eventloop_single(self):
...@@ -134,17 +146,21 @@ class Engine(): ...@@ -134,17 +146,21 @@ class Engine():
time.sleep(0.005) time.sleep(0.005)
class Event(): class Event():
def __init__(self,name="unknown",data={},action=None,condition=None,group_id=None): def __init__(self,name="unknown",data={},action=None,condition=None,group_id=None,enabled=False):
#print (action) #print (action)
self.name = name self.name = name
self.eventtype = None self.eventtype = None
self.data = data self.data = data
self.action = action self.action = action
self.condition = condition self.condition = condition
self.enabled = enabled
if not condition: if not condition:
self.condition = lambda x: True self.condition = lambda x: True
self.group_id=group_id self.group_id=group_id
the_engine.add(self)
if enabled:
self.set_enabled()
def trigger(self,triggerdata={}): def trigger(self,triggerdata={}):
...@@ -153,11 +169,22 @@ class Event(): ...@@ -153,11 +169,22 @@ class Event():
triggerdata.update(self.data) triggerdata.update(self.data)
self.action(triggerdata) self.action(triggerdata)
def set_enabled(self,enabled=True):
self.enabled=enabled
if enabled:
the_engine.add(self)
else:
self.remove()
def remove(self): def remove(self):
if self in the_engine.events_input: print ("remove",self)
while self in the_engine.events_input:
print ("from input")
the_engine.events_input.remove(self) the_engine.events_input.remove(self)
if self in the_engine.events_timed: while self in the_engine.events_timed:
print("from timed")
the_engine.events_timed.remove(self) the_engine.events_timed.remove(self)
the_engine._sort_timed()
class EventTimed(Event): class EventTimed(Event):
def __init__(self,ms,name="timer", *args, **kwargs): def __init__(self,ms,name="timer", *args, **kwargs):
...@@ -172,23 +199,48 @@ class EventTimed(Event): ...@@ -172,23 +199,48 @@ class EventTimed(Event):
return ("event on tick {} ({})".format(self.deadline,self.name)) return ("event on tick {} ({})".format(self.deadline,self.name))
#hack, make this oo
def on_restart(data):
print("loop sequence")
obj = data["object"]
if obj.is_running:
obj.start()
class Sequence(): class Sequence():
def __init__(self,bpm=60,loop=True,steps=16,action=None): def __init__(self,bpm=60,loop=True,steps=16,action=None):
self.group_id = random.randint(0,100000000) self.group_id = random.randint(0,100000000)
self.bpm = bpm self.bpm = bpm
self.steps = steps
self.repeat_event = None
self.loop = loop
self.events = []
self.is_running = False
if not action: if not action:
self.action = lambda data: print("step {}".format(data.get("step"))) self.action = lambda data: print("step {}".format(data.get("step")))
else: else:
self.action = action 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}, group_id=self.group_id)
if loop: def start(self):
EventTimed(stepsize_ms*steps,name="loop", group_id=self.group_id, action=lambda data: Sequence(bpm=bpm,loop=loop,steps=steps,action=action)) if self.is_running: self.stop()
stepsize_ms = int(60*1000/self.bpm)
for i in range(self.steps):
print("adding sequence event ", i)
self.events.append(EventTimed(stepsize_ms*i,name="seq{}".format(i),action=self.action, data={'step':i}, group_id=self.group_id,enabled=True))
if self.loop:
self.repeat_event=EventTimed(stepsize_ms*self.steps,name="loop", group_id=self.group_id, enabled=True, action=on_restart, data={"object":self})
self.is_running=True
def stop(self):
#for e in self.events: e.remove()
print("sequence stop")
the_engine.remove_timed(group_id=self.group_id)
self.events = []
if self.repeat_event: self.repeat_event.remove()
self.is_running=False
def remove(self):
the_engine.remove_timed(self.group_id)
global the_engine global the_engine
the_engine = Engine() the_engine = Engine()
\ No newline at end of file
...@@ -58,3 +58,19 @@ def foreground(): ...@@ -58,3 +58,19 @@ def foreground():
tmp = chord_index tmp = chord_index
chord_index = -1 chord_index = -1
set_chord(tmp) set_chord(tmp)
from application import Application
class HarmonicApp(Application):
def on_init(self):
init()
#foreground()
def on_foreground(self):
#foreground()
pass
def main_foreground(self):
print ("hererer")
run()
app=HarmonicApp("harmonic")
\ No newline at end of file
...@@ -65,3 +65,17 @@ def init(): ...@@ -65,3 +65,17 @@ def init():
def foreground(): def foreground():
adjust_playing_field_to_octave() adjust_playing_field_to_octave()
from application import Application
class MelodicApp(Application):
def on_init(self):
init()
def on_foreground(self):
foreground()
def main_foreground(self):
run()
app=MelodicApp("melodic")
\ No newline at end of file
...@@ -100,6 +100,15 @@ class MenuItem(): ...@@ -100,6 +100,15 @@ class MenuItem():
if self.action: if self.action:
self.action(data) self.action(data)
class MenuItemApp(MenuItem):
def __init__(self,app):
super().__init__(name=app.title)
self.target = app
def enter(self,data={}):
if self.target:
self.target.run()
class MenuItemSubmenu(MenuItem): class MenuItemSubmenu(MenuItem):
def __init__(self,submenu): def __init__(self,submenu):
super().__init__(name=submenu.name) super().__init__(name=submenu.name)
...@@ -168,37 +177,45 @@ def on_release(d): ...@@ -168,37 +177,45 @@ def on_release(d):
def on_enter(d): def on_enter(d):
if active_menu is None: if active_menu is None:
#TODO this should not bee needed...
event.the_engine.userloop=None event.the_engine.userloop=None
menu_back() menu_back()
return return
else:
if active_menu:
active_menu.get_hovered_item().enter() active_menu.get_hovered_item().enter()
render()
else:
return
event.Event(name="menu rotation button",group_id="menu", event.Event(name="menu rotation button",group_id="menu",
condition=lambda e: e["type"] =="button" and not e["change"] and abs(e["value"])==1, condition=lambda e: e["type"] =="button" and not e["change"] and abs(e["value"])==1,
action=on_scroll action=on_scroll, enabled=True
) )
event.Event(name="menu rotation captouch",group_id="menu", event.Event(name="menu rotation captouch",group_id="menu",
condition=lambda e: e["type"] =="captouch" and not e["change"] and abs(e["value"])==1 and e["index"]==2, condition=lambda e: e["type"] =="captouch" and not e["change"] and abs(e["value"])==1 and e["index"]==2,
action=on_scroll_captouch action=on_scroll_captouch, enabled=False
) )
event.Event(name="menu rotation button release",group_id="menu", event.Event(name="menu rotation button release",group_id="menu",
condition=lambda e: e["type"] =="button" and e["change"] and e["value"] ==0, condition=lambda e: e["type"] =="button" and e["change"] and e["value"] ==0,
action=on_release action=on_release, enabled=True
) )
event.Event(name="menu button enter",group_id="menu", event.Event(name="menu button enter",group_id="menu",
condition=lambda e: e["type"] =="button" and e["change"] and e["value"] == 2, condition=lambda e: e["type"] =="button" and e["change"] and e["from"] == 2,
action=on_enter action=on_enter, enabled=True
) )
def render(): def render():
print (active_menu) print (active_menu)
if active_menu is None: if active_menu is None:
return return
hardware.get_ctx().rectangle(-120,-120,240,240).rgb(0,0,0).fill()
ui.the_ctx.rectangle(-120,-120,240,240).rgb(0,0,0).fill()
active_menu.draw() active_menu.draw()
#hardware.display_update() #hardware.display_update()
...@@ -206,9 +223,18 @@ def set_active_menu(menu): ...@@ -206,9 +223,18 @@ def set_active_menu(menu):
global active_menu global active_menu
active_menu = menu active_menu = menu
def menu_disable():
global active_menu
if active_menu:
menu_stack.append(active_menu)
active_menu=None
def menu_back(): def menu_back():
if not menu_stack: if not menu_stack:
return return
previous = menu_stack.pop() previous = menu_stack.pop()
set_active_menu(previous) set_active_menu(previous)
render()
\ No newline at end of file
...@@ -4,30 +4,78 @@ import math ...@@ -4,30 +4,78 @@ import math
import time import time
from math import sin,cos,pi from math import sin,cos,pi
import gc
WIDTH = 240
HEIGHT = 240
#Define a few RGB (0.0 to 1.0) colors
BLACK = (0,0,0)
RED = (1,0,0)
GREEN = (0,1,0)
BLUE = (0,0,1)
WHITE = (1,1,1)
GREY = (0.5,0.5,0.5)
GO_GREEN = (63/255,255/255,33/53) GO_GREEN = (63/255,255/255,33/53)
PUSH_RED = (251/255,72/255,196/255) PUSH_RED = (251/255,72/255,196/255)
the_ctx = hardware.get_ctx()
# Utility functions
def xy_from_polar(r,deg):
#rad = deg/180*math.pi
return (
r * math.sin(deg), #x
r * math.cos(deg) #y
)
#def ctx_circle(self, x,y, radius, arc_from = -math.pi, arc_to = math.pi):
# return self.arc(x,y,radius,arc_from,arc_to,True)
#the_ctx.circle = ctx_circle
def randrgb():
return ((random.random(),random.random(),random.random()))
class UIElement(): class UIElement():
def __init__(self,origin=(0,0)): def __init__(self,origin=(0,0)):
self.children = [] self.children = []
self.origin = origin self.origin = origin
self.ctx = hardware.get_ctx() self.ctx = the_ctx
def draw(self, offset=(0,0)): def draw(self, offset=(0,0)):
pos = (self.origin[0]+offset[0],self.origin[1]+offset[1]) pos = (self.origin[0]+offset[0],self.origin[1]+offset[1])
self._draw(pos)
for child in self.children: for child in self.children:
child.draw(pos) child.draw(pos)
self._draw(pos)
def _draw(self,pos): def _draw(self,pos):
pass pass
def add(self, child): def add(self, child):
self.children.append(child) self.children.append(child)
class Viewport(UIElement):
def _draw(self,pos):
self.ctx.rgb(0.3,0.3,0.3).rectangle(-WIDTH/2,-HEIGHT/2,WIDTH,HEIGHT).fill()
class Circle(UIElement):
def __init__(self,radius,color=PUSH_RED,arc_from=-math.pi, arc_to=math.pi, *args, **kwargs):
self.radius = radius
self.color = color
self.arc_from = arc_from
self.arc_to = arc_to
super().__init__()
def _draw(self,pos):
(x,y)=pos
self.ctx.move_to(x,y).rgb(*self.color).arc(x,y,self.radius,self.arc_from,self.arc_to,True).fill()
class Text(UIElement): class Text(UIElement):
def __init__(self,s="foo"): def __init__(self,s="foo"):
self.s = s self.s = s
...@@ -40,9 +88,7 @@ class Text(UIElement): ...@@ -40,9 +88,7 @@ class Text(UIElement):
class Icon(UIElement): class Icon(UIElement):
def __init__(self,label="?", size=60): def __init__(self,label="?", size=60):
self.bg_r = random.random() self.bg = (random.random(),random.random(),random.random())
self.bg_g = random.random()
self.bg_b = random.random()
self.fg = 0 self.fg = 0
self.label=label self.label=label
self.size=size self.size=size
...@@ -50,8 +96,8 @@ class Icon(UIElement): ...@@ -50,8 +96,8 @@ class Icon(UIElement):
super().__init__() super().__init__()
def _draw(self,pos): def _draw(self,pos):
x = int(pos[0]) #print("ui.Icon._draw()")
y = int(pos[1]) (x,y) = pos
self.ctx.text_align = self.ctx.CENTER self.ctx.text_align = self.ctx.CENTER
self.ctx.text_baseline = self.ctx.MIDDLE self.ctx.text_baseline = self.ctx.MIDDLE
...@@ -63,15 +109,23 @@ class Icon(UIElement): ...@@ -63,15 +109,23 @@ class Icon(UIElement):
y-hs/2, y-hs/2,
hs,hs,hs//2 hs,hs,hs//2
).fill() ).fill()
self.ctx.move_to(x,y).rgb(self.bg_r,self.bg_g,self.bg_b).arc(x,y,self.size/2,-math.pi,math.pi,True).fill()
#self.ctx.move_to(x,y-self.size/2).rgb(self.bg_r,self.bg_g,self.bg_b). self.ctx.move_to(x,y).rgb(*self.bg).arc(x,y,self.size/2,-math.pi,math.pi,True).fill()
#.round_rectangle( #self.ctx.move_to(x,y).rgb(self.bg_r,self.bg_g,self.bg_b).circle(x,y,self.size/2).fill()
# 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) self.ctx.rgb(1,1,1).move_to(x,y).text(self.label)
class IconFlower(Icon):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.petal_size=size/2
self.petal_count=5
def _draw(self,pos):
(x,y)=pos
class IconValue(Icon): class IconValue(Icon):
def __init__(self, value=0, *args, **kwargs): def __init__(self, value=0, *args, **kwargs):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
...@@ -89,6 +143,7 @@ class IconValue(Icon): ...@@ -89,6 +143,7 @@ class IconValue(Icon):
class GroupStackedVertical(UIElement): class GroupStackedVertical(UIElement):
pass pass
...@@ -104,10 +159,12 @@ class GroupRing(UIElement): ...@@ -104,10 +159,12 @@ class GroupRing(UIElement):
pos = (self.origin[0]+offset[0],self.origin[1]+offset[1]) pos = (self.origin[0]+offset[0],self.origin[1]+offset[1])
self._draw(pos) self._draw(pos)
for index in range(len(self.children)): for index in range(len(self.children)):
#print("child",index)
child = self.children[index] child = self.children[index]
angle = 2*math.pi/len(self.children)*index+self.angle_offset angle = 2*math.pi/len(self.children)*index+self.angle_offset
x = math.sin(angle)*self.r+pos[0] x = math.sin(angle)*self.r+pos[0]
y = -math.cos(angle)*self.r+pos[1] y = -math.cos(angle)*self.r+pos[1]
#print("pos",(x,y))
child.draw(offset=(x,y)) child.draw(offset=(x,y))
def _draw(self,pos): def _draw(self,pos):
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment