qwt_data_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 # -*- coding: utf-8 -*-
00034 from __future__ import division
00035 import math
00036 import sys
00037 
00038 from python_qt_binding.QtCore import QEvent, QSize, QPointF, Qt, Signal, Slot, qWarning
00039 from python_qt_binding.QtGui import QColor, QPen, QBrush, QVector2D
00040 import Qwt
00041 
00042 from numpy import arange, zeros, concatenate
00043 
00044 
00045 # create real QwtDataPlot class
00046 class QwtDataPlot(Qwt.QwtPlot):
00047     mouseCoordinatesChanged = Signal(QPointF)
00048     _num_value_saved = 1000
00049     _num_values_ploted = 1000
00050 
00051     limits_changed = Signal()
00052 
00053     def __init__(self, *args):
00054         super(QwtDataPlot, self).__init__(*args)
00055         self.setCanvasBackground(Qt.white)
00056         self.insertLegend(Qwt.QwtLegend(), Qwt.QwtPlot.BottomLegend)
00057 
00058         self._curves = {}
00059 
00060         # TODO: rejigger these internal data structures so that they're easier
00061         # to work with, and easier to use with set_xlim and set_ylim
00062         #  this may also entail rejiggering the _time_axis so that it's
00063         #  actually time axis data, or just isn't required any more
00064         # new data structure
00065         self._x_limits = [0.0, 10.0]
00066         self._y_limits = [0.0, 10.0]
00067 
00068         # old data structures
00069         self._last_canvas_x = 0
00070         self._last_canvas_y = 0
00071         self._pressed_canvas_y = 0
00072         self._pressed_canvas_x = 0
00073         self._last_click_coordinates = None
00074 
00075         marker_axis_y = Qwt.QwtPlotMarker()
00076         marker_axis_y.setLabelAlignment(Qt.AlignRight | Qt.AlignTop)
00077         marker_axis_y.setLineStyle(Qwt.QwtPlotMarker.HLine)
00078         marker_axis_y.setYValue(0.0)
00079         marker_axis_y.attach(self)
00080 
00081         self._picker = Qwt.QwtPlotPicker(
00082             Qwt.QwtPlot.xBottom, Qwt.QwtPlot.yLeft, Qwt.QwtPicker.PolygonSelection,
00083             Qwt.QwtPlotPicker.PolygonRubberBand, Qwt.QwtPicker.AlwaysOn, self.canvas()
00084         )
00085         self._picker.setRubberBandPen(QPen(Qt.blue))
00086         self._picker.setTrackerPen(QPen(Qt.blue))
00087 
00088         # Initialize data
00089         self.rescale()
00090         # self.move_canvas(0, 0)
00091         self.canvas().setMouseTracking(True)
00092         self.canvas().installEventFilter(self)
00093 
00094     def eventFilter(self, _, event):
00095         if event.type() == QEvent.MouseButtonRelease:
00096             x = self.invTransform(Qwt.QwtPlot.xBottom, event.pos().x())
00097             y = self.invTransform(Qwt.QwtPlot.yLeft, event.pos().y())
00098             self._last_click_coordinates = QPointF(x, y)
00099             self.limits_changed.emit()
00100         elif event.type() == QEvent.MouseMove:
00101             x = self.invTransform(Qwt.QwtPlot.xBottom, event.pos().x())
00102             y = self.invTransform(Qwt.QwtPlot.yLeft, event.pos().y())
00103             coords = QPointF(x, y)
00104             if self._picker.isActive() and self._last_click_coordinates is not None:
00105                 toolTip = 'origin x: %.5f, y: %.5f' % (
00106                     self._last_click_coordinates.x(), self._last_click_coordinates.y())
00107                 delta = coords - self._last_click_coordinates
00108                 toolTip += '\ndelta x: %.5f, y: %.5f\nlength: %.5f' % (
00109                     delta.x(), delta.y(), QVector2D(delta).length())
00110             else:
00111                 toolTip = 'buttons\nleft: measure\nmiddle: move\nright: zoom x/y\nwheel: zoom y'
00112             self.setToolTip(toolTip)
00113             self.mouseCoordinatesChanged.emit(coords)
00114         return False
00115 
00116     def log(self, level, message):
00117         # self.emit(SIGNAL('logMessage'), level, message)
00118         pass
00119 
00120     def resizeEvent(self, event):
00121         Qwt.QwtPlot.resizeEvent(self, event)
00122         self.rescale()
00123 
00124     def add_curve(self, curve_id, curve_name, curve_color=QColor(Qt.blue), markers_on=False):
00125         curve_id = str(curve_id)
00126         if curve_id in self._curves:
00127             return
00128         curve_object = Qwt.QwtPlotCurve(curve_name)
00129         curve_object.attach(self)
00130         curve_object.setPen(curve_color)
00131         if markers_on:
00132             curve_object.setSymbol(
00133                 Qwt.QwtSymbol(Qwt.QwtSymbol.Ellipse, QBrush(curve_color), QPen(Qt.black), QSize(4, 4)))
00134         self._curves[curve_id] = curve_object
00135 
00136     def remove_curve(self, curve_id):
00137         curve_id = str(curve_id)
00138         if curve_id in self._curves:
00139             self._curves[curve_id].hide()
00140             self._curves[curve_id].attach(None)
00141             del self._curves[curve_id]
00142 
00143     def set_values(self, curve_id, data_x, data_y):
00144         curve = self._curves[curve_id]
00145         curve.setData(data_x, data_y)
00146 
00147     def redraw(self):
00148         self.replot()
00149 
00150     # ----------------------------------------------
00151     # begin qwtplot internal methods
00152     # ----------------------------------------------
00153     def rescale(self):
00154         self.setAxisScale(Qwt.QwtPlot.yLeft,
00155                           self._y_limits[0],
00156                           self._y_limits[1])
00157         self.setAxisScale(Qwt.QwtPlot.xBottom,
00158                           self._x_limits[0],
00159                           self._x_limits[1])
00160 
00161         self._canvas_display_height = self._y_limits[1] - self._y_limits[0]
00162         self._canvas_display_width = self._x_limits[1] - self._x_limits[0]
00163         self.redraw()
00164 
00165     def rescale_axis_x(self, delta__x):
00166         """
00167         add delta_x to the length of the x axis
00168         """
00169         x_width = self._x_limits[1] - self._x_limits[0]
00170         x_width += delta__x
00171         self._x_limits[1] = x_width + self._x_limits[0]
00172         self.rescale()
00173 
00174     def scale_axis_y(self, max_value):
00175         """
00176         set the y axis height to max_value, about the current center
00177         """
00178         canvas_display_height = max_value
00179         canvas_offset_y = (self._y_limits[1] + self._y_limits[0]) / 2.0
00180         y_lower_limit = canvas_offset_y - (canvas_display_height / 2)
00181         y_upper_limit = canvas_offset_y + (canvas_display_height / 2)
00182         self._y_limits = [y_lower_limit, y_upper_limit]
00183         self.rescale()
00184 
00185     def move_canvas(self, delta_x, delta_y):
00186         """
00187         move the canvas by delta_x and delta_y in SCREEN COORDINATES
00188         """
00189         canvas_offset_x = delta_x * self._canvas_display_width / self.canvas().width()
00190         canvas_offset_y = delta_y * self._canvas_display_height / self.canvas().height()
00191         self._x_limits = [l + canvas_offset_x for l in self._x_limits]
00192         self._y_limits = [l + canvas_offset_y for l in self._y_limits]
00193         self.rescale()
00194 
00195     def mousePressEvent(self, event):
00196         self._last_canvas_x = event.x() - self.canvas().x()
00197         self._last_canvas_y = event.y() - self.canvas().y()
00198         self._pressed_canvas_y = event.y() - self.canvas().y()
00199 
00200     def mouseMoveEvent(self, event):
00201         canvas_x = event.x() - self.canvas().x()
00202         canvas_y = event.y() - self.canvas().y()
00203         if event.buttons() & Qt.MiddleButton:  # middle button moves the canvas
00204             delta_x = self._last_canvas_x - canvas_x
00205             delta_y = canvas_y - self._last_canvas_y
00206             self.move_canvas(delta_x, delta_y)
00207         elif event.buttons() & Qt.RightButton:   # right button zooms
00208             zoom_factor = max(-0.6, min(0.6, (self._last_canvas_y - canvas_y) / 20.0 / 2.0))
00209             delta_y = (self.canvas().height() / 2.0) - self._pressed_canvas_y
00210             self.move_canvas(0, zoom_factor * delta_y * 1.0225)
00211             self.scale_axis_y(
00212                 max(0.005, self._canvas_display_height - (zoom_factor * self._canvas_display_height)))
00213             self.rescale_axis_x(self._last_canvas_x - canvas_x)
00214         self._last_canvas_x = canvas_x
00215         self._last_canvas_y = canvas_y
00216 
00217     def wheelEvent(self, event):  # mouse wheel zooms the y-axis
00218         # y position of pointer in graph coordinates
00219         canvas_y = event.y() - self.canvas().y()
00220 
00221         try:
00222             delta = event.angleDelta().y()
00223         except AttributeError:
00224             delta = event.delta()
00225         zoom_factor = max(-0.6, min(0.6, (delta / 120) / 6.0))
00226         delta_y = (self.canvas().height() / 2.0) - canvas_y
00227         self.move_canvas(0, zoom_factor * delta_y * 1.0225)
00228 
00229         self.scale_axis_y(
00230             max(0.0005, self._canvas_display_height - zoom_factor * self._canvas_display_height))
00231         self.limits_changed.emit()
00232 
00233     def vline(self, x, color):
00234         qWarning("QwtDataPlot.vline is not implemented yet")
00235 
00236     def set_xlim(self, limits):
00237         self.setAxisScale(Qwt.QwtPlot.xBottom, limits[0], limits[1])
00238         self._x_limits = limits
00239 
00240     def set_ylim(self, limits):
00241         self.setAxisScale(Qwt.QwtPlot.yLeft, limits[0], limits[1])
00242         self._y_limits = limits
00243 
00244     def get_xlim(self):
00245         return self._x_limits
00246 
00247     def get_ylim(self):
00248         return self._y_limits
00249 
00250 
00251 if __name__ == '__main__':
00252     from python_qt_binding.QtGui import QApplication
00253 
00254     app = QApplication(sys.argv)
00255     plot = QwtDataPlot()
00256     plot.resize(700, 500)
00257     plot.show()
00258     plot.add_curve(0, '(x/500)^2')
00259     plot.add_curve(1, 'sin(x / 20) * 500')
00260     for i in range(plot._num_value_saved):
00261         plot.update_value(0, (i / 500.0) * (i / 5.0))
00262         plot.update_value(1, math.sin(i / 20.0) * 500)
00263 
00264     sys.exit(app.exec_())


rqt_plot
Author(s): Dorian Scholz
autogenerated on Sun Mar 17 2019 02:29:34