jrl-umi3218 / mc_rtc

mc_rtc is an interface for simulated and real robotic systems suitable for real-time control
BSD 2-Clause "Simplified" License
122 stars 37 forks source link

[GUI] Add helpers to create common gui elements #299

Closed arntanguy closed 1 year ago

arntanguy commented 2 years ago

This PR proposes to add helper functions to simplify the syntax for creating common GUI elements, in particular those handling direct display/input of member variables, and labels. Some of these were originally introduced in mc_state_observation to simplify the GUI code there.

For instance, taken from https://github.com/jrl-umi3218/mc_state_observation/blob/0fd1bb4ed37463d762232feef8c0d990130cf2fb/src/AttitudeObserver.cpp#L216-L231:

  struct KalmanFilterConfig
  {
    bool compensateMode = false;
    double acceleroCovariance = 0.003;
    double gyroCovariance = 1e-10;
    double orientationAccCov = 0.003;
    double linearAccCov = 1e-13;
    double stateCov = 3e-14;
    double stateInitCov = 1e-8;
    Eigen::Matrix3d offset = Eigen::Matrix3d::Identity(); ///< Offset to apply to the estimation result
    void addToGUI(mc_rtc::gui::StateBuilder & gui, const std::vector<std::string> & category);
  };

void AttitudeObserver::KalmanFilterConfig::addToGUI(mc_rtc::gui::StateBuilder & gui,
                                                    const std::vector<std::string> & category)
{
  using namespace mc_rtc::gui;
  // clang-format off
  gui.addElement(category,
    make_input_element("Compensate Mode", compensateMode), // bool, so diplay a Checkbox
    make_input_element("acceleroCovariance", acceleroCovariance), // double, display a NumberInput
    make_input_element("gyroCovariance", gyroCovariance),
    make_input_element("orientationAccCov", orientationAccCov),
    make_input_element("linearAccCov", linearAccCov),
    make_input_element("stateCov", stateCov),
    make_input_element("stateInitCov", stateInitCov),
    make_rpy_input("offset", offset)); // Displays a rotation matrix as RPY expressed in degrees, with the appropriate labels
  // clang-format on
}

This will:

More complete working examples in dummyServer.

  builder.addElement({"Inputs"}, 
                                     mc_rtc::gui::StringInput(
                                     "StringInput", [this]() { return string_; },
                                     [this](const std::string & data) {
                                       string_ = data;
                                     }),
                                     mc_rtc::gui::IntegerInput(
                                     "IntegerInput", [this]() { return int_; },
                                     [this](int data) {
                                       int_ = data;
                                     }),
                                    mc_rtc::gui::NumberInput(
                                     "NumberInput", [this]() { return d_; },
                                     [this](double data) {
                                       d_ = data;
                                     }),
                                     mc_rtc::gui::NumberSlider(
                                     "NumberSlider", [this]() { return slide_; },
                                     [this](double s) {
                                       slide_ = s;
                                     },
                                     -100.0, 100.0),
                                     mc_rtc::gui::ArrayInput(
                                     "ArrayInput", [this]() { return v_; },
                                     [this](const Eigen::VectorXd & data) {
                                       v_ = data;
                                     }),
                                     mc_rtc::gui::ArrayInput(
                                     "ArrayInput with labels", {"x", "y", "z"}, [this]() { return v3_; },
                                     [this](const Eigen::Vector3d & data) {
                                       v3_ = data;
                                     }),
                                    mc_rtc::gui::ComboInput(
                                     "ComboInput", {"a", "b", "c", "d"}, [this]() { return combo_; },
                                     [this](const std::string & s) {
                                       combo_ = s;
                                     }));

simply becomes

  builder.addElement({"Helpers", "Inputs"},
                     mc_rtc::gui::make_input_element("String", string_),
                     mc_rtc::gui::make_input_element("IntegerInput", int_),
                     mc_rtc::gui::make_input_element("NumberInput", d_),
                     mc_rtc::gui::make_number_slider("NumberSlider", slide_, -100.0, 100.0),
                     mc_rtc::gui::make_input_element("ArrayInput", v_),
                     mc_rtc::gui::make_input_element("ArrayInput with labels", {"x", "y", "z"}, v3_),
                     mc_rtc::gui::make_input_element("ComboInput", {"a", "b", "c", "d"}, combo_)}

If you agree with the idea, I'll finish adding documentation ;)

gergondet commented 1 year ago

Hi @arntanguy

Sorry for the long delay in answering this properly. I like the idea but I went a step further and added the option under the same name as the existing function. So to take the example here:

void AttitudeObserver::KalmanFilterConfig::addToGUI(mc_rtc::gui::StateBuilder & gui,
                                                    const std::vector<std::string> & category)
{
  using namespace mc_rtc::gui;
  // clang-format off
  gui.addElement(category,
    make_input_element("Compensate Mode", compensateMode), // bool, so diplay a Checkbox
    make_input_element("acceleroCovariance", acceleroCovariance), // double, display a NumberInput
    make_input_element("gyroCovariance", gyroCovariance),
    make_input_element("orientationAccCov", orientationAccCov),
    make_input_element("linearAccCov", linearAccCov),
    make_input_element("stateCov", stateCov),
    make_input_element("stateInitCov", stateInitCov),
    make_rpy_input("offset", offset)); // Displays a rotation matrix as RPY expressed in degrees, with the appropriate labels
  // clang-format on
}

Is instead:

void AttitudeObserver::KalmanFilterConfig::addToGUI(mc_rtc::gui::StateBuilder & gui,
                                                    const std::vector<std::string> & category)
{
  using namespace mc_rtc::gui;
  // clang-format off
  gui.addElement(category,
    Input("Compensate Mode", compensateMode), // bool, so diplay a Checkbox
    Input("acceleroCovariance", acceleroCovariance), // double, display a NumberInput
    Input("gyroCovariance", gyroCovariance),
    Input("orientationAccCov", orientationAccCov),
    Input("linearAccCov", linearAccCov),
    Input("stateCov", stateCov),
    Input("stateInitCov", stateInitCov),
    RPYInput("offset", offset)); // Displays a rotation matrix as RPY expressed in degrees, with the appropriate labels
  // clang-format on
}

There is also more specific helpers that go along with this generic Input helper so Checkbox("name", var_) or ArrayInput("something", value_) work (ArrayInput and ArrayLabel) also automatically generate labels depending on the type of the variable.

I have also added a similar helper for Point3D and Transform (the caveat is we have to specifically use Point3DRO and TransformRO to specify read-only entries. For other elements the callbacks are not easy to generate automatically.

This PR also adds some helpers to generate Visual elements: (see #348)

Screenshot from 2023-02-28 15-19-33

Finally, I have done some general cleanups, taking advantage of C++17 features.

mmurooka commented 1 year ago

No rush, but could you please update the release to include this change? MCC starts to use this feature.

https://github.com/isri-aist/MultiContactController/actions/runs/4339479182