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()