plot.py
Go to the documentation of this file.
00001 #!/usr/bin/env python
00002 
00003 # Copyright (c) 2011, Dorian Scholz, TU Darmstadt
00004 # All rights reserved.
00005 #
00006 # Redistribution and use in source and binary forms, with or without
00007 # modification, are permitted provided that the following conditions
00008 # are met:
00009 #
00010 #   * Redistributions of source code must retain the above copyright
00011 #     notice, this list of conditions and the following disclaimer.
00012 #   * Redistributions in binary form must reproduce the above
00013 #     copyright notice, this list of conditions and the following
00014 #     disclaimer in the documentation and/or other materials provided
00015 #     with the distribution.
00016 #   * Neither the name of the TU Darmstadt nor the names of its
00017 #     contributors may be used to endorse or promote products derived
00018 #     from this software without specific prior written permission.
00019 #
00020 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00021 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00022 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00023 # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
00024 # COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00025 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00026 # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00027 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
00028 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00029 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
00030 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00031 # POSSIBILITY OF SUCH DAMAGE.
00032 
00033 import roslib
00034 roslib.load_manifest('rqt_plot')
00035 
00036 from python_qt_binding import QT_BINDING
00037 from python_qt_binding.QtCore import qDebug
00038 from qt_gui_py_common.simple_settings_dialog import SimpleSettingsDialog
00039 from rqt_gui_py.plugin import Plugin
00040 
00041 from .plot_widget import PlotWidget
00042 
00043 try:
00044     qDebug('rqt_plot.plot: importing PyQtGraphDataPlot')
00045     from pyqtgraph_data_plot import PyQtGraphDataPlot
00046 except ImportError:
00047     qDebug('rqt_plot.plot: import of PyQtGraphDataPlot failed')
00048     PyQtGraphDataPlot = None
00049 
00050 try:
00051     qDebug('rqt_plot.plot: importing MatDataPlot')
00052     from mat_data_plot import MatDataPlot
00053 except ImportError:
00054     qDebug('rqt_plot.plot: import of MatDataPlot failed')
00055     MatDataPlot = None
00056 
00057 try:
00058     qDebug('rqt_plot.plot: importing QwtDataPlot')
00059     from qwt_data_plot import QwtDataPlot
00060 except ImportError:
00061     qDebug('rqt_plot.plot: import of QwtDataPlot failed')
00062     QwtDataPlot = None
00063 
00064 
00065 class Plot(Plugin):
00066     # plot types in order of priority
00067     plot_types = [
00068         {
00069             'title': 'PyQtGraph',
00070             'widget_class': PyQtGraphDataPlot,
00071             'description': 'Based on PyQtGraph\n- installer: http://luke.campagnola.me/code/pyqtgraph',
00072             'enabled': PyQtGraphDataPlot is not None,
00073         },
00074         {
00075             'title': 'MatPlot',
00076             'widget_class': MatDataPlot,
00077             'description': 'Based on MatPlotLib\n- needs most CPU\n- needs matplotlib >= 1.1.0\n- if using PySide: PySide > 1.1.0',
00078             'enabled': MatDataPlot is not None,
00079         },
00080         {
00081             'title': 'QwtPlot',
00082             'widget_class': QwtDataPlot,
00083             'description': 'Based on QwtPlot\n- does not use timestamps\n- uses least CPU\n- needs Python Qwt bindings',
00084             'enabled': QwtDataPlot is not None,
00085         },
00086     ]
00087 
00088     def __init__(self, context):
00089         super(Plot, self).__init__(context)
00090         self.setObjectName('Plot')
00091 
00092         enabled_plot_types = [pt for pt in self.plot_types if pt['enabled']]
00093         if not enabled_plot_types:
00094             version_info = ' and PySide > 1.1.0' if QT_BINDING == 'pyside' else ''
00095             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)
00096 
00097         self._plot_type_index = 0
00098         self._context = context
00099 
00100         args, topics = self._process_arguments(context.argv())
00101         self._widget = PlotWidget(args, topics)
00102         if context.serial_number() > 1:
00103             self._widget.setWindowTitle(self._widget.windowTitle() + (' (%d)' % context.serial_number()))
00104         context.add_widget(self._widget)
00105 
00106     def _process_arguments(self, argv):
00107         from argparse import ArgumentParser
00108         parser = ArgumentParser()
00109 
00110         # TODO add more configuration arguments here
00111         parser.add_argument("-P", "--pause", action="store_true",
00112                       dest="start_paused",
00113                       help="start in paused state")
00114 
00115         args, topics = parser.parse_known_args(argv)
00116 
00117         # Process topic arguments into topic names
00118         topic_list = []
00119         for t in topics:
00120             # c_topics is the list of topics to plot
00121             c_topics = []
00122             # compute combined topic list, t == '/foo/bar1,/baz/bar2'
00123             for sub_t in [x for x in t.split(',') if x]:
00124                 # check for shorthand '/foo/field1:field2:field3'
00125                 if ':' in sub_t:
00126                     base = sub_t[:sub_t.find(':')]
00127                     # the first prefix includes a field name, so save then strip it off
00128                     c_topics.append(base)
00129                     if not '/' in base:
00130                         parser.error("%s must contain a topic and field name" % sub_t)
00131                     base = base[:base.rfind('/')]
00132 
00133                     # compute the rest of the field names
00134                     fields = sub_t.split(':')[1:]
00135                     c_topics.extend(["%s/%s" % (base, f) for f in fields if f])
00136                 else:
00137                     c_topics.append(sub_t)
00138             # #1053: resolve command-line topic names
00139             import rosgraph
00140             c_topics = [rosgraph.names.script_resolve_name('rqt_plot', n) for n in c_topics]
00141             if type(c_topics) == list:
00142                 topic_list.extend(c_topics)
00143             else:
00144                 topic_list.append(c_topics)
00145 
00146         #flatten for printing
00147         print_topic_list = []
00148         for l in topic_list:
00149             if type(l) == list:
00150                 print_topic_list.extend(l)
00151             else:
00152                 print_topic_list.append(l)
00153 
00154         print "plotting topics", ', '.join(print_topic_list)
00155 
00156         return (args, topic_list)
00157 
00158     def _switch_data_plot_widget(self, plot_type_index):
00159         # check if selected plot type is available
00160         if not self.plot_types[plot_type_index]['enabled']:
00161             # find other available plot type
00162             for index, plot_type in enumerate(self.plot_types):
00163                 if plot_type['enabled']:
00164                     plot_type_index = index
00165                     break
00166 
00167         self._plot_type_index = plot_type_index
00168         selected_plot = self.plot_types[plot_type_index]
00169 
00170         self._widget.switch_data_plot_widget(selected_plot['widget_class'](self._widget))
00171         self._widget.setWindowTitle(selected_plot['title'])
00172         if self._context.serial_number() > 1:
00173             self._widget.setWindowTitle(self._widget.windowTitle() + (' (%d)' % self._context.serial_number()))
00174 
00175     def save_settings(self, plugin_settings, instance_settings):
00176         instance_settings.set_value('plot_type', self._plot_type_index)
00177 
00178     def restore_settings(self, plugin_settings, instance_settings):
00179         self._switch_data_plot_widget(int(instance_settings.value('plot_type', 0)))
00180 
00181     def trigger_configuration(self):
00182         dialog = SimpleSettingsDialog(title='Plot Options')
00183         dialog.add_exclusive_option_group(title='Plot Type', options=self.plot_types, selected_index=self._plot_type_index)
00184         plot_type = dialog.get_settings()[0]
00185         if plot_type is not None and plot_type['selected_index'] is not None and self._plot_type_index != plot_type['selected_index']:
00186             self._switch_data_plot_widget(plot_type['selected_index'])
00187 
00188     def shutdown_plugin(self):
00189         self._widget.clean_up_subscribers()


rqt_plot
Author(s): Dorian Scholz
autogenerated on Fri Jan 3 2014 11:55:13