ros-visualization / rqt_reconfigure

http://wiki.ros.org/rqt_reconfigure
Other
22 stars 50 forks source link

Grouping parameters #69

Open yossioo opened 4 years ago

yossioo commented 4 years ago

Hello!

I am looking for a way to separate parameters in certain node to several groups, in a way similar to ROS1 functionality.

Defining the parameters in YAML file allows nesting parameters in subgroups, and these groups can be defined using dot in parameter names, e.g: node.declare_parameter("group_a.param_b")

I have created the example node to demonstrate the issue. https://gist.github.com/yossioo/c20d2966c9e27e57a3153d9ec176e896

I can see the declared parameters:

$ ros2 param list
/node_w_params:
  group_a.param_b
  group_a.param_c
  param_a
  use_sim_time

Checking the list of parameters shows that the prefixes are indeed recognized by ROS2:

$ ros2 service call /node_w_params/list_parameters rcl_interfaces/srv/ListParameters 
waiting for service to become available...
requester: making request: rcl_interfaces.srv.ListParameters_Request(prefixes=[], depth=0)

response:
rcl_interfaces.srv.ListParameters_Response(result=rcl_interfaces.msg.ListParametersResult(names=['use_sim_time', 'param_a', 'group_a.param_b', 'group_a.param_c'], prefixes=['group_a']))

And yet, the parameters are not grouped in the rqt_reconfigure interface and are simply presented in a single alphabetically ordered list.

Is there a proper way to define parameters so they will be displayed in groups?

cottsay commented 4 years ago

I don't think groups are supported yet in rqt_reconfigure.

Flova commented 2 years ago

I would also look forward to this functionality. The question would be how they are displayed. The group name can be inferred pretty simple, but the type of grouping is not stored anywhere. I can imagine three main solutions for this:

authaldo commented 1 year ago

Hi! Since we struggled with the missing grouping functionality in RQT, we wrote a small standalone application for modifying the parameters of nodes which displays the parameters as a tree: https://github.com/teamspatzenhirn/rig_reconfigure

The tool is also published as a separate package and can thus be installed via apt. Currently, it doesn't provide all the features of rqt_reconfigure, but was at least for our use case sufficient.

Sagexs commented 7 months ago

to get the following behavior image

change the following lines from:

# def add_editor_widgets(self, parameters, descriptors):
#     for parameter, descriptor in zip(parameters, descriptors):
#         if parameter.type_ not in EDITOR_TYPES:
#             continue
#         logging.debug('Adding editor widget for {}'.format(parameter.name))
#         editor_widget = EDITOR_TYPES[parameter.type_](
#             self._param_client, parameter, descriptor
#         )
#         self._editor_widgets[parameter.name] = editor_widget
#         editor_widget.display(self.grid)

# def display(self, grid):
#     grid.addRow(self)

to:

def add_editor_widgets(self, parameters, descriptors):
        # Create a dictionary to store parameter editors grouped by prefix
        self.editor_groups = {}

        for parameter, descriptor in zip(parameters, descriptors):
            if parameter.type_ not in EDITOR_TYPES:
                continue

            # Get the prefix from the parameter name
            prefix = parameter.name.split('.')[0]
 
            if len(prefix) == len(parameter.name):
                prefix = 'other'
           
            if prefix not in self.editor_groups:
                # Create a new group if the prefix is not already in the dictionary
                group_box = QGroupBox(self)
                group_box.setLayout(QFormLayout())

   
                group_box.setTitle(prefix)
                group_box.setCheckable(True)  # Make the group collapsible
                group_box.setChecked(False)  # Initially, set the group as expanded
                group_box.toggled.connect(lambda state, prefix=prefix: self.group_toggled(state, prefix))
                self.grid.addRow(group_box)

                self.editor_groups[prefix] = {"group_box": group_box, "widgets": []}

            # Create the editor widget for the parameter
            logging.debug('Adding editor widget for {}'.format(parameter.name))
            editor_widget = EDITOR_TYPES[parameter.type_](
                self._param_client, parameter, descriptor
            )
            self._editor_widgets[parameter.name] = editor_widget
            editor_widget.display(self.editor_groups[prefix]["group_box"].layout())

            editor_widget._paramname_label.setVisible(self.editor_groups[prefix]["group_box"].isChecked())
            editor_widget.setVisible(self.editor_groups[prefix]["group_box"].isChecked())

            # Add the editor widget to the list of widgets in the group
            self.editor_groups[prefix]["widgets"].append(editor_widget)

    def group_toggled(self, state, prefix):
        # Handle the group box toggled event
        group_data = self.editor_groups[prefix]
        for widget in group_data["widgets"]:
            widget.setVisible(state)
            widget._paramname_label.setVisible(state)
           

    def display(self, grid):
        # Do not add the main widget to the grid here
        pass

like pkg above i also don't like that multiple parameters from diffrent nodes can be visible at the same time. I like this behavior more:

def _selection_changed_slot(self, selected, deselected):
        """
        Send "open ROS Node box" signal.

        ONLY IF the selected treenode is the terminal treenode.
        Receives args from signal QItemSelectionModel.selectionChanged.

        :param selected: All indexes where selected (could be multiple)
        :type selected: QItemSelection
        :type deselected: QItemSelection
        """
        # Getting the index where the user just selected. Should be single.
        if not selected.indexes() and not deselected.indexes():
            logging.error('Nothing selected? Not ideal to reach here')
            return

        index_current = None
        if selected.indexes():
            index_current = selected.indexes()[0]
        elif len(deselected.indexes()) == 1:
            # Setting length criteria as 1 is only a workaround, to avoid
            # Node boxes on right-hand side disappears when the filter key doesn't
            # match them.
            # Indeed, this workaround leaves another issue. Question for
            # permanent solution is asked here http://goo.gl/V4DT1
            index_current = deselected.indexes()[0]

        logging.debug('  - - - index_current={}'.format(index_current))

        rosnode_name_selected = RqtRosGraph.get_upper_grn(index_current, '')

        # If retrieved node name isn't in the list of all nodes.
        if rosnode_name_selected not in self._nodeitems.keys():
            # De-select the selected item.
            self.selectionModel.select(index_current,
                                    QItemSelectionModel.Deselect)
            return

        # Keep track of the previously selected index
        if hasattr(self, '_previous_selected_index'):
            # Hide the previously selected widget
            self._selection_deselected(self._previous_selected_index,
                                    self._previous_selected_node)

        # Show the currently selected widget
        try:
            self._selection_selected(index_current, rosnode_name_selected)
            self._previous_selected_index = index_current
            self._previous_selected_node = rosnode_name_selected
        except Exception as e:
            # TODO: print to sysmsg pane
            err_msg = 'Connection to node={} failed:\n{}'.format(
                rosnode_name_selected, e
            )
            import traceback
            traceback.print_exc()
            self._signal_msg.emit(err_msg)
            logging.error(err_msg)
nicolas-cadart commented 2 months ago

Any update for this grouping feature in rqt_configure? I am dealing with a lot of parameters for some nodes, and without the ability to correctly group and even hide/display them, setting parameters with ROS2 is quite cumbersome...