-
Notifications
You must be signed in to change notification settings - Fork 656
add LVGL app to XML conveter #426
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
Open
kisvegabor
wants to merge
10
commits into
master
Choose a base branch
from
lvgl_c2pro_xml
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 4 commits
Commits
Show all changes
10 commits
Select commit
Hold shift + click to select a range
c3bc89d
add the tools and docs and CI
kisvegabor f928666
ci fixes
kisvegabor 6286031
fixes
kisvegabor 55507c9
fixes based on feedback
kisvegabor 3c05014
gdb fixes
kisvegabor d63f86f
new round of fix
kisvegabor d081027
readme updates
kisvegabor cd2267e
add images
kisvegabor d00e850
Update README.md
kisvegabor bb1aaaf
gdb fixes based on feedback in https://github.com/lvgl/lvgl/pull/10477
kisvegabor File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,86 @@ | ||
| name: lvgl_app2pro | ||
|
|
||
| # Builds the two test apps, converts them with GDB, and checks the XML against | ||
| # tests/reference_xml_projects. The screenshot comparison needs the Pro CLI and | ||
| # is skipped here; run it locally with `tests/test.py --cli <path>`. | ||
|
|
||
| on: | ||
| pull_request: | ||
| paths: | ||
| - 'tools/lvgl_app2pro/**' | ||
| - 'lvgl_widgets_xml/**' | ||
| - '.github/workflows/tools-lvgl_app2pro.yml' | ||
| push: | ||
| branches: | ||
| - master | ||
| paths: | ||
| - 'tools/lvgl_app2pro/**' | ||
| - 'lvgl_widgets_xml/**' | ||
| - '.github/workflows/tools-lvgl_app2pro.yml' | ||
|
|
||
| jobs: | ||
| test: | ||
| runs-on: ubuntu-latest | ||
|
|
||
| steps: | ||
| - uses: actions/checkout@v4 | ||
|
|
||
| # 3.10 is the documented floor for running the converter. It is | ||
| # deliberately not the interpreter GDB uses: GDB embeds its own, so this | ||
| # combination is also what most users have. | ||
| - name: Set up Python 3.10 | ||
| uses: actions/setup-python@v5 | ||
| with: | ||
| python-version: '3.10' | ||
|
|
||
| - name: Install GDB and what the example needs | ||
| run: | | ||
| sudo apt-get update | ||
| sudo apt-get install -y gdb libsdl2-dev | ||
|
|
||
| - name: Install the plugin's dependencies for the interpreter GDB embeds | ||
| # lvglgdb imports numpy, Pillow and prettytable at module level, so it | ||
| # cannot load without them. They have to go to the *system* Python, | ||
| # which is the one GDB embeds - installing them with the 3.10 above | ||
| # would put them somewhere GDB never looks. | ||
| run: | | ||
| /usr/bin/python3 -m pip install --break-system-packages \ | ||
| -r tools/lvgl_app2pro/src/gdb_plugin/requirements.txt | ||
|
|
||
| - name: Check GDB can load the plugin | ||
| # The strongest preflight there is: if lvglgdb imports here, it will | ||
| # import during the run. Without it, a missing dependency surfaces much | ||
| # later and much less clearly. | ||
| run: | | ||
| gdb -batch \ | ||
| -ex "python import sys; print('GDB Python:', sys.version)" \ | ||
| -ex "python sys.path.insert(0, 'tools/lvgl_app2pro/src/gdb_plugin')" \ | ||
| -ex "python import lvglgdb; print('lvglgdb', lvglgdb.__version__, 'loaded')" | ||
|
|
||
| - name: Cache the LVGL checkout the example builds against | ||
| uses: actions/cache@v4 | ||
| with: | ||
| path: tools/lvgl_app2pro/tests/.work/lvgl | ||
| key: lvgl-${{ hashFiles('tools/lvgl_app2pro/tests/test.py') }} | ||
|
|
||
| - name: Build, convert and compare | ||
| working-directory: tools/lvgl_app2pro | ||
| run: python3 tests/test.py | ||
|
|
||
| - name: Show what changed if the comparison failed | ||
| if: failure() | ||
| working-directory: tools/lvgl_app2pro | ||
| run: | | ||
| for app in test_ui lv_demo_widgets; do | ||
| diff -ru "tests/reference_xml_projects/$app" \ | ||
| "tests/.work/$app/converted" || true | ||
| done | ||
|
|
||
| - name: Keep the converted projects for inspection | ||
| if: failure() | ||
| uses: actions/upload-artifact@v4 | ||
| with: | ||
| name: converted | ||
| path: | | ||
| tools/lvgl_app2pro/tests/.work/*/converted | ||
| tools/lvgl_app2pro/tests/.work/*/*.png |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -5,6 +5,7 @@ | |
| "syntax", | ||
| "built_in_widgets", | ||
| "integration", | ||
| "migration", | ||
| "editor", | ||
| "online-viewer", | ||
| "figma", | ||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,262 @@ | ||
| --- | ||
| title: From C code | ||
| description: lvgl_app2pro converts a running LVGL application into an LVGL Pro XML project by reading the live widget tree out of the process with GDB. | ||
| --- | ||
|
|
||
| `lvgl_app2pro` turns an existing LVGL application into an LVGL Pro XML project. | ||
|
|
||
| It needs an LVGL application that runs on your PC. No source code is needed: it | ||
| starts the app under GDB, stops it once the UI is up, and reads the real widget | ||
| tree out of the process with | ||
| [LVGL's GDB plugin](https://docs.lvgl.io/master/debugging/gdb_plugin.html). Once | ||
| the screens, widgets, properties, images and styles are known, it writes the | ||
| equivalent XML as a ready to use LVGL Pro project. | ||
|
|
||
| The tool lives in the | ||
| [lvgl_pro repository](https://github.com/lvgl/lvgl_pro) in `tools/lvgl_app2pro`. | ||
| There is nothing to install: clone the repository and run the converter from | ||
| there. | ||
|
|
||
| ## What the result looks like | ||
|
|
||
| Each tab of `lv_demo_widgets` as the C application draws it, next to the | ||
| converted XML project. Nothing was edited by hand in between. | ||
|
|
||
| | Original (C) | Converted (XML) | | ||
| | --- | --- | | ||
| | <img src="/_static/images/migration/app2pro_c_tab0.png" width="400" alt="Profile tab in the C application" /> | <img src="/_static/images/migration/app2pro_xml_tab0.png" width="400" alt="Profile tab converted to XML" /> | | ||
| | <img src="/_static/images/migration/app2pro_c_tab1.png" width="400" alt="Analytics tab in the C application" /> | <img src="/_static/images/migration/app2pro_xml_tab1.png" width="400" alt="Analytics tab converted to XML" /> | | ||
| | <img src="/_static/images/migration/app2pro_c_tab2.png" width="400" alt="Shop tab in the C application" /> | <img src="/_static/images/migration/app2pro_xml_tab2.png" width="400" alt="Shop tab converted to XML" /> | | ||
|
|
||
| All the layouts, properties and images were extracted and converted correctly. | ||
| The fonts and the custom functions and callbacks need to be added by you — | ||
| see [Improvement options](#improvement-options). | ||
|
|
||
| ## Why read a running app | ||
|
|
||
| A UI is usually built in loops, in helper functions, and behind `#if`s. Working | ||
| out what that adds up to is exactly what a source-code converter is bad at, and | ||
| what the running program already knows. | ||
|
|
||
| - **No parsing and no guessing.** Whatever your code actually built is what is | ||
| converted, including anything a static reader would miss. | ||
| - **The design intent survives.** `LV_PCT(50)` and `LV_SIZE_CONTENT` are stored | ||
| as such at runtime, so widths convert to `50%` and `content` rather than | ||
| frozen pixel values. Flex and grid convert as layouts, with their tracks. | ||
| - **The theme is left behind.** On a typical screen about three quarters of all | ||
| style properties come from the default theme. Those are ignored, so the XML | ||
| describes your design and LVGL Pro applies its own theme underneath. | ||
| - **Images are recovered.** An image compiled into your binary as a C array is | ||
| decoded out of memory and written next to the project as a PNG. | ||
| - **Nothing is dropped in silence.** Anything that cannot be expressed in XML | ||
| becomes a `TODO` in the file and a line in the report the run prints. | ||
|
|
||
| ## Requirements | ||
|
|
||
| - **GDB.** It usually embeds Python as well. Check that its interpreter is at | ||
| least 3.10 with `gdb -batch -ex "python import sys; print(sys.version)"`. | ||
| - **Python 3.10** or newer to run the converter itself. This is a separate | ||
| interpreter from the one inside GDB, and the versions need not match. | ||
| - An LVGL application that runs on your PC, built with no optimization (`-O0`) | ||
| and debug symbols (`-g3`), LVGL included. | ||
| - **CMake** and **SDL2**, only to build the example in the repository. | ||
|
|
||
| On Debian and Ubuntu: | ||
|
|
||
| ```bash | ||
| sudo apt install gdb python3 build-essential cmake libsdl2-dev | ||
| ``` | ||
|
|
||
| On Windows, use [MSYS2](https://www.msys2.org) and install the same tools in its | ||
| MinGW64 shell: | ||
|
|
||
| ```bash | ||
| pacman -S mingw-w64-x86_64-gdb mingw-w64-x86_64-python mingw-w64-x86_64-gcc \ | ||
| mingw-w64-x86_64-cmake mingw-w64-x86_64-SDL2 | ||
| ``` | ||
|
|
||
| On macOS, GDB is not usable out of the box: it has to be code-signed to control | ||
| another process, and it does not support Apple Silicon. Convert on Linux or | ||
| Windows, or in a container, for now. | ||
|
|
||
| ## Quick start | ||
|
|
||
| First, get the repository that holds the converter: | ||
|
|
||
| ```bash | ||
| git clone https://github.com/lvgl/lvgl_pro | ||
| cd lvgl_pro/tools/lvgl_app2pro | ||
| ``` | ||
|
|
||
| Next you need a compiled LVGL application. If you want to try the tool before | ||
| pointing it at your own, build the example in the repository, which creates a UI | ||
| from `lv_demo_widgets`: | ||
|
|
||
| ```bash | ||
| cmake -B examples/lv_demo_widgets/build -S examples/lv_demo_widgets | ||
| cmake --build examples/lv_demo_widgets/build -j | ||
| ``` | ||
|
|
||
| Then convert it — replace the path with your own application's when you switch: | ||
|
|
||
| ```bash | ||
| python3 lvgl_app2pro.py examples/lv_demo_widgets/build/lv_demo_widgets -o widgets_demo | ||
| ``` | ||
|
|
||
| The terminal output is the report of what happened, and it is worth reading | ||
| before anything else: | ||
|
|
||
| ``` | ||
| GDB Python: 3.12 | ||
| Running examples/lv_demo_widgets/build/lv_demo_widgets to lv_timer_handler ... | ||
|
|
||
| Wrote 3 files to widgets_demo | ||
| 1 screens, 129 widgets | ||
| 20 styles (0 shared) | ||
| 3 color consts, 16 number consts | ||
| 4 images rebuilt as PNG | ||
| 2 fonts to declare: lv_font_montserrat_16, lv_font_montserrat_24 | ||
|
|
||
| 10 event callbacks to implement (10 attachments): | ||
| slider_event_cb(lv_event_t * e) on all | ||
| chart_event_cb(lv_event_t * e) on all | ||
| ... | ||
| ``` | ||
|
|
||
| Out comes a project you can open in the | ||
| [Editor](../editor/overview) straight away: | ||
|
|
||
| ``` | ||
| widgets_demo/ | ||
| project.xml display size and LVGL version | ||
| globals.xml consts, shared styles, image declarations | ||
| screens/*.xml one file per screen | ||
| images/*.png images recovered from the binary | ||
| ``` | ||
|
|
||
| A snippet from the resulting XML: | ||
|
|
||
| ```xml | ||
| <lv_tabview active="0" tab_bar_position="top" style_layout="flex" style_flex_flow="column"> | ||
| <lv_tabview-tab_bar height="75" width="100%" style_pad_left="400"> | ||
| <lv_image align="left_mid" x="-375" src="img_lvgl_logo" /> | ||
| </lv_tabview-tab_bar> | ||
| <lv_tabview-tab text="Profile" style_layout="grid" | ||
| style_grid_column_dsc_array="content 5 content 2fr 1fr 1fr"> | ||
| <lv_label text="Elena Smith" /> | ||
| ... | ||
| ``` | ||
|
|
||
| If your application takes a command line, pass it through with `--app-args`. | ||
|
|
||
| ## Features and limitations | ||
|
|
||
| **What converts** | ||
|
|
||
| - ✅ Every screen, from one stop | ||
| - ✅ The widget tree, with each widget's properties, states and flags | ||
| - ✅ Elements of complex widgets, like chart series, tabview tabs and scale | ||
| sections | ||
| - ✅ [Flex](../syntax/styles) and Grid layouts | ||
| - ✅ `LV_PCT()` and `LV_SIZE_CONTENT`, as `50%` and `content` | ||
| - ✅ [Styles](../syntax/styles): local ones as inline attributes, shared ones as | ||
| named styles in `globals.xml`. Theme styles are ignored. | ||
| - ✅ [Images](../syntax/images) stored as a C array are recreated as PNG | ||
| - ✅ Colors and repeated numbers are turned into | ||
| [consts](../syntax/constants) | ||
| - ✅ [Event](../syntax/events) hooks, as `<event_cb>` with the callback name and | ||
| trigger | ||
|
|
||
| **Limitations** | ||
|
|
||
| - ❌ Only LVGL v9.5 is supported | ||
| - ❌ No [components](../syntax/components) are created. Each screen contains all | ||
| its widgets in one screen XML file. | ||
| - ❌ Event callback code needs to be added manually. See the `TODO` comments in | ||
| the XML. | ||
| - ❌ [Animations](../syntax/animations) are ignored. If a value was being | ||
| animated, the value it had when the UI was read is used on the widget. | ||
| - ❌ [Fonts](../syntax/fonts) cannot be restored from the app. The names are | ||
| reported where possible, but the fonts have to be added to `globals.xml` | ||
| manually. | ||
| - ❌ [Subjects and bindings](../syntax/data-binding) | ||
| - ❌ [Translations](../syntax/translations) | ||
| - ❌ Timers | ||
| - ❌ Compressed and indexed images are reported but not restored | ||
| - ❌ Screens that do not exist yet, but are created later. Only the screens | ||
| available initially are converted. | ||
| - ❌ Constant and define names are not kept; generated names are used instead. | ||
|
|
||
| ## Improvement options | ||
|
|
||
| The converted project opens and renders as it is, so everything below is | ||
| optional polish. Do it yourself, or hand the project and the report to an AI | ||
| agent: | ||
|
|
||
| 1. **Read the report** the run printed, to see what could not be converted. | ||
| 2. **Open the project in the LVGL Pro Editor**, where the | ||
| [preview](../syntax/preview) shows every change as you make it. | ||
| 3. **Add the fonts** in `globals.xml`, then set `text_font` where the `TODO` | ||
| comments point. | ||
| 4. **Add the events** in `<project_name>.c`, using the callback names and | ||
| triggers the XML already declares. | ||
| 5. **Add the animations, timers and data bindings** the app had. | ||
| 6. **Create XML components** from the repeating widget subtrees, so the screens | ||
| stop repeating themselves. | ||
|
|
||
| ## How does it work? | ||
|
|
||
| 1. The app is started under GDB with | ||
| [LVGL's plugin](https://docs.lvgl.io/master/debugging/gdb_plugin.html) | ||
| loaded, and stopped at the **second** `lv_timer_handler()` call. By then LVGL | ||
| has run a full cycle, so layouts are calculated and coordinates are real. | ||
| 2. Every screen is dumped from that one stop. `lv_display_t` keeps them all in | ||
| `screens[]`, so they are alive at the same time and `act_scr` only says which | ||
| is shown. | ||
| 3. Each widget is read for its properties, styles, states, flags and events, and | ||
| compiled-in image data is pulled out of memory. | ||
| 4. The result is written as XML, using the widget schemas that ship with the | ||
| tool to decide which properties a widget has, what type each one is, and what | ||
| its enum values are named. | ||
|
|
||
| Two things are measured rather than assumed. The version of LVGL the app was | ||
| built against is read out of the binary and used to pick the matching schemas, | ||
| so the XML only uses properties and enum values that version has. And class | ||
| defaults are probed by creating one widget of each class in the running process, | ||
| reading what LVGL's constructor and theme gave it, then deleting it — everything | ||
| written out is a difference from that, which is why a converted checkbox is not | ||
| littered with the flags every checkbox has. | ||
|
|
||
| ### Where to stop | ||
|
|
||
| The application is stopped once, and by default that is the second | ||
| `lv_timer_handler()` call. If yours builds its screens later than that, say so: | ||
|
|
||
| ```bash | ||
| python3 lvgl_app2pro.py ./my_app --stop-at my_ui.c:412 | ||
| ``` | ||
|
|
||
| Every screen is reached from that single stop, because LVGL keeps them all alive | ||
| in `screens[]` at the same time. Two cases cannot be reached and are reported | ||
| rather than hidden: screens built lazily when the user first navigates to them, | ||
| and screens freed with `lv_screen_load_and_delete()`. | ||
|
|
||
| ## Options | ||
|
|
||
| | Option | | | ||
| | --- | --- | | ||
| | `--stop-at LOCATION` | Any GDB location. Default `lv_timer_handler` | | ||
| | `--app-args ARGS` | Command line to start the app with, if it takes one | | ||
| | `--lvgl DIR` | Load `scripts/gdb` from an LVGL checkout instead of the plugin shipped with the tool | | ||
| | `--schema DIR` | Widget schemas to use, if not the ones shipped with the tool | | ||
| | `--number-consts N` | Name numbers used N or more times. `0` keeps them inline. Default 3 | | ||
| | `--include-layers` | Also convert the bottom/top/system layers | | ||
| | `--keep-fonts` | Write `text_font` even though the font cannot be declared | | ||
| | `--dump FILE` / `--from-dump FILE` | Save the raw JSON, or convert a saved one offline | | ||
|
|
||
| ## If something converts wrong | ||
|
|
||
| Open an issue with the C code that built it and the XML you got. A widget that | ||
| converts badly makes a good bug report, because it becomes a test case: the | ||
| tool's own test suite builds two applications, converts them through GDB, and | ||
| compares both the XML and the rendered screenshots against committed references. | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| --- | ||
| title: Migration | ||
| description: Bring an existing user interface into LVGL Pro, so you can keep the UI you already have and continue it in XML. | ||
| --- | ||
|
|
||
| You do not have to start from scratch to use LVGL Pro. If you already have a | ||
| working user interface, it can be brought across and continued in XML. | ||
|
|
||
| <Cards> | ||
| <Card | ||
| icon={<FileCode />} | ||
| href="./from-c-code" | ||
| title="From C code" | ||
| > | ||
| Convert a running LVGL application into an LVGL Pro XML project, by reading | ||
| the finished UI out of the process instead of parsing the source. | ||
| </Card> | ||
| </Cards> | ||
|
|
||
| ## What migration can and cannot do | ||
|
|
||
| A migration tool moves the **user interface**: the widget tree, the layout, the | ||
| styles, the text and the images. That is the part that is tedious to redo by | ||
| hand and easy to get subtly wrong. | ||
|
|
||
| It does not move your **application logic**. Event handlers, data binding and | ||
| state machines stay yours to connect, and the tools tell you exactly which ones | ||
| to wire up rather than dropping them silently. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| { | ||
| "title": "Migration", | ||
| "pages": [ | ||
| "from-c-code" | ||
| ] | ||
| } |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
P3: The claim that "Only LVGL v9.5 is supported" is inaccurate. The tool ships widget schemas for both
v9.4.0andv9.5.0(lvgl_widgets_xml/), andschema.py#find_schema_dirpicks an exact match for the app's detected version and falls back to the newest available set for anything newer. This also contradicts this same page's "How does it work" point 4, which says the version is read from the binary to pick the matching schemas. List the actually supported versions (or say schemas ship for v9.4 and v9.5, with newer apps converting against the newest set) so users with a v9.4 app are not misled.Prompt for AI agents