00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033 from __future__ import division
00034 import os
00035
00036 from python_qt_binding import loadUi
00037 from python_qt_binding.QtCore import Qt, QTimer, Slot
00038 from python_qt_binding.QtGui import QHeaderView, QIcon, QMenu, QTreeWidgetItem, QWidget
00039
00040 import roslib
00041 roslib.load_manifest('rqt_topic')
00042 import rospy
00043 from .topic_info import TopicInfo
00044
00045
00046
00047 class TopicWidget(QWidget):
00048 _column_names = ['topic', 'type', 'bandwidth', 'rate', 'value']
00049
00050 def __init__(self, plugin):
00051 super(TopicWidget, self).__init__()
00052 ui_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'TopicWidget.ui')
00053 loadUi(ui_file, self)
00054 self._plugin = plugin
00055 self.topics_tree_widget.sortByColumn(0, Qt.AscendingOrder)
00056 header = self.topics_tree_widget.header()
00057 header.setResizeMode(QHeaderView.ResizeToContents)
00058 header.customContextMenuRequested.connect(self.handle_header_view_customContextMenuRequested)
00059 header.setContextMenuPolicy(Qt.CustomContextMenu)
00060
00061 self._current_topic_list = []
00062 self._topics = {}
00063 self._tree_items = {}
00064 self._column_index = {}
00065 for column_name in self._column_names:
00066 self._column_index[column_name] = len(self._column_index)
00067
00068 self.refresh_topics()
00069
00070 self._timer_refresh_topics = QTimer(self)
00071 self._timer_refresh_topics.timeout.connect(self.refresh_topics)
00072 self._timer_refresh_topics.start(1000)
00073
00074 @Slot()
00075 def refresh_topics(self):
00076
00077 topic_list = rospy.get_published_topics()
00078 if self._current_topic_list != topic_list:
00079 self._current_topic_list = topic_list
00080
00081
00082 new_topics = {}
00083
00084 for topic_name, topic_type in topic_list:
00085
00086 if topic_name not in self._topics or self._topics[topic_name]['type'] != topic_type:
00087
00088 topic_info = TopicInfo(topic_name)
00089
00090 if topic_info._topic_name:
00091 topic_item = self._recursive_create_widget_items(self.topics_tree_widget, topic_name, topic_type, topic_info.message_class())
00092 new_topics[topic_name] = {
00093 'item': topic_item,
00094 'info': topic_info,
00095 'type': topic_type,
00096 }
00097 else:
00098
00099 new_topics[topic_name] = self._topics[topic_name]
00100 del self._topics[topic_name]
00101
00102
00103 for topic_name in self._topics.keys():
00104 self._topics[topic_name]['info'].stop_monitoring()
00105 index = self.topics_tree_widget.indexOfTopLevelItem(self._topics[topic_name]['item'])
00106 self.topics_tree_widget.takeTopLevelItem(index)
00107 del self._topics[topic_name]
00108
00109
00110 self._topics = new_topics
00111
00112 self._update_topics_data()
00113
00114 def _update_topics_data(self):
00115 for topic in self._topics.values():
00116 topic_info = topic['info']
00117 if topic_info.monitoring:
00118
00119 rate, _, _, _ = topic_info.get_hz()
00120 rate_text = '%1.2f' % rate if rate != None else 'unknown'
00121
00122
00123 bytes_per_s, _, _, _ = topic_info.get_bw()
00124 if bytes_per_s is None:
00125 bandwidth_text = 'unknown'
00126 elif bytes_per_s < 1000:
00127 bandwidth_text = '%.2fB/s' % bytes_per_s
00128 elif bytes_per_s < 1000000:
00129 bandwidth_text = '%.2fKB/s' % (bytes_per_s / 1000.)
00130 else:
00131 bandwidth_text = '%.2fMB/s' % (bytes_per_s / 1000000.)
00132
00133
00134 value_text = ''
00135 self.update_value(topic_info._topic_name, topic_info.last_message)
00136
00137 else:
00138 rate_text = ''
00139 bandwidth_text = ''
00140 value_text = 'not monitored'
00141
00142 self._tree_items[topic_info._topic_name].setText(self._column_index['rate'], rate_text)
00143 self._tree_items[topic_info._topic_name].setText(self._column_index['bandwidth'], bandwidth_text)
00144 self._tree_items[topic_info._topic_name].setText(self._column_index['value'], value_text)
00145
00146 def update_value(self, topic_name, message):
00147 if hasattr(message, '__slots__') and hasattr(message, '_slot_types'):
00148 for slot_name in message.__slots__:
00149 self.update_value(topic_name + '/' + slot_name, getattr(message, slot_name))
00150
00151 elif type(message) in (list, tuple) and (len(message) > 0) and hasattr(message[0], '__slots__'):
00152 for index, slot in enumerate(message):
00153 if topic_name + '[%d]' % index in self._tree_items:
00154 self.update_value(topic_name + '[%d]' % index, slot)
00155 else:
00156 base_type_str, _ = self._extract_array_info(self._tree_items[topic_name].text(self._column_index['type']))
00157 self._recursive_create_widget_items(self._tree_items[topic_name], topic_name + '[%d]' % index, base_type_str, slot)
00158
00159 else:
00160 if topic_name in self._tree_items:
00161 self._tree_items[topic_name].setText(self._column_index['value'], repr(message))
00162
00163 def _extract_array_info(self, type_str):
00164 array_size = None
00165 if '[' in type_str and type_str[-1] == ']':
00166 type_str, array_size_str = type_str.split('[', 1)
00167 array_size_str = array_size_str[:-1]
00168 if len(array_size_str) > 0:
00169 array_size = int(array_size_str)
00170 else:
00171 array_size = 0
00172
00173 return type_str, array_size
00174
00175 def _recursive_create_widget_items(self, parent, topic_name, type_name, message):
00176 if parent is self.topics_tree_widget:
00177
00178 topic_text = topic_name
00179 else:
00180 topic_text = topic_name.split('/')[-1]
00181 if '[' in topic_text:
00182 topic_text = topic_text[topic_text.index('['):]
00183 item = QTreeWidgetItem(parent)
00184 item.setText(self._column_index['topic'], topic_text)
00185 item.setText(self._column_index['type'], type_name)
00186 item.setData(0, Qt.UserRole, topic_name)
00187 self._tree_items[topic_name] = item
00188 if hasattr(message, '__slots__') and hasattr(message, '_slot_types'):
00189 for slot_name, type_name in zip(message.__slots__, message._slot_types):
00190 self._recursive_create_widget_items(item, topic_name + '/' + slot_name, type_name, getattr(message, slot_name))
00191
00192 else:
00193 base_type_str, array_size = self._extract_array_info(type_name)
00194 try:
00195 base_instance = roslib.message.get_message_class(base_type_str)()
00196 except ValueError:
00197 base_instance = None
00198 if array_size is not None and hasattr(base_instance, '__slots__'):
00199 for index in range(array_size):
00200 self._recursive_create_widget_items(item, topic_name + '[%d]' % index, base_type_str, base_instance)
00201 return item
00202
00203 @Slot('QPoint')
00204 def handle_header_view_customContextMenuRequested(self, pos):
00205 header = self.topics_tree_widget.header()
00206
00207
00208 menu = QMenu(self)
00209 action_toggle_auto_resize = menu.addAction('Toggle Auto-Resize')
00210 action = menu.exec_(header.mapToGlobal(pos))
00211
00212
00213 if action is action_toggle_auto_resize:
00214 if header.resizeMode(0) == QHeaderView.ResizeToContents:
00215 header.setResizeMode(QHeaderView.Interactive)
00216 else:
00217 header.setResizeMode(QHeaderView.ResizeToContents)
00218
00219 @Slot('QPoint')
00220 def on_topics_tree_widget_customContextMenuRequested(self, pos):
00221 item = self.topics_tree_widget.itemAt(pos)
00222 if item is None:
00223 return
00224
00225
00226 menu = QMenu(self)
00227 action_moggle_monitoring = menu.addAction(QIcon.fromTheme('search'), 'Toggle Monitoring')
00228 action_item_expand = menu.addAction(QIcon.fromTheme('zoom-in'), 'Expand All Children')
00229 action_item_collapse = menu.addAction(QIcon.fromTheme('zoom-out'), 'Collapse All Children')
00230 action = menu.exec_(self.topics_tree_widget.mapToGlobal(pos))
00231
00232
00233 if action is action_moggle_monitoring:
00234 root_item = item
00235 while root_item.parent() is not None:
00236 root_item = root_item.parent()
00237 root_topic_name = root_item.data(0, Qt.UserRole)
00238 self._topics[root_topic_name]['info'].toggle_monitoring()
00239
00240 elif action in (action_item_expand, action_item_collapse):
00241 expanded = (action is action_item_expand)
00242
00243 def recursive_set_expanded(item):
00244 item.setExpanded(expanded)
00245 for index in range(item.childCount()):
00246 recursive_set_expanded(item.child(index))
00247 recursive_set_expanded(item)
00248
00249 def shutdown_plugin(self):
00250 for topic in self._topics.values():
00251 topic['info'].stop_monitoring()
00252 self._timer_refresh_topics.stop()