lvgl / lv_demos

Examples, tutorials and applications for the LVGL embedded GUI library
https://lvgl.io
492 stars 366 forks source link

Mpy examples #85

Closed uraich closed 3 years ago

uraich commented 3 years ago

These are my micropython examples. I tried to do a "word by word" translation from the C originals. The examples using images may not run yet on the simulator. However, I prepared examples with the extension ...sim.py for which a conversion to a program running on the simulator should be easy. Only the filename of the binary file must be changed to the URL of the binary image. Please check

kisvegabor commented 3 years ago

Thank you very much! It will be extremely helpful.

@embeddedt What is the current state/idea of integration into the docs?

embeddedt commented 3 years ago

I should be able to generate links to the simulator for each of these as long as the names and paths are consistent with the C examples (looks like they are).

uraich commented 3 years ago

The names are compatible except for:

embeddedt commented 3 years ago

It would be nice to have a button to show or hide the code the same way it is done for the C code.

That should be doable.

We can however rename them to e.g. lv_ex_gauge_2.py for the file that runs on the simulator and lv_ex_gauge_2_png.py for the one that does not.

That would probably be best. We can thus avoid needing to make a symlink for all the working examples.

kisvegabor commented 3 years ago

Great! Let me know if it's ready to merge or need assistance from my side.

amirgon commented 3 years ago

The examples using images may not run yet on the simulator.

@uraich Why? Did you try the raw images instead of PNG? Let me if you need any advice.

uraich commented 3 years ago

@amirgon: Maybe I expressed myself wrongly. I have 2 versions of the programs: One using raw images, which work on the simulator and the corresponding ones using png images, which work only on the unix/SDL version or on the hardware. No problem there.

uraich commented 3 years ago

I wrote another little micropython example for the canvas. This is an analogue watch, showing time and date. The app updates every sec. If you want to have a look at the code, you will find it here: https://github.com/uraich/lv_mpy_examples/blob/main/widgets/canvas/aclock.py

image

kisvegabor commented 3 years ago

@uraich It's not necessary to keep the C and Mpy examples in sync. So if you wish, you can add a clock example to this PR.

amirgon commented 3 years ago

I wrote another little micropython example for the canvas. This is an analogue watch, showing time and date. The app updates every sec. If you want to have a look at the code, you will find it here:

Very nice!

A few questions/comments:

uraich commented 3 years ago

Hi Amir, Thanks for your comments. I will try to improve. In fact I am about to write a few programs to be run on the LilyGo t-watch 2020 which uses an ESP32 and time exists there. Which method should I use to get the current time in a portable fashion? and how do I make the hands a widget? I have seen no docs on how to make custom widgets. The t-watch programs I have so far are:

embeddedt commented 3 years ago

utime is available on the simulator and STM32 last time I checked. I assume it's also available on Unix and ESP32.

uraich commented 3 years ago

I modified the analogue clock according to the recommendations by embeddedt and amir. (I still don't know, how to create a widget for the hands) When trying to run it on the simulator however, I get an error when I try to create the update task. The code works ok on the SDL unix port.

amirgon commented 3 years ago

Which method should I use to get the current time in a portable fashion?

Apparently, Micropython's JS port contains an incomplete utime module, and real-time clock functionality is completely missing.
Other ports (unix, esp32, etc.) seem to have a more complete utime implementation. It seems to be a problem specific to the JS port utime implementation, I've opened an issue on Micropython: https://github.com/micropython/micropython/issues/6670

how do I make the hands a widget?

I didn't do it a lot, but here is an example of a pure python custom widget. It's only a simple line, but it demonstrate how to write the design and signal callbacks required for implementing a new widget.

When trying to run it on the simulator however, I get an error when I try to create the update task. The code works ok on the SDL unix port.

The only problem is the missing functions in utime module. When removing them, it works fine (although cannot point to the correct time)

embeddedt commented 3 years ago

Perhaps the example can detect the presence of the functions in utime and just use a fake time if they aren't present?

amirgon commented 3 years ago

Perhaps the example can detect the presence of the functions in utime and just use a fake time if they aren't present?

Good idea!

uraich commented 3 years ago

You may want to check this one as well: image You find it here: https://github.com/uraich/lv_mpy_examples/blob/main/t-watch_examples/calculator.py

embeddedt commented 3 years ago

It works well in the online simulator, though I had to remove the while True: pass loop, as the simulator does not need it, and crashes if it is present.

uraich commented 3 years ago

Well, I uploaded the program as I run it in the lvgl unix port, where the loop is needed. I expected that you had to remove the loop for the simulator.

amirgon commented 3 years ago

Well, I uploaded the program as I run it in the lvgl unix port, where the loop is needed.

@uraich That loop is not necessarily needed for the unix simulator. I usually run the unix simulator with -i option, which opens the REPL after loading the script and would not close it until you exit. When running with the -i option you should not put this endless loop, otherwise you would not reach the REPL.

uraich commented 3 years ago

Hi Amir, I tried your custom widget, which works fine on the simulator but gives me trouble when running it on the lvgl unix port (v7.8). I see the line coming up. When pressing it it changes direction as expected. However, when releasing the mouse pointer it does not switch back. I see the release event but design is not called. I am interested in it because I would like to create 2 custom widgets:

amirgon commented 3 years ago

I see the line coming up. When pressing it it changes direction as expected. However, when releasing the mouse pointer it does not switch back. I see the release event but design is not called.

@uraich you are right, there is a small bug in this script - I forgot to call invalidate. Here is a fixed script which calls invalidate whenever the pressed state changes. It should work correctly now.

uraich commented 3 years ago

Thanks Amir, Now it works as expected.

uraich commented 3 years ago

Sorry, I have another problem: I try to follow this tutorial and write the examples in Python: https://blog.lvgl.io/2018-12-13/extend-lvgl-objects I guess that instead of adding fields to the extended data structure I should use class variables in Python? When trying to follow the temperature button example I can get the original design_cb with self.get_design_cb, however I get a blob that is not callable. How do I have to do this correctly?

amirgon commented 3 years ago

I guess that instead of adding fields to the extended data structure I should use class variables in Python?

Correct. You can't access the extended data structure from Python, but you can extend the Python object which is really a cleaner approach than using C structs.

I can get the original design_cb with self.get_design_cb, however I get a blob that is not callable. How do I have to do this correctly?

Today the Micropython binding does not provide a way to call a C function pointer.
I've added an enhancement issue for that: https://github.com/lvgl/lv_binding_micropython/issues/110

In the meanwhile there might be a workaround. Have a look at how signal_cb is called on the example script:

        saved_signal = self.get_signal_cb()
        self.set_signal_cb(self.ancestor_signal)
        res = lv.signal_send(self, sign, param)
        self.set_signal_cb(saved_signal)       

In case of design there is no lv.design_send function, but it's very easy to add one in the C code for that purpose.

uraich commented 3 years ago

Are there still problems with this pull request?

amirgon commented 3 years ago

lv_colors.py is already in lib. Do we need it here as well?

uraich commented 3 years ago

No, wd need it only onceSent from my Samsung Galaxy smartphone. -------- Original message --------From: Amir Gonnen notifications@github.com Date: 12/11/20 21:22 (GMT+01:00) To: lvgl/lv_examples lv_examples@noreply.github.com Cc: Uli Raich uli.raich@gmail.com, Mention mention@noreply.github.com Subject: Re: [lvgl/lv_examples] Mpy examples (#85) lv_colors.py is already in lib. Do we need it here as well?

—You are receiving this because you were mentioned.Reply to this email directly, view it on GitHub, or unsubscribe. [ { "@context": "http://schema.org", "@type": "EmailMessage", "potentialAction": { "@type": "ViewAction", "target": "https://github.com/lvgl/lv_examples/pull/85#issuecomment-743407831", "url": "https://github.com/lvgl/lv_examples/pull/85#issuecomment-743407831", "name": "View Pull Request" }, "description": "View this Pull Request on GitHub", "publisher": { "@type": "Organization", "name": "GitHub", "url": "https://github.com" } } ]

uraich commented 3 years ago

I saw that reading the binary images did not work for the ili9341. The reason is the different colour format as compared to the SDL driver. I created the rgb565 binary image files and added them. The programs using binary images where adapted to this change.

amirgon commented 3 years ago

The reason is the different colour format as compared to the SDL driver.

That's right, and imagetools.py contains special conversion functions for that purpose ("convert_rgba8888_to_XXXX").
It automatically detects the required color mode and performs a conversion if needed.

These functions are currently used only for PNG decoding, but they can also be used with binary images saved in rgba8888 format.

uraich commented 3 years ago

Thanks for the hint. I now have 2 versions of binary image files: xxx.argb (for the rgb8888 format) and xxx.rgb565 (for the 16 bit format with swap) I created the files from the C image code. I also converted blue_flower_argb.bin to blue_flower_rgb565.bin. All image examples in

kisvegabor commented 3 years ago

If you agree I merge this PR. Even if they are minor issues I'm sure it will be extremely useful for the users.

Small fixes (if needed can be) added in a new PR.

So, if you say go, I hit the merge button. :slightly_smiling_face:

uraich commented 3 years ago

I guess there will be a few issues but it will be easier for me to fix those once the PR is merged (e.g. once the image files are available on their default github locations)

kisvegabor commented 3 years ago

Okay, great! Merged.

Thank you very much for the amazing and exhaustive work, @uraich.

uraich commented 3 years ago

Can someone add the necessary "try yourself" links into the documentation? I will then check each example one be one on the simulator.

kisvegabor commented 3 years ago

@embeddedt is the wizard who can cast these magics :slightly_smiling_face:

@uraich Can you open a new issue for it?

embeddedt commented 3 years ago

Starting to work on it now.

amirgon commented 3 years ago

When trying to follow the temperature button example I can get the original design_cb with self.get_design_cb, however I get a blob that is not callable. How do I have to do this correctly?

@uraich I've added support for function pointers (https://github.com/lvgl/lv_binding_micropython/issues/110), so you can now call the returned function from get_design_cb.
Please let me know if you see any problems.

uraich commented 3 years ago

Sorry, Where did you make the changes? I pulled lv_bindings and lvgl but your example program from https://github.com/lvgl/lv_binding_micropython/issues/110 still gives me "Blob' object isn't callable" (and so does mine) It would be very nice to have a call: lv_version() returning the current version of lvgl such that I can be sure I have the latest and the greatest

uraich commented 3 years ago

Sorry, I made a super stupid mistake! Your examples works fine. Will still have to check a few things on mine. The error "Blob' object isn't callable" has gone.

amirgon commented 3 years ago

It would be very nice to have a call: lv_version() returning the current version of lvgl such that I can be sure I have the latest and the greatest

There are macros for LVGL version but macros are not accessible from Micropython. We can add an inline function that returns the version according the macros, which can be called from Micropython.

But take into account that there are multiple components, LVGL is only one them.
There's LVGL core, Micropython core, the binding scripts, drivers etc. each with its own version. Unfortunately we don't have today a consistent way to identify each version of each component, other than git commit hash.
The recent change for example didn't modify LVGL version, only updated the binding script.

embeddedt commented 3 years ago

It seems to me that most users would care mainly about the LVGL version. I have yet to see any problems with MicroPython breaking old scripts (though that could also happen down the road).

embeddedt commented 3 years ago

The documentation links are almost ready. I have run into a bit of a logic issue with the simulator and I've forgotten what the approach we discussed was.

Currently, the simulator's default program has a startup script which looks like this:

# Initialize 

import imp, sys
sys.path.append('https://raw.githubusercontent.com/littlevgl/lv_binding_micropython/master/lib')
import display_driver

This hack is used to load display_driver.py straight from the repository instead of having to bake an old version into the simulator.

The header.py file @uraich is using does not contain the lines which manipulate the import functionality, which means that the simulator is unable to find display_driver.py.

I see two possible solutions:

Which would be preferable?

uraich commented 3 years ago

I don't think you need header.py to run the demos on the simulator. When I tried this last time it worked without. However: https://raw.githubusercontent.com/littlevgl/lv_binding_micropython/master/lib does not exists any longer. It should be replaced by https://raw.githubusercontent.comlvgl/lv_binding_micropython/master/lib but maybe I missed something?

amirgon commented 3 years ago
  • Have the simulator always run the import imp, sys and sys.path.append lines regardless of what script the user provides.

This is a bit too hard-coded. What if we want to import display_driver from someplace else for some reason? What if we want to import a specific version of the display driver (v7, v8 etc.)?

Another example - what if we want to run with uasyncio? When using uasyncio LVGL event loop (which calls task_handler) is initialized differently and today the display initializes the even loop.

My opinion is that paths into github should not be hard-coded in the simulator.

embeddedt commented 3 years ago

I don't think you need header.py to run the demos on the simulator. When I tried this last time it worked without.

https://github.com/lvgl/lv_examples/blob/master/src/lv_ex_widgets/lv_ex_arc/lv_ex_arc_1.py

This example isn't importing the display driver, etc. at the top. I assume the other widget examples work similarly.

https://raw.githubusercontent.com/littlevgl/lv_binding_micropython/master/lib does not exists any longer.

I believe GitHub redirects to the new name automatically.

My opinion is that paths into github should not be hard-coded in the simulator.

In that case, I will modify header.py to check whether it is running on JS or not.

amirgon commented 3 years ago

https://github.com/lvgl/lv_examples/blob/master/src/lv_ex_widgets/lv_ex_arc/lv_ex_arc_1.py

This example isn't importing the display driver, etc. at the top. I assume the other widget examples work similarly.

I think you can add to the URL two scripts, one is the header (fixed for all examples) and the other is the example with no imports. (script and script_startup)

uraich commented 3 years ago

Hi Amir, Concerning the function pointers: I still had a problem with my program (did not return lv.RES.OK after my new design callback) but once I found that, the program worked flawlessly. Thanks very much.