Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(color,Lua): Lvgl support for Lua scripts #4887

Open
wants to merge 6 commits into
base: main
Choose a base branch
from

Conversation

philmoz
Copy link
Collaborator

@philmoz philmoz commented Apr 16, 2024

As discussed in #4627

Sample scripts attached.

BattAnLvgl

  • Rewrite of the BattAnalog script. This will work either with this PR or without. It demonstrates a simple pattern where the UI logic is split and the correct version is loaded when the script is run.
    BattAnLvgl.zip
    screenshot_tx16s_24-04-16_20-25-20

Lvgl

  • Simple test widget demonstrating various features.
    Lvgl.zip
    screenshot_tx16s_24-04-16_20-26-48

LvglMeter

Lvgl Test Tool

  • Test tool script demonstrating using the EdgeTX styled UI elements (buttons, toggle switch, edit box, etc).
    Lvgl Test Tool.lua.zip
    screenshot_tx16s_24-04-16_20-25-57

@philmoz
Copy link
Collaborator Author

philmoz commented Apr 17, 2024

Here is a summary of the API as it currently stands (20th Apr).

The Lvgl system is accessed through ‘lvgl’ object in Lua.
A Widget must set ‘useLvgl=true’ in the table returned when the widget is first loaded to enable the Lvgl API.

return { name="Lvgl Test", options=opts, create=create, update=update, refresh=refresh, background=background, useLvgl=true }

To clear the screen and delete all existng Lvgl objects call:

lvgl.clear()

This should be done if the layout changes (e.g. going to/from full screen).

When creating objects some of the properties can be either a static value, or a getter function. If a getter function is supplied the property will be updated whenever the getter function returns a different value.

Creating new Lvgl objects:

  • All functions to create a new object expect a single table parameter with the relevant properties
    • General parameters (all functions):
      • x - x position (relative to top left of widget or parent)
      • y - y position (relative to top left of widget or parent)
      • color - main color. Can be a color value or a getter function.
      • pos - getter function called by object to update position (must return two integer values - x, y)
      • size - getter function called by object to update size (must return two integer values - w, h)
      • visible - getter function called by object to update visibility (must return a boolean - true = visible, false = hidden)
    • Functions:
      • label - lvgl.label({parameter table})
        • Specific parameters:
          • text - string to show on the label. Can be a static string or a getter function.
          • w - optional width to set (required if RIGHT justified text is used)
          • h - optional height to set
          • font - text font
      • rectangle - lvgl.rectangle({parameter table})
        • Specific parameters:
          • w - width
          • h - height
          • thickness - outline thickness
          • filled - true if filled rectangle is required (thickness is not used in this case)
          • rounded - controls corner rounding, 0 - no rounding, > 0 applies rounding
      • circle - lvgl.circle({parameter table})
        • x,y position is the centre of the circle
        • Specific parameters:
          • radius - radius. Can be a static value or a getter function.
          • thickness - outline thickness
          • filled - true if filled circle is required (thickness is not used in this case)
      • arc - lvgl.arc({parameter table})
        • x,y position is the centre of the arc
        • Specific parameters:
          • radius - radius. Can be a static value or a getter function.
          • thickness - outline thickness
          • startAngle - start angle for arc 0 - 360 (0 is up). Can be a static value or a getter function.
          • endAngle - end angle for arc 0 - 360. Can be a static value or a getter function.
      • image - lvgl.image({parameter table})
        • The image will be centered in frame defined by x,y,w,h. If the 'fill' property is false the image will be scaled to fit fully within the frame. If the image aspect ratio is not the same as the frame there will be transparent borders as needed to centre the image. If the 'fill' property is true the image will be scaled to fill the frame. The scaled image may be cropped if the aspect ratio does not match the frame.
        • Specific parameters:
          • file - filename (including path) of file to display
          • fill - false to fit image to frame, true to fill frame with image.

Creating new objects using styled EdgeTx controls (only available for standalone scripts):

  • All functions to create a new object expect a single table parameter with the relevant properties
    • General parameters (all functions):
      • x - x position (relative to top left of widget or parent)
      • y - y position (relative to top left of widget or parent)
      • pos - getter function called by object to update position (must return two integer values - x, y)
      • size - getter function called by object to update size (must return two integer values - w, h)
      • visible - getter function called by object to update visibility (must return a boolean - true = visible, false = hidden)
    • Functions:
      • button - lvgl.button({parameter table})
        • Specific parameters:
          • text - initial string to show on the label
          • w - optional width to set
          • press - function to call when button is pressed
      • toggle switch - lvgl.toggle({parameter table})
        • Specific parameters:
          • get - function to call to get state of toggle switch (return 0 = off, 1 = on)
          • set - function to call when toggle switch state is changed by user (has a single parameter 0 = off, 1 = on)
      • slider - lvgl.slider({parameter table})
        Note: 'h' property is ignored
        • Specific parameters:
          • min - minimum (left) value for slider
          • max - maximum (right) value for slider
          • get - function to call to get the value for the slider position (must be between min and max)
          • set - function to call when value is changed by user (has a single parameter of the new value)
      • confirmation dialog - lvgl.confirm({parameter table})
        • Specific parameters:
          • title - popup dialog title
          • message - popup dialog message
          • confirm - function to call when user selects 'Yes' button on dialog
          • cancel - function to call when user selects 'No' button on dialog
      • text entry - lvgl.textEdit({‘parameter table})
        • Specific parameters:
          • maxLen - maximum allowed length of text that can be entered
          • value - initial value of the text entry field
          • set - function to call when text has been edited by the user (has a single parameter with the new string)
      • number entry - lvgl.numberEdit({parameter table})
        • Specific parameters:
          • min - minimum allowed value
          • max - maximum allowed value
          • get - function to call to get value to be edited (must return an integer)
          • set - function to call when the value is edited by the user (has a single integer parameter with the new value)
          • display - function to call to format the way the value is displayed (has a single parameter with the value to display, must return a string with the formatted value)
      • popup menu - lvgl.choice({parameter table})
        • Specific parameters:
          • title - title string for the popup menu
          • values - table of string values that can be selected by the user
          • get - function to call to get current selected option (must return an integer from 1 to size of values table)
          • set - function to call when the user selects an option (has a single parameter with the selected index from 1 to the size of the values table)

All functions return a reference to the Lvgl object that can be used to set attributes later.

local uiLabel = lvgl.label({text=Hello World!”, x=10, y=10, color=WHITE, font=DBLSIZE})

It is not necessary to save the return value unless you want to change attributes. Object lifetime is managed by the system.

Setting attributes:
Attribute values can be updated using the 'set' function. The 'set' function takes a table parameter with the same properties as used when creating the object. Note: not all properties can be changed.
The 'set' function can be called via ‘lvgl’ or using the saved reference

local uiLabel = lvgl.label({text=Hello World!”, x=10, y=10, color=WHITE, font=DBLSIZE})
lvgl.set(uiLabel, {text=New string”})
uiLabel:set({text=New string”})

Setting state:
UI elements can be hidden and shown as needed using ‘show’ and ‘hide’ functions
E.G.:

lvgl.hide(uiLabel)
uiLabel:show()

Grouping ui elements:

  • By default the parent of all UI elements is the widget itself
  • UI elements can contain other elements. This can be used to show/hide/move a group of objects by simply updating the parent.
  • To create a nested group of objects:
  •   Create the parent, call lvgl.setParent(parent), create children, call lvgl.setParent(nil)
    

E.G.

local box = lvgl.rectangle({x=10, y=10, w=50, h=50, thickness=1, color=WHITE})
lvgl.setParent(box)
local lbl = lvgl.label({text=BOX”, x=10, y=10})
lvgl.setParent(nil)

Creating UI elements in bulk:
It is possible to create a complex UI layout by defining all the elements in a Lua table, then calling lvgl.build() to create the entire layout in one operation.
E.G. create the layout with a nested table structure:

  local lyt = {
    {type="label", text="CTR", x=0, y=0, color=wgt.opts.Color, font=MIDSIZE},
    {type="label", text="TMR", x=0, y=50, color=wgt.opts.Color, font=MIDSIZE},
    {type="label", x=80, y=0, color=wgt.opts.Color, font=MIDSIZE, name="ctr"},
    {type="label", x=120, y=0, color=wgt.opts.Color, font=MIDSIZE, name="bgctr"},
    {type="label", x=80, y=50, color=wgt.opts.Color, font=MIDSIZE, name="tmr"},
    {type="rectangle", x=50, y=80, w=30, h=30, thickness=4, color=COLOR_THEME_ACTIVE, name="box1"},
    {type="filledRectangle", x=90, y=80, w=30, h=30, color=COLOR_THEME_EDIT, name="box2", children={
        {type="circle", x=15, y=15, radius=13, thickness=2, color=WHITE, children={
            {type="label", text="0", x=7, y=1, name="nctr"},
        }},
    }},
    {type="circle", x=65, y=135, radius=15, thickness=4, color=COLOR_THEME_ACTIVE, name="cir1"},
    {type="filledCircle", x=105, y=135, radius=15, color=COLOR_THEME_EDIT, name="cir2"},
    {type="arc", x=15, y=95, radius=15, thickness=4, startAngle=0, endAngle=210, color=COLOR_THEME_WARNING, name="arc"},
  }

Elements can be nested and grouped using the ‘children’ property.
Then build the UI elements, lvgl.build returns a table containing the references to all the named elements in the layout. Only named elements can be accessed.

local uiElements = lgvl.build(lyt)

The named elements can be updated later:

uiElements[“ctr”]:set({text=New string”})

You can also copy the UI element references into other variables to make updating easier

local ctr = uiElements[“ctr”]
ctr:set({color=COLOR_THEME_PRIMARY2})

@philmoz
Copy link
Collaborator Author

philmoz commented Apr 17, 2024

Added the 'image' widget object to display bitmap images.

@rotorman rotorman changed the title feat(color,Lua): Lvgl suppport for Lua scripts feat(color,Lua): Lvgl support for Lua scripts Apr 21, 2024
@philmoz philmoz force-pushed the lvgl-for-lua branch 3 times, most recently from 14add66 to 36666c6 Compare May 1, 2024 00:57
@philmoz philmoz force-pushed the lvgl-for-lua branch 3 times, most recently from ae28dec to d18bce2 Compare May 10, 2024 05:02
@philmoz philmoz force-pushed the lvgl-for-lua branch 2 times, most recently from 3e164dd to 693f112 Compare May 16, 2024 10:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

1 participant