Skip to content
This repository has been archived by the owner on Oct 15, 2021. It is now read-only.

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

Open
AGlass0fMilk opened this issue Mar 6, 2019 · 11 comments
Labels
enhancement New feature or request

Comments

@AGlass0fMilk
Copy link
Collaborator

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.

@AGlass0fMilk AGlass0fMilk added the enhancement New feature or request label Mar 6, 2019
@kisvegabor
Copy link
Member

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:

  • 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).
    We can add postfixes like text + _str, title + _str, bg + _color, anim_time + _ms, x + _coord, width + _size
  • 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.
  • 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?)
  • Anything else?

@AGlass0fMilk
Copy link
Collaborator Author

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 😉

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
Copy link
Member

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
Copy link

rreilink commented Mar 7, 2019

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
Copy link
Member

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
Copy link

@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
Copy link

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

@AGlass0fMilk
Copy link
Collaborator Author

@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
Copy link

@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
Copy link
Collaborator Author

AGlass0fMilk commented Mar 11, 2019 via email

@kisvegabor
Copy link
Member

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

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

3 participants