Lv_obj_t pass event

Hello,

I have a function to deply a button that I’ve made, these buttons will be used many times so no reason to have the same code over and over. In the function I name the objects (btn create and event_cb among other things), problem tho is that LVGL don’t seem to actually get the object name as it should.

Function is this:
void settingsMenuBtn(lv_obj_t* settingsBtn, char* label, int pos_x, int pos_y, int width, int height) {

Btn and callback which is inside the function above:

settingsBtn = lv_btn_create(popup, NULL);
lv_obj_set_event_cb(settingsBtn, event_handler);

Event handler is as usual:

static void event_handler(lv_obj_t * obj, lv_event_t event){
if (obj == settingsBtn) {
      lv_obj_del_async(popup);
      Serial.println("TEST");
    }
}

What am I missing? It wont fire. I tried doing obj==obj and that works just fine when I click one of the buttons.

If I write the code manually it also works just fine.

Appreciate any help.

Where is settingsBtn declared? You’re passing it as a parameter to settingsMenuBtn, then referencing it in another function.

Ensure that you are not declaring a global variable and then shadowing it with the function parameter.

Thanks for your reply.
I have it declared in main.h where all my other global objects are defined

lv_obj_t * settingsBtn;

I changed that so that it’s only declared in the function
“void settingsMenuBtn”

It still wont work. I’m surely missing something here.

Where is your event handler declared? It sounds like you have several copies of the settingsBtn variable. There needs to be only one (global) for things to work properly.

Event handler is in the main.cpp file, I have other things tied to the same event handler and they work just fine, but they are not created by a function like I’m trying to do now.

I did actually use the same object variable for something else, you made me realise that. I have changed it and now the object variable for this is unique.

main.h:

lv_obj_t * settingsMenuButtons;
lv_obj_t * displaySettingsBtn;

main.cpp:

static void event_handler(lv_obj_t * obj, lv_event_t event){
	if(event == LV_EVENT_CLICKED) {
		if (obj == displaySettingsBtn) {
		  Serial.print("TRIGGERED");
		}
	}
}

Custom function to call for one of these buttons:

void settingsMenuBtn(lv_obj_t* settingsMenuButtons, char* label, int pos_x, int pos_y, int width, int height) {
	settingsMenuButtons = lv_btn_create(popup, NULL);
	lv_obj_set_event_cb(settingsMenuButtons, event_handler);
}

And finally calling for a button:
settingsMenuBtn(displaySettingsBtn, "Display", -75, 20, 140, 50);

I hope this makes sense. I appreciate your assistance!

Why are you declaring settingsMenuButtons as a parameter inside the settingsMenuBtn function? That will shadow the global variable in main.h and the latter won’t be initialized.

I declare it in main.h so it’s available for the event handler and the function to deploy the button. Then I need to give the lv_btn_create a variable so that I can refer to it. I did try this without the function, and it works just fine. As soon as I pass this through the function it won’t work. So I should do this instead?

lv_btn_create(popup, NULL);

Keep the original settingsMenuButtons = lv_btn_create(popup, NULL); statement. Simply ensure you do not declare settingsMenuButtons as a parameter being passed to settingsMenuBtn. The declaration in main.h is enough.

I don’t quite get what I need to change. The way I see it, I need all these declarations in order for all parts to work. I need to give the function a name of the button in order for each created button to be unique as they will do different things. Otherwise all buttons will be duplicates. But I’m clearly missing your point here. Could you please give an example?

Let me try to explain the whole problem from beginning to end - maybe that will help.

The problem I see from the code you’ve been showing me is that you are declaring settingsMenuButtons in two places:

  • Inside main.h.
  • As a parameter of settingsMenuBtn.

The redeclaration in settingsMenuBtn causes variable shadowing and results in the global variable being effectively ignored by the compiler within that function. (I’m pretty sure you understand shadowing, but let me know if that needs further explanation.)

The shadowing means that when you reference the global variable in your event handler, it has no object reference assigned to it. This results in your check within the event handler failing.

To fix this, you need to ensure that the variable is declared in one spot that is accessible to all functions involved.

  • In one of your C++ files, the globals should be declared (settingsMenuButtons and displaySettingsBtn).
  • In main.h, prepend extern to the current declarations.
  • Remove the settingsMenuButtons from settingsMenuBtn, as it will shadow the global variable if you don’t do that.

I understand the shadowing part, I just don’t see how I can remove settingsMenuBtn parameter, the whole idea of it is to supply the function itself with a name for the button when I call the function. That same name is an object, and the object will have an event handler. In order for this code to compile, the event handler needs to get the variable, that’s why I specify it in main.h.

Extern is something totally new to me. I changed main.h and did
extern lv_obj_t * settingsMenuButtons;

unfortunately the code wont compile.

main.cpp.o:(.literal._ZL13event_handlerP9_lv_obj_th+0x14): undefined reference to `settingsMenuButtons'

Extern tells the compiler not to reserve space for the variable. This prevents the variable from being allocated once for every C++ file.

To solve your problem, you need one declaration of it in one of the .cpp files as well, without extern.

Ok, so you are trying to have this function assign to a different variable each time it’s called? That means you are using the wrong syntax.

To avoid confusion let’s think of lv_obj_t * as lv_obj_handle_t for the purposes of our discussion.

Right now this is what you have.

lv_obj_handle_t settingsMenuButtons; /* global */
lv_obj_handle_t displaySettingsBtn; /* global */

/* Note that this function takes lv_obj_handle_t (lv_obj_t *) and not lv_obj_handle_t * (lv_obj_t **) */
/* This means that object handles will be passed by value */
void settingsMenuBtn(lv_obj_handle_t settingsMenuButtons, char* label, int pos_x, int pos_y, int width, int height) {
    /* this assigns to the local variable (function parameter) */
    /* and does not update the global */
	settingsMenuButtons = lv_btn_create(popup, NULL);
	lv_obj_set_event_cb(settingsMenuButtons, event_handler);
}
/* This call passes the current _value_ of displaySettingsBtn to the function, */
/* not a reference. That means the global will never be updated. */
settingsMenuBtn(displaySettingsBtn, ...);

Hopefully the comments explained it. Here is the fixed version.

lv_obj_handle_t settingsMenuButtons; /* global */
lv_obj_handle_t displaySettingsBtn; /* global */

/* adjusted to receive lv_obj_handle_t * (lv_obj_t **) */
void settingsMenuBtn(lv_obj_handle_t settingsMenuButtons, char* label, int pos_x, int pos_y, int width, int height) {
    /* this assigns to the global variable referenced by settingsMenuButtons */
    /* caution: since the parameter is lv_obj_t ** (a pointer to lv_obj_t *), you need */
    /* to dereference it before passing it to LVGL functions */
	*settingsMenuButtons = lv_btn_create(popup, NULL);
	lv_obj_set_event_cb(*settingsMenuButtons, event_handler);
}
/* This call passes a reference to `displaySettingsBtn` to the function, */
/* so the global will get updated */
settingsMenuBtn(&displaySettingsBtn, ...);
1 Like

This taught me a few things!
I implemented this, but I get a ton of errors, most likely due to this:
error: 'lv_obj_handle_t' does not name a type

As I put this in main.h to make it accessable for everyone.

lv_obj_handle_t settingsMenuButtons; /* global */
lv_obj_handle_t displaySettingsBtn; /* global */

lv_obj_handle_t is an example type; you need to use lv_obj_t * (or lv_obj_t ** if you’re replacing lv_obj_handle_t *).

I got it working, but I must say, this was quite confusing. Secret is to put lv_obj_t with two stars ( lv_obj_t **) only in the function, while using one star on all other places for the same object.

Thanks for teaching me!