00001
00002 from rqt_gui_py.plugin import Plugin
00003 from python_qt_binding import loadUi
00004 from python_qt_binding.QtCore import Qt, QTimer, qWarning, Slot
00005 from python_qt_binding.QtGui import QAction, QIcon, QMenu, QWidget
00006 from python_qt_binding.QtGui import QWidget, QVBoxLayout, QSizePolicy, QColor
00007 from rqt_py_common.topic_completer import TopicCompleter
00008 from matplotlib.colors import colorConverter
00009 from rqt_py_common.topic_helpers import is_slot_numeric
00010 from rqt_plot.rosplot import ROSData as _ROSData
00011 from rqt_plot.rosplot import RosPlotException
00012 from matplotlib.collections import PolyCollection, PathCollection, LineCollection
00013 import matplotlib
00014
00015 import rospkg
00016 import rospy
00017
00018 import os, sys
00019 import argparse
00020
00021 try:
00022 from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
00023 except ImportError:
00024
00025 import sys
00026 import thread
00027 sys.modules['_thread'] = thread
00028 from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
00029 from matplotlib.backends.backend_qt4agg import NavigationToolbar2QTAgg as NavigationToolbar
00030 from matplotlib.figure import Figure
00031
00032 import numpy as np
00033 import matplotlib.pyplot as plt
00034
00035 class ROSData(_ROSData):
00036 def _get_data(self, msg):
00037 val = msg
00038 try:
00039 if not self.field_evals:
00040 return val
00041 for f in self.field_evals:
00042 val = f(val)
00043 return val
00044 except IndexError:
00045 self.error = RosPlotException("[%s] index error for: %s" % (self.name, str(val).replace('\n', ', ')))
00046 except TypeError:
00047 self.error = RosPlotException("[%s] value was not numeric: %s" % (self.name, val))
00048
00049
00050
00051 class HistogramPlot(Plugin):
00052 def __init__(self, context):
00053 super(HistogramPlot, self).__init__(context)
00054 self.setObjectName('HistogramPlot')
00055 self._args = self._parse_args(context.argv())
00056 self._widget = HistogramPlotWidget(self._args.topics)
00057 context.add_widget(self._widget)
00058 def _parse_args(self, argv):
00059 parser = argparse.ArgumentParser(prog='rqt_histogram_plot', add_help=False)
00060 HistogramPlot.add_arguments(parser)
00061 args = parser.parse_args(argv)
00062 return args
00063 @staticmethod
00064 def add_arguments(parser):
00065 group = parser.add_argument_group('Options for rqt_histogram plugin')
00066
00067
00068
00069
00070
00071
00072
00073
00074 group.add_argument('topics', nargs='?', default=[], help='Topics to plot')
00075
00076 class HistogramPlotWidget(QWidget):
00077 _redraw_interval = 40
00078 def __init__(self, topics):
00079 super(HistogramPlotWidget, self).__init__()
00080 self.setObjectName('HistogramPlotWidget')
00081 rp = rospkg.RosPack()
00082 ui_file = os.path.join(rp.get_path('jsk_rqt_plugins'),
00083 'resource', 'plot_histogram.ui')
00084 loadUi(ui_file, self)
00085 self.subscribe_topic_button.setIcon(QIcon.fromTheme('add'))
00086 self.pause_button.setIcon(QIcon.fromTheme('media-playback-pause'))
00087 self.clear_button.setIcon(QIcon.fromTheme('edit-clear'))
00088 self.data_plot = MatHistogramPlot(self)
00089 self.data_plot_layout.addWidget(self.data_plot)
00090 self._topic_completer = TopicCompleter(self.topic_edit)
00091 self._topic_completer.update_topics()
00092 self.topic_edit.setCompleter(self._topic_completer)
00093 self.data_plot.dropEvent = self.dropEvent
00094 self.data_plot.dragEnterEvent = self.dragEnterEvent
00095 self._start_time = rospy.get_time()
00096 self._rosdata = None
00097 if len(topics) != 0:
00098 self.subscribe_topic(topics)
00099 self._update_plot_timer = QTimer(self)
00100 self._update_plot_timer.timeout.connect(self.update_plot)
00101 self._update_plot_timer.start(self._redraw_interval)
00102 @Slot('QDropEvent*')
00103 def dropEvent(self, event):
00104 print "hello"
00105 if event.mimeData().hasText():
00106 topic_name = str(event.mimeData().text())
00107 else:
00108 droped_item = event.source().selectedItems()[0]
00109 topic_name = str(droped_item.data(0, Qt.UserRole))
00110 self.subscribe_topic(topic_name)
00111 @Slot()
00112 def on_topic_edit_returnPressed(self):
00113 if self.subscribe_topic_button.isEnabled():
00114 self.subscribe_topic(str(self.topic_edit.text()))
00115 @Slot()
00116 def on_subscribe_topic_button_clicked(self):
00117 self.subscribe_topic(str(self.topic_edit.text()))
00118
00119 def subscribe_topic(self, topic_name):
00120 if not self._rosdata:
00121 self._rosdata = ROSData(topic_name, self._start_time)
00122 else:
00123 if self._rosdata != topic_name:
00124 self._rosdata.close()
00125 self.data_plot.clear()
00126 self._rosdata = ROSData(topic_name, self._start_time)
00127 else:
00128 rospy.logwarn("%s is already subscribed", topic_name)
00129
00130 def enable_timer(self, enabled=True):
00131 if enabled:
00132 self._update_plot_timer.start(self._redraw_interval)
00133 else:
00134 self._update_plot_timer.stop()
00135 @Slot()
00136 def on_clear_button_clicked(self):
00137 self.data_plot.clear()
00138
00139 @Slot(bool)
00140 def on_pause_button_clicked(self, checked):
00141 self.enable_timer(not checked)
00142
00143 def update_plot(self):
00144 if not self._rosdata:
00145 return
00146 data_x, data_y = self._rosdata.next()
00147 if len(data_y) == 0:
00148 return
00149 xs = data_y[-1]
00150 axes = self.data_plot._canvas.axes
00151 axes.cla()
00152 axes.set_xlim(xmin=0, xmax=len(xs))
00153 pos = np.arange(len(xs))
00154
00155 for p, x in zip(pos, xs):
00156 axes.bar(p, x, color='r', align='center')
00157 self.data_plot._canvas.draw()
00158
00159 class MatHistogramPlot(QWidget):
00160 class Canvas(FigureCanvas):
00161 def __init__(self, parent=None):
00162 super(MatHistogramPlot.Canvas, self).__init__(Figure())
00163 self.axes = self.figure.add_subplot(111)
00164 self.figure.tight_layout()
00165 self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
00166 self.updateGeometry()
00167 def resizeEvent(self, event):
00168 super(MatHistogramPlot.Canvas, self).resizeEvent(event)
00169 self.figure.tight_layout()
00170 def __init__(self, parent=None):
00171 super(MatHistogramPlot, self).__init__(parent)
00172 self._canvas = MatHistogramPlot.Canvas()
00173 self._toolbar = NavigationToolbar(self._canvas, self._canvas)
00174 vbox = QVBoxLayout()
00175 vbox.addWidget(self._toolbar)
00176 vbox.addWidget(self._canvas)
00177 self.setLayout(vbox)
00178 def redraw(self):
00179 pass
00180 def clear(self):
00181 self._canvas.axes.cla()
00182 self._canvas.draw()