lvgl / lv_gui_builder

[WIP] Drag end drop GUI designer for LVGL
25 stars 7 forks source link

Introduce standardized, parsable comments to facilitate automatic binding and metadata #3

Open AGlass0fMilk opened 5 years ago

AGlass0fMilk commented 5 years ago

I'm not sure how needed this is, but it may be useful to introduce a standardized, parsable comment format for widget structs (and maybe other things?) to facilitate more reliable automatic binding and discovery of metadata (ie: what does a struct's field do?)

There are already doxygen comments but some things are missing tagged comments.

kisvegabor commented 5 years ago

It was already discussed with @rreilink to aid the Python binding generation with naming conventions. The main issue was that Python bindings (including Micropython) use the C pre-processed code where defines and comments are already not available.

So to get the comments the source code needs to be processed. Parsing the comment seems doable if they are added correctly, however, it will make the maintenance more difficult.

Let's collect what we need to see what we can do:

AGlass0fMilk commented 5 years ago

So to get the comments the source code needs to be processed. Parsing the comment seems doable if they are added correctly, however, it will make the maintenance more difficult.

It may make maintenance of the code more difficult, but I think overall it will be less work. This way, all the information about the code is contained in the code itself. The same reason doxygen is so prevalent. Writing the same documentation twice is more difficult, and maintaining code comments and documentation is twice as much confusion :wink:

Let's collect what we need to see what we can do:

* Variable types (color, coordinate, string) can be seen from the variable type, right? Maybe some new typedef should be added for too general types (e.g. instead of `uint16_t`).

From my poking around in the python binding, object members that decompose to a primitive just appear to python as that primitive. For example, lv_color_t will not show up as lvgl.lv_color_t or anything special. It will be an int just like body_padding_ver.

Perhaps we can just use the variable names to differentiate types like this. So anything ending with _color will be interpreted by the GUI builder as a color parameter and a color picker control will be shown.

  We can add postfixes like `text + _str`, `title + _str`, `bg + _color`, `anim_time + _ms`, `x + _coord`, `width + _size`

Yes, that's what I'm thinking we should do.

* Tooltips: It seems we can't avoid parsing the code to get this info. But at least only the header files are enough as they contain all the public API.

This should be fairly simple. For things like enums or structures we could tag things with @tooltip or something. Maybe doxygen has something compatible so it doesn't mess with the documentation layout.

* For Python it would be nice to know if a parameter (e.g. a string) is saved by lvgl or it needs to be "residual". What is the real term for "residual" variables?)

Do you mean something that's non-static? Like a temporary string pointer in RAM that is passed to lvgl when setting the text on something?

* Anything else?

... still thinking about this.

kisvegabor commented 5 years ago

From my poking around in the python binding, object members that decompose to a primitive just appear to python as that primitive. For example, lv_color_t will not show up as lvgl.lv_color_t or anything special. It will be an int just like body_padding_ver.

Oh, I really missed that. Anyway, the pre/postfixes should work.

Do you mean something that's non-static? Like a temporary string pointer in RAM that is passed to lvgl when setting the text on something?

I mean for example in lv_label_set_text(label, "some text") memory will be allocated by LittelvGL to save the text. So it also works:

char buf[32] = {"some text"};
lv_label_set_text(label, buf);

return; // buf is destroyed

However, for example lv_btnm_set_map(btnm, map) required map to remain in the memory because only its pointer is saved.

Some updates should be done in this regard to make it clearer.

rreilink commented 5 years ago

For example, lv_color_t will not show up as lvgl.lv_color_t or anything special. It will be an int just like body_padding_ver.

However, it is detected as an lv_color_t in the C parser when the bindings are generated. I could easily store this information in the Python bindings, e.g. have a get_color method which has a 'type' attribute that contains the C type, or give every object class a list of (attribute, type) tuples.

kisvegabor commented 5 years ago

However, it is detected as an lv_color_t in the C parser when the bindings are generated. I could easily store this information in the Python bindings, e.g. have a get_color method which has a 'type' attribute that contains the C type, or give every object class a list of (attribute, type) tuples.

Can you show an example about how it would look like for example in case of
void lv_label_set_text(lv_obj_t * label, const char * text)?

rreilink commented 5 years ago

@kisvegabor for lv_label_set_text this is not an issue, since the bindings already map C char* to Python str, C bool to Python bool, and will map structured types (like lv_style_t *) to a Python equivalent class (MicroPython bindings already do the latter, this is still to be implemented).

The issue is for enumerated types, colors and the like which are mapped to integers, so from the return value of mychart.get_type() the GUI builder could not deduct that this is an lv_chart_type_t enum and possible values are LV_CHART_TYPE_LINE, LV_CHART_TYPE_COLUMN, ...

The issue might also be for the members of structured types (like lv_style_t.line.color), however I think that setting the style could be a specific dialog in the GUI builder as opposed to 'generic' properties.

For the enumerated types, the bindings generator does know that the expected type is e.g. lv_chart_type_t, so how to get this information into the Python bindings?

  1. Add the C signature as doc string to the Python Bindings mychart.get_type.__doc__ --> "lv_chart_type_t lv_chart_get_type(const lv_obj_t * chart)"

    Additional documentation could follow on the next line(s) of the doc string and would be shown by the GUI builder as tooltip or help pop-up.

    pro: easy to implement, compatibility with micropython bindings, flexibility for future requirements, type can be deducted without calling the method, C-signature documentation is also usefull for the programmer when using lvgl interatively via REPL. con: requires bit of parsing of the GUI builder (but only minimal)

  2. Add a _type_ attribute to the get and set functions mychart.get_type._type_ --> <lvgl.chart.TYPE enum>

    The attribute points directly to the TYPE enum object in Python, which has the possible options + their values as attributes

    pro: easy access to the possible options, compatibility with micropython bindings, type can be deducted without calling the method con: more cumbersome to implement

  3. Have get functions return a specific object mychart.get_type() --> <lvgl.chart.TYPE enum (1)> int(mychart.get_type()) --> 1 Functions which return an enumerate type, return an instance of that particular enum class, whose int(x) value evaluates to the integer value that it represents. Functions that return other types like lv_coord_t, lv_color_t etc. would return a specific lv_coord_t object whose int(x) value evaluates to the integer value that it represents.

    pro: easy access to possible options con: less compatible to micropython bindings, method needs to be called before type can be deducted.

    one would have to write int(mychart.get_type()) == 2 or mychart.get_type() ==mychart.TYPE.COLUMN to do a comparison of the value; mychart.get_type() == 2 would always evaluate to False.

I would propose to go for solution 1, since it provides the most flexibility, is easy to implement and is 100% compatible with the micropython bindings.

rreilink commented 5 years ago

Actually, the change was so trivial that I have already implemented option 1: see this commit

AGlass0fMilk commented 5 years ago

@rreilink

I’ll have to check out that code and get started on the builder. I have a basic GTK layout started so far.

rreilink commented 5 years ago

@AGlass0fMilk : any chance of using Qt (PyQt / PySide) instead of GTK? In my experience that creates cross-platform applications that have a more 'native' feeling. I also have quite some experience with it so could provide some help with the GUI builder, too.

Obviously, of you are more experienced in using GTK, surely go ahead with that, and I'll try to support by providing the required Python bindings features.

AGlass0fMilk commented 5 years ago

@rreilink it’s early on enough I can try out pyQt/pySide. I don’t have a preference as long as it’s not wxPython or tkinter.

On Mar 11, 2019, at 5:58 PM, rreilink notifications@github.com<mailto:notifications@github.com> wrote:

@AGlass0fMilkhttps://nam01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub.com%2FAGlass0fMilk&data=02%7C01%7C%7Ceff4ed491efd4869b5ad08d6a66cbbd6%7C84df9e7fe9f640afb435aaaaaaaaaaaa%7C1%7C0%7C636879383263291630&sdata=Mg0ILIIOj54yVE4SaGPdre7HUboDi3MBPxgdiYOe7QQ%3D&reserved=0 : any chance of using Qt (PyQt / PySide) instead of GTK? In my experience that creates cross-platform applications that have a more 'native' feeling. I also have quite some experience with it so could provide some help with the GUI builder, too.

Obviously, of you are more experienced in using GTK, surely go ahead with that, and I'll try to support by providing the required Python bindings features.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHubhttps://nam01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub.com%2Flittlevgl%2Flv_gui_builder%2Fissues%2F3%23issuecomment-471753093&data=02%7C01%7C%7Ceff4ed491efd4869b5ad08d6a66cbbd6%7C84df9e7fe9f640afb435aaaaaaaaaaaa%7C1%7C0%7C636879383263301635&sdata=PW5jLMOMMc48a1jTmRY5ZGGSQxaJ0EixkQns2wCA5Ms%3D&reserved=0, or mute the threadhttps://nam01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub.com%2Fnotifications%2Funsubscribe-auth%2FAAVZJBLvCQDcg16w7YyoQ-HnGAUdK5zAks5vVtGUgaJpZM4bf8Eo&data=02%7C01%7C%7Ceff4ed491efd4869b5ad08d6a66cbbd6%7C84df9e7fe9f640afb435aaaaaaaaaaaa%7C1%7C0%7C636879383263311646&sdata=XCgxRi1bca5YL4LEqbmBltbVDdJyiZrAiy3bm9Scm%2Bo%3D&reserved=0.

kisvegabor commented 5 years ago

@rreilink Option 1 (what you already implemented) seems very flexible and easy to use for me too. Good idea!