I'm trying to add a dynamic number of sliders to a GUI window, and update an std::vector<float>
with the values according to how each slider changes. The GUI library uses callbacks and I can successfully do it for one slider (or when copy&pasting the slider code 20 times but that's not a solution), however I'm having problems factoring the slider creation into a function: There are issues with variable lifetime in how to tell each slider which element in the vector it should update.
I'm using nanogui (or libigl specifically, which uses nanogui) but I think the problem is actually quite generic and should apply to other GUI frameworks or situations as well.
Here's a short version of the code of how to add one slider and it updates my_values[0]
(note I had to put "0" manually):
int main(int argc, char *argv[])
{
igl::viewer::Viewer viewer;
std::vector<float> my_values(50);
viewer.callback_init = [&](igl::viewer::Viewer& viewer)
{
nanogui::Slider* slider = new nanogui::Slider(viewer.ngui->window());
slider->setCallback([&](float value) {
my_values[0] = value; // this slider sets the value at element '0'
});
viewer.ngui->addWidget("0", slider);
viewer.screen->performLayout();
return false;
};
viewer.launch();
}
Now as mentioned before, in viewer.callback_init
, I'd now like to add 50 of these sliders but obviously I don't want to copy&paste the slider and its callback code 50 times. But somehow I need to pass my_values[i]
, i.e. which element in the vector each slider is going to update.
I tried something like this which doesn't work, because at the time that the function/callback is actually called, the variable value_id
is not defined:
int main(int argc, char *argv[])
{
igl::viewer::Viewer viewer;
std::vector<float> my_values(50);
auto add_slider = [&](igl::viewer::Viewer& viewer, std::vector<float>& my_values, int value_id, std::string value_name)
{
nanogui::Slider* slider = new nanogui::Slider(viewer.ngui->window());
slider->setCallback([&](float value) {
my_values[value_id] = value; // offending line, 'value_id' is not initialized
// also I'm using 'value_name' to give the slider a name - code omitted
});
return slider;
};
viewer.callback_init = [&](igl::viewer::Viewer& viewer) {
// Now add all 50 sliders (in a for-loop in real code obviously):
viewer.ngui->addWidget("0", add_slider(viewer, 0, "0"));
viewer.ngui->addWidget("1", add_slider(viewer, 1, "1"));
// etc.
viewer.screen->performLayout();
return false;
};
viewer.launch();
}
I tried various things, making value_id
a (const) reference, even an std::shared_ptr<int>
(I know, horrible), but still value_id
is always not initialized on the line my_values[value_id] = value;
.
How can I accomplish this? My guess is it must be a well-known pattern/solution in GUI/callback programming, I just haven't figured it out yet.
Instead of storing slider values in a std::vector<float>
, use map<string, float>
or map<Slider*, float>
. Therefore, whenever your callback is called, you'll know which variable to update(by the address or name of the slider).
map<Slider*, float> my_values;
slider->setCallback([&](float value) {
my_values[slider] = value;
});