35 from __future__
import division
37 from collections
import OrderedDict
41 import dynamic_reconfigure
as dyn_reconf
43 from python_qt_binding
import loadUi
44 from python_qt_binding.QtCore
import Qt, Signal
46 from python_qt_binding.QtCore
import (
47 QItemSelectionModel, QModelIndex)
49 from python_qt_binding.QtGui
import (
50 QItemSelectionModel, QModelIndex)
51 from python_qt_binding.QtWidgets
import QHeaderView, QWidget
53 from rospy.exceptions
import ROSException
59 from rqt_reconfigure
import logging
70 sig_node_selected = Signal(ParamClientWidget)
72 def __init__(self, parent, rospack, signal_msg=None):
74 @param signal_msg: Signal to carries a system msg that is shown on GUI. 75 @type signal_msg: QtCore.Signal 77 super(NodeSelectorWidget, self).
__init__()
82 ui_file = os.path.join(rospack.get_path(
'rqt_reconfigure'),
'resource',
96 self.
_rootitem = self._item_model.invisibleRootItem()
109 self._collapse_button.pressed.connect(
110 self._node_selector_view.collapseAll)
111 self._expand_button.pressed.connect(self._node_selector_view.expandAll)
116 self._proxy_model.setDynamicSortFilter(
True)
125 self._node_selector_view.header().setResizeMode(
126 0, QHeaderView.ResizeToContents)
127 except AttributeError:
136 self.selectionModel.selectionChanged.connect(
141 Deselect the index that corresponds to the given GRN. 146 indexes_selected = self.selectionModel.selectedIndexes()
147 for index
in indexes_selected:
148 grn_from_selectedindex = RqtRosGraph.get_upper_grn(index,
'')
149 logging.debug(
' Compare given grn={} from selected={}'.format(
150 grn, grn_from_selectedindex))
152 if grn == grn_from_selectedindex:
154 self.selectionModel.select(index, QItemSelectionModel.Deselect)
158 Select the index that corresponds to the given GRN. 164 grn_from_index = RqtRosGraph.get_upper_grn(index,
'')
165 logging.debug(
' Compare given grn={} from selected={}'.format(
166 grn, grn_from_index))
168 if grn == grn_from_index:
170 self.selectionModel.select(index, QItemSelectionModel.Select)
172 self._node_selector_view.scrollTo(index)
176 model = self.selectionModel.model()
177 for row
in range(0, model.rowCount(parent)):
178 index = model.index(row, 0, parent)
180 if model.hasChildren(index):
186 Intended to be called from _selection_changed_slot. 188 self.selectionModel.select(index_current, QItemSelectionModel.Deselect)
192 rosnode_name_selected].get_param_client_widget()
193 except ROSException
as e:
197 self.sig_node_selected.emit(param_client_widget)
200 """Intended to be called from _selection_changed_slot.""" 201 logging.debug(
'_selection_changed_slot row={} col={} data={}'.format(
202 index_current.row(), index_current.column(),
203 index_current.data(Qt.DisplayRole)))
207 for nodeitem
in self._nodeitems.values():
208 name_nodeitem = nodeitem.data(Qt.DisplayRole)
209 name_rosnode_leaf = rosnode_name_selected[
210 rosnode_name_selected.rfind(RqtRosGraph.DELIM_GRN) + 1:]
214 if ((name_nodeitem == rosnode_name_selected)
and 216 name_nodeitem.rfind(RqtRosGraph.DELIM_GRN) + 1:] ==
219 logging.debug(
'terminal str {} MATCH {}'.format(
220 name_nodeitem, name_rosnode_leaf))
224 self.selectionModel.select(index_current,
225 QItemSelectionModel.Deselect)
230 item_child = self.
_nodeitems[rosnode_name_selected]
233 item_widget = item_child.get_param_client_widget()
234 except ROSException
as e:
236 logging.debug(
'item_selected={} child={} widget={}'.format(
237 index_current, item_child, item_widget))
238 self.sig_node_selected.emit(item_widget)
245 Sends "open ROS Node box" signal ONLY IF the selected treenode is the 247 Receives args from signal QItemSelectionModel.selectionChanged. 249 :param selected: All indexs where selected (could be multiple) 250 :type selected: QItemSelection 251 :type deselected: QItemSelection 254 if not selected.indexes()
and not deselected.indexes():
255 logging.error(
'Nothing selected? Not ideal to reach here')
259 if selected.indexes():
260 index_current = selected.indexes()[0]
261 elif len(deselected.indexes()) == 1:
267 index_current = deselected.indexes()[0]
269 logging.debug(
' - - - index_current={}'.format(index_current))
271 rosnode_name_selected = RqtRosGraph.get_upper_grn(index_current,
'')
274 if rosnode_name_selected
not in self._nodeitems.keys():
276 self.selectionModel.select(index_current,
277 QItemSelectionModel.Deselect)
280 if selected.indexes():
283 except ROSException
as e:
285 err_msg =
'Connection to node={} failed:\n{}'.format(
286 rosnode_name_selected, e.message
288 self._signal_msg.emit(err_msg)
289 logging.error(err_msg)
291 elif deselected.indexes():
294 rosnode_name_selected)
295 except ROSException
as e:
296 logging.error(e.message)
301 :rtype: OrderedDict 1st elem is node's GRN name, 302 2nd is TreenodeQstdItem instance 314 nodes = dyn_reconf.find_reconfigure_services()
315 except rosservice.ROSServiceIOException
as e:
316 logging.error(
'Reconfigure GUI cannot connect to master.')
321 num_nodes = len(nodes)
322 elapsedtime_overall = 0.0
323 for node_name_grn
in nodes:
325 if node_name_grn
in self.
_nodeitems or node_name_grn ==
'':
329 time_siglenode_loop = time.time()
341 TreenodeQstdItem.NODE_FULLPATH
343 _treenode_names = treenodeitem_toplevel.get_treenode_names()
347 self.
_nodeitems[node_name_grn] = treenodeitem_toplevel
352 time_siglenode_loop = time.time() - time_siglenode_loop
353 elapsedtime_overall += time_siglenode_loop
355 _str_progress =
'reconf ' + \
356 'loading #{}/{} {} / {}sec node={}'.format(
357 i_node_curr, num_nodes, round(time_siglenode_loop, 2),
358 round(elapsedtime_overall, 2), node_name_grn
363 logging.debug(_str_progress)
367 treenodeitem_parent, child_names_left):
369 Evaluate current treenode and the previous treenode at the same depth. 370 If the name of both nodes is the same, current node instance is 371 ignored (that means children will be added to the same parent). If not, 372 the current node gets added to the same parent node. At the end, this 373 function gets called recursively going 1 level deeper. 375 :type treenodeitem_toplevel: TreenodeQstdItem 376 :type treenodeitem_parent: TreenodeQstdItem. 377 :type child_names_left: List of str 378 :param child_names_left: List of strings that is sorted in hierarchical 383 name_currentnode = child_names_left.pop(0)
384 grn_curr = treenodeitem_toplevel.get_raw_param_name()
386 TreenodeQstdItem.NODE_FULLPATH)
389 row_index_parent = treenodeitem_parent.rowCount() - 1
394 if treenodeitem_parent.child(row_index_parent):
395 stditem_prev = treenodeitem_parent.child(row_index_parent)
396 name_prev = stditem_prev.text()
401 if name_prev != name_currentnode:
402 stditem_currentnode.setText(name_currentnode)
406 while (insert_index < treenodeitem_parent.rowCount()
and 407 treenodeitem_parent.child(insert_index)
408 .text() < name_currentnode):
411 treenodeitem_parent.insertRow(insert_index, stditem_currentnode)
412 stditem = stditem_currentnode
414 stditem = stditem_prev
424 self._item_model.set_item_from_index(grn_curr, stditem.index())
428 nodes = dyn_reconf.find_reconfigure_services()
429 except rosservice.ROSServiceIOException
as e:
430 logging.error(
'Reconfigure GUI cannot connect to master.')
433 for i
in reversed(range(0, self._rootitem.rowCount())):
434 candidate_for_removal = \
435 self._rootitem.child(i).get_raw_param_name()
436 if candidate_for_removal
not in nodes:
438 'Removing {} because the server is no longer available.'.
439 format(candidate_for_removal))
441 disconnect_param_server()
442 self._rootitem.removeRow(i)
443 self._nodeitems.pop(candidate_for_removal)
450 logging.debug(
' in close_node')
455 Pass fileter instance to the child proxymodel. 456 :type filter_: BaseFilter 458 self._proxy_model.set_filter(filter_)
462 Method for Debug only 467 index_deselected =
None 469 curr_qstd_item =
None 470 if selected.indexes():
471 index_current = selected.indexes()[0]
472 index_parent = index_current.parent()
473 curr_qstd_item = src_model.itemFromIndex(index_current)
474 elif deselected.indexes():
475 index_deselected = deselected.indexes()[0]
476 index_parent = index_deselected.parent()
477 curr_qstd_item = src_model.itemFromIndex(index_deselected)
479 if selected.indexes() > 0:
481 'sel={} par={} desel={} ' 482 'sel.d={} par.d={} desel.d={} cur.item={}' 484 index_current, index_parent, index_deselected,
485 index_current.data(Qt.DisplayRole),
486 index_parent.data(Qt.DisplayRole),
489 elif deselected.indexes():
491 'sel={} par={} desel={} ' 492 'sel.d={} par.d={} desel.d={} cur.item={}' 494 index_current, index_parent, index_deselected,
495 None, index_parent.data(Qt.DisplayRole),
496 index_deselected.data(Qt.DisplayRole),
502 if self._node_selector_view.isExpanded(index):
503 grn = RqtRosGraph.get_upper_grn(index,
'')
505 expanded_nodes.append(grn)
506 instance_settings.set_value(
'expanded_nodes', expanded_nodes)
509 expanded_nodes = instance_settings.value(
'expanded_nodes', [])
512 if RqtRosGraph.get_upper_grn(index,
'')
in expanded_nodes:
513 self._node_selector_view.setExpanded(index,
True)