Open kisvegabor opened 5 years ago
@kisvegabor In 6.1 we should probably add some relevant object-related symbols (e.g. LED, line, slider).
Since the XML is unlikely to be used directly by a user (and in the case that it does, btn would be familiar as it is the abbreviation used inside the library) I think we can stick with
for now.
I agree. The short names should be fine.
I'm also very impressed! You have made a great progress.
@embeddedt
In 6.1 we should probably add some relevant object-related symbols (e.g. LED, line, slider).
I don't think it's required for two reasons:
@kaiakz I have a conceptual question: how do you plan to add the object-specific parameters? (E.g. slider range, min/max values, current value etc). By hand or by a script?
@kisvegabor @embeddedt
In 6.1 we should probably add some relevant object-related symbols (e.g. LED, line, slider).
It's application specific and probably won't be used by other users.
I found a solution:
In lv_gui_designer, I use lv_obj_user_data_t
to attach some info(id, type, etc) that we can't get by LittlevGL API(lv_get_xx/lv_set_xx). It points to a widget_info_t
structure. The structure will be allocated(malloc) when a widget is created. (There might be a memory problem because I did not free it after a widget was deleted by lv_obj_del()
). It stores:
lv_obj_t
is )lv_obj_t
then we can get all the info. I'm impressed by how far you've gotten so quickly.
Thanks to LittlevGL v6.0, lv_gui_designer is very dependent on v6.0 . For example, it uses EVENT
to create a widget, update the property displaying in Setting_Win, etc. Actually, I have worked with LittlevGL since a month ago, I am still a noob in some ways. I think a guy who masters LittlevGL may do it better and faster.
I have a conceptual question: how do you plan to add the object-specific parameters? (E.g. slider range, min/max values, current value etc). By hand or by a script?
I have planned to write a script to generate, but in fact, I wrote it by hand. It is difficult to handle a lot of the object-specific parameters, maybe I need to wrote a really complex function:
set_attribute_by_name(lv_obj_t * obj, TYPE, NAME, VALUE);
Just like a hash table. It will called relevant lvgl functions inside. It is stupid. Do you have any idea?I find an another GUI---IUP. In Attribute Guide:
IupSetAttribute(ih, name, value)
{
if ih.SetClassAttribute(name, value)==store then
ih.SetHashTableAttribute(name, value)
endif
........
}
That's fine. Now I am working on generating a XML file-----Maybe I can write lv_gui_designer by lv_designer soon. :smile:
@kaiakz Provided that you named the XML attributes to match the LittlevGL APIs, you could do something like this (pseudocode):
var function_call = "lv_" + get_obj_type_name() + "_set_" + get_xml_attr_name() + "(" + get_obj_variable_name() + ", " + get_xml_attr_val() + ");";
I am the author of lv_gui_desiner, a similar tool to lv_gui_builder but built with LittlevGL.
Now I working at project file generation. A project file should store all meta data. Editor can read and convert it into live view, and output the final C or Python code.
Hi @kaiakz !
Just wanted to throw an idea.
If you wrote your project in Python/Micropython instead of C, you wouldn't need XML file at all to describe your meta-data!
Instead, you could take advantage of the fact that Python is a dynamic introspective language, as opposed to C. Instead of using XML (or JSON or whatever) for your meta-data, you could simply create LittlevGL python objects dynamically, that would serve for several purposes:
Just an idea. Not sure it's right for you. So feel free to ignore it and keep going with your XML metadata :wink:
Instead, you could take advantage of the fact that Python is a dynamic introspective language, as opposed to C. Instead of using XML (or JSON or whatever) for your meta-data, you could simply create LittlevGL python objects dynamically, that would serve for several purposes:
@amirgon Thanks! Here are my brainstorm:
If I understood your idea well, what you means is that I can write it in Python and store all meta-data in Python code(generate by tool), just convert the Python to C if we need C code. Snce Python is an interpreted language, user can not only create/modify widget in live view, but also add a callback for it. Adding/changing a callback function in run-time is what lv_gui_designer
can't do, because you need to recomplie and re-link anythings(I know we also can complie it into dynamic .so/.dll, but recomplie is still required).
I admit that's a advntage. But if we use Python as meta-data, how can we convert Python code to C code? C is now widely used to call LittlevGL API. I think it is too difficult to Convert an Object-Oriented and interpreted language to C. C does not support class, and its type is static (you need to determine every varible's type). Maybe I need to write a super complex parser to do this. So, I chose XML to describe meta-data, it's simple.
Besides, I don't see more advantages. Add a callback in run-time is not necessay, because I let user fill the callback function after code generation, just like what Glade and Qt Creator do. It's not difficult for lv_gui_designer to embed Python. , make the project as a scripable-tool. :smile:
If I understood your idea well, what you means is that I can write it in Python and store all meta-data in Python code(generate by tool)
@kaiakz Not exactly.
What I meant is that LittlevGL python objects already contains the metadata, in a way.
I'll give you an example.
First You create some LittlevGL python object, let's say a button. You set its properties by calling setter functions, just like you do in C. For example set its position, or whatever.
Now you want to save the meta-data to a file. Meta data consists of many attributes, for example width, position, text, etc.
Instead of storing this metadata, attribute by attribute, you can use introspection on your object, to find out all member functions which start with get_
and call them one by one to extract the object state.
You don't need to know in advance which getter functions there are - you enumerate them on runtime and call them to extract the object state.
Then you can save the state (all attribute values) to file, and later load them from a file and call the corresponding setter functions.
My point is - that you don't need to keep any information about what different attributes a LittlevGL object has. This can be deduced automatically by introspection. The introspection result can be used to call functions dynamically on runtime.
Adding/changing a callback function in run-time is what
lv_gui_designer
can't do, because you need to recomplie and re-link anythings
True, this is another advantage of using python - you can define callbacks on runtime.
Or, as you suggest, let the user add them after the code is generated.
But if we use Python as meta-data, how can we convert Python code to C code? C is now widely used to call LittlevGL API.
Since LittlevGL Python/MicroPython binding is generated automatically from the C header files, I think it should be pretty simple to convert it back from Python to C. Just need to prefix some function names, add types etc. I don't think you need a complex parser for that. As an example, try to write some LittlevGL code in C and in Micropython. You will find out it's very similar.
@amirgon
Yesterday, I have tried to install lv_micropython. Sadly, I met some trouble when execute make -C ports/unix/
, then shows:
Use make V=1 or set BUILD_VERBOSE in your environment to increase build verbosity.
MPY modules/upip.py
make: ../../mpy-cross/mpy-cross: Command not found
make: *** [../../py/mkrules.mk:114: build/frozen_mpy/upip.mpy] Error 127
make: Leaving directory '/run/media/kaiakx/Data/Pro/lv_micropython/ports/unix'
I haven't solved this yet. However, your work is excellent. I tried live-demo, I tried to convert a few lines code from lv_gui_designer to Micropython, it works well. Since Micropython is developed for microcontrol, it can't support full feature of Python 3. Then I tried pylvgl. I wonder if there are any different between pylvgl and lv_micropython. It seems pygl still has a lot of things to do, and it's unstable? It confuses me a lot at the beginning of the project, so I choose to C for maximal compatibility. C & lvgl is stable.
As an example, try to write some LittlevGL code in C and in Micropython. You will find out it's very similar.
I agree with you. I think it's not difficult to rewrite lv_gui_designer by python if py supports full-lvgl, xml, SDL(or some texture display).
Yesterday, I have tried to install lv_micropython. Sadly, I met some trouble when execute
make -C ports/unix/
, then shows:Use make V=1 or set BUILD_VERBOSE in your environment to increase build verbosity. MPY modules/upip.py make: ../../mpy-cross/mpy-cross: Command not found make: *** [../../py/mkrules.mk:114: build/frozen_mpy/upip.mpy] Error 127 make: Leaving directory '/run/media/kaiakx/Data/Pro/lv_micropython/ports/unix'
I haven't solved this yet.
lv_micropython is stable and aligned with recent upstream Micropython and with lvgl v6.1. I've tested it with the unix port and ESP32 port.
Did you remember to run git submodule update --recureive --init
?
Also, please have a look at Micropython readme:
Most ports require the MicroPython cross-compiler to be built first. This program, called mpy-cross, is used to pre-compile Python scripts to .mpy files which can then be included (frozen) into the firmware/executable for a port. To build mpy-cross use:
$ cd mpy-cross $ make
Then to build the unix port, run make -C ports/unix
It should build and run fine on the unix port, so I guess you missed one of the steps above.
Since Micropython is developed for microcontrol, it can't support full feature of Python 3.
True. Micropython strives to be compatible with Python 3. It's very close, but not fully compatible.
Especially when it comes to libraries - not every library in Python 3 is available on Micropython.
There is a "standard library" for micropython which is not part of the core Micropython, and especially fits the unix port, you can have a look at it.
Then I tried pylvgl. I wonder if there are any different between pylvgl and lv_micropython.
Yes - pylvgl provides lvgl API to (non-micro) Python.
It's a different project, written and maintained by @rreilink. We once considered merging them, but I don't think this will happen any time soon.
It seems pygl still has a lot of things to do, and it's unstable?
Last time I checked there were still a lot of things missing there. These include memory allocation and deallocation by reference counting, support for structs and unions and maybe more. But perhaps @rreilink could give you an accurate status and work plan.
My suggestion above is valid for either Python or Micropython, so you can choose any of them.
I agree with you. I think it's not difficult to rewrite lv_gui_designer by python if py supports full-lvgl, xml, SDL(or some texture display).
lv_micropython supports full lvgl and SDL for display and mouse.
lv_micropython is based on upstream micropython and don't have a built-in XML support. There is another micropython fork with many additional libraries, including XML. With a little work it's possible to port lv_micropython to other micropython fork like that one.
But anyway, my point above was that if you use introspection in python/micropython - you don't need XML at all.
Yes but XML may give more readable UI layout files.
It would more easily facilitate export of UI designs to multiple languages (like C and JS) rather than just python.
Also, if UI designs are saved as some intermediate representation (ie: XML) the designs become largely decoupled from the version/variant of LVGL being used to render them.
I think there are a lot of benefits to using something like XML. It’s what is typically done in these scenarios and follows good software design paradigms.
@kisvegabor @embeddedt @amirgon This issue gives me some ideas, so I created another project. Use lvgl-micropython(WASM) and front-end component library (Bootstrap or other) to build an online designer. Just a webpage, even can work with a simple server. You can find more feature in README. Welcome to share your idea in the issue!
This issue gives me some ideas, so I created another project. Use lvgl-micropython(WASM) and front-end component library (Bootstrap or other) to build an online designer
@kaiakz it reminds me of this idea to build an lvgl designer as a web application.
It wasn't received very well so I didn't pursue it any further.
@amirgon My main reason for opposing your original proposal was that all the UI was to be made in LittlevGL. This would be very clunky and/or take a long time as LVGL wasn’t designed for such complex UI (as it stands).
The web-based designer only using LVGL for simulating the WYSIWYG view sounds like a good idea.
I have made a decent amount of progress with my python/Qt-based version. It is close to being a useful beta. Need to figure out saving and drag/drop/resize operations still.
@kaiakz, I encourage you to contribute to the python-based effort (unless you’re already further along with your builder).
I don’t see the builder being web-based as much of an advantage.
I have thought about making the builder an extension for VSCode (which would require it to be written with TypeScript)...
I have thought about making the builder an extension for VSCode (which would require it to be written with TypeScript)...
Yes, this is what a web-based builder advantage is---to be a VSCode plugin.
In fact, VSCode is a very large webpage, plugins(Nodejs, JavaScript, Type) can control the special parts. Plugin developer can't write a complex GUI by native API due to the limitation of VSCode. but we can use WebView API to insert a webpage.
A webview can render almost any HTML content in this frame, and it communicates with extensions using message passing. This freedom makes webviews incredibly powerful, and opens up a whole new range of extension possibilities.
Here are a demo gif of my project walv:
@kaiakz it reminds me of this idea to build an lvgl designer as a web application. It wasn't received very well so I didn't pursue it any further.
I think walv is more flexible and combines more advantages. walv uses JS to warp lvgl-MicroPython(WASM), both are powerful and magic. WebPage is more convienient, and gives a beautiful interface. @amirgon I have some problem
get_ext_attr
?
>>> dl=lv.ddlist()
>>> ext=dl.get_ext_attr()
>>> type(ext)
<class 'Blob'>
What is Blob? For example, DDList is made up of Page, Label, etc(ext obj), how can I get the Page in DDList?
dir
& getattr
. Is there a way to get further information about each function? So I can get how many and what arguments of each function.(a list of a function parametre) Maybe I can folk lv_mpy and import somethings, such as (doc, dict)? Thanks!How to use
get_ext_attr
?
I don't think using that function is going to yield the results you want in MicroPython. In C, it returns a pointer to an internal structure that should usually only be manipulated by LittlevGL itself. Maybe Python does something different.
how can I get the Page in DDList?
ddlist
is a page
:
Therefore, you can treat it as such.
Fist of all congrats to the progress! :+1:
Do you use 8 bit colors or the strips are because of the gif format?
Using MicroPython Online would really make some things easier. However, I need to mention that when I have created the online font and image converters I didn't publish offline versions. However, a lot of people have complained about it:
So I concluded that the offline version is important too.
I don't think using that function is going to yield the results you want in MicroPython. In C, it returns a pointer to an internal structure that should usually only be manipulated by LittlevGL itself. Maybe Python does something different.
Sorry,my question is incorrect.
how can I get the Page in DDList?
It should be how to get the ext->label in ddlist by lv_mpy. ddlist is a special page that has a label. In C, the label is in a lv_ddlist_ext_t
struct. Python has class
, I just wonder how does it work, and how can I create a new widget? Is lv_obj_allocate_ext_attr
and lv_obj_get_ext_attr
deprecated in lv_mpy? Anyways, I tried to use count_children() && get_child()
to find out DDLIST CLASS layer: ddlist---obj1---obj2, saddly, type()
only show it is a <class 'obj'>
not <class 'page'>
or <class 'label'>
@amirgon I read a blog and found:
If we just pass pointers around, this is very easy. Each pointer is wrapped in a Micopython “Blob” object which is convertible to pointer between API functions, even if they belong to different libraries. OMG, it is a bit difficult to understand for me.
@kisvegabor It's the gif format problem. Yes, walv also has the offline version that runs on Nodejs, maybe you can open the the webpage from disk if your browser supports(Firefox works) Maybe walv can import the online font and image converters.
Hi @kaiakz ! Nice progress!
@amirgon I have some problem
- How to use
get_ext_attr
?
The Micropython binding exposes LittlevGL user API to the user, but not the internals of LittlevGL. I considered the ext struct internal to the user.
You may want to use the ext attr when you extend a LittlevGL component in C, but not when doing so in Micropython. In python it makes more sense to inherit from a parent object and add functionality in the children. In this example you can see how SymbolButton
inherits from lv.btn
the python way, no need to use the ext attr.
What is Blob?
Think of a Blob as a simple wrapper for a C pointer.
It's a micropython object who's payload is the C pointer value.
The micropython binding is using Blob when a function returns or receives void *
, for example.
If a function returns a Blob, you can pass it on to a function that receives Blob, thus passing pointer around.
It's also possible to dereference a pointer or to cast it to a struct if you need to, more details are exaplained in a blog post about pure micropython display driver. But that is advanced functionality, I'm not sure you need it in your project.
Alternative to inspect.getargspec(). MicroPython does not support inspect well, and can only use
dir
&getattr
. Is there a way to get further information about each function? So I can get how many and what arguments of each function.(a list of a function parametre) Maybe I can folk lv_mpy and import somethings, such as (doc, dict)?
That's right, currently Micropython does not provide a native way to inspect number of arguments and their types. This is related to the fact that Micropython is targeted to Microcontrollers so some functionality was removed to save RAM/ROM or to improve performance.
If that's really needed, I could add this information to Micropython at the expense of more program memory. But is it really needed? Please explain - what is the use-case?
In lvgl most getters don't receive any parameters (get_height
) and setters get one parameter (set_height(h)
), and if there's more than one option you can always catch an exception and try again the other option.
how can I get the Page in DDList?
ddlist
is apage
:Therefore, you can treat it as such.
I tried to use
count_children() && get_child()
to find out DDLIST CLASS layer: ddlist---obj1---obj2, saddly,type()
only show it is a<class 'obj'>
not<class 'page'>
or<class 'label'>
We had a discussion about inheritance, related to the Python/Micropython bindings. (here is a link)
The way lvgl is designed now (@kisvegabor , @embeddedt correct me if I'm wrong), is that each component inherits only from lv_obj
. If a component such as DDList is also a page, it still inherits from lv_obj
but implements all lv_page
functions, so you can treat it as such.
It's a bit strange from Object Oriented perspective, but it makes things simpler in C.
Did you find a function in lv_page
that is missing in DDList? In such case, it should be added to DDList on lvgl upstream.
ddlist is a special page that has a label. In C, the label is in a
lv_ddlist_ext_t
struct.
No I think ddlist itself is the page, and all page functions should be accessible on it, without using the "ext" object.
and how can I create a new widget?
By inheriting in python, not by using the "ext" struct.
Is
lv_obj_allocate_ext_attr
andlv_obj_get_ext_attr
deprecated in lv_mpy?
No lvgl function is deprecated in lv_mpy. But structs internal to objects (that shouldn't be part of the API exposed to the user, such as lv_ddlist_ext_t
) are not exported to python so you only get a pointer but no convenient way to access the struct fields.
OMG, it is a bit difficult to understand for me.
Feel free to ask! :smile:
Maybe there was something I didn't explain good enough. I'll try to make it clearer but please let me know what wasn't clear to you.
Yes, walv also has the offline version that runs on Nodejs, maybe you can open the the webpage from disk if your browser supports(Firefox works)
My opinion is that it's best to have both online and offline instaces of the designer, since the same effort could yield them both!
each component inherits only from lv_obj. If a component such as DDList is also a page, it still inherits from lv_obj but implements all lv_page functions, so you can treat it as such.
This is the utopian perspective of the API. :smiley:
In practice, we haven't actually gone through and ensured that all lv_page
functions are reimplemented for the ddlist with the lv_ddlist
prefix. For example, lv_page_get_scrl
exists on page
but not on ddlist
.
To solve that, I think @amirgon's generator script goes through and automatically adds the relevant "parent" methods to each object. However, the same API is not necessarily available on the C end.
To solve that, I think @amirgon's generator script goes through and automatically adds the relevant "parent" methods to each object. However, the same API is not necessarily available on the C end.
Actually, I don't think I'm really adding "parent" methods automatically now, except from lv_obj
methods.
@embeddedt - Let's discuss this from C user perspective. Is it ok to call any lv_page
methods on lv_ddlist
? What about methods with the same name that override each other?
Here is a quote from a previous discussion about inheritance:
The API of the objects are designed to use only lvtype... and lvobj... with them. For example, you shouldn't use lv_btnm_set_map() directly on a keyboard. I wasn't sure there are no naming conflicts within common words like width but @rreilink confirmed that luckily it's fine. Anyway a review on API function names with this in mind might be required.
Doesn't it mean that lv_ddlist
must implement every function on lv_page
(at least as a static inline function)?
Actually, I don't think I'm really adding "parent" methods automatically now, except from lv_obj methods.
That was mainly what I was referring to. Usually parent methods are only used for highly advanced special cases.
Is it ok to call any lv_page methods on lv_ddlist?
It depends. Usually, you only need to do that when you want to do something advanced (like modifying the behavior of the object). Functions providing commonly used features are usually redeclared with the correct prefix (see below).
What about methods with the same name that override each other?
In that case, for safety, you should always use the one that matches your object (i.e. ddlist
).
It depends. Usually, you only need to do that when you want to do something advanced (like modifying the behavior of the object). Functions providing commonly used features are usually redeclared with the correct prefix (see below).
So what's the takeaway?
Suppose there's a missing function in lv_ddlist
, that appears in lv_page
, how do we access it from Micropython?
lv_ddlist
on lvgl upstream?lv_page
) functions to lv_ddlist
? This would inflate the API with many functions that are usually not used.lv_ddlist
to lv_page
in Micropython in order to access lv_page
methods?Should it be added (as a static inline function) to
lv_ddlist
on lvgl upstream?
Yes; I think so. Someone (i.e. me) probably needs to review all of the objects and fix the situation.
@amirgon @embeddedt
Maybe we can use super()
and multiple inheritance
? for example, class win(obj, page, header). But LittlevGL in C doesn't use multiple inheritance, lv_win is just a lv_obj with lv_obj(header), etc.
each component inherits only from lv_obj. If a component such as DDList is also a page, it still inherits from lv_obj but implements all lv_page functions, so you can treat it as such.
OK, I know why ddlist
in LittlevGL is page
, but is obj
in lv_mpy. In my opinion, if ddlist inherits from page that inherits from obj, ddlist should own all method of obj and page.
Should the binding add all parent (lv_page) functions to lv_ddlist? This would inflate the API with many functions that are usually not used.
I think lv_xxx_ext_t
gives us a flexible way to control the children of a widget. For example, we use lv_win_get_content to get ext->page, and then we can use all lv_page_xxx
function to control it, we don't need to add lv_win_page_xxx
. In lv_mpy, we use class and method, method can only modify its class instance If all widget inherit from obj
, maybe it will lose flex. For example:
lv_obj_t
, but each lv_obj_t
is different: lv_win_create
return-value and lv_page_create
return-value. lv_win_get_content
will return a type lv_obj_t
of lv_page
.win
is a win
class, page
is a page
class. But win.get_content()
will only return a obj
instance, not a page
class instance, so we can't use any method of page
class .To solve this:
Actually, I don't think I'm really adding "parent" methods automatically now, except from
lv_obj
methods.
- Bind all parent method.
- Or have a way to "cast" lv_ddlist to lv_page in Micropython in order to access lv_page methods
- Or insert a python-dict as c-ext-struct in class, use
get_ext_attr
method to get the dict. For instance,ext={'page' : page, 'title' : label, 'header' : obj}
That was mainly what I was referring to. Usually parent methods are only used for highly advanced special cases.
I can confirm this. Imagine, for example, the case of lv_sw
. It is inherited from lv_slider
and lv_bar
however it doesn't make sense to have an lv_sw_set_value
or lv_sw_set_range
. Instead we have lv_sw_on
and lv_sw_off
. Or lv_bar
has lv_bar_set_sym
to draw the indicator from the zero value instead of from the left. It also doesn't make sen for lv_sw
.
So there are 4 cases:
lv_win_set_title
.lv_slider_set_value
just a static inline
version of lv_bar_set_value
.lv_roller_set_options
is more than simply calling lv_ddlist_set_options
.IMO adding all intermediate APIs would be quite confusing.
Should we have a way to "cast" lv_ddlist to lv_page in Micropython in order to access lv_page methods?
It should work for the rare special cases when an "non-API" thing needs to be used.
In addition, we can add some functions for the less rare cases such as lv_ddlist_get_scrl
but not lv_ddlist_set_scrl_fit
.
It should work for the rare special cases when an "non-API" thing needs to be used. In addition, we can add some functions for the less rare cases such as
lv_ddlist_get_scrl
but notlv_ddlist_set_scrl_fit
. Good idea. Since Python supports OO, I think we don't need some C-style function at all. See below: Or insert a python-dict as c-ext-struct in class, use get_ext_attr method to get the dict. For instance, ext={'page' : page, 'title' : label, 'header' : obj} You said all widgets inherit fromobj
, so here are some code:class win(lv.obj): #Init something page = lv.page(par) header = lv.obj(page) title = lv.label(header) ext = {'page' : page , 'header' : header, 'title' : title} #....... def get_ext(self): return self.ext
@kisvegabor Then we don't need
lv_win_set_title
,lv_win_get_content
or other at all. Justext=win.get_ext()
, to access title, useext['title']
, it is equal to alabel
class instance, so we can use alllabel
method. Just similiar tolv_xxx_ext_t
in C: to access something useful.
@amirgon
If that's really needed, I could add this information to Micropython at the expense of more program memory. But is it really needed? Please explain - what is the use-case? Maybe my project need it, so I plan to folk lv_mpy and customize it. It is said that you use some tool to generate code, it might not difficult to add some information(inspect number of arguments and their types), and add more docs(convert some LittlevGL C comment) which is helpful for beginner(me, I always forget how many augments is needed), walv has a lv_mpy terminal for user to write mpy code directly, and also have a 'Get Start' for beginner to learn how a widget is created by write codes. Can you guide me for this? I am also want make lv_mpy on wasm faster and add new module, function. @embeddedt Do you konw how to swap data between lv_mpy_wasm and js? There is some js function for lv_mpy_wasm to excute code, but how can I get the response after lv_mpy_wasm excuting the code more efficiently? Now I just use js to catch the
Maybe we can use
super()
andmultiple inheritance
? for example, class win(obj, page, header). But LittlevGL in C doesn't use multiple inheritance, lv_win is just a lv_obj with lv_obj(header), etc.
In Micropython there is a strict distinction between native types and user types.
LittlevGL Micropython objects are native types.
Currently inheritance is not supported for native types, and there's an open PR for supporting single inheritance, not multiple.
On Micropython binding I'm using a trick - instead of inheriting each object from lv_obj
, I'm adding all of lv_obj
functions to each and every LittlevGL object. It's less efficient than inheritance (costs a little more program memory) but it exposes all base lv_obj
functions on every object.
I would like to avoid adding more functions that are rarely used and bloating lvgl objects even more.
OK, I know why
ddlist
in LittlevGL ispage
, but isobj
in lv_mpy. In my opinion, if ddlist inherits from page that inherits from obj, ddlist should own all method of obj and page.
DDList object contains all lv_obj
methods, and relevant methods from lv_page
. Which method is missing?
In case of missing methods - we should consider adding them in lvgl upstream as ddlist inline static functions.
- In lv-mpy,
win
is awin
class,page
is apage
class. Butwin.get_content()
will only return aobj
instance, not apage
class instance, so we can't use any method ofpage
class .
If you need to access page
methods of the page returned by lv_win_get_content
, we may need to add functionality to cast from lv_obj
to lv_page
to the Micropython binding.
@kisvegabor - I think there is actually something missing in lvgl coding conventions:
Is there a way to automatically deduce the type of a returned lv_obj_t
?
Today the user has to know (from the docs) that that returned lv_obj_t
is actually a page (or some other object), in order to know which methods he could call.
Actually, I don't think I'm really adding "parent" methods automatically now, except from
lv_obj
methods.
- Bind all parent method.
That wouldn't solve the problem, as long as lv_win_get_content
returns lv_obj_t
.
If we can convert lv_obj
to some other object, you don't need to bind parent methods - they are all exposed (should be) on the child.
Good idea. Since Python supports OO, I think we don't need some C-style function at all. See below: Or insert a python-dict as c-ext-struct in class, use get_ext_attr method to get the dict. For instance, ext={'page' : page, 'title' : label, 'header' : obj}
LittlevGL API is built from python native objects, not python user objects, so this would not work.
However, it should be possible to automatically add a "super" function that returns the current object casted to the parent object type.
To get internal objects (such as the label of ddlist) we would need to
lv_obj
to other objects (such as lv.label
)Maybe my project need it, so I plan to folk lv_mpy and customize it
Instead of creating another fork, if that's generic functionality, let's consider adding this to the original.
It is said that you use some tool to generate code, it might not difficult to add some information(inspect number of arguments and their types)
That's what I meant when I said I can add functionality to extract information about the number and types of the arguments.
However, it's important to remember that lv_mpy is targeted to microcontrollers, and this extra meta-data would consumes resources (mainly program memory).
A possible solution could be to make this extra meta-data optional, such that it would be excluded by default on embedded architectures, but included on the js port.
Can you guide me for this?
This is the script that generates lvgl bindings. All the logic is there.
If we decide on a feature that is general enough and also useful for microcontrollers, I'll try to implement it for lvgl bindings. (for example, the casting we discussed above).
@kisvegabor - I think there is actually something missing in lvgl coding conventions: Is there a way to automatically deduce the type of a returned
lv_obj_t
? Today the user has to know (from the docs) that that returnedlv_obj_t
is actually a page (or some other object), in order to know which methods he could call.
In pylvgl, I use lv_obj_get_type();
on every lv_obj_t
which is returned from lvgl, in order to return a Python object of the appropriate type.
In pylvgl, I use
lv_obj_get_type();
on everylv_obj_t
which is returned from lvgl, in order to return a Python object of the appropriate type.
Thanks for the suggestion.
Since Micropython is targeted to embedded architectures, I would prefer not doing this on runtime and waste cycles (string processing) every time lv_obj_t
is returned.
If a specific function that returns lv_obj_t
is always returning a specific object type, it's best to deduce it from the code on build time.
Is there a way to automatically deduce the type of a returned
lv_obj_t
?
Only by using lv_obj_get_type
, like @rreilink suggested.
Today the user has to know (from the docs) that that returned lv_obj_t is actually a page (or some other object), in order to know which methods he could call.
Right. But since the only place where you actually retrieve an lv_obj_t *
is at the lv_xxx_create
call, it's not that confusing to keep track of, for a human.
lv_page_create
returns lv_page
lv_ta_create
returns lv_ta
So the easiest way to "remember" the type of an lv_obj_t
(if that is important) is just to know which lv_xxx_create
function you called.
@embeddedt Let's take lv_win_get_content
for example.
It always returns lv_page
, but its return value is lv_obj_t
.
My point was that if lv_win_get_content
returned something like lv_page_t
, it would make more sense, be more clear for the user and allow an automatic script to deduce the returned object's type.
At present, the user must know (from the docs) that lv_win_get_content
returns a page, or has to use lv_obj_get_type
which is more expensive from cycle budget perspective (string processing) and adds complexity.
is more expensive from cycle budget perspective (string processing)
Isn't QSTR supposed to mitigate that problem?
Isn't QSTR supposed to mitigate that problem?
Not in this case. lv_obj_get_type()
returns the data as an array of strings, not QSTRs.
QSTRs are only used internally in Micropython to represent object names, attr names, function names etc.
What if we added a second API (lv_obj_get_type_num
) that represents the type with an enum instead of a string? Would that work?
What if we added a second API (
lv_obj_get_type_num
) that represents the type with an enum instead of a string? Would that work?
It's better than string processing, but still -
My suggestion is a generic naming convention that would allow identifying a returned object type when processing the H files during build time.
Let's find out what @kisvegabor thinks about it.
If I understood well the "type of return value" issue affects only the get
and create
functions which returns lv_obj_t *
. If so it seems possible to introduce a naming convention for it. For example change lv_win_get_content
:arrow_right: lv_win_get_content_page
. So the last word could be the type.
An other example: lv_page_get_scrl
:arrow_right: lv_page_get_scrl_cont
What do you think?
What do you think?
How about, instead of returning lv_obj_t
- return lv_win_obj_t
which can be a typedef to a lv_obj_t
?
This would not require changing the function names, so it's not a breaking change.
@amirgon
How about, instead of returning lv_obj_t - return lv_win_obj_t which can be a typedef to a lv_obj_t?
I think it would confuse users because they wouldn't know that it's an lv_obj_t
in the background and they might expect lv_btn_create
to return lv_btn_obj_t
.
However, I understand your concerns about lv_obj_get_type_num
... Let's continue thinking...
@kaiakz I've added "casting" support to lvgl objects. (you would need to pull latest lv_micropython, and update git submodules).
Here is an example, which could be useful by itself, to extract object's children and cast each of them to the right type using the get_type
function of lv_obj
:
import lvgl as lv
def get_children(obj):
result = []
n = obj.count_children()
child = None
for i in range(0,n):
child = obj.get_child(child)
child_type = lv.obj_type_t()
child.get_type(child_type)
type_name = child_type.type[0][3:]
child_mp_type = getattr(lv, type_name)
result.append(child_mp_type.__cast__(child))
return result
Then you can call get_children(lv.scr_act())
, or with any other container object.
Does it solve your problem there?
@amirgon Oh, thanks! I'd like to have a try.
For lv_gui_designer and walv, my goal is to use LittlevGL in standard API and only generates code to standard LittlevGL API. So I use some feature LittlevGL provides: drag attribute
, event
and user_data
.
Why I need to get children of a ddlist
is to make ddlist
draggable. I just use a simple and stupid way: set_drag(True)
, but it doesn't work for some widgets: ddlist
, page
, roller
, because children are designed for un-draggable, some drag signal won't pass to parent. So I find another way two days ago: .get_child(None).set_drag_parent(True)
. It works. Just a compromise.
Maybe you have some better idea, please let me know.
I also have some problem about event
and user_data
, and I have opened an issue in lv_mpy.
So I find another way two days ago:
.get_child(None).set_drag_parent(True)
. It works. Just a compromise.
Instead of dragging the element itself, how about painting the element on a canvas and dragging only the element's "image"? When the user places the element, you can create it there as a real element.
I never tried this but I think it could work, perhaps @kisvegabor or @embeddedt could comment on that.
how about painting the element on a canvas and dragging only the element's "image"?
If you are referring to dragging the canvas object, that could work, but it would add additional complexity. I think the method that @kaiakz is using now is fine.
This gif shows where my implementation currently stands as a desktop/Qt-based application. All the editable parameters and placeable objects are inferred using python introspection so effort to support littlevgl updates and new widgets is minimized:
Still need to implement dragging and resizing objects using the mouse. Then saving and exporting to code.
@AGlass0fMilk Congratulations!
About dragging, you can refer to my implement, or bind the QRect
with the widget: when I drag the highlight rectangle, adjust the position and size(resize the rectangle) of the widget dynamically.
I noticed your project has highlight
, that's what I also want to achieve. I have some way to implement in the issue
@kisvegabor @embeddedt Maybe you can give me some details about lvgl.
@amirgon
Instead of dragging the element itself, how about painting the element on a canvas and dragging only the element's "image"? When the user places the element, you can create it there as a real element. I never tried this but I think it could work, perhaps @kisvegabor or @embeddedt could comment on that.
With multi-display support, it's possible to create a dummy screen whose buffer is the canvas thus after a refresh LittlevGL will draw objects there. If you are interested I can shaw an example.
@kaiakz I've commented on the new issue.
@kisvegabor @embeddedt @amirgon
With multi-display support, it's possible to create a dummy screen whose buffer is the canvas thus after a refresh LittlevGL will draw objects there. If you are interested I can show an example.
I am interested in it, for I want to support more feature. Now you can try the online demo. Sorry for its ugly interface. :sweat_smile:
@kaiakz Nice!
Instead of making the user download multiple files immediately, maybe it's better to let them preview the header and source files, and then choose to download them (or copy them).
You might want to indent child objects based on their position in the hierarchy. I was confused about why all my objects were being created as children of the button - until I realized that objects are created as children of whatever you have selected.
You might want to indent child objects based on their position in the hierarchy. I was confused about why all my objects were being created as children of the button - until I realized that objects are created as children of whatever you have selected.
I think the title Select A Parent
is better than Select A Widget
:smiley: I plan to change the web-UI with more widget(bootstrap has no treeview and switch).
Instead of making the user download multiple files immediately, maybe it's better to let them preview the header and source files, and then choose to download them (or copy them).
OK, I will add it to TODO. I think I can use monaco-editor to support the preview.
In continue of https://github.com/littlevgl/lvgl/issues/601
So according to my current understanding, the concept is the following:
I expect that some extensions should be developed. For example one basic thing came to mind: when you click on the canvas to select an object to edit it, the object on that coordinate should be get and highlighted.