How to pass an Python array as event user_data

Hi,

I’m trying save an array in the user_data of an event like this:

a = [1, 2, 3]
btn.add_event_cb(event_cb, lv.EVENT.ALL, a)

But got:

SyntaxError: Cannot convert 'list' to pointer!

Can you help me how to pass an array as an argument here?

And how to get it in the event? Maybe this naive approach:

def event_cb(e):
    if e.get_code() == lv.EVENT.CLICKED:
        print("Hello")
        b = e.get_user_data()
        print(b[2])

I believe @amirgon has recommended that one avoids user_data in MicroPython code. You don’t need it since you can pass anything that’s callable as a callback. Here is an example of how to implement what you want using lambdas.

Thank you very much!

Probably I’m over complicating it and thinking to C-ish, but the full scenario is to store arbitrary data (the a array in the example) in some objects and get them them later. In C I added an event handler and passed the pointer to the custom data as user_data. And with a custom event code, e.g. LV_EVENT_GET_MY_DATA I set it in the event parameter. Like:

static int my_data = 10;
lv_obj_add_event_cb(obj, obj_event_handler, LV_EVENT_GET_MY_DATA, &my_data);

void * p;
lv_event_send(obj, LV_EVENT_GET_MY_DATA, &p);

void obj_event_handler(e) {
   lv_obj_t * obj = lv_event_get_target(e);
   void ** p = lv_event_get_param(e);
   void * d = lv_obj_get_event_user_data(obj, obj_event_handler);
   *p = d;
}

Quite cumbersome but at least flexible.

I wonder if in MP we can do something like:

btn1.new_memer = my_array

I tried a few things that should work in normal Python, but seemingly they don’t work in MP. :frowning:

That’s correct.

Here is a previous discussion on the subject:

  • Do you want to attach some data to an LVGL object?
  • Or do you want to pass context to a callback?

To pass some extra context to an event handler, the simplest way is to use a lambda as explained above. This allows you to pass any number of arguments to the event callback function (arguments which you set when you add the event callback).

If you want to “assign” some values to an LVGL object, the simplest way is to inherit from the LVGL object and add members to your derived objects. For a derived object to keep its identity you need to set user_data to point to the derived object instead of the base object.

For example:

class MyBtn(lv.btn):
    def __init__(self, parent, additional_data):
        super().__init__(parent)
        self.additional_data = additional_data
        self.set_user_data(self)

Then you can use btn.additional_data anywhere, also in an event callbacks. (Online example)

(if you don’t set user_data in MyBtn constructor it will still work, but when calling evt.get_target() you would get the parent lv.btn instead of MyBtn and you will not have access to your data members)

We use this technique today when creating custom widgets in Micropython:

Thanks for the details answer. I need to “attach some data to an LVGL object”.

Creating a new class can’t really work for me as it should work with any object type. E.g.

btn1.my_data
chart3.my_data
label5.my_data

So somehow lv_obj should be extended.

Well, I think that inheriting is the cleanest way.
You can create a function to wrap any derived class, like this:

def wrap(cls):
    class MyCls(cls):
        def __init__(self, parent, array):
            super().__init__(parent)
            self.array = array
            self.set_user_data(self)
    return MyCls

Then you can use it like that:

MyBtn = wrap(lv.btn)
btn1 = MyBtn(lv.scr_act(), a)

(online example)

That way you can wrap all the classes you use very easily.

But if you insist… there are other ways too.
You could use a global dict that maps your objects to some values using their id.

map = dict()
...
map[id(btn1)] = my_btn1_data

(online example)

Both look good, thanks you very much! I can try them out tomorrow.