Direct access to Button State

Description

I’ve created a load of UI buttons and got a fully working UI.

I want to use the state of those buttons in an array, to match up with my other arrays of things like motors and so on. I’m trying to create pointers to the button states in an array by doing this:

short unsigned int* SelArr[box_count] = { &ui_box01button->state, &ui_box02button->state, &ui_box03button->state, &ui_box04button->state, &ui_box05button->state, &ui_box06button->state, &ui_box07button->state, &ui_box08button->state };

While this compiles it doesn’t work, just giving me the value 43425 for every button no matter what state it’s in.

I’m right at the absolute limits of my C++ capabilities here and I suspect I’m somehow addressing things in the wrong way.

What I want to achieve:
An array with the states of the buttons by directly accessing their states via the array.

What I’ve done so far:
I did a bit of a clunky stuff of values into an array every loop. While this works it sort of feels a bit wrong to have to run this every loop when I only really need to access the states of the buttons on an occasional basis.

  sel[0] = (lv_obj_has_state(ui_box01button, LV_STATE_CHECKED) == true);
  sel[1] = (lv_obj_has_state(ui_box02button, LV_STATE_CHECKED) == true);
  sel[2] = (lv_obj_has_state(ui_box03button, LV_STATE_CHECKED) == true);
  sel[3] = (lv_obj_has_state(ui_box04button, LV_STATE_CHECKED) == true);
  sel[4] = (lv_obj_has_state(ui_box05button, LV_STATE_CHECKED) == true);
  sel[5] = (lv_obj_has_state(ui_box06button, LV_STATE_CHECKED) == true);
  sel[6] = (lv_obj_has_state(ui_box07button, LV_STATE_CHECKED) == true);
  sel[7] = (lv_obj_has_state(ui_box08button, LV_STATE_CHECKED) == true);

Any help with this would be greatly appreciated!

What MCU/Processor/Board and compiler are you using?

Arduino Giga

What LVGL version are you using?

8.4

Hello,

I do not have time to test this now myself but it seems to me that you want to have a pointer to a member of the struct.

I don’t think this is even possible in regular C but it might be possible in C++. See some of the suggestions here: Pointer to struct member - C++ Forum. Especially “pointer-to-a-member”:

What are you trying to do anyway? Perhaps there is some better solution.

You can reference objects by their child index within their parent. If your needs are to occasionally check the state of a button for a given index, one way would be:

if (lv_obj_has_state(lv_obj_get_child(parentObj, <indexToCheck>), LV_STATE_CHECKED) == true)

and create your buttons under a dummy parent object, or use the parent they are already under (warning - if the parent contains other widgets or you add other widgets later, indices will change and you’ll be debugging a while until you realize you reference by index). Or, if you want to go the array way, does this not work?

lv_obj_t * SelArr[box_count] = {ui_box01button, ui_box02button, ...};

if (lv_obj_has_state(SelArr[<indexToCheck>], LV_STATE_CHECKED) == true) {
//
}

Thanks @Tinus, I will check that out. I’m using arrays to try and reduce code volume. It’s mostly working just this last bit to crack!

Thanks @egonbeermat that’s an interesting way of dealing with it!

The frustrating thing is knowing that somewhere in the variables on my controller is the value that I want to create a pointer to, in a nice neat array.
I might also take a peek at the source code for LVGL and see if there are any clues there.

You won’t have an array of values that indicate the live state of the buttons, you’ll have a pointer to the variable that holds the state, which has many other values besides checked (DISABLED, HIDDEN, etc) and may be changed by the app, which isn’t updating your array, it’s updating the values of the variable that you have a pointer to. You’ll still need to check the state variable to see if it’s checked, which doesn’t differ much in boilerplate from if you stored the object pointers and did this:

if (lv_obj_has_state(SelArr[<indexToCheck>], LV_STATE_CHECKED) == true)

Additionally, if LVGL change the implementation of storing state, it’ll break your code, but lv_obj_has_state will still likely work, just re-implemented unde the hood. That’s the beauty of interfaces…

If you REALLY want to do it your way, this works in the sim:

uint16_t * SelArr[box_count] = {&ui_box01button->state,  &ui_box02button->state, ...};

if (!!(*SelArr[<indexToCheck>] & LV_STATE_CHECKED) == true) {
 //You checked if it's checked, and it checks out
}

If the buttons are removed via lv_obj_delete, your array is now pointing to memory addresses that are meaningless but you wont know that, whereas lv_obj_has_state will assert the objects existence…

You were probably getting 43425 because you referenced SelArr[5], which is storing the address of a pointer, so you need to reference *SelArr[5] to get the value your pointer to a pointer holds.

1 Like

@egonbeermat Brilliant, thank you Ian for taking the time to give such a comprehensive, concise and insight providing answer to this - I really appreciate it! Giles

1 Like

No probs! Let us know what you used, and what worked in the end, for future readers!

This is what I went with in the end. It runs every loop but it’s not pushing anything at the screen so that seems to work ok:

int box_count = 8;
int sel[box_count];
lv_obj_t** ui_boxbutton[box_count] = { &ui_box01button, &ui_box02button,...}

  for (int i = 0; i < box_count; i++) {
    sel[i] = (lv_obj_has_state(*ui_boxbutton[i], LV_STATE_CHECKED) == true);
  }

Ok, but I still don’t know why you have to set an array of bools every single loop, reading the state of every single button each time, just to occasionally later check if one of the buttons has that state by referencing the array? Seems very inefficient, vs just checking the object at the time you need to!

Create that ui_boxbutton array once at the start, and instead of checking sel[i] == true do lv_obj_has_state(*ui_boxbutton[i], LV_STATE_CHECKED) == true, no need to read every button state every loop?

yep, good point - I’ll do that instead :slight_smile:

1 Like