hist.py
Go to the documentation of this file.
1 #!/usr/bin/env python
2 
3 import argparse
4 import collections
5 import os
6 import sys
7 
8 # https://stackoverflow.com/questions/11914472/stringio-in-python3
9 # https://stackoverflow.com/questions/50797043/string-argument-expected-got-bytes-in-buffer-write
10 try:
11  from cStringIO import StringIO
12 except ImportError:
13  from io import BytesIO as StringIO
14 import cv2
15 from cv_bridge import CvBridge
16 from distutils.version import LooseVersion
17 from matplotlib.figure import Figure
18 import numpy as np
19 import python_qt_binding
20 from python_qt_binding import loadUi
21 from python_qt_binding.QtCore import Qt
22 from python_qt_binding.QtCore import QTimer
23 from python_qt_binding.QtCore import Slot
24 from python_qt_binding.QtGui import QIcon
25 import rospkg
26 import rospy
27 from rqt_gui_py.plugin import Plugin
28 from rqt_plot.rosplot import ROSData as _ROSData
29 from rqt_plot.rosplot import RosPlotException
30 from rqt_py_common.topic_completer import TopicCompleter
31 from sensor_msgs.msg import Image
32 
33 from jsk_recognition_msgs.msg import HistogramWithRange
34 
35 # qt5 in kinetic
36 if LooseVersion(python_qt_binding.QT_BINDING_VERSION).version[0] >= 5:
37  from python_qt_binding.QtWidgets import QSizePolicy
38  from python_qt_binding.QtWidgets import QVBoxLayout
39  from python_qt_binding.QtWidgets import QWidget
40  try:
41  from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg \
42  as FigureCanvas
43  except ImportError:
44  # work around bug in dateutil
45  import thread
46  sys.modules['_thread'] = thread
47  from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg \
48  as FigureCanvas
49  try:
50  from matplotlib.backends.backend_qt5agg import NavigationToolbar2QTAgg \
51  as NavigationToolbar
52  except ImportError:
53  from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT \
54  as NavigationToolbar
55 else:
56  from python_qt_binding.QtGui import QSizePolicy
57  from python_qt_binding.QtGui import QVBoxLayout
58  from python_qt_binding.QtGui import QWidget
59  try:
60  from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg \
61  as FigureCanvas
62  except ImportError:
63  # work around bug in dateutil
64  import thread
65  sys.modules['_thread'] = thread
66  from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg \
67  as FigureCanvas
68  try:
69  from matplotlib.backends.backend_qt4agg import NavigationToolbar2QTAgg \
70  as NavigationToolbar
71  except ImportError:
72  from matplotlib.backends.backend_qt4agg import NavigationToolbar2QT \
73  as NavigationToolbar
74 
75 
76 class ROSData(_ROSData):
77  def _get_data(self, msg):
78  val = msg
79  try:
80  if not self.field_evals:
81  return val
82  for f in self.field_evals:
83  val = f(val)
84  return val
85  except IndexError:
87  "{0} index error for: {1}".format(
88  self.name, str(val).replace('\n', ', ')))
89  except TypeError:
90  self.error = RosPlotException(
91  "{0} value was not numeric: {1}".format(
92  self.name, val))
93 
94 
96  def __init__(self, context):
97  super(HistogramPlot, self).__init__(context)
98  self.setObjectName('HistogramPlot')
99  self._args = self._parse_args(context.argv())
100  self._widget = HistogramPlotWidget(self._args.topics)
101  context.add_widget(self._widget)
102 
103  def _parse_args(self, argv):
104  parser = argparse.ArgumentParser(
105  prog='rqt_histogram_plot', add_help=False)
106  HistogramPlot.add_arguments(parser)
107  args = parser.parse_args(argv)
108  return args
109 
110  @staticmethod
111  def add_arguments(parser):
112  group = parser.add_argument_group('Options for rqt_histogram plugin')
113  group.add_argument(
114  'topics', nargs='?', default=[], help='Topics to plot')
115 
116 
117 class HistogramPlotWidget(QWidget):
118  _redraw_interval = 40
119 
120  def __init__(self, topics):
121  super(HistogramPlotWidget, self).__init__()
122  self.setObjectName('HistogramPlotWidget')
123  rp = rospkg.RosPack()
124  ui_file = os.path.join(rp.get_path('jsk_rqt_plugins'),
125  'resource', 'plot_histogram.ui')
126  loadUi(ui_file, self)
127  self.cv_bridge = CvBridge()
128  self.subscribe_topic_button.setIcon(QIcon.fromTheme('add'))
129  self.pause_button.setIcon(QIcon.fromTheme('media-playback-pause'))
130  self.clear_button.setIcon(QIcon.fromTheme('edit-clear'))
132  self.data_plot_layout.addWidget(self.data_plot)
133  self._topic_completer = TopicCompleter(self.topic_edit)
134  self._topic_completer.update_topics()
135  self.topic_edit.setCompleter(self._topic_completer)
136  self.data_plot.dropEvent = self.dropEvent
137  self.data_plot.dragEnterEvent = self.dragEnterEvent
138  self._start_time = rospy.get_time()
139  self._rosdata = None
140  if len(topics) != 0:
141  self.subscribe_topic(topics)
142  self._update_plot_timer = QTimer(self)
143  self._update_plot_timer.timeout.connect(self.update_plot)
144  self._update_plot_timer.start(self._redraw_interval)
145 
146  @Slot('QDropEvent*')
147  def dropEvent(self, event):
148  if event.mimeData().hasText():
149  topic_name = str(event.mimeData().text())
150  else:
151  droped_item = event.source().selectedItems()[0]
152  topic_name = str(droped_item.data(0, Qt.UserRole))
153  self.subscribe_topic(topic_name)
154 
155  @Slot()
157  if self.subscribe_topic_button.isEnabled():
158  self.subscribe_topic(str(self.topic_edit.text()))
159 
160  @Slot()
162  self.subscribe_topic(str(self.topic_edit.text()))
163 
164  def subscribe_topic(self, topic_name):
165  self.topic_with_field_name = topic_name
166  self.pub_image = rospy.Publisher(
167  topic_name + "/histogram_image", Image, queue_size=1)
168  if not self._rosdata:
169  self._rosdata = ROSData(topic_name, self._start_time)
170  else:
171  if self._rosdata != topic_name:
172  self._rosdata.close()
173  self.data_plot.clear()
174  self._rosdata = ROSData(topic_name, self._start_time)
175  else:
176  rospy.logwarn("%s is already subscribed", topic_name)
177 
178  def enable_timer(self, enabled=True):
179  if enabled:
180  self._update_plot_timer.start(self._redraw_interval)
181  else:
182  self._update_plot_timer.stop()
183 
184  @Slot()
186  self.data_plot.clear()
187 
188  @Slot(bool)
189  def on_pause_button_clicked(self, checked):
190  self.enable_timer(not checked)
191 
192  def update_plot(self):
193  if not self._rosdata:
194  return
195  data_x, data_y = self._rosdata.next()
196 
197  if len(data_y) == 0:
198  return
199  axes = self.data_plot._canvas.axes
200  axes.cla()
201  if isinstance(data_y[-1], HistogramWithRange):
202  xs = [y.count for y in data_y[-1].bins]
203  pos = [y.min_value for y in data_y[-1].bins]
204  widths = [y.max_value - y.min_value for y in data_y[-1].bins]
205  axes.set_xlim(xmin=pos[0], xmax=pos[-1] + widths[-1])
206  elif isinstance(data_y[-1], collections.Sequence):
207  xs = data_y[-1]
208  pos = np.arange(len(xs))
209  widths = [1] * len(xs)
210  axes.set_xlim(xmin=0, xmax=len(xs))
211  else:
212  rospy.logerr(
213  "Topic/Field name '%s' has unsupported '%s' type."
214  "List of float values and "
215  "jsk_recognition_msgs/HistogramWithRange are supported."
216  % (self.topic_with_field_name,
217  self._rosdata.sub.data_class))
218  return
219  # axes.xticks(range(5))
220  for p, x, w in zip(pos, xs, widths):
221  axes.bar(p, x, color='r', align='center', width=w)
222  axes.legend([self.topic_with_field_name], prop={'size': '8'})
223  self.data_plot._canvas.draw()
224  buffer = StringIO()
225  self.data_plot._canvas.figure.savefig(buffer, format="png")
226  buffer.seek(0)
227  img_array = np.asarray(bytearray(buffer.read()), dtype=np.uint8)
228  if LooseVersion(cv2.__version__).version[0] < 3:
229  iscolor = cv2.CV_LOAD_IMAGE_COLOR
230  else:
231  iscolor = cv2.IMREAD_COLOR
232  img = cv2.imdecode(img_array, iscolor)
233  self.pub_image.publish(self.cv_bridge.cv2_to_imgmsg(img, "bgr8"))
234 
235 
236 class MatHistogramPlot(QWidget):
237  class Canvas(FigureCanvas):
238  def __init__(self, parent=None):
239  super(MatHistogramPlot.Canvas, self).__init__(Figure())
240  self.axes = self.figure.add_subplot(111)
241  self.figure.tight_layout()
242  self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
243  self.updateGeometry()
244 
245  def resizeEvent(self, event):
246  super(MatHistogramPlot.Canvas, self).resizeEvent(event)
247  self.figure.tight_layout()
248 
249  def __init__(self, parent=None):
250  super(MatHistogramPlot, self).__init__(parent)
252  self._toolbar = NavigationToolbar(self._canvas, self._canvas)
253  vbox = QVBoxLayout()
254  vbox.addWidget(self._toolbar)
255  vbox.addWidget(self._canvas)
256  self.setLayout(vbox)
257 
258  def redraw(self):
259  pass
260 
261  def clear(self):
262  self._canvas.axes.cla()
263  self._canvas.draw()
rqt_plot::rosplot::RosPlotException
jsk_rqt_plugins.hist.HistogramPlotWidget.pub_image
pub_image
Definition: hist.py:166
jsk_rqt_plugins.hist.MatHistogramPlot._canvas
_canvas
Definition: hist.py:251
jsk_rqt_plugins.hist.HistogramPlotWidget._rosdata
_rosdata
Definition: hist.py:139
jsk_rqt_plugins.hist.MatHistogramPlot
Definition: hist.py:236
jsk_rqt_plugins.hist.HistogramPlotWidget.dropEvent
def dropEvent(self, event)
Definition: hist.py:147
jsk_rqt_plugins.hist.MatHistogramPlot.Canvas.resizeEvent
def resizeEvent(self, event)
Definition: hist.py:245
jsk_rqt_plugins.hist.MatHistogramPlot.Canvas.axes
axes
Definition: hist.py:240
jsk_rqt_plugins.hist.HistogramPlotWidget.cv_bridge
cv_bridge
Definition: hist.py:127
jsk_rqt_plugins.hist.HistogramPlotWidget._redraw_interval
int _redraw_interval
Definition: hist.py:118
rqt_plot::rosplot::ROSData
rqt_gui_py::plugin::Plugin
jsk_rqt_plugins.hist.MatHistogramPlot.__init__
def __init__(self, parent=None)
Definition: hist.py:249
jsk_rqt_plugins.hist.HistogramPlotWidget.on_topic_edit_returnPressed
def on_topic_edit_returnPressed(self)
Definition: hist.py:156
jsk_rqt_plugins.hist.HistogramPlot._widget
_widget
Definition: hist.py:100
jsk_rqt_plugins.hist.HistogramPlotWidget.on_pause_button_clicked
def on_pause_button_clicked(self, checked)
Definition: hist.py:189
jsk_rqt_plugins.hist.HistogramPlotWidget.on_subscribe_topic_button_clicked
def on_subscribe_topic_button_clicked(self)
Definition: hist.py:161
jsk_rqt_plugins.hist.MatHistogramPlot.Canvas
Definition: hist.py:237
jsk_rqt_plugins.hist.HistogramPlotWidget.__init__
def __init__(self, topics)
Definition: hist.py:120
jsk_rqt_plugins.hist.HistogramPlot
Definition: hist.py:95
jsk_rqt_plugins.hist.MatHistogramPlot.redraw
def redraw(self)
Definition: hist.py:258
jsk_rqt_plugins.hist.HistogramPlotWidget.update_plot
def update_plot(self)
Definition: hist.py:192
jsk_rqt_plugins.hist.MatHistogramPlot.Canvas.__init__
def __init__(self, parent=None)
Definition: hist.py:238
jsk_rqt_plugins.hist.HistogramPlotWidget._update_plot_timer
_update_plot_timer
Definition: hist.py:142
jsk_rqt_plugins.hist.HistogramPlotWidget._topic_completer
_topic_completer
Definition: hist.py:133
jsk_rqt_plugins.hist.HistogramPlot.add_arguments
def add_arguments(parser)
Definition: hist.py:111
jsk_rqt_plugins.hist.HistogramPlot.__init__
def __init__(self, context)
Definition: hist.py:96
jsk_rqt_plugins.hist.ROSData.error
error
Definition: hist.py:86
jsk_rqt_plugins.hist.HistogramPlot._args
_args
Definition: hist.py:99
jsk_rqt_plugins.hist.MatHistogramPlot.clear
def clear(self)
Definition: hist.py:261
jsk_rqt_plugins.hist.HistogramPlotWidget
Definition: hist.py:117
rqt_plot::rosplot
jsk_rqt_plugins.hist.HistogramPlotWidget.data_plot
data_plot
Definition: hist.py:131
jsk_rqt_plugins.hist.HistogramPlot._parse_args
def _parse_args(self, argv)
Definition: hist.py:103
jsk_rqt_plugins.hist.HistogramPlotWidget._start_time
_start_time
Definition: hist.py:138
rqt_gui_py::plugin
jsk_rqt_plugins.hist.HistogramPlotWidget.topic_with_field_name
topic_with_field_name
Definition: hist.py:165
rqt_py_common::topic_completer
jsk_rqt_plugins.hist.HistogramPlotWidget.subscribe_topic
def subscribe_topic(self, topic_name)
Definition: hist.py:164
jsk_rqt_plugins.hist.MatHistogramPlot._toolbar
_toolbar
Definition: hist.py:252
rqt_plot::rosplot::ROSData::_get_data
def _get_data(self, msg)
jsk_rqt_plugins.hist.HistogramPlotWidget.enable_timer
def enable_timer(self, enabled=True)
Definition: hist.py:178
rqt_py_common::topic_completer::TopicCompleter
jsk_rqt_plugins.hist.HistogramPlotWidget.on_clear_button_clicked
def on_clear_button_clicked(self)
Definition: hist.py:185


jsk_rqt_plugins
Author(s):
autogenerated on Mon Apr 7 2025 02:49:46