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' % (self._last_click_coordinates.x(), self._last_click_coordinates.y())
00106                 delta = coords - self._last_click_coordinates
00107                 toolTip += '\ndelta x: %.5f, y: %.5f\nlength: %.5f' % (delta.x(), delta.y(), QVector2D(delta).length())
00108             else:
00109                 toolTip = 'buttons\nleft: measure\nmiddle: move\nright: zoom x/y\nwheel: zoom y'
00110             self.setToolTip(toolTip)
00111             self.mouseCoordinatesChanged.emit(coords)
00112         return False
00113 
00114     def log(self, level, message):
00115         # self.emit(SIGNAL('logMessage'), level, message)
00116         pass
00117 
00118     def resizeEvent(self, event):
00119         Qwt.QwtPlot.resizeEvent(self, event)
00120         self.rescale()
00121 
00122     def add_curve(self, curve_id, curve_name, curve_color=QColor(Qt.blue), markers_on=False):
00123         curve_id = str(curve_id)
00124         if curve_id in self._curves:
00125             return
00126         curve_object = Qwt.QwtPlotCurve(curve_name)
00127         curve_object.attach(self)
00128         curve_object.setPen(curve_color)
00129         if markers_on:
00130             curve_object.setSymbol(Qwt.QwtSymbol(Qwt.QwtSymbol.Ellipse, QBrush(curve_color), QPen(Qt.black), QSize(4,4)))
00131         self._curves[curve_id] = curve_object
00132 
00133     def remove_curve(self, curve_id):
00134         curve_id = str(curve_id)
00135         if curve_id in self._curves:
00136             self._curves[curve_id].hide()
00137             self._curves[curve_id].attach(None)
00138             del self._curves[curve_id]
00139 
00140     def set_values(self, curve_id, data_x, data_y):
00141         curve = self._curves[curve_id]
00142         curve.setData(data_x, data_y)
00143 
00144     def redraw(self):
00145         self.replot()
00146 
00147     # ----------------------------------------------
00148     # begin qwtplot internal methods
00149     # ----------------------------------------------
00150     def rescale(self):
00151         self.setAxisScale(Qwt.QwtPlot.yLeft,
00152                           self._y_limits[0],
00153                           self._y_limits[1])
00154         self.setAxisScale(Qwt.QwtPlot.xBottom,
00155                           self._x_limits[0],
00156                           self._x_limits[1])
00157 
00158         self._canvas_display_height = self._y_limits[1] - self._y_limits[0]
00159         self._canvas_display_width  = self._x_limits[1] - self._x_limits[0]
00160         self.redraw()
00161 
00162     def rescale_axis_x(self, delta__x):
00163         """
00164         add delta_x to the length of the x axis
00165         """
00166         x_width = self._x_limits[1] - self._x_limits[0]
00167         x_width += delta__x
00168         self._x_limits[1] = x_width + self._x_limits[0]
00169         self.rescale()
00170 
00171     def scale_axis_y(self, max_value):
00172         """
00173         set the y axis height to max_value, about the current center
00174         """
00175         canvas_display_height = max_value
00176         canvas_offset_y = (self._y_limits[1] + self._y_limits[0])/2.0
00177         y_lower_limit = canvas_offset_y - (canvas_display_height / 2)
00178         y_upper_limit = canvas_offset_y + (canvas_display_height / 2)
00179         self._y_limits = [y_lower_limit, y_upper_limit]
00180         self.rescale()
00181 
00182     def move_canvas(self, delta_x, delta_y):
00183         """
00184         move the canvas by delta_x and delta_y in SCREEN COORDINATES
00185         """
00186         canvas_offset_x = delta_x * self._canvas_display_width / self.canvas().width()
00187         canvas_offset_y = delta_y * self._canvas_display_height / self.canvas().height()
00188         self._x_limits = [ l + canvas_offset_x for l in self._x_limits]
00189         self._y_limits = [ l + canvas_offset_y for l in self._y_limits]
00190         self.rescale()
00191 
00192     def mousePressEvent(self, event):
00193         self._last_canvas_x = event.x() - self.canvas().x()
00194         self._last_canvas_y = event.y() - self.canvas().y()
00195         self._pressed_canvas_y = event.y() - self.canvas().y()
00196 
00197     def mouseMoveEvent(self, event):
00198         canvas_x = event.x() - self.canvas().x()
00199         canvas_y = event.y() - self.canvas().y()
00200         if event.buttons() & Qt.MiddleButton:  # middle button moves the canvas
00201             delta_x = self._last_canvas_x - canvas_x
00202             delta_y = canvas_y - self._last_canvas_y
00203             self.move_canvas(delta_x, delta_y)
00204         elif event.buttons() & Qt.RightButton:   # right button zooms
00205             zoom_factor = max(-0.6, min(0.6, (self._last_canvas_y - canvas_y) / 20.0 / 2.0))
00206             delta_y = (self.canvas().height() / 2.0) - self._pressed_canvas_y
00207             self.move_canvas(0, zoom_factor * delta_y * 1.0225)
00208             self.scale_axis_y(max(0.005, self._canvas_display_height - (zoom_factor * self._canvas_display_height)))
00209             self.rescale_axis_x(self._last_canvas_x - canvas_x)
00210         self._last_canvas_x = canvas_x
00211         self._last_canvas_y = canvas_y
00212 
00213     def wheelEvent(self, event):  # mouse wheel zooms the y-axis
00214         # y position of pointer in graph coordinates
00215         canvas_y = event.y() - self.canvas().y()
00216 
00217         try:
00218             delta = event.angleDelta().y()
00219         except AttributeError:
00220             delta = event.delta()
00221         zoom_factor = max(-0.6, min(0.6, (delta / 120) / 6.0))
00222         delta_y = (self.canvas().height() / 2.0) - canvas_y
00223         self.move_canvas(0, zoom_factor * delta_y * 1.0225)
00224 
00225         self.scale_axis_y(max(0.0005, self._canvas_display_height - zoom_factor * self._canvas_display_height))
00226         self.limits_changed.emit()
00227 
00228 
00229     def vline(self, x, color):
00230         qWarning("QwtDataPlot.vline is not implemented yet")
00231 
00232     def set_xlim(self, limits):
00233         self.setAxisScale(Qwt.QwtPlot.xBottom, limits[0], limits[1])
00234         self._x_limits = limits
00235 
00236     def set_ylim(self, limits):
00237         self.setAxisScale(Qwt.QwtPlot.yLeft, limits[0], limits[1])
00238         self._y_limits = limits
00239 
00240     def get_xlim(self):
00241         return self._x_limits
00242 
00243     def get_ylim(self):
00244         return self._y_limits
00245 
00246 
00247 if __name__ == '__main__':
00248     from python_qt_binding.QtGui import QApplication
00249 
00250     app = QApplication(sys.argv)
00251     plot = QwtDataPlot()
00252     plot.resize(700, 500)
00253     plot.show()
00254     plot.add_curve(0, '(x/500)^2')
00255     plot.add_curve(1, 'sin(x / 20) * 500')
00256     for i in range(plot._num_value_saved):
00257         plot.update_value(0, (i / 500.0) * (i / 5.0))
00258         plot.update_value(1, math.sin(i / 20.0) * 500)
00259 
00260     sys.exit(app.exec_())


rqt_plot
Author(s): Dorian Scholz
autogenerated on Mon May 1 2017 02:41:23