Skip to content

Commit

Permalink
Refactor graph backends into wrapper
Browse files Browse the repository at this point in the history
  • Loading branch information
trainman419 committed May 23, 2014
1 parent a205c25 commit f7a485e
Show file tree
Hide file tree
Showing 5 changed files with 131 additions and 66 deletions.
120 changes: 120 additions & 0 deletions rqt_plot/src/rqt_plot/data_plot/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
#!/usr/bin/env python

# Copyright (c) 2014, Austin Hendrix
# Copyright (c) 2011, Dorian Scholz, TU Darmstadt
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following
# disclaimer in the documentation and/or other materials provided
# with the distribution.
# * Neither the name of the TU Darmstadt nor the names of its
# contributors may be used to endorse or promote products derived
# from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.


try:
from pyqtgraph_data_plot import PyQtGraphDataPlot
except ImportError:
qDebug('[DEBUG] rqt_plot.plot: import of PyQtGraphDataPlot failed (trying other backends)')
PyQtGraphDataPlot = None

try:
from mat_data_plot import MatDataPlot
except ImportError:
qDebug('[DEBUG] rqt_plot.plot: import of MatDataPlot failed (trying other backends)')
MatDataPlot = None

try:
from qwt_data_plot import QwtDataPlot
except ImportError:
qDebug('[DEBUG] rqt_plot.plot: import of QwtDataPlot failed (trying other backends)')
QwtDataPlot = None


class DataPlot(object):
# plot types in order of priority
plot_types = [
{
'title': 'PyQtGraph',
'widget_class': PyQtGraphDataPlot,
'description': 'Based on PyQtGraph\n- installer: http://luke.campagnola.me/code/pyqtgraph',
'enabled': PyQtGraphDataPlot is not None,
},
{
'title': 'MatPlot',
'widget_class': MatDataPlot,
'description': 'Based on MatPlotLib\n- needs most CPU\n- needs matplotlib >= 1.1.0\n- if using PySide: PySide > 1.1.0',
'enabled': MatDataPlot is not None,
},
{
'title': 'QwtPlot',
'widget_class': QwtDataPlot,
'description': 'Based on QwtPlot\n- does not use timestamps\n- uses least CPU\n- needs Python Qwt bindings',
'enabled': QwtDataPlot is not None,
},
]

def __init__(self, parent):
self._parent = parent
self._plot_index = 0

enabled_plot_types = [pt for pt in self.plot_types if pt['enabled']]
if not enabled_plot_types:
version_info = ' and PySide > 1.1.0' if QT_BINDING == 'pyside' else ''
raise RuntimeError('No usable plot type found. Install at least one of: PyQtGraph, MatPlotLib (at least 1.1.0%s) or Python-Qwt5.' % version_info)

def _switch_data_plot_widget(self, plot_index):
# check if selected plot type is available
if not self.plot_types[plot_index]['enabled']:
# find other available plot type
for index, plot_type in enumerate(self.plot_types):
if plot_type['enabled']:
plot_index = index
break

self._plot_index = plot_index
selected_plot = self.plot_types[plot_index]

# TODO: something else here ?
self._parent.switch_data_plot_widget(selected_plot['widget_class'](self._parent))

# interface out to the managing GUI component: get title, save, restore,
# etc
def getTitle(self):
return plot_types[self._plot_index]['title']

def save_settings(self, plugin_settings, instance_settings):
instance_settings.set_value('plot_type', self._plot_index)

def restore_settings(self, plugin_settings, instance_settings):
self._switch_data_plot_widget(int(instance_settings.value('plot_type', 0)))

def doSettingsDialog(self):
dialog = SimpleSettingsDialog(title='Plot Options')
dialog.add_exclusive_option_group(title='Plot Type', options=self.plot_types, selected_index=self._plot_index)
plot_type = dialog.get_settings()[0]
if plot_type is not None and plot_type['selected_index'] is not None and self._plot_index != plot_type['selected_index']:
self._switch_data_plot_widget(plot_type['selected_index'])

# interface out to the managing DATA component: load data, update data,
# etc
File renamed without changes.
File renamed without changes.
77 changes: 11 additions & 66 deletions rqt_plot/src/rqt_plot/plot.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,62 +41,20 @@

from .plot_widget import PlotWidget

try:
from pyqtgraph_data_plot import PyQtGraphDataPlot
except ImportError:
qDebug('[DEBUG] rqt_plot.plot: import of PyQtGraphDataPlot failed (trying other backends)')
PyQtGraphDataPlot = None

try:
from mat_data_plot import MatDataPlot
except ImportError:
qDebug('[DEBUG] rqt_plot.plot: import of MatDataPlot failed (trying other backends)')
MatDataPlot = None

try:
from qwt_data_plot import QwtDataPlot
except ImportError:
qDebug('[DEBUG] rqt_plot.plot: import of QwtDataPlot failed (trying other backends)')
QwtDataPlot = None

# TODO: move into rqt_py_common
from .data_plot import DataPlot

class Plot(Plugin):
# plot types in order of priority
plot_types = [
{
'title': 'PyQtGraph',
'widget_class': PyQtGraphDataPlot,
'description': 'Based on PyQtGraph\n- installer: http://luke.campagnola.me/code/pyqtgraph',
'enabled': PyQtGraphDataPlot is not None,
},
{
'title': 'MatPlot',
'widget_class': MatDataPlot,
'description': 'Based on MatPlotLib\n- needs most CPU\n- needs matplotlib >= 1.1.0\n- if using PySide: PySide > 1.1.0',
'enabled': MatDataPlot is not None,
},
{
'title': 'QwtPlot',
'widget_class': QwtDataPlot,
'description': 'Based on QwtPlot\n- does not use timestamps\n- uses least CPU\n- needs Python Qwt bindings',
'enabled': QwtDataPlot is not None,
},
]

def __init__(self, context):
super(Plot, self).__init__(context)
self.setObjectName('Plot')

enabled_plot_types = [pt for pt in self.plot_types if pt['enabled']]
if not enabled_plot_types:
version_info = ' and PySide > 1.1.0' if QT_BINDING == 'pyside' else ''
raise RuntimeError('No usable plot type found. Install at least one of: PyQtGraph, MatPlotLib (at least 1.1.0%s) or Python-Qwt5.' % version_info)

self._plot_type_index = 0
self._context = context

self._args = self._parse_args(context.argv())
self._widget = PlotWidget(initial_topics=self._args.topics, start_paused=self._args.start_paused)
self._data_plot = DataPlot(self._widget)
if context.serial_number() > 1:
self._widget.setWindowTitle(self._widget.windowTitle() + (' (%d)' % context.serial_number()))
context.add_widget(self._widget)
Expand Down Expand Up @@ -147,31 +105,21 @@ def add_arguments(parser):
help='Start without restoring previous topics')
group.add_argument('topics', nargs='*', default=[], help='Topics to plot')

def _switch_data_plot_widget(self, plot_type_index):
# check if selected plot type is available
if not self.plot_types[plot_type_index]['enabled']:
# find other available plot type
for index, plot_type in enumerate(self.plot_types):
if plot_type['enabled']:
plot_type_index = index
break

self._plot_type_index = plot_type_index
selected_plot = self.plot_types[plot_type_index]

self._widget.switch_data_plot_widget(selected_plot['widget_class'](self._widget))
self._widget.setWindowTitle(selected_plot['title'])
def _update_title(self):
self._widget.setWindowTitle(self._data_plot.getTitle())
if self._context.serial_number() > 1:
self._widget.setWindowTitle(self._widget.windowTitle() + (' (%d)' % self._context.serial_number()))

def save_settings(self, plugin_settings, instance_settings):
instance_settings.set_value('plot_type', self._plot_type_index)
self._data_plot.save_settings(plugin_settings, instance_settings)
instance_settings.set_value('autoscroll', self._widget.autoscroll_checkbox.isChecked())
instance_settings.set_value('topics', pack(self._widget._rosdata.keys()))

def restore_settings(self, plugin_settings, instance_settings):
self._widget.autoscroll_checkbox.setChecked(instance_settings.value('autoscroll', True) in [True, 'true'])
self._switch_data_plot_widget(int(instance_settings.value('plot_type', 0)))

self._data_plot.restore_settings(plugin_settings, instance_settings)
self._update_title()

if len(self._widget._rosdata.keys()) == 0 and not self._args.start_empty:
topics = unpack(instance_settings.value('topics', []))
Expand All @@ -180,11 +128,8 @@ def restore_settings(self, plugin_settings, instance_settings):
self._widget.add_topic(topic)

def trigger_configuration(self):
dialog = SimpleSettingsDialog(title='Plot Options')
dialog.add_exclusive_option_group(title='Plot Type', options=self.plot_types, selected_index=self._plot_type_index)
plot_type = dialog.get_settings()[0]
if plot_type is not None and plot_type['selected_index'] is not None and self._plot_type_index != plot_type['selected_index']:
self._switch_data_plot_widget(plot_type['selected_index'])
self._data_plot.doSettingsDialog()
self._update_title()

def shutdown_plugin(self):
self._widget.clean_up_subscribers()

0 comments on commit f7a485e

Please sign in to comment.