You've already forked MicroPythonOS
mirror of
https://github.com/m5stack/MicroPythonOS.git
synced 2026-05-20 11:51:27 -07:00
fd9996d729
Launching threads works fine, which is great. When the main app gets killed because "Referenced object was deleted!", the sub threads don't die, they stay active...
605 lines
18 KiB
Python
605 lines
18 KiB
Python
import lvgl as lv
|
|
from machine import Pin, SPI
|
|
import st7789
|
|
import lcd_bus
|
|
import machine
|
|
import task_handler
|
|
import cst816s
|
|
import i2c
|
|
import network
|
|
import urequests
|
|
|
|
# Pin configuration
|
|
SPI_BUS = 2
|
|
SPI_FREQ = 40000000
|
|
LCD_SCLK = 39
|
|
LCD_MOSI = 38
|
|
LCD_MISO = 40
|
|
LCD_DC = 42
|
|
LCD_CS = 45
|
|
LCD_BL = 1
|
|
|
|
I2C_BUS = 0
|
|
I2C_FREQ = 100000
|
|
TP_SDA = 48
|
|
TP_SCL = 47
|
|
TP_ADDR = 0x15
|
|
TP_REGBITS = 8
|
|
|
|
TFT_HOR_RES=320
|
|
TFT_VER_RES=240
|
|
|
|
lv.init()
|
|
spi_bus = machine.SPI.Bus(
|
|
host=SPI_BUS,
|
|
mosi=LCD_MOSI,
|
|
miso=LCD_MISO,
|
|
sck=LCD_SCLK
|
|
)
|
|
display_bus = lcd_bus.SPIBus(
|
|
spi_bus=spi_bus,
|
|
freq=SPI_FREQ,
|
|
dc=LCD_DC,
|
|
cs=LCD_CS,
|
|
)
|
|
display = st7789.ST7789(
|
|
data_bus=display_bus,
|
|
display_width=TFT_VER_RES,
|
|
display_height=TFT_HOR_RES,
|
|
backlight_pin=LCD_BL,
|
|
color_space=lv.COLOR_FORMAT.RGB565,
|
|
color_byte_order=st7789.BYTE_ORDER_BGR,
|
|
rgb565_byte_swap=True,
|
|
)
|
|
display.init()
|
|
display.set_power(True)
|
|
display.set_backlight(100)
|
|
|
|
# Touch handling:
|
|
i2c_bus = i2c.I2C.Bus(host=I2C_BUS, scl=TP_SCL, sda=TP_SDA, freq=I2C_FREQ, use_locks=False)
|
|
touch_dev = i2c.I2C.Device(bus=i2c_bus, dev_id=TP_ADDR, reg_bits=TP_REGBITS)
|
|
indev=cst816s.CST816S(touch_dev,startup_rotation=lv.DISPLAY_ROTATION._180) # button in top left, good
|
|
|
|
th = task_handler.TaskHandler()
|
|
display.set_rotation(lv.DISPLAY_ROTATION._90)
|
|
|
|
# Custom touch interrupt handler:
|
|
indev._write_reg(0xEC,0x06)
|
|
indev._write_reg(0xFA,0x50)
|
|
irq_pin=machine.Pin(46,machine.Pin.IN,machine.Pin.PULL_UP)
|
|
# gesture ids:
|
|
# 0: press
|
|
# 1: swipe from left to USB port
|
|
# 2: swipe from USB port to left
|
|
# 3: top to bottom
|
|
# 4: bottom to top
|
|
# 5: release
|
|
# 12: long press
|
|
def handle_gesture(pin):
|
|
indev._read_reg(0x01)
|
|
gesture_id=indev._rx_buf[0]
|
|
indev._read_reg(0x02)
|
|
finger_num=indev._rx_buf[0]
|
|
indev._read_reg(0x03)
|
|
x_h=indev._rx_buf[0]
|
|
indev._read_reg(0x04)
|
|
x_l=indev._rx_buf[0]
|
|
x=((x_h&0x0F)<<8)|x_l
|
|
indev._read_reg(0x05)
|
|
y_h=indev._rx_buf[0]
|
|
indev._read_reg(0x06)
|
|
y_l=indev._rx_buf[0]
|
|
y=((y_h&0x0F)<<8)|y_l
|
|
#print(f"GestureID={gesture_id},FingerNum={finger_num},X={x},Y={y}")
|
|
if gesture_id==0x04:
|
|
#print("Swipe Up Detected")
|
|
close_drawer()
|
|
elif gesture_id==0x03:
|
|
#print("Swipe Down Detected")
|
|
open_drawer()
|
|
|
|
irq_pin.irq(trigger=machine.Pin.IRQ_FALLING,handler=handle_gesture)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# GUI:
|
|
|
|
# Below works at https://sim.lvgl.io/v9.0/micropython/ports/webassembly/index.html
|
|
|
|
import time
|
|
|
|
# Constants
|
|
TFT_HOR_RES=320
|
|
TFT_VER_RES=240
|
|
NOTIFICATION_BAR_HEIGHT=24
|
|
BUTTON_WIDTH=100
|
|
BUTTON_HEIGHT=40
|
|
PADDING_TINY=5
|
|
PADDING_SMALL=10
|
|
PADDING_MEDIUM=20
|
|
PADDING_LARGE=30
|
|
DRAWER_ANIM_DURATION=300
|
|
SLIDER_MIN_VALUE=1
|
|
SLIDER_MAX_VALUE=100
|
|
SLIDER_DEFAULT_VALUE=80
|
|
OFFSET_WIFI_ICON = -60
|
|
OFFSET_BATTERY_ICON = -40
|
|
TIME_UPDATE_INTERVAL = 1000
|
|
|
|
# Color palette
|
|
DARKPINK = lv.color_hex(0xEC048C)
|
|
MEDIUMPINK = lv.color_hex(0xF480C5)
|
|
LIGHTPINK = lv.color_hex(0xF9E9F2)
|
|
DARKYELLOW = lv.color_hex(0xFBDC05)
|
|
LIGHTYELLOW = lv.color_hex(0xFBE499)
|
|
PUREBLACK = lv.color_hex(0x000000)
|
|
|
|
COLOR_DRAWER_BG=MEDIUMPINK
|
|
COLOR_TEXT_WHITE=LIGHTPINK
|
|
COLOR_NOTIF_BAR_BG = DARKPINK
|
|
COLOR_DRAWER_BUTTON_BG=DARKYELLOW
|
|
COLOR_DRAWER_BUTTONTEXT=PUREBLACK
|
|
COLOR_SLIDER_BG=LIGHTPINK
|
|
COLOR_SLIDER_KNOB=DARKYELLOW
|
|
COLOR_SLIDER_INDICATOR=LIGHTPINK
|
|
|
|
|
|
|
|
drawer=None
|
|
wifi_screen=None
|
|
drawer_open=False
|
|
|
|
scr = lv.screen_active()
|
|
scr.set_style_bg_color(lv.color_hex(0x000000), 0)
|
|
|
|
# Create a button (using lv.obj as a base)
|
|
btn = lv.button(scr)
|
|
btn.set_size(100, 50)
|
|
#btn.align(lv.ALIGN.LEFT_MID, 60, 60) doesnt work in web version?
|
|
|
|
# Add button style
|
|
style = lv.style_t()
|
|
style.init()
|
|
style.set_bg_color(lv.palette_main(lv.PALETTE.BLUE))
|
|
style.set_border_width(2)
|
|
style.set_border_color(lv.palette_darken(lv.PALETTE.BLUE, 3))
|
|
btn.add_style(style, 0)
|
|
|
|
# Add a label to the button
|
|
label = lv.label(btn)
|
|
label.set_text("Click Me")
|
|
label.center()
|
|
|
|
# Button event callback
|
|
def btn_event_cb(evt):
|
|
if evt.get_code() == lv.EVENT.CLICKED:
|
|
print("Button clicked!")
|
|
|
|
# Register the event callback
|
|
btn.add_event_cb(btn_event_cb, lv.EVENT.CLICKED, None)
|
|
|
|
|
|
|
|
|
|
# Create a slider
|
|
slider = lv.slider(scr)
|
|
slider.set_size(200, 20)
|
|
slider.set_range(0, 100)
|
|
slider.set_value(50, lv.ANIM.OFF)
|
|
slider.align(lv.ALIGN.BOTTOM_MID, 0, -40)
|
|
|
|
slider_label=lv.label(scr)
|
|
slider_label.set_text("80%")
|
|
slider_label.set_style_text_color(COLOR_TEXT_WHITE,0)
|
|
slider_label.align_to(slider,lv.ALIGN.OUT_TOP_MID,0,-5)
|
|
|
|
def slider_event(e):
|
|
value=slider.get_value()
|
|
print("slider value:")
|
|
print(value)
|
|
slider_label.set_text(f"{value}%")
|
|
#display.set_backlight(value) doesnt do anything and doesnt work in web version
|
|
|
|
slider.add_event_cb(slider_event,lv.EVENT.VALUE_CHANGED,None)
|
|
|
|
|
|
# Style for the slider background
|
|
bg_style = lv.style_t()
|
|
bg_style.init()
|
|
bg_style.set_bg_color(lv.palette_main(lv.PALETTE.GREY))
|
|
bg_style.set_border_width(1)
|
|
bg_style.set_border_color(lv.color_make(50, 50, 50))
|
|
bg_style.set_radius(10)
|
|
slider.add_style(bg_style, lv.PART.MAIN)
|
|
|
|
# Style for the slider indicator (track)
|
|
indic_style = lv.style_t()
|
|
indic_style.init()
|
|
indic_style.set_bg_color(lv.palette_main(lv.PALETTE.BLUE))
|
|
indic_style.set_radius(10)
|
|
slider.add_style(indic_style, lv.PART.INDICATOR)
|
|
|
|
# Style for the slider knob
|
|
knob_style = lv.style_t()
|
|
knob_style.init()
|
|
knob_style.set_bg_color(lv.palette_main(lv.PALETTE.RED))
|
|
knob_style.set_border_width(2)
|
|
knob_style.set_border_color(lv.color_make(50, 50, 50))
|
|
knob_style.set_radius(100) # Circular knob
|
|
knob_style.set_pad_all(5)
|
|
slider.add_style(knob_style, lv.PART.KNOB)
|
|
|
|
|
|
#show_block_height()
|
|
|
|
# Connect to Wi-Fi and fetch block height
|
|
#if connect_wifi():
|
|
#else:
|
|
# label.set_text("Block Height: Wi-Fi Error")
|
|
|
|
|
|
|
|
#print(os.listdir('/'))
|
|
#try:
|
|
# with open('/boot.py', 'r') as file:
|
|
# print("Contents of /boot.py:")
|
|
# print("-" * 20)
|
|
# for line in file:
|
|
# print(line.rstrip()) # Remove trailing newlines for clean output
|
|
#except OSError as e:
|
|
# print("Error reading /boot.py:", e)
|
|
#with open('/block_height.txt', 'w') as f:
|
|
# f.write('853123')
|
|
|
|
|
|
def create_notification_bar():
|
|
# Create notification bar object
|
|
notification_bar = lv.obj(lv.screen_active())
|
|
notification_bar.set_style_bg_color(COLOR_NOTIF_BAR_BG, 0)
|
|
notification_bar.set_size(320, NOTIFICATION_BAR_HEIGHT)
|
|
notification_bar.set_pos(0, 0)
|
|
notification_bar.set_scrollbar_mode(lv.SCROLLBAR_MODE.OFF)
|
|
notification_bar.set_scroll_dir(lv.DIR.VER)
|
|
notification_bar.set_style_border_width(0, 0)
|
|
notification_bar.set_style_radius(0, 0)
|
|
# Time label
|
|
time_label = lv.label(notification_bar)
|
|
time_label.set_text("12:00")
|
|
time_label.align(lv.ALIGN.LEFT_MID, PADDING_TINY, 0)
|
|
time_label.set_style_text_color(COLOR_TEXT_WHITE, 0)
|
|
# Notification icon (bell)
|
|
notif_icon = lv.label(notification_bar)
|
|
notif_icon.set_text(lv.SYMBOL.BELL)
|
|
notif_icon.align_to(time_label, lv.ALIGN.OUT_RIGHT_MID, PADDING_LARGE, 0)
|
|
notif_icon.set_style_text_color(COLOR_TEXT_WHITE, 0)
|
|
# WiFi icon
|
|
wifi_icon = lv.label(notification_bar)
|
|
wifi_icon.set_text(lv.SYMBOL.WIFI)
|
|
wifi_icon.align(lv.ALIGN.RIGHT_MID, OFFSET_WIFI_ICON, 0)
|
|
wifi_icon.set_style_text_color(COLOR_TEXT_WHITE, 0)
|
|
# Battery icon
|
|
battery_icon = lv.label(notification_bar)
|
|
battery_icon.set_text(lv.SYMBOL.BATTERY_FULL)
|
|
battery_icon.align(lv.ALIGN.RIGHT_MID, OFFSET_BATTERY_ICON, 0)
|
|
battery_icon.set_style_text_color(COLOR_TEXT_WHITE, 0)
|
|
# Battery percentage
|
|
battery_label = lv.label(notification_bar)
|
|
battery_label.set_text("100%")
|
|
battery_label.align(lv.ALIGN.RIGHT_MID, 0, 0)
|
|
battery_label.set_style_text_color(COLOR_TEXT_WHITE, 0)
|
|
openbutton = lv.button(notification_bar)
|
|
openbutton.align(lv.ALIGN.TOP_MID, -20, -8)
|
|
openbutton.add_event_cb(lambda event: open_drawer(), lv.EVENT.CLICKED, None)
|
|
closebutton = lv.button(notification_bar)
|
|
closebutton.align(lv.ALIGN.TOP_MID, 20, -8)
|
|
closebutton.add_event_cb(lambda event: close_drawer(), lv.EVENT.CLICKED, None)
|
|
# Timer to update time every second
|
|
def update_time(timer):
|
|
ticks = time.ticks_ms()
|
|
hours = (ticks // 3600000) % 24
|
|
minutes = (ticks // 60000) % 60
|
|
seconds = (ticks // 1000) % 60
|
|
time_label.set_text(f"{hours:02d}:{minutes:02d}:{seconds:02d}")
|
|
lv.timer_create(update_time, TIME_UPDATE_INTERVAL, None)
|
|
|
|
|
|
|
|
# Create the notification bar
|
|
create_notification_bar()
|
|
|
|
|
|
# Subwindow is created before drawer so that drawer is on top
|
|
screen = lv.screen_active()
|
|
subwindow = lv.obj(screen)
|
|
subwindow.set_size(TFT_HOR_RES, TFT_VER_RES - NOTIFICATION_BAR_HEIGHT)
|
|
subwindow.set_pos(0, NOTIFICATION_BAR_HEIGHT)
|
|
subwindow.set_style_border_width(0, 0)
|
|
subwindow.set_style_pad_all(0, 0)
|
|
|
|
|
|
|
|
def create_drawer():
|
|
global drawer,wifi_screen
|
|
drawer=lv.obj(lv.screen_active())
|
|
drawer.set_size(TFT_HOR_RES,TFT_VER_RES-NOTIFICATION_BAR_HEIGHT)
|
|
drawer.set_pos(0,-TFT_VER_RES+NOTIFICATION_BAR_HEIGHT)
|
|
drawer.set_style_bg_color(COLOR_DRAWER_BG,0)
|
|
drawer.set_scroll_dir(lv.DIR.NONE)
|
|
slider=lv.slider(drawer)
|
|
slider.set_range(SLIDER_MIN_VALUE,SLIDER_MAX_VALUE)
|
|
slider.set_value(SLIDER_DEFAULT_VALUE,False)
|
|
slider.set_width(TFT_HOR_RES-PADDING_MEDIUM)
|
|
slider.align(lv.ALIGN.TOP_MID,0,PADDING_SMALL)
|
|
slider.set_style_bg_color(COLOR_SLIDER_BG,lv.PART.MAIN)
|
|
slider.set_style_bg_color(COLOR_SLIDER_INDICATOR,lv.PART.INDICATOR)
|
|
slider.set_style_bg_color(COLOR_SLIDER_KNOB,lv.PART.KNOB)
|
|
slider_label=lv.label(drawer)
|
|
slider_label.set_text("80%")
|
|
slider_label.set_style_text_color(COLOR_TEXT_WHITE,0)
|
|
slider_label.align_to(slider,lv.ALIGN.OUT_TOP_MID,0,-5)
|
|
# works here
|
|
def slider_event(e):
|
|
slider=e.get_target()
|
|
label=e.get_user_data()
|
|
value=slider.get_value()
|
|
label.set_text(f"{value}%")
|
|
display.set_backlight(value)
|
|
# this crashes it: slider.add_event_cb(slider_event,lv.EVENT.VALUE_CHANGED,slider_label)
|
|
# this crashes it: slider.add_event_cb(slider_event,lv.EVENT.VALUE_CHANGED,slider_label)
|
|
wifi_btn=lv.button(drawer)
|
|
wifi_btn.set_size(BUTTON_WIDTH,BUTTON_HEIGHT)
|
|
wifi_btn.align(lv.ALIGN.LEFT_MID,PADDING_SMALL,0)
|
|
wifi_btn.set_style_bg_color(COLOR_DRAWER_BUTTON_BG,0)
|
|
wifi_label=lv.label(wifi_btn)
|
|
wifi_label.set_text(lv.SYMBOL.WIFI+" WiFi")
|
|
wifi_label.center()
|
|
wifi_label.set_style_text_color(COLOR_DRAWER_BUTTONTEXT,0)
|
|
def wifi_event(e):
|
|
global drawer_open
|
|
#wifi_screen.set_y(0) # TODO: make this
|
|
close_drawer()
|
|
drawer_open=False
|
|
wifi_btn.add_event_cb(wifi_event,lv.EVENT.CLICKED,None)
|
|
#
|
|
settings_btn=lv.button(drawer)
|
|
settings_btn.set_size(BUTTON_WIDTH,BUTTON_HEIGHT)
|
|
settings_btn.align(lv.ALIGN.RIGHT_MID,-PADDING_SMALL,0)
|
|
settings_btn.set_style_bg_color(COLOR_DRAWER_BUTTON_BG,0)
|
|
settings_label=lv.label(settings_btn)
|
|
settings_label.set_text(lv.SYMBOL.SETTINGS+" Settings")
|
|
settings_label.center()
|
|
settings_label.set_style_text_color(COLOR_DRAWER_BUTTONTEXT,0)
|
|
def settings_event(e):
|
|
global drawer_open
|
|
close_drawer()
|
|
drawer_open=False
|
|
settings_btn.add_event_cb(settings_event,lv.EVENT.CLICKED,None)
|
|
#
|
|
launcher_btn=lv.button(drawer)
|
|
launcher_btn.set_size(BUTTON_WIDTH,BUTTON_HEIGHT)
|
|
launcher_btn.align(lv.ALIGN.BOTTOM_LEFT,PADDING_SMALL,0)
|
|
launcher_btn.set_style_bg_color(COLOR_DRAWER_BUTTON_BG,0)
|
|
launcher_label=lv.label(launcher_btn)
|
|
launcher_label.set_text(lv.SYMBOL.HOME+" Launcher")
|
|
launcher_label.center()
|
|
launcher_label.set_style_text_color(COLOR_DRAWER_BUTTONTEXT,0)
|
|
def launcher_event(e):
|
|
print("Launcher button pressed!")
|
|
global drawer_open
|
|
close_drawer()
|
|
drawer_open=False
|
|
run_app(launcher_script,False)
|
|
launcher_btn.add_event_cb(launcher_event,lv.EVENT.CLICKED,None)
|
|
#
|
|
restart_btn=lv.button(drawer)
|
|
restart_btn.set_size(BUTTON_WIDTH,BUTTON_HEIGHT)
|
|
restart_btn.align(lv.ALIGN.BOTTOM_RIGHT,-PADDING_SMALL,0)
|
|
restart_btn.set_style_bg_color(COLOR_DRAWER_BUTTON_BG,0)
|
|
restart_label=lv.label(restart_btn)
|
|
restart_label.set_text(lv.SYMBOL.POWER+" Reset")
|
|
restart_label.center()
|
|
restart_label.set_style_text_color(COLOR_DRAWER_BUTTONTEXT,0)
|
|
def launcher_event(e):
|
|
print("Reset button pressed!")
|
|
global drawer_open
|
|
close_drawer()
|
|
drawer_open=False
|
|
run_app(launcher_script,False)
|
|
restart_btn.add_event_cb(lambda event: machine.reset(),lv.EVENT.CLICKED,None)
|
|
|
|
def open_drawer():
|
|
global drawer_open
|
|
if not drawer_open:
|
|
drawer.set_y(NOTIFICATION_BAR_HEIGHT)
|
|
drawer_open=True
|
|
|
|
|
|
def close_drawer():
|
|
global drawer_open
|
|
if drawer_open:
|
|
drawer.set_y(-TFT_VER_RES+NOTIFICATION_BAR_HEIGHT)
|
|
drawer_open=False
|
|
|
|
|
|
|
|
create_drawer()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# uasyncio and _thread aren't available on web
|
|
|
|
import _thread
|
|
|
|
# Function to execute the child script as a coroutine
|
|
def execute_script(script_source, is_file, lvgl_obj, return_to_launcher):
|
|
thread_id = _thread.get_ident();
|
|
print(f"Thread {thread_id}: executing script")
|
|
try:
|
|
script_globals = {
|
|
'lv': lv,
|
|
'subwindow': lvgl_obj,
|
|
'run_app': run_app,
|
|
'app1_script': app1_script,
|
|
'app2_script': app2_script
|
|
}
|
|
if is_file:
|
|
print(f"Thread {thread_id}: reading script from file: {script_source}")
|
|
with open(script_source, 'r') as f:
|
|
script_source = f.read()
|
|
print(f"Thread {thread_id}: starting script")
|
|
exec(script_source, script_globals)
|
|
print(f"Thread {thread_id}: script finished")
|
|
if return_to_launcher:
|
|
print(f"Thread {thread_id}: running launcher_script")
|
|
run_app(launcher_script,False,False)
|
|
except Exception as e:
|
|
print(f"Thread {thread_id}: error ", e)
|
|
|
|
|
|
def run_app(scriptname,is_file,return_to_launcher=True):
|
|
# Start the event loop in a background thread
|
|
gc.collect()
|
|
print("Free memory before starting new script thread:", gc.mem_free())
|
|
try:
|
|
# 168KB maximum at startup but 136KB after loading display, drivers, LVGL gui etc so let's go for 128KB for now, still a lot...
|
|
# But then no additional threads can be created. So 32KB seems like a good balance, allowing for 4 threads in apps...
|
|
subwindow.clean()
|
|
_thread.stack_size(32768)
|
|
_thread.start_new_thread(execute_script, (scriptname, False, subwindow, return_to_launcher))
|
|
print("Event loop started in background thread")
|
|
except Exception as e:
|
|
print("Error starting event loop thread:", e)
|
|
|
|
|
|
# app1: updates label, adds button and slider
|
|
app1_script = """
|
|
import time
|
|
print("Child coroutine: Creating UI")
|
|
# Label
|
|
label = lv.label(subwindow)
|
|
label.set_text("App1: 0")
|
|
label.align(lv.ALIGN.TOP_MID, 0, 10)
|
|
# Button
|
|
button = lv.button(subwindow)
|
|
button.set_size(100, 60)
|
|
button.align(lv.ALIGN.CENTER, 0, 0)
|
|
button_label = lv.label(button)
|
|
button_label.set_text("Quit")
|
|
button_label.center()
|
|
# Slider
|
|
slider = lv.slider(subwindow)
|
|
slider.set_range(0, 100)
|
|
slider.set_value(50, lv.ANIM.OFF)
|
|
slider.align(lv.ALIGN.BOTTOM_MID, 0, -30)
|
|
# Quit flag
|
|
should_continue = True
|
|
# Button callback
|
|
def button_cb(e):
|
|
global should_continue
|
|
print("Quit button clicked, exiting child")
|
|
should_continue = False
|
|
button.add_event_cb(button_cb, lv.EVENT.CLICKED, None)
|
|
# Slider callback
|
|
def slider_cb(e):
|
|
value = slider.get_value()
|
|
#print("Child slider value:", value)
|
|
slider.add_event_cb(slider_cb, lv.EVENT.VALUE_CHANGED, None)
|
|
# Update loop
|
|
count = 0
|
|
while should_continue:
|
|
count += 1
|
|
#print("Child coroutine: Updating label to", count)
|
|
label.set_text(f"App1: {count}")
|
|
time.sleep_ms(100) # shorter makes it more responive to the quit button
|
|
print("Child coroutine: Exiting")
|
|
"""
|
|
|
|
app2_script = """
|
|
import time
|
|
import _thread
|
|
print("App2 running")
|
|
|
|
# Quit flag
|
|
should_continue = True
|
|
|
|
def app2_thread():
|
|
count=0
|
|
while should_continue:
|
|
print(f"app2_thread: thread_id {_thread.get_ident()} - {count}")
|
|
count+=1
|
|
time.sleep(4)
|
|
|
|
_thread.start_new_thread(app2_thread, ())
|
|
|
|
|
|
# Label
|
|
label = lv.label(subwindow)
|
|
label.set_text("App2: 0")
|
|
label.align(lv.ALIGN.TOP_MID, 0, 10)
|
|
# Button
|
|
button = lv.button(subwindow)
|
|
button.set_size(100, 60)
|
|
button.align(lv.ALIGN.CENTER, 0, 0)
|
|
button_label = lv.label(button)
|
|
button_label.set_text("Quit")
|
|
button_label.center()
|
|
# Slider
|
|
slider = lv.slider(subwindow)
|
|
slider.set_range(0, 100)
|
|
slider.set_value(50, lv.ANIM.OFF)
|
|
slider.align(lv.ALIGN.BOTTOM_MID, 0, -30)
|
|
# Button callback
|
|
def button_cb(e):
|
|
global should_continue
|
|
print("Quit button clicked, exiting child")
|
|
should_continue = False
|
|
button.add_event_cb(button_cb, lv.EVENT.CLICKED, None)
|
|
# Slider callback
|
|
def slider_cb(e):
|
|
value = slider.get_value()
|
|
#print("Child slider value:", value)
|
|
slider.add_event_cb(slider_cb, lv.EVENT.VALUE_CHANGED, None)
|
|
|
|
|
|
|
|
# Update loop
|
|
count = 0
|
|
while should_continue:
|
|
count += 1
|
|
#print("Child coroutine: Updating label to", count)
|
|
label.set_text(f"App2: {count}")
|
|
time.sleep_ms(100) # shorter makes it more responive to the quit button
|
|
print("Child coroutine: Exiting")
|
|
"""
|
|
|
|
|
|
launcher_script = """
|
|
print("Launcher script running")
|
|
app1_button = lv.button(subwindow)
|
|
app1_button.set_size(120, 40)
|
|
app1_button.align(lv.ALIGN.LEFT_MID, 20, 0)
|
|
app1_button_label = lv.label(app1_button)
|
|
app1_button_label.set_text("Start App 1")
|
|
app1_button_label.center()
|
|
app1_button.add_event_cb(lambda event: run_app(app1_script,False), lv.EVENT.CLICKED, None)
|
|
app2_button = lv.button(subwindow)
|
|
app2_button.set_size(120, 40)
|
|
app2_button.align(lv.ALIGN.RIGHT_MID, -20, 0)
|
|
app2_button_label = lv.label(app2_button)
|
|
app2_button_label.set_text("Start App 2")
|
|
app2_button_label.center()
|
|
app2_button.add_event_cb(lambda event: run_app(app2_script,False), lv.EVENT.CLICKED, None)
|
|
print("Launcher script exiting")
|
|
"""
|
|
|
|
|
|
run_app(launcher_script,False,False)
|