qwt_data_plot.py
Go to the documentation of this file.
1 #!/usr/bin/env python
2 
3 # Copyright (c) 2011, Dorian Scholz, TU Darmstadt
4 # All rights reserved.
5 #
6 # Redistribution and use in source and binary forms, with or without
7 # modification, are permitted provided that the following conditions
8 # are met:
9 #
10 # * Redistributions of source code must retain the above copyright
11 # notice, this list of conditions and the following disclaimer.
12 # * Redistributions in binary form must reproduce the above
13 # copyright notice, this list of conditions and the following
14 # disclaimer in the documentation and/or other materials provided
15 # with the distribution.
16 # * Neither the name of the TU Darmstadt nor the names of its
17 # contributors may be used to endorse or promote products derived
18 # from this software without specific prior written permission.
19 #
20 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23 # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
24 # COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26 # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
30 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 # POSSIBILITY OF SUCH DAMAGE.
32 
33 # -*- coding: utf-8 -*-
34 from __future__ import division
35 import math
36 import sys
37 
38 from python_qt_binding.QtCore import QEvent, QSize, QPointF, Qt, Signal, Slot, qWarning
39 from python_qt_binding.QtGui import QColor, QPen, QBrush, QVector2D
40 import qwt as Qwt
41 
42 
43 # create real QwtDataPlot class
44 class QwtDataPlot(Qwt.QwtPlot):
45  mouseCoordinatesChanged = Signal(QPointF)
46  _num_value_saved = 1000
47  _num_values_ploted = 1000
48 
49  limits_changed = Signal()
50 
51  def __init__(self, *args):
52  super(QwtDataPlot, self).__init__(*args)
53  self.setCanvasBackground(Qt.white)
54  self.insertLegend(Qwt.QwtLegend(), Qwt.QwtPlot.BottomLegend)
55  # attach a grid
56  grid = Qwt.QwtPlotGrid()
57  grid.attach(self)
58  grid.setPen(QPen(Qt.black, 0, Qt.DotLine))
59 
60  self._curves = {}
61 
62  # TODO: rejigger these internal data structures so that they're easier
63  # to work with, and easier to use with set_xlim and set_ylim
64  # this may also entail rejiggering the _time_axis so that it's
65  # actually time axis data, or just isn't required any more
66  # new data structure
67  self._x_limits = [0.0, 10.0]
68  self._y_limits = [0.0, 10.0]
69 
70  # old data structures
71  self._last_canvas_x = 0
72  self._last_canvas_y = 0
75  self._start_coordinates = None
76 
77  marker_axis_y = Qwt.QwtPlotMarker()
78  marker_axis_y.setLabelAlignment(Qt.AlignRight | Qt.AlignTop)
79  marker_axis_y.setLineStyle(Qwt.QwtPlotMarker.HLine)
80  marker_axis_y.setYValue(0.0)
81  marker_axis_y.attach(self)
82 
83  # Initialize data
84  self.rescale()
85  # self.move_canvas(0, 0)
86  self.canvas().setMouseTracking(True)
87  self.canvas().installEventFilter(self)
88 
89  def eventFilter(self, _, event):
90  if event.type() == QEvent.MouseButtonPress:
91  x = self.invTransform(Qwt.QwtPlot.xBottom, event.pos().x())
92  y = self.invTransform(Qwt.QwtPlot.yLeft, event.pos().y())
93  self._start_coordinates = QPointF(x, y)
94  elif event.type() == QEvent.MouseButtonRelease:
95  self._start_coordinates = None
96  self.limits_changed.emit()
97  elif event.type() == QEvent.MouseMove:
98  x = self.invTransform(Qwt.QwtPlot.xBottom, event.pos().x())
99  y = self.invTransform(Qwt.QwtPlot.yLeft, event.pos().y())
100  coords = QPointF(x, y)
101  if self._start_coordinates is not None:
102  delta = coords - self._start_coordinates
103  toolTip = 'origin x: %.5f, y: %.5f\ndelta x: %.5f, y: %.5f\nlength: %.5f' % (
104  self._start_coordinates.x(), self._start_coordinates.y(),
105  delta.x(), delta.y(), QVector2D(delta).length())
106  else:
107  toolTip = 'buttons\nleft: measure\nmiddle: move\nright: zoom x/y\nwheel: zoom y'
108  self.setToolTip(toolTip)
109  self.mouseCoordinatesChanged.emit(coords)
110  return False
111 
112  def log(self, level, message):
113  # self.emit(SIGNAL('logMessage'), level, message)
114  pass
115 
116  def resizeEvent(self, event):
117  Qwt.QwtPlot.resizeEvent(self, event)
118  self.rescale()
119 
120  def add_curve(self, curve_id, curve_name, curve_color=QColor(Qt.blue), markers_on=False):
121  curve_id = str(curve_id)
122  if curve_id in self._curves:
123  return
124  curve_object = Qwt.QwtPlotCurve(curve_name)
125  curve_object.attach(self)
126  curve_object.setPen(curve_color)
127  if markers_on:
128  curve_object.setSymbol(
129  Qwt.QwtSymbol(Qwt.QwtSymbol.Ellipse, QBrush(curve_color), QPen(Qt.black), QSize(4, 4)))
130  self._curves[curve_id] = curve_object
131 
132  def remove_curve(self, curve_id):
133  curve_id = str(curve_id)
134  if curve_id in self._curves:
135  self._curves[curve_id].hide()
136  self._curves[curve_id].attach(None)
137  del self._curves[curve_id]
138 
139  def set_values(self, curve_id, data_x, data_y):
140  curve_id = str(curve_id)
141  curve = self._curves[curve_id]
142  curve.setData(data_x, data_y)
143 
144  def redraw(self):
145  self.replot()
146 
147  # ----------------------------------------------
148  # begin qwtplot internal methods
149  # ----------------------------------------------
150  def rescale(self):
151  self.setAxisScale(Qwt.QwtPlot.yLeft,
152  self._y_limits[0],
153  self._y_limits[1])
154  self.setAxisScale(Qwt.QwtPlot.xBottom,
155  self._x_limits[0],
156  self._x_limits[1])
157 
158  self._canvas_display_height = self._y_limits[1] - self._y_limits[0]
159  self._canvas_display_width = self._x_limits[1] - self._x_limits[0]
160  self.redraw()
161 
162  def rescale_axis_x(self, delta__x):
163  """
164  add delta_x to the length of the x axis
165  """
166  x_width = self._x_limits[1] - self._x_limits[0]
167  x_width += delta__x
168  self._x_limits[1] = x_width + self._x_limits[0]
169  self.rescale()
170 
171  def scale_axis_y(self, max_value):
172  """
173  set the y axis height to max_value, about the current center
174  """
175  canvas_display_height = max_value
176  canvas_offset_y = (self._y_limits[1] + self._y_limits[0]) / 2.0
177  y_lower_limit = canvas_offset_y - (canvas_display_height / 2)
178  y_upper_limit = canvas_offset_y + (canvas_display_height / 2)
179  self._y_limits = [y_lower_limit, y_upper_limit]
180  self.rescale()
181 
182  def move_canvas(self, delta_x, delta_y):
183  """
184  move the canvas by delta_x and delta_y in SCREEN COORDINATES
185  """
186  canvas_offset_x = delta_x * self._canvas_display_width / self.canvas().width()
187  canvas_offset_y = delta_y * self._canvas_display_height / self.canvas().height()
188  self._x_limits = [l + canvas_offset_x for l in self._x_limits]
189  self._y_limits = [l + canvas_offset_y for l in self._y_limits]
190  self.rescale()
191 
192  def mousePressEvent(self, event):
193  self._last_canvas_x = event.x() - self.canvas().x()
194  self._last_canvas_y = event.y() - self.canvas().y()
195  self._pressed_canvas_y = event.y() - self.canvas().y()
196 
197  def mouseMoveEvent(self, event):
198  canvas_x = event.x() - self.canvas().x()
199  canvas_y = event.y() - self.canvas().y()
200  if event.buttons() & Qt.MiddleButton: # middle button moves the canvas
201  delta_x = self._last_canvas_x - canvas_x
202  delta_y = canvas_y - self._last_canvas_y
203  self.move_canvas(delta_x, delta_y)
204  elif event.buttons() & Qt.RightButton: # right button zooms
205  zoom_factor = max(-0.6, min(0.6, (self._last_canvas_y - canvas_y) / 20.0 / 2.0))
206  delta_y = (self.canvas().height() / 2.0) - self._pressed_canvas_y
207  self.move_canvas(0, zoom_factor * delta_y * 1.0225)
208  self.scale_axis_y(
209  max(0.005, self._canvas_display_height - (zoom_factor * self._canvas_display_height)))
210  self.rescale_axis_x(self._last_canvas_x - canvas_x)
211  self._last_canvas_x = canvas_x
212  self._last_canvas_y = canvas_y
213 
214  def wheelEvent(self, event): # mouse wheel zooms the y-axis
215  # y position of pointer in graph coordinates
216  canvas_y = event.y() - self.canvas().y()
217 
218  try:
219  delta = event.angleDelta().y()
220  except AttributeError:
221  delta = event.delta()
222  zoom_factor = max(-0.6, min(0.6, (delta / 120) / 6.0))
223  delta_y = (self.canvas().height() / 2.0) - canvas_y
224  self.move_canvas(0, zoom_factor * delta_y * 1.0225)
225 
226  self.scale_axis_y(
227  max(0.0005, self._canvas_display_height - zoom_factor * self._canvas_display_height))
228  self.limits_changed.emit()
229 
230  def vline(self, x, color):
231  qWarning("QwtDataPlot.vline is not implemented yet")
232 
233  def set_xlim(self, limits):
234  self.setAxisScale(Qwt.QwtPlot.xBottom, limits[0], limits[1])
235  self._x_limits = limits
236 
237  def set_ylim(self, limits):
238  self.setAxisScale(Qwt.QwtPlot.yLeft, limits[0], limits[1])
239  self._y_limits = limits
240 
241  def get_xlim(self):
242  return self._x_limits
243 
244  def get_ylim(self):
245  return self._y_limits
246 
247 
248 if __name__ == '__main__':
249  from python_qt_binding.QtWidgets import QApplication
250  from numpy import arange, sin
251 
252  app = QApplication(sys.argv)
253  plot = QwtDataPlot()
254  plot.resize(700, 500)
255  plot.show()
256  plot.add_curve(0, '(x/50)^2')
257  plot.add_curve(1, 'sin(x / 20) * 500')
258  x = arange(plot._num_value_saved)
259  plot.set_values(0, x, (x / 50.0)**2)
260  plot.set_values(1, x, sin(x / 20.0) * 500)
261  plot.set_xlim([x[0], x[-1]])
262  plot.set_ylim([-500, 500])
263 
264  sys.exit(app.exec_())
rqt_plot.data_plot.qwt_data_plot.QwtDataPlot.rescale_axis_x
def rescale_axis_x(self, delta__x)
Definition: qwt_data_plot.py:162
rqt_plot.data_plot.qwt_data_plot.x
x
Definition: qwt_data_plot.py:258
rqt_plot.data_plot.qwt_data_plot.QwtDataPlot.mouseMoveEvent
def mouseMoveEvent(self, event)
Definition: qwt_data_plot.py:197
rqt_plot.data_plot.qwt_data_plot.QwtDataPlot.mouseCoordinatesChanged
mouseCoordinatesChanged
Definition: qwt_data_plot.py:45
rqt_plot.data_plot.qwt_data_plot.QwtDataPlot.__init__
def __init__(self, *args)
Definition: qwt_data_plot.py:51
rqt_plot.data_plot.qwt_data_plot.QwtDataPlot._canvas_display_width
_canvas_display_width
Definition: qwt_data_plot.py:159
rqt_plot.data_plot.qwt_data_plot.QwtDataPlot._pressed_canvas_x
_pressed_canvas_x
Definition: qwt_data_plot.py:74
rqt_plot.data_plot.qwt_data_plot.QwtDataPlot.mousePressEvent
def mousePressEvent(self, event)
Definition: qwt_data_plot.py:192
rqt_plot.data_plot.qwt_data_plot.QwtDataPlot.resizeEvent
def resizeEvent(self, event)
Definition: qwt_data_plot.py:116
rqt_plot.data_plot.qwt_data_plot.QwtDataPlot._x_limits
_x_limits
Definition: qwt_data_plot.py:67
rqt_plot.data_plot.qwt_data_plot.QwtDataPlot.add_curve
def add_curve(self, curve_id, curve_name, curve_color=QColor(Qt.blue), markers_on=False)
Definition: qwt_data_plot.py:120
rqt_plot.data_plot.qwt_data_plot.QwtDataPlot._last_canvas_x
_last_canvas_x
Definition: qwt_data_plot.py:71
rqt_plot.data_plot.qwt_data_plot.QwtDataPlot.get_xlim
def get_xlim(self)
Definition: qwt_data_plot.py:241
rqt_plot.data_plot.qwt_data_plot.QwtDataPlot
Definition: qwt_data_plot.py:44
rqt_plot.data_plot.qwt_data_plot.QwtDataPlot.log
def log(self, level, message)
Definition: qwt_data_plot.py:112
rqt_plot.data_plot.qwt_data_plot.QwtDataPlot._pressed_canvas_y
_pressed_canvas_y
Definition: qwt_data_plot.py:73
rqt_plot.data_plot.qwt_data_plot.QwtDataPlot.move_canvas
def move_canvas(self, delta_x, delta_y)
Definition: qwt_data_plot.py:182
rqt_plot.data_plot.qwt_data_plot.QwtDataPlot.rescale
def rescale(self)
Definition: qwt_data_plot.py:150
rqt_plot.data_plot.qwt_data_plot.QwtDataPlot.set_xlim
def set_xlim(self, limits)
Definition: qwt_data_plot.py:233
rqt_plot.data_plot.qwt_data_plot.QwtDataPlot.eventFilter
def eventFilter(self, _, event)
Definition: qwt_data_plot.py:89
rqt_plot.data_plot.qwt_data_plot.QwtDataPlot.wheelEvent
def wheelEvent(self, event)
Definition: qwt_data_plot.py:214
rqt_plot.data_plot.qwt_data_plot.QwtDataPlot._y_limits
_y_limits
Definition: qwt_data_plot.py:68
rqt_plot.data_plot.qwt_data_plot.QwtDataPlot._canvas_display_height
_canvas_display_height
Definition: qwt_data_plot.py:158
rqt_plot.data_plot.qwt_data_plot.QwtDataPlot.remove_curve
def remove_curve(self, curve_id)
Definition: qwt_data_plot.py:132
rqt_plot.data_plot.qwt_data_plot.QwtDataPlot.vline
def vline(self, x, color)
Definition: qwt_data_plot.py:230
rqt_plot.data_plot.qwt_data_plot.QwtDataPlot.set_ylim
def set_ylim(self, limits)
Definition: qwt_data_plot.py:237
rqt_plot.data_plot.qwt_data_plot.QwtDataPlot._curves
_curves
Definition: qwt_data_plot.py:60
rqt_plot.data_plot.qwt_data_plot.QwtDataPlot.limits_changed
limits_changed
Definition: qwt_data_plot.py:49
rqt_plot.data_plot.qwt_data_plot.QwtDataPlot._last_canvas_y
_last_canvas_y
Definition: qwt_data_plot.py:72
rqt_plot.data_plot.qwt_data_plot.QwtDataPlot.redraw
def redraw(self)
Definition: qwt_data_plot.py:144
rqt_plot.data_plot.qwt_data_plot.QwtDataPlot.set_values
def set_values(self, curve_id, data_x, data_y)
Definition: qwt_data_plot.py:139
rqt_plot.data_plot.qwt_data_plot.QwtDataPlot.scale_axis_y
def scale_axis_y(self, max_value)
Definition: qwt_data_plot.py:171
rqt_plot.data_plot.qwt_data_plot.QwtDataPlot._start_coordinates
_start_coordinates
Definition: qwt_data_plot.py:75
rqt_plot.data_plot.qwt_data_plot.QwtDataPlot.get_ylim
def get_ylim(self)
Definition: qwt_data_plot.py:244


rqt_plot
Author(s): Dorian Scholz, Dirk Thomas
autogenerated on Sun Apr 27 2025 02:27:17