4 from distutils.version
import LooseVersion
9 from matplotlib.collections
import LineCollection
10 from matplotlib.collections
import PathCollection
11 from matplotlib.collections
import PolyCollection
12 from matplotlib.colors
import colorConverter
13 from matplotlib.figure
import Figure
14 import matplotlib.pyplot
as plt
15 from mpl_toolkits.mplot3d
import axes3d
16 from mpl_toolkits.mplot3d
import Axes3D
18 import python_qt_binding
19 from python_qt_binding
import loadUi
20 from python_qt_binding.QtCore
import Qt
21 from python_qt_binding.QtCore
import QTimer
22 from python_qt_binding.QtCore
import qWarning
23 from python_qt_binding.QtCore
import Slot
24 from python_qt_binding.QtGui
import QColor
25 from python_qt_binding.QtGui
import QIcon
35 if LooseVersion(python_qt_binding.QT_BINDING_VERSION).version[0] >= 5:
36 from python_qt_binding.QtWidgets
import QAction
37 from python_qt_binding.QtWidgets
import QMenu
38 from python_qt_binding.QtWidgets
import QSizePolicy
39 from python_qt_binding.QtWidgets
import QVBoxLayout
40 from python_qt_binding.QtWidgets
import QWidget
42 from matplotlib.backends.backend_qt5agg
import FigureCanvasQTAgg \
47 sys.modules[
'_thread'] = thread
48 from matplotlib.backends.backend_qt5agg
import FigureCanvasQTAgg \
51 from matplotlib.backends.backend_qt5agg \
52 import NavigationToolbar2QTAgg
as NavigationToolbar
54 from matplotlib.backends.backend_qt5agg
import NavigationToolbar2QT \
58 from python_qt_binding.QtGui
import QAction
59 from python_qt_binding.QtGui
import QMenu
60 from python_qt_binding.QtGui
import QSizePolicy
61 from python_qt_binding.QtGui
import QVBoxLayout
62 from python_qt_binding.QtGui
import QWidget
64 from matplotlib.backends.backend_qt4agg
import FigureCanvasQTAgg \
69 sys.modules[
'_thread'] = thread
70 from matplotlib.backends.backend_qt4agg
import FigureCanvasQTAgg \
73 from matplotlib.backends.backend_qt4agg \
74 import NavigationToolbar2QTAgg
as NavigationToolbar
76 from matplotlib.backends.backend_qt4agg
import NavigationToolbar2QT \
82 """Ultimately, this is a QWidget (as well as a FigureCanvasAgg, etc.). 87 self.
axes = self.figure.add_subplot(111, projection=
'3d')
90 self.axes.set_xlabel(
't')
91 self.axes.set_xlim3d(0, 10)
92 self.axes.set_ylabel(
'Y')
93 self.axes.set_ylim3d(-1, 1)
94 self.axes.set_zlabel(
'Z')
95 self.axes.set_zlim3d(0, 1)
97 self.figure.tight_layout()
98 self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
103 self.figure.tight_layout()
105 _colors = [QColor(c)
for c
in [
106 Qt.red, Qt.blue, Qt.magenta, Qt.cyan, Qt.green, Qt.darkYellow,
107 Qt.black, Qt.darkRed, Qt.gray, Qt.darkCyan]]
109 def __init__(self, parent=None, buffer_length=100, use_poly=True,
111 super(MatDataPlot3D, self).
__init__(parent)
136 self.
_curves[curve_id] = [[], [], line, [
None,
None],
137 (color.red() / 255.0,
138 color.green() / 255.0,
139 color.blue() / 255.0,
145 curve_id = str(curve_id)
154 labels = self._curves.keys()
156 plt.Rectangle((0, 0), 1, 1, fc=self.
_curves[labels[i]][4])
157 for i
in range(len(labels))]
158 self._canvas.axes.legend(handles, labels, loc=
'upper left')
160 @Slot(str, list, list)
162 data_x, data_y, line, range_y, c = self.
_curves[curve_id]
168 self.
_curves[curve_id][0] = data_x
169 self.
_curves[curve_id][1] = data_y
174 ymin = min(ymin, range_y[0])
178 ymax = max(ymax, range_y[1])
182 self._canvas.axes.grid(
True, color=
'gray')
187 for curve
in self._curves.values():
188 data_x, _, _, range_y, c = curve
191 xmax = max(xmax, data_x[-1])
192 xmin = min(xmin, data_x[0])
197 ymin = min(range_y[0], ymin)
198 ymax = max(range_y[1], ymax)
206 self._canvas.axes.set_xbound(lower=xmin, upper=xmax)
207 self._canvas.axes.set_zbound(lower=ymin, upper=ymax)
208 self._canvas.axes.set_ybound(lower=0,
209 upper=len(self._curves.keys()))
213 for curve_id
in self._curves_verts.keys():
215 colors.append(self.
_curves[curve_id][4])
217 verts.append([(xmin, ymin)] + list(zip(data_x, data_y))
220 verts.append(zip(data_x, data_y))
221 line_num = len(self._curves.keys())
223 poly = PolyCollection(verts, facecolors=colors, closed=
False)
225 poly = LineCollection(verts, colors=colors)
227 self._canvas.axes.cla()
228 self._canvas.axes.add_collection3d(poly,
229 zs=range(line_num), zdir=
'y')
236 super(Plot3D, self).
__init__(context)
237 self.setObjectName(
'Plot3D')
240 initial_topics=self._args.topics,
241 start_paused=self._args.start_paused,
242 buffer_length=self._args.buffer,
243 use_poly=
not self._args.show_line,
244 no_legend=self._args.no_legend)
245 context.add_widget(self.
_widget)
248 parser = argparse.ArgumentParser(prog=
'rqt_3d_plot', add_help=
False)
249 Plot3D.add_arguments(parser)
250 args = parser.parse_args(argv)
252 for t
in args.topics:
256 for sub_t
in [x
for x
in t.split(
',')
if x]:
259 base = sub_t[:sub_t.find(
':')]
262 c_topics.append(base)
265 "%s must contain a topic and field name" % sub_t)
266 base = base[:base.rfind(
'/')]
269 fields = sub_t.split(
':')[1:]
270 c_topics.extend([
"%s/%s" % (base, f)
for f
in fields
if f])
272 c_topics.append(sub_t)
275 c_topics = [rosgraph.names.script_resolve_name(
'rqt_plot', n)
277 if type(c_topics) == list:
278 topic_list.extend(c_topics)
280 topic_list.append(c_topics)
281 args.topics = topic_list
287 group = parser.add_argument_group(
'Options for rqt_plot plugin')
289 '-P',
'--pause', action=
'store_true', dest=
'start_paused',
290 help=
'Start in paused state')
292 '-L',
'--line', action=
'store_true', dest=
'show_line',
293 help=
'Show lines rather than polygon representation')
295 '--no-legend', action=
'store_true', dest=
'no_legend',
296 help=
'do not show legend')
298 '-B',
'--buffer', dest=
'buffer', action=
"store",
299 help=
'the length of the buffer', default=100, type=int)
304 'topics', nargs=
'*', default=[], help=
'Topics to plot')
308 _redraw_interval = 40
310 def __init__(self, initial_topics=None, start_paused=False,
311 buffer_length=100, use_poly=
True, no_legend=
False):
312 super(Plot3DWidget, self).
__init__()
313 self.setObjectName(
'Plot3DWidget')
317 rp = rospkg.RosPack()
318 ui_file = os.path.join(rp.get_path(
'jsk_rqt_plugins'),
319 'resource',
'plot3d.ui')
320 loadUi(ui_file, self)
321 self.subscribe_topic_button.setIcon(QIcon.fromTheme(
'add'))
322 self.remove_topic_button.setIcon(QIcon.fromTheme(
'remove'))
323 self.pause_button.setIcon(QIcon.fromTheme(
'media-playback-pause'))
324 self.clear_button.setIcon(QIcon.fromTheme(
'edit-clear'))
327 self.data_plot_layout.addWidget(self.
data_plot)
328 self.data_plot.autoscroll(self.autoscroll_checkbox.isChecked())
329 self.data_plot.dropEvent = self.
dropEvent 332 self.subscribe_topic_button.setEnabled(
False)
334 self.pause_button.setChecked(
True)
337 self._topic_completer.update_topics()
346 self._update_plot_timer.timeout.connect(self.
update_plot)
352 @Slot(
'QDragEnterEvent*')
355 if not event.mimeData().hasText():
356 if not hasattr(event.source(),
'selectedItems')
or \
357 len(event.source().selectedItems()) == 0:
359 'Plot.dragEnterEvent(): not hasattr(event.source(), selectedItems) or len(event.source().selectedItems()) == 0')
361 item = event.source().selectedItems()[0]
362 topic_name = item.data(0, Qt.UserRole)
363 if topic_name ==
None:
365 'Plot.dragEnterEvent(): not hasattr(item, ros_topic_name_)')
368 topic_name = str(event.mimeData().text())
371 is_numeric, is_array, message = is_slot_numeric(topic_name)
372 if is_numeric
and not is_array:
373 event.acceptProposedAction()
375 qWarning(
'Plot.dragEnterEvent(): rejecting: "%s"' % (message))
379 if event.mimeData().hasText():
380 topic_name = str(event.mimeData().text())
382 droped_item = event.source().selectedItems()[0]
383 topic_name = str(droped_item.data(0, Qt.UserRole))
389 if topic_name
in (
'',
'/'):
390 self._topic_completer.update_topics()
392 is_numeric, is_array, message = is_slot_numeric(topic_name)
393 self.subscribe_topic_button.setEnabled(is_numeric
and not is_array)
394 self.subscribe_topic_button.setToolTip(message)
398 if self.subscribe_topic_button.isEnabled():
399 self.
add_topic(str(self.topic_edit.text()))
403 self.
add_topic(str(self.topic_edit.text()))
411 self.data_plot.autoscroll(checked)
420 for topic_name, rosdata
in self._rosdata.items():
422 data_x, data_y = rosdata.next()
424 self.data_plot.update_values(
425 topic_name, data_x, data_y)
427 except RosPlotException
as e:
429 'PlotWidget.update_plot(): error in rosplot: %s' % e)
431 self.data_plot.redraw()
435 if not self.pause_button.isChecked():
441 def make_remove_topic_function(x):
444 self._remove_topic_menu.clear()
445 for topic_name
in sorted(self._rosdata.keys()):
447 action.triggered.connect(make_remove_topic_function(topic_name))
448 self._remove_topic_menu.addAction(action)
454 qWarning(
'PlotWidget.add_topic(): topic already subscribed: %s' % topic_name)
458 if self.
_rosdata[topic_name].error
is not None:
459 qWarning(str(self.
_rosdata[topic_name].error))
462 data_x, data_y = self.
_rosdata[topic_name].next()
463 self.data_plot.add_curve(topic_name, topic_name, data_x, data_y)
470 self.data_plot.remove_curve(topic_name)
475 for topic_name, rosdata
in self._rosdata.items():
477 self.data_plot.remove_curve(topic_name)
486 self._update_plot_timer.stop()
def update_values(self, curve_id, x, y)
def __init__(self, parent=None, buffer_length=100, use_poly=True, no_legend=False)
def remove_curve(self, curve_id)
def autoscroll(self, enabled=True)
def resizeEvent(self, event)
def __init__(self, parent=None)
def __init__(self, context)
def add_arguments(parser)
def _parse_args(self, argv)
def add_curve(self, curve_id, curve_name, x, y)