hist.py
Go to the documentation of this file.
00001 #!/usr/bin/env python
00002 
00003 import argparse
00004 import collections
00005 import os
00006 import sys
00007 
00008 from cStringIO import StringIO
00009 import cv2
00010 from cv_bridge import CvBridge
00011 from distutils.version import LooseVersion
00012 from matplotlib.figure import Figure
00013 import numpy as np
00014 import python_qt_binding
00015 from python_qt_binding import loadUi
00016 from python_qt_binding.QtCore import Qt
00017 from python_qt_binding.QtCore import QTimer
00018 from python_qt_binding.QtCore import Slot
00019 from python_qt_binding.QtGui import QIcon
00020 import rospkg
00021 import rospy
00022 from rqt_gui_py.plugin import Plugin
00023 from rqt_plot.rosplot import ROSData as _ROSData
00024 from rqt_plot.rosplot import RosPlotException
00025 from rqt_py_common.topic_completer import TopicCompleter
00026 from sensor_msgs.msg import Image
00027 
00028 from jsk_recognition_msgs.msg import HistogramWithRange
00029 
00030 # qt5 in kinetic
00031 if LooseVersion(python_qt_binding.QT_BINDING_VERSION).version[0] >= 5:
00032     from python_qt_binding.QtWidgets import QSizePolicy
00033     from python_qt_binding.QtWidgets import QVBoxLayout
00034     from python_qt_binding.QtWidgets import QWidget
00035     try:
00036         from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg \
00037             as FigureCanvas
00038     except ImportError:
00039         # work around bug in dateutil
00040         import thread
00041         sys.modules['_thread'] = thread
00042         from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg \
00043             as FigureCanvas
00044     try:
00045         from matplotlib.backends.backend_qt5agg import NavigationToolbar2QTAgg \
00046             as NavigationToolbar
00047     except ImportError:
00048         from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT \
00049             as NavigationToolbar
00050 else:
00051     from python_qt_binding.QtGui import QSizePolicy
00052     from python_qt_binding.QtGui import QVBoxLayout
00053     from python_qt_binding.QtGui import QWidget
00054     try:
00055         from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg \
00056             as FigureCanvas
00057     except ImportError:
00058         # work around bug in dateutil
00059         import thread
00060         sys.modules['_thread'] = thread
00061         from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg \
00062             as FigureCanvas
00063     try:
00064         from matplotlib.backends.backend_qt4agg import NavigationToolbar2QTAgg \
00065             as NavigationToolbar
00066     except ImportError:
00067         from matplotlib.backends.backend_qt4agg import NavigationToolbar2QT \
00068             as NavigationToolbar
00069 
00070 
00071 class ROSData(_ROSData):
00072     def _get_data(self, msg):
00073         val = msg
00074         try:
00075             if not self.field_evals:
00076                 return val
00077             for f in self.field_evals:
00078                 val = f(val)
00079             return val
00080         except IndexError:
00081             self.error = RosPlotException(
00082                 "{0} index error for: {1}".format(
00083                     self.name, str(val).replace('\n', ', ')))
00084         except TypeError:
00085             self.error = RosPlotException(
00086                 "{0} value was not numeric: {1}".format(
00087                     self.name, val))
00088 
00089 
00090 class HistogramPlot(Plugin):
00091     def __init__(self, context):
00092         super(HistogramPlot, self).__init__(context)
00093         self.setObjectName('HistogramPlot')
00094         self._args = self._parse_args(context.argv())
00095         self._widget = HistogramPlotWidget(self._args.topics)
00096         context.add_widget(self._widget)
00097 
00098     def _parse_args(self, argv):
00099         parser = argparse.ArgumentParser(
00100             prog='rqt_histogram_plot', add_help=False)
00101         HistogramPlot.add_arguments(parser)
00102         args = parser.parse_args(argv)
00103         return args
00104 
00105     @staticmethod
00106     def add_arguments(parser):
00107         group = parser.add_argument_group('Options for rqt_histogram plugin')
00108         group.add_argument(
00109             'topics', nargs='?', default=[], help='Topics to plot')
00110 
00111 
00112 class HistogramPlotWidget(QWidget):
00113     _redraw_interval = 40
00114 
00115     def __init__(self, topics):
00116         super(HistogramPlotWidget, self).__init__()
00117         self.setObjectName('HistogramPlotWidget')
00118         rp = rospkg.RosPack()
00119         ui_file = os.path.join(rp.get_path('jsk_rqt_plugins'),
00120                                'resource', 'plot_histogram.ui')
00121         loadUi(ui_file, self)
00122         self.cv_bridge = CvBridge()
00123         self.subscribe_topic_button.setIcon(QIcon.fromTheme('add'))
00124         self.pause_button.setIcon(QIcon.fromTheme('media-playback-pause'))
00125         self.clear_button.setIcon(QIcon.fromTheme('edit-clear'))
00126         self.data_plot = MatHistogramPlot(self)
00127         self.data_plot_layout.addWidget(self.data_plot)
00128         self._topic_completer = TopicCompleter(self.topic_edit)
00129         self._topic_completer.update_topics()
00130         self.topic_edit.setCompleter(self._topic_completer)
00131         self.data_plot.dropEvent = self.dropEvent
00132         self.data_plot.dragEnterEvent = self.dragEnterEvent
00133         self._start_time = rospy.get_time()
00134         self._rosdata = None
00135         if len(topics) != 0:
00136             self.subscribe_topic(topics)
00137         self._update_plot_timer = QTimer(self)
00138         self._update_plot_timer.timeout.connect(self.update_plot)
00139         self._update_plot_timer.start(self._redraw_interval)
00140 
00141     @Slot('QDropEvent*')
00142     def dropEvent(self, event):
00143         if event.mimeData().hasText():
00144             topic_name = str(event.mimeData().text())
00145         else:
00146             droped_item = event.source().selectedItems()[0]
00147             topic_name = str(droped_item.data(0, Qt.UserRole))
00148         self.subscribe_topic(topic_name)
00149 
00150     @Slot()
00151     def on_topic_edit_returnPressed(self):
00152         if self.subscribe_topic_button.isEnabled():
00153             self.subscribe_topic(str(self.topic_edit.text()))
00154 
00155     @Slot()
00156     def on_subscribe_topic_button_clicked(self):
00157         self.subscribe_topic(str(self.topic_edit.text()))
00158 
00159     def subscribe_topic(self, topic_name):
00160         self.topic_with_field_name = topic_name
00161         self.pub_image = rospy.Publisher(
00162             topic_name + "/histogram_image", Image, queue_size=1)
00163         if not self._rosdata:
00164             self._rosdata = ROSData(topic_name, self._start_time)
00165         else:
00166             if self._rosdata != topic_name:
00167                 self._rosdata.close()
00168                 self.data_plot.clear()
00169                 self._rosdata = ROSData(topic_name, self._start_time)
00170             else:
00171                 rospy.logwarn("%s is already subscribed", topic_name)
00172 
00173     def enable_timer(self, enabled=True):
00174         if enabled:
00175             self._update_plot_timer.start(self._redraw_interval)
00176         else:
00177             self._update_plot_timer.stop()
00178 
00179     @Slot()
00180     def on_clear_button_clicked(self):
00181         self.data_plot.clear()
00182 
00183     @Slot(bool)
00184     def on_pause_button_clicked(self, checked):
00185         self.enable_timer(not checked)
00186 
00187     def update_plot(self):
00188         if not self._rosdata:
00189             return
00190         data_x, data_y = self._rosdata.next()
00191 
00192         if len(data_y) == 0:
00193             return
00194         axes = self.data_plot._canvas.axes
00195         axes.cla()
00196         if isinstance(data_y[-1], HistogramWithRange):
00197             xs = [y.count for y in data_y[-1].bins]
00198             pos = [y.min_value for y in data_y[-1].bins]
00199             widths = [y.max_value - y.min_value for y in data_y[-1].bins]
00200             axes.set_xlim(xmin=pos[0], xmax=pos[-1] + widths[-1])
00201         elif isinstance(data_y[-1], collections.Sequence):
00202             xs = data_y[-1]
00203             pos = np.arange(len(xs))
00204             widths = [1] * len(xs)
00205             axes.set_xlim(xmin=0, xmax=len(xs))
00206         else:
00207             rospy.logerr(
00208                 "Topic/Field name '%s' has unsupported '%s' type."
00209                 "List of float values and "
00210                 "jsk_recognition_msgs/HistogramWithRange are supported."
00211                 % (self.topic_with_field_name,
00212                    self._rosdata.sub.data_class))
00213             return
00214         # axes.xticks(range(5))
00215         for p, x, w in zip(pos, xs, widths):
00216             axes.bar(p, x, color='r', align='center', width=w)
00217         axes.legend([self.topic_with_field_name], prop={'size': '8'})
00218         self.data_plot._canvas.draw()
00219         buffer = StringIO()
00220         self.data_plot._canvas.figure.savefig(buffer, format="png")
00221         buffer.seek(0)
00222         img_array = np.asarray(bytearray(buffer.read()), dtype=np.uint8)
00223         if LooseVersion(cv2.__version__).version[0] < 3:
00224             iscolor = cv2.CV_LOAD_IMAGE_COLOR
00225         else:
00226             iscolor = cv2.IMREAD_COLOR
00227         img = cv2.imdecode(img_array, iscolor)
00228         self.pub_image.publish(self.cv_bridge.cv2_to_imgmsg(img, "bgr8"))
00229 
00230 
00231 class MatHistogramPlot(QWidget):
00232     class Canvas(FigureCanvas):
00233         def __init__(self, parent=None):
00234             super(MatHistogramPlot.Canvas, self).__init__(Figure())
00235             self.axes = self.figure.add_subplot(111)
00236             self.figure.tight_layout()
00237             self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
00238             self.updateGeometry()
00239 
00240         def resizeEvent(self, event):
00241             super(MatHistogramPlot.Canvas, self).resizeEvent(event)
00242             self.figure.tight_layout()
00243 
00244     def __init__(self, parent=None):
00245         super(MatHistogramPlot, self).__init__(parent)
00246         self._canvas = MatHistogramPlot.Canvas()
00247         self._toolbar = NavigationToolbar(self._canvas, self._canvas)
00248         vbox = QVBoxLayout()
00249         vbox.addWidget(self._toolbar)
00250         vbox.addWidget(self._canvas)
00251         self.setLayout(vbox)
00252 
00253     def redraw(self):
00254         pass
00255 
00256     def clear(self):
00257         self._canvas.axes.cla()
00258         self._canvas.draw()


jsk_rqt_plugins
Author(s):
autogenerated on Wed May 1 2019 02:40:16