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 QItemSelectionModel
48 from python_qt_binding.QtGui
import QItemSelectionModel
49 from python_qt_binding.QtWidgets
import QHeaderView, QWidget
51 from rospy.exceptions
import ROSException
57 from rqt_reconfigure
import logging
68 sig_node_selected = Signal(DynreconfClientWidget)
70 def __init__(self, parent, rospack, signal_msg=None):
72 @param signal_msg: Signal to carries a system msg that is shown on GUI. 73 @type signal_msg: QtCore.Signal 75 super(NodeSelectorWidget, self).
__init__()
80 ui_file = os.path.join(rospack.get_path(
'rqt_reconfigure'),
'resource',
94 self.
_rootitem = self._item_model.invisibleRootItem()
107 self._collapse_button.pressed.connect(
108 self._node_selector_view.collapseAll)
109 self._expand_button.pressed.connect(self._node_selector_view.expandAll)
114 self._proxy_model.setDynamicSortFilter(
True)
123 self._node_selector_view.header().setResizeMode(
124 0, QHeaderView.ResizeToContents)
125 except AttributeError:
134 self.selectionModel.selectionChanged.connect(
139 Deselect the index that corresponds to the given GRN. 144 qindex_tobe_deselected = self._item_model.get_index_from_grn(grn)
145 logging.debug(
'NodeSelWidt node_deselected qindex={} data={}'.format(
146 qindex_tobe_deselected,
147 qindex_tobe_deselected.data(Qt.DisplayRole)))
150 indexes_selected = self.selectionModel.selectedIndexes()
151 for index
in indexes_selected:
152 grn_from_selectedindex = RqtRosGraph.get_upper_grn(index,
'')
153 logging.debug(
' Compare given grn={} grn from selected={}'.format(
154 grn, grn_from_selectedindex))
156 if grn == grn_from_selectedindex:
158 self.selectionModel.select(index, QItemSelectionModel.Deselect)
162 Select the index that corresponds to the given GRN. 167 qindex_tobe_selected = self._item_model.get_index_from_grn(grn)
168 logging.debug(
'NodeSelWidt node_selected qindex={} data={}'.format(
169 qindex_tobe_selected, qindex_tobe_selected.data(Qt.DisplayRole)))
172 if qindex_tobe_selected:
173 self.selectionModel.select(
174 qindex_tobe_selected, QItemSelectionModel.Select)
178 Intended to be called from _selection_changed_slot. 180 self.selectionModel.select(index_current, QItemSelectionModel.Deselect)
184 rosnode_name_selected].get_dynreconf_widget()
185 except ROSException
as e:
189 self.sig_node_selected.emit(reconf_widget)
193 """Intended to be called from _selection_changed_slot.""" 194 logging.debug(
'_selection_changed_slot row={} col={} data={}'.format(
195 index_current.row(), index_current.column(),
196 index_current.data(Qt.DisplayRole)))
200 for nodeitem
in self._nodeitems.values():
201 name_nodeitem = nodeitem.data(Qt.DisplayRole)
202 name_rosnode_leaf = rosnode_name_selected[
203 rosnode_name_selected.rfind(RqtRosGraph.DELIM_GRN) + 1:]
207 if ((name_nodeitem == rosnode_name_selected)
and 209 name_nodeitem.rfind(RqtRosGraph.DELIM_GRN) + 1:] ==
212 logging.debug(
'terminal str {} MATCH {}'.format(
213 name_nodeitem, name_rosnode_leaf))
217 self.selectionModel.select(index_current,
218 QItemSelectionModel.Deselect)
223 item_child = self.
_nodeitems[rosnode_name_selected]
226 item_widget = item_child.get_dynreconf_widget()
227 except ROSException
as e:
229 logging.debug(
'item_selected={} child={} widget={}'.format(
230 index_current, item_child, item_widget))
231 self.sig_node_selected.emit(item_widget)
238 Sends "open ROS Node box" signal ONLY IF the selected treenode is the 240 Receives args from signal QItemSelectionModel.selectionChanged. 242 :param selected: All indexs where selected (could be multiple) 243 :type selected: QItemSelection 244 :type deselected: QItemSelection 247 if not selected.indexes()
and not deselected.indexes():
248 logging.error(
'Nothing selected? Not ideal to reach here')
252 if selected.indexes():
253 index_current = selected.indexes()[0]
254 elif len(deselected.indexes()) == 1:
260 index_current = deselected.indexes()[0]
262 logging.debug(
' - - - index_current={}'.format(index_current))
264 rosnode_name_selected = RqtRosGraph.get_upper_grn(index_current,
'')
267 if rosnode_name_selected
not in self._nodeitems.keys():
269 self.selectionModel.select(index_current,
270 QItemSelectionModel.Deselect)
273 if selected.indexes():
276 except ROSException
as e:
278 err_msg = e.message +
'. Connection to node=' + \
279 format(rosnode_name_selected) +
' failed' 280 self._signal_msg.emit(err_msg)
281 logging.error(err_msg)
283 elif deselected.indexes():
286 rosnode_name_selected)
287 except ROSException
as e:
288 logging.error(e.message)
293 :rtype: OrderedDict 1st elem is node's GRN name, 294 2nd is TreenodeQstdItem instance 306 nodes = dyn_reconf.find_reconfigure_services()
307 except rosservice.ROSServiceIOException
as e:
308 logging.error(
'Reconfigure GUI cannot connect to master.')
313 num_nodes = len(nodes)
314 elapsedtime_overall = 0.0
315 for node_name_grn
in nodes:
321 time_siglenode_loop = time.time()
332 node_name_grn, TreenodeQstdItem.NODE_FULLPATH)
333 _treenode_names = treenodeitem_toplevel.get_treenode_names()
337 self.
_nodeitems[node_name_grn] = treenodeitem_toplevel
342 time_siglenode_loop = time.time() - time_siglenode_loop
343 elapsedtime_overall += time_siglenode_loop
345 _str_progress =
'reconf ' + \
346 'loading #{}/{} {} / {}sec node={}'.format(
347 i_node_curr, num_nodes, round(time_siglenode_loop, 2),
348 round(elapsedtime_overall, 2), node_name_grn
353 logging.debug(_str_progress)
357 treenodeitem_parent, child_names_left):
359 Evaluate current treenode and the previous treenode at the same depth. 360 If the name of both nodes is the same, current node instance is 361 ignored (that means children will be added to the same parent). If not, 362 the current node gets added to the same parent node. At the end, this 363 function gets called recursively going 1 level deeper. 365 :type treenodeitem_toplevel: TreenodeQstdItem 366 :type treenodeitem_parent: TreenodeQstdItem. 367 :type child_names_left: List of str 368 :param child_names_left: List of strings that is sorted in hierarchical 373 name_currentnode = child_names_left.pop(0)
374 grn_curr = treenodeitem_toplevel.get_raw_param_name()
376 TreenodeQstdItem.NODE_FULLPATH)
379 row_index_parent = treenodeitem_parent.rowCount() - 1
384 if treenodeitem_parent.child(row_index_parent):
385 stditem_prev = treenodeitem_parent.child(row_index_parent)
386 name_prev = stditem_prev.text()
391 if name_prev != name_currentnode:
392 stditem_currentnode.setText(name_currentnode)
396 while (insert_index < treenodeitem_parent.rowCount()
and 397 treenodeitem_parent.child(insert_index)
398 .text() < name_currentnode):
401 treenodeitem_parent.insertRow(insert_index, stditem_currentnode)
402 stditem = stditem_currentnode
404 stditem = stditem_prev
414 self._item_model.set_item_from_index(grn_curr, stditem.index())
418 nodes = dyn_reconf.find_reconfigure_services()
419 except rosservice.ROSServiceIOException
as e:
420 logging.error(
'Reconfigure GUI cannot connect to master.')
423 for i
in reversed(range(0, self._rootitem.rowCount())):
424 candidate_for_removal = \
425 self._rootitem.child(i).get_raw_param_name()
426 if candidate_for_removal
not in nodes:
428 'Removing {} because the server is no longer available.'.
429 format(candidate_for_removal))
431 disconnect_param_server()
432 self._rootitem.removeRow(i)
433 self._nodeitems.pop(candidate_for_removal)
440 logging.debug(
' in close_node')
445 Pass fileter instance to the child proxymodel. 446 :type filter_: BaseFilter 448 self._proxy_model.set_filter(filter_)
452 Method for Debug only 457 index_deselected =
None 459 curr_qstd_item =
None 460 if selected.indexes():
461 index_current = selected.indexes()[0]
462 index_parent = index_current.parent()
463 curr_qstd_item = src_model.itemFromIndex(index_current)
464 elif deselected.indexes():
465 index_deselected = deselected.indexes()[0]
466 index_parent = index_deselected.parent()
467 curr_qstd_item = src_model.itemFromIndex(index_deselected)
469 if selected.indexes() > 0:
471 'sel={} par={} desel={} ' 472 'sel.d={} par.d={} desel.d={} cur.item={}' 474 index_current, index_parent, index_deselected,
475 index_current.data(Qt.DisplayRole),
476 index_parent.data(Qt.DisplayRole),
479 elif deselected.indexes():
481 'sel={} par={} desel={} ' 482 'sel.d={} par.d={} desel.d={} cur.item={}' 484 index_current, index_parent, index_deselected,
485 None, index_parent.data(Qt.DisplayRole),
486 index_deselected.data(Qt.DisplayRole),