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 import os
00034 import roslib
00035 roslib.load_manifest('rqt_plot')
00036
00037 from python_qt_binding import loadUi
00038 from python_qt_binding.QtCore import Qt, QTimer, qWarning, Slot
00039 from python_qt_binding.QtGui import QAction, QIcon, QMenu, QWidget
00040
00041 import rospy
00042
00043 from rqt_py_common.topic_completer import TopicCompleter
00044 from rqt_py_common.topic_helpers import is_slot_numeric
00045
00046 from . rosplot import ROSData, RosPlotException
00047
00048
00049 class PlotWidget(QWidget):
00050 _redraw_interval = 40
00051
00052 def __init__(self, arguments=None, initial_topics=None):
00053 super(PlotWidget, self).__init__()
00054 self.setObjectName('PlotWidget')
00055
00056 ui_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'plot.ui')
00057 loadUi(ui_file, self)
00058 self.subscribe_topic_button.setIcon(QIcon.fromTheme('add'))
00059 self.remove_topic_button.setIcon(QIcon.fromTheme('remove'))
00060 self.pause_button.setIcon(QIcon.fromTheme('media-playback-pause'))
00061 self.clear_button.setIcon(QIcon.fromTheme('edit-clear'))
00062 self.data_plot = None
00063
00064 self.subscribe_topic_button.setEnabled(False)
00065
00066 self._topic_completer = TopicCompleter(self.topic_edit)
00067 self._topic_completer.update_topics()
00068 self.topic_edit.setCompleter(self._topic_completer)
00069
00070 self._start_time = rospy.get_time()
00071 self._rosdata = {}
00072 self._remove_topic_menu = QMenu()
00073
00074
00075 self._update_plot_timer = QTimer(self)
00076 self._update_plot_timer.timeout.connect(self.update_plot)
00077
00078
00079 self._arguments = arguments
00080 self._initial_topics = initial_topics
00081
00082 def switch_data_plot_widget(self, data_plot):
00083 self.enable_timer(enabled=False)
00084
00085 self.data_plot_layout.removeWidget(self.data_plot)
00086 if self.data_plot is not None:
00087 self.data_plot.close()
00088
00089 self.data_plot = data_plot
00090 self.data_plot_layout.addWidget(self.data_plot)
00091
00092
00093 self.data_plot.dropEvent = self.dropEvent
00094 self.data_plot.dragEnterEvent = self.dragEnterEvent
00095
00096 if self._initial_topics:
00097 for topic_name in self._initial_topics:
00098 self.add_topic(topic_name)
00099 self._initial_topics = None
00100 else:
00101 for topic_name, rosdata in self._rosdata.items():
00102 data_x, data_y = rosdata.next()
00103 self.data_plot.add_curve(topic_name, topic_name, data_x, data_y)
00104
00105 self._subscribed_topics_changed()
00106
00107 @Slot('QDragEnterEvent*')
00108 def dragEnterEvent(self, event):
00109
00110 if not event.mimeData().hasText():
00111 if not hasattr(event.source(), 'selectedItems') or len(event.source().selectedItems()) == 0:
00112 qWarning('Plot.dragEnterEvent(): not hasattr(event.source(), selectedItems) or len(event.source().selectedItems()) == 0')
00113 return
00114 item = event.source().selectedItems()[0]
00115 topic_name = item.data(0, Qt.UserRole)
00116 if topic_name == None:
00117 qWarning('Plot.dragEnterEvent(): not hasattr(item, ros_topic_name_)')
00118 return
00119 else:
00120 topic_name = str(event.mimeData().text())
00121
00122
00123 is_numeric, is_array, message = is_slot_numeric(topic_name)
00124 if is_numeric and not is_array:
00125 event.acceptProposedAction()
00126 else:
00127 qWarning('Plot.dragEnterEvent(): rejecting: "%s"' % (message))
00128
00129 @Slot('QDropEvent*')
00130 def dropEvent(self, event):
00131 if event.mimeData().hasText():
00132 topic_name = str(event.mimeData().text())
00133 else:
00134 droped_item = event.source().selectedItems()[0]
00135 topic_name = str(droped_item.data(0, Qt.UserRole))
00136 self.add_topic(topic_name)
00137
00138 @Slot(str)
00139 def on_topic_edit_textChanged(self, topic_name):
00140
00141 if topic_name in ('', '/'):
00142 self._topic_completer.update_topics()
00143
00144 is_numeric, is_array, message = is_slot_numeric(topic_name)
00145 self.subscribe_topic_button.setEnabled(is_numeric and not is_array)
00146 self.subscribe_topic_button.setToolTip(message)
00147
00148 @Slot()
00149 def on_subscribe_topic_button_clicked(self):
00150 self.add_topic(str(self.topic_edit.text()))
00151
00152 @Slot(bool)
00153 def on_pause_button_clicked(self, checked):
00154 self.enable_timer(not checked)
00155
00156 @Slot()
00157 def on_clear_button_clicked(self):
00158 self.clean_up_subscribers()
00159
00160 def update_plot(self):
00161 if self.data_plot is not None:
00162 for topic_name, rosdata in self._rosdata.items():
00163 try:
00164 data_x, data_y = rosdata.next()
00165 self.data_plot.update_values(topic_name, data_x, data_y)
00166 except RosPlotException as e:
00167 qWarning('PlotWidget.update_plot(): error in rosplot: %s' % e)
00168 self.data_plot.redraw()
00169
00170 def _subscribed_topics_changed(self):
00171 self._update_remove_topic_menu()
00172 if self._arguments:
00173 if self._arguments.start_paused:
00174 self.pause_button.setChecked(True)
00175 if not self.pause_button.isChecked():
00176
00177 self.enable_timer(self._rosdata)
00178
00179 def _update_remove_topic_menu(self):
00180 def make_remove_topic_function(x):
00181 return lambda: self.remove_topic(x)
00182
00183 self._remove_topic_menu.clear()
00184 for topic_name in sorted(self._rosdata.keys()):
00185 action = QAction(topic_name, self._remove_topic_menu)
00186 action.triggered.connect(make_remove_topic_function(topic_name))
00187 self._remove_topic_menu.addAction(action)
00188
00189 self.remove_topic_button.setMenu(self._remove_topic_menu)
00190
00191 def add_topic(self, topic_name):
00192 if topic_name in self._rosdata:
00193 qWarning('PlotWidget.add_topic(): topic already subscribed: %s' % topic_name)
00194 return
00195
00196 self._rosdata[topic_name] = ROSData(topic_name, self._start_time)
00197 data_x, data_y = self._rosdata[topic_name].next()
00198 self.data_plot.add_curve(topic_name, topic_name, data_x, data_y)
00199
00200 self._subscribed_topics_changed()
00201
00202 def remove_topic(self, topic_name):
00203 self._rosdata[topic_name].close()
00204 del self._rosdata[topic_name]
00205 self.data_plot.remove_curve(topic_name)
00206
00207 self._subscribed_topics_changed()
00208
00209 def clean_up_subscribers(self):
00210 for topic_name, rosdata in self._rosdata.items():
00211 rosdata.close()
00212 self.data_plot.remove_curve(topic_name)
00213 self._rosdata = {}
00214
00215 self._subscribed_topics_changed()
00216
00217 def enable_timer(self, enabled=True):
00218 if enabled:
00219 self._update_plot_timer.start(self._redraw_interval)
00220 else:
00221 self._update_plot_timer.stop()