33 from __future__
import division
36 from python_qt_binding
import loadUi
37 from python_qt_binding.QtCore
import Qt, QTimer, Signal, Slot
38 from python_qt_binding.QtGui
import QIcon
39 from python_qt_binding.QtWidgets
import QHeaderView, QMenu, QTreeWidgetItem, QWidget, QSlider, QAbstractItemView
45 from rospy.exceptions
import ROSException
47 from .topic_info
import TopicInfo
54 main class inherits from the ui window class. 56 You can specify the topics that the topic pane. 58 TopicWidget.start must be called in order to update topic pane. 65 _column_names = [
'topic',
'type',
'value',
'checkbox']
67 selectionChanged = Signal(list, name=
'selectionChanged')
68 topicsRefreshed = Signal(name=
'topicsRefreshed')
71 def __init__(self, plugin=None, selected_topics=None, select_topic_type=SELECT_BY_NAME):
73 @type selected_topics: list of tuples. 74 @param selected_topics: [($NAME_TOPIC$, $TYPE_TOPIC$), ...] 75 @type select_topic_type: int 76 @param select_topic_type: Can specify either the name of topics or by 77 the type of topic, to filter the topics to 78 show. If 'select_topic_type' argument is 79 None, this arg shouldn't be meaningful. 86 ui_file = os.path.join(rp.get_path(
'rqt_dyn_tune'),
'resource',
'TopicWidget.ui')
89 self.topics_tree_widget.sortByColumn(0, Qt.AscendingOrder)
90 header = self.topics_tree_widget.header()
92 setSectionResizeMode = header.setSectionResizeMode
93 except AttributeError:
94 setSectionResizeMode = header.setResizeMode
96 setSectionResizeMode(i, QHeaderView.Stretch)
98 header.setStretchLastSection(
False)
99 setSectionResizeMode(0, QHeaderView.Stretch)
100 setSectionResizeMode(3, QHeaderView.ResizeToContents)
102 self.topics_tree_widget.setSelectionMode(QAbstractItemView.MultiSelection)
120 self.topics_tree_widget.itemExpanded.connect(self.
expanded)
121 self.topics_tree_widget.itemCollapsed.connect(self.
collapsed)
128 self.topics_tree_widget.setAlternatingRowColors(
True)
130 self.topics_tree_widget.header().resizeSection(3, 50)
141 This method needs to be called to start updating topic pane. 143 self._timer_refresh_topics.start(1000)
151 for key, item
in self._tree_items.items():
152 if item.isSelected():
153 item.setText(3,
'{%d}' % selected.index(key))
157 self.selectionChanged.emit(selected)
161 @Slot(
'QTreeWidgetItem')
164 name = item.data(0, Qt.UserRole)
166 if not isinstance(item, TreeWidgetItem)
or not item._is_topic:
169 print "expanded", name
170 self.
_topics[item._topic_name][
'info'].start_monitoring()
173 @Slot(
'QTreeWidgetItem')
176 name = item.data(0, Qt.UserRole)
178 if not isinstance(item, TreeWidgetItem)
or not item._is_topic:
181 print "collapsed", name
182 self.
_topics[item._topic_name][
'info'].stop_monitoring()
185 if not hasattr(items,
"__iter__"):
189 self._selected_items.remove(item)
190 self.
_tree_items[item].setData(self._column_names.index(
"checkbox"), Qt.CheckStateRole,
False)
194 self.topics_tree_widget.clearSelection()
198 self.
_tree_items[item].setData(self._column_names.index(
"checkbox"), Qt.CheckStateRole,
False)
203 self.
_selected_items = [ item._topic_name
for item
in self.topics_tree_widget.selectedItems() ]
210 values = {item: self.
values[item]
for item
in items
if item
in self.
values}
217 return selected_types
223 refresh tree view items 228 topic_list = rospy.get_published_topics()
229 if topic_list
is None:
230 rospy.logerr(
'Not even a single published topic found. Check network configuration')
234 topic_specifiers_server_all =
None 235 topic_specifiers_required =
None 237 rospy.logdebug(
'refresh_topics) self._selected_topics=%s' % (topic_list,))
240 topic_specifiers_server_all = [name
for name, type
in rospy.get_published_topics()]
241 topic_specifiers_required = [name
for name, type
in topic_list]
244 topic_specifiers_required = [type
for name, type
in topic_list]
247 topics_match = [(name, type)
for name, type
in rospy.get_published_topics()
if type
in topic_specifiers_required]
248 topic_list = topics_match
249 rospy.logdebug(
'selected & published topic types=%s' % (topic_list,))
251 rospy.logdebug(
'server_all=%s\nrequired=%s\ntlist=%s' % (topic_specifiers_server_all, topic_specifiers_required, topic_list))
252 if len(topic_list) == 0:
253 rospy.logerr(
'None of the following required topics are found.\n(NAME, TYPE): %s' % (self.
_selected_topics,))
256 rospy.logerr(
"Communication with rosmaster failed: {0}".
format(e.strerror))
267 for topic_name, topic_type
in topic_list:
269 if topic_name
not in self.
_topics or \
270 self.
_topics[topic_name][
'type'] != topic_type:
272 topic_info = TopicInfo(topic_name, topic_type)
273 message_instance =
None 274 if topic_info.message_class
is not None:
275 message_instance = topic_info.message_class()
278 new_topics[topic_name] = {
286 new_topics[topic_name] = self.
_topics[topic_name]
290 for topic_name
in self._topics.keys():
291 self.
_topics[topic_name][
'info'].stop_monitoring()
292 index = self.topics_tree_widget.indexOfTopLevelItem(
293 self.
_topics[topic_name][
'item'])
294 self.topics_tree_widget.takeTopLevelItem(index)
304 self.topicsRefreshed.emit()
308 index = self.topics_tree_widget.indexOfTopLevelItem(item)
309 self.topics_tree_widget.takeTopLevelItem(index)
318 for topic
in self._topics.values():
319 topic_info = topic[
'info']
320 if topic_info.monitoring:
322 rate, _, _, _ = topic_info.get_hz()
323 rate_text =
'%1.2f' % rate
if rate !=
None else 'unknown' 326 bytes_per_s, _, _, _ = topic_info.get_bw()
327 if bytes_per_s
is None:
328 bandwidth_text =
'unknown' 329 elif bytes_per_s < 1000:
330 bandwidth_text =
'%.2fB/s' % bytes_per_s
331 elif bytes_per_s < 1000000:
332 bandwidth_text =
'%.2fKB/s' % (bytes_per_s / 1000.)
334 bandwidth_text =
'%.2fMB/s' % (bytes_per_s / 1000000.)
338 self.
update_value(topic_info._topic_name, topic_info.last_message)
343 value_text =
'not monitored' if topic_info.error
is None else topic_info.error
350 if hasattr(value,
'__iter__'):
352 value = np.array(value)
357 self.values.update({topic_name:value})
359 if hasattr(message,
'__slots__')
and hasattr(message,
'_slot_types'):
360 slots = zip(message.__slots__, message._slot_types)
361 for slot_name, slot_type
in slots:
362 self.
update_value(topic_name +
'/' + slot_name, getattr(message, slot_name), vtype = slot_type)
364 elif type(message)
in (list, tuple)
and (len(message) > 0):
366 for index, slot
in enumerate(message):
367 if topic_name +
'[%d]' % index
in self.
_tree_items:
373 if len(message) < self.
_tree_items[topic_name].childCount():
374 for i
in range(len(message), self.
_tree_items[topic_name].childCount()):
375 item_topic_name = topic_name +
'[%d]' % i
383 if '[' in type_str
and type_str[-1] ==
']':
384 type_str, array_size_str = type_str.split(
'[', 1)
385 array_size_str = array_size_str[:-1]
386 if len(array_size_str) > 0:
387 array_size = int(array_size_str)
391 return type_str, array_size
394 if parent
is self.topics_tree_widget:
396 topic_text = topic_name
399 topic_names = topic_name.split(
'/')
401 for name
in topic_names:
405 name_space = name_space + _name
410 if name_space == topic_name:
416 _item.setData(0, Qt.UserRole, name_space)
427 topic_text = topic_name.split(
'/')[-1]
428 if '[' in topic_text:
429 topic_text = topic_text[topic_text.index(
'['):]
435 item.setData(0, Qt.UserRole, topic_name)
440 if hasattr(message,
'__slots__')
and hasattr(message,
'_slot_types'):
441 for slot_name, type_name
in zip(message.__slots__, message._slot_types):
447 base_instance = roslib.message.get_message_class(base_type_str)()
448 except (ValueError, TypeError):
450 if array_size
is not None and hasattr(base_instance,
'__slots__'):
451 for index
in range(array_size):
461 for i
in range(len(sel)):
463 _item.setText(3,
'{%d}' % i )
469 def _recursive_remove_items_from_tree(item):
470 for index
in reversed(range(item.childCount())):
471 _recursive_remove_items_from_tree(item.child(index))
472 topic_name = item.data(0, Qt.UserRole)
474 _recursive_remove_items_from_tree(item)
475 item.parent().removeChild(item)
479 header = self.topics_tree_widget.header()
483 action_toggle_auto_resize = menu.addAction(
'Toggle Auto-Resize')
484 action = menu.exec_(header.mapToGlobal(pos))
487 if action
is action_toggle_auto_resize:
488 if header.resizeMode(0) == QHeaderView.ResizeToContents:
489 header.setResizeMode(QHeaderView.Interactive)
491 header.setResizeMode(QHeaderView.ResizeToContents)
495 item = self.topics_tree_widget.itemAt(pos)
501 action_item_expand = menu.addAction(QIcon.fromTheme(
'zoom-in'),
'Expand All Children')
502 action_item_collapse = menu.addAction(QIcon.fromTheme(
'zoom-out'),
'Collapse All Children')
503 action = menu.exec_(self.topics_tree_widget.mapToGlobal(pos))
506 if action
in (action_item_expand, action_item_collapse):
507 expanded = (action
is action_item_expand)
509 def recursive_set_expanded(item):
510 item.setExpanded(expanded)
511 for index
in range(item.childCount()):
512 recursive_set_expanded(item.child(index))
513 recursive_set_expanded(item)
516 for topic
in self._topics.values():
517 topic[
'info'].stop_monitoring()
518 self._timer_refresh_topics.stop()
522 @param selected_topics: list of tuple. [(topic_name, topic_type)] 523 @type selected_topics: [] 525 rospy.logdebug(
'set_selected_topics topics={}'.
format(
526 len(selected_topics)))
531 header_state = self.topics_tree_widget.header().saveState()
532 instance_settings.set_value(
'tree_widget_header_state', header_state)
535 if instance_settings.contains(
'tree_widget_header_state'):
536 header_state = instance_settings.value(
'tree_widget_header_state')
537 if not self.topics_tree_widget.header().restoreState(header_state):
538 rospy.logwarn(
"rqt_dyn_tune: Failed to restore header state.")
542 def __init__(self, check_state_changed_callback, topic_name, parent=None, is_topic = False):
543 super(TreeWidgetItem, self).
__init__(parent)
551 super(TreeWidgetItem, self).
setData(column, role, value)
def format(color, style='')