I’m trying to use lvgl in async mode to connect to a wifi network.
I use classes to create the individual screens as shown in the advanced example on github.
The classes hold the individual widgets and the corresponding callbacks of a screen.
My problem lies in updating the screens contents in the callback functions with data from blocking funtions.
This code snippet shows a callback function using data from a blocking function (last elif block). I’m trying to disable a text area, retrive data from the blocking function (could be a network-scan function),add the data to the screen and enable the TA again. However, the states of the widgets won’t update correctly (it looks like the update style is only applied partially).
The i tried to create an async task from an external function, to which I forward the handles of the elements I want to modify and update the data (first if block). This is working as expected and all element states get updated as supposed to. But I think it’s inconvenient to use external functions outside the screen-classes.
A similar problem arises when I want to load a new screen in the button-callback (middle elif block):
It only works when I use an external task). If I try to use the update function of the app directly I recieve an error message (Error: can’t cancel self).
Does anyone have an idea how I can keep the update functions in the screen classes and update the screen with data from a blocking function?
def cb(self, e):
obj = e.get_target()
if obj == self.asyncButton:
task = asyncio.create_task(returnDelayAsync(self.app, self, 3))
# res await returnDelayAsync(self.app, self, 3)
#print(res) # Not working
elif obj == self.nextButton:
task = asyncio.create_task(loadScreenAsync(self.app, 'WifiSuccess'))
#self.app.loadScreen('WifiSuccess') # not working
elif obj == self.syncButton: # Widget states won't update properly
disable(self.pwdTa)
res = returnDelay(3)
self.titleLbl.set_text(res)
enable(self.pwdTa)
Summary
import usys as sys
sys.path.append('')
from machine import SoftSPI, Pin
from utime import ticks_us
import lv_utils
import lvgl as lv
import uasyncio as asyncio
from time import sleep, sleep_ms
from xpt2046_cyd import xpt2046
import ili9XXX
def enable(el):
if el.has_state(lv.STATE.DISABLED):
el.clear_state(lv.STATE.DISABLED)
def disable(el):
if not el.has_state(lv.STATE.DISABLED):
el.add_state(lv.STATE.DISABLED)
def returnDelay(delay):
for i in range(delay):
print('### Sleep ###')
sleep(1)
return 'sync Data'
async def returnDelayAsync(app, screen, delay):
disable(screen.pwdTa)
for i in range(delay):
print('### Sleep Async ###')
await asyncio.sleep(1)
screen.titleLbl.set_text('async Data')
enable(screen.pwdTa)
async def loadScreenAsync(app, sid):
await asyncio.sleep(0)
app.loadScreen(sid)
class ScreenWifiConnect(lv.obj):
def __init__(self, app, *args, **kwds):
self.app = app
super().__init__(*args, **kwds)
self.set_style_bg_color(lv.color_white(), lv.PART.MAIN)
self.kb = lv.keyboard(self)
self.kb.set_size(lv.pct(100), lv.pct(50))
self.kb.align(lv.ALIGN.TOP_LEFT, lv.pct(0), lv.pct(50))
self.syncButton = lv.btn(self)
self.syncButton.set_size(80, 30)
self.syncButton.align_to(self.kb, lv.ALIGN.OUT_TOP_RIGHT, -15, -15)
self.syncButton.add_event_cb(self.cb, lv.EVENT.CLICKED, None)
self.syncButtonLbl = lv.label(self.syncButton)
self.syncButtonLbl.set_text('Sync')
self.syncButtonLbl.center()
self.asyncButton = lv.btn(self)
self.asyncButton.set_size(60, 30)
self.asyncButton.align_to(self.syncButton, lv.ALIGN.OUT_LEFT_TOP, -5, 0)
self.asyncButton.add_event_cb(self.cb, lv.EVENT.CLICKED, None)
self.asyncButtonLbl = lv.label(self.asyncButton)
self.asyncButtonLbl.set_text('Async')
self.asyncButtonLbl.center()
self.nextButton = lv.btn(self)
self.nextButton.set_size(60, 30)
self.nextButton.align_to(self.asyncButton, lv.ALIGN.OUT_LEFT_TOP, -5, 0)
self.nextButton.add_event_cb(self.cb, lv.EVENT.CLICKED, None)
self.nextButtonLbl = lv.label(self.nextButton)
self.nextButtonLbl.set_text('Next')
self.nextButtonLbl.center()
self.pwdTa = lv.textarea(self)
self.pwdTa.set_placeholder_text('Enter Password')
self.pwdTa.set_password_mode(True)
self.pwdTa.set_one_line(True)
self.pwdTa.set_width(lv.pct(90))
self.pwdTa.align_to(self.syncButton, lv.ALIGN.OUT_TOP_RIGHT, 0, -5)
self.pwdTa.add_state(lv.STATE.FOCUSED)
self.kb.set_textarea(self.pwdTa)
self.titleLbl = lv.label(self)
self.titleLbl.set_text('Standby')
self.titleLbl.align_to(self.pwdTa,lv.ALIGN.OUT_TOP_LEFT, 0, -5)
def cb(self, e):
obj = e.get_target()
if obj == self.asyncButton:
task = asyncio.create_task(returnDelayAsync(self.app, self, 3))
# res await returnDelayAsync(self.app, self, 3)
#print(res) # Not working
elif obj == self.nextButton:
task = asyncio.create_task(loadScreenAsync(self.app, 'WifiSuccess'))
#self.app.loadScreen('WifiSuccess') # not working
elif obj == self.syncButton: # Widget states won't update properly
disable(self.pwdTa)
res = returnDelay(3)
self.titleLbl.set_text(res)
enable(self.pwdTa)
class ScreenWifiSuccess(lv.obj):
def __init__(self, app, *args, **kwds):
self.app = app
super().__init__(*args, **kwds)
self.title = lv.label(self)
self.title.set_text('Settings')
self.title.center()
class App:
def __init__(self):
self.actScreen = None
self.prevScreen = None
self.sharedData = {}
lv.init()
self.disp = ili9XXX.ili9341(clk=14, cs=15,
dc=2, rst=12, power=23, miso=12,
mosi=13, width = 320, height = 240,
rot = 0xC0, colormode=ili9XXX.COLOR_MODE_RGB,
double_buffer = False, factor = 16, asynchronous=True)
self.spiTouch = SoftSPI(baudrate = 2500000, sck = Pin(25),
mosi = Pin(32), miso = Pin(39))
self.touch = xpt2046(spi = self.spiTouch, cs = Pin(33), cal_x0 = 3700,
cal_y0 = 3820, cal_x1 = 180, cal_y1 = 250, transpose=False)
self.backlight = Pin(21, Pin.OUT)
self.backlight(1)
self.group = lv.group_create()
self.group.set_default()
self.screens = {'WifiConnect': ScreenWifiConnect,
'WifiSuccess': ScreenWifiSuccess
}
self.loadScreen('WifiConnect')
def loadScreen(self, screenName):
if not(screenName in self.screens):
print('Unknown screen!')
return False
if not(self.actScreen is None):
self.prevScreen = self.actScreen
self.actScreen = self.screens[screenName](self)
lv.scr_load(self.actScreen)
if not(self.prevScreen is None):
self.prevScreen.del_async()
try:
app = App()
asyncio.Loop.run_forever()
except KeyboardInterrupt:
print('Programm terminated by user.')
#finally:
asyncio.new_event_loop()