00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034 from __future__ import division
00035 import math
00036 import sys
00037
00038 from python_qt_binding.QtCore import QEvent, QPointF, Qt, SIGNAL, Signal, Slot
00039 from python_qt_binding.QtGui import QPen, QVector2D
00040 import Qwt
00041
00042 from numpy import arange, zeros, concatenate
00043
00044
00045
00046 class QwtDataPlot(Qwt.QwtPlot):
00047 mouseCoordinatesChanged = Signal(QPointF)
00048 _colors = [Qt.red, Qt.blue, Qt.magenta, Qt.cyan, Qt.green, Qt.darkYellow, Qt.black, Qt.darkRed, Qt.gray, Qt.darkCyan]
00049 _num_value_saved = 1000
00050 _num_values_ploted = 1000
00051
00052 def __init__(self, *args):
00053 super(QwtDataPlot, self).__init__(*args)
00054 self.setCanvasBackground(Qt.white)
00055 self.insertLegend(Qwt.QwtLegend(), Qwt.QwtPlot.BottomLegend)
00056
00057 self._curves = {}
00058 self._data_offset_x = 0
00059 self._canvas_offset_x = 0
00060 self._canvas_offset_y = 0
00061 self._last_canvas_x = 0
00062 self._last_canvas_y = 0
00063 self._pressed_canvas_y = 0
00064 self._last_click_coordinates = None
00065 self._color_index = 0
00066
00067 marker_axis_y = Qwt.QwtPlotMarker()
00068 marker_axis_y.setLabelAlignment(Qt.AlignRight | Qt.AlignTop)
00069 marker_axis_y.setLineStyle(Qwt.QwtPlotMarker.HLine)
00070 marker_axis_y.setYValue(0.0)
00071 marker_axis_y.attach(self)
00072
00073 self._picker = Qwt.QwtPlotPicker(
00074 Qwt.QwtPlot.xBottom, Qwt.QwtPlot.yLeft, Qwt.QwtPicker.PolygonSelection,
00075 Qwt.QwtPlotPicker.PolygonRubberBand, Qwt.QwtPicker.AlwaysOn, self.canvas()
00076 )
00077 self._picker.setRubberBandPen(QPen(self._colors[-1]))
00078 self._picker.setTrackerPen(QPen(self._colors[-1]))
00079
00080
00081 self._time_axis = arange(self._num_values_ploted)
00082 self._canvas_display_height = 1000
00083 self._canvas_display_width = self.canvas().width()
00084 self._data_offset_x = self._num_value_saved - len(self._time_axis)
00085 self.redraw()
00086 self.move_canvas(0, 0)
00087 self.canvas().setMouseTracking(True)
00088 self.canvas().installEventFilter(self)
00089
00090 def eventFilter(self, _, event):
00091 if event.type() == QEvent.MouseButtonRelease:
00092 x = self.invTransform(Qwt.QwtPlot.xBottom, event.pos().x())
00093 y = self.invTransform(Qwt.QwtPlot.yLeft, event.pos().y())
00094 self._last_click_coordinates = QPointF(x, y)
00095 elif event.type() == QEvent.MouseMove:
00096 x = self.invTransform(Qwt.QwtPlot.xBottom, event.pos().x())
00097 y = self.invTransform(Qwt.QwtPlot.yLeft, event.pos().y())
00098 coords = QPointF(x, y)
00099 if self._picker.isActive() and self._last_click_coordinates is not None:
00100 toolTip = 'origin x: %.5f, y: %.5f' % (self._last_click_coordinates.x(), self._last_click_coordinates.y())
00101 delta = coords - self._last_click_coordinates
00102 toolTip += '\ndelta x: %.5f, y: %.5f\nlength: %.5f' % (delta.x(), delta.y(), QVector2D(delta).length())
00103 else:
00104 toolTip = 'buttons\nleft: measure\nmiddle: move\nright: zoom x/y\nwheel: zoom y'
00105 self.setToolTip(toolTip)
00106 self.mouseCoordinatesChanged.emit(coords)
00107 return False
00108
00109 def log(self, level, message):
00110 self.emit(SIGNAL('logMessage'), level, message)
00111
00112 def resizeEvent(self, event):
00113
00114 Qwt.QwtPlot.resizeEvent(self, event)
00115 self.rescale()
00116
00117 def add_curve(self, curve_id, curve_name, values_x, values_y):
00118 curve_id = str(curve_id)
00119 if self._curves.get(curve_id):
00120 return
00121 curve_object = Qwt.QwtPlotCurve(curve_name)
00122 curve_object.attach(self)
00123 curve_object.setPen(QPen(self._colors[self._color_index % len(self._colors)]))
00124 self._color_index += 1
00125 self._curves[curve_id] = {
00126 'name': curve_name,
00127 'data': zeros(self._num_value_saved),
00128 'object': curve_object,
00129 }
00130
00131 def remove_curve(self, curve_id):
00132 curve_id = str(curve_id)
00133 if curve_id in self._curves:
00134 self._curves[curve_id]['object'].hide()
00135 self._curves[curve_id]['object'].attach(None)
00136 del self._curves[curve_id]['object']
00137 del self._curves[curve_id]
00138
00139 @Slot(str, list, list)
00140 def update_values(self, curve_id, values_x, values_y):
00141 for value_x, value_y in zip(values_x, values_y):
00142 self.update_value(curve_id, value_x, value_y)
00143
00144 @Slot(str, float, float)
00145 def update_value(self, curve_id, value_x, value_y):
00146 curve_id = str(curve_id)
00147
00148 if curve_id in self._curves:
00149
00150 self._curves[curve_id]['data'] = concatenate((self._curves[curve_id]['data'][1:], self._curves[curve_id]['data'][:1]), 1)
00151 self._curves[curve_id]['data'][-1] = float(value_y)
00152
00153 def redraw(self):
00154 for curve_id in self._curves.keys():
00155 self._curves[curve_id]['object'].setData(self._time_axis, self._curves[curve_id]['data'][self._data_offset_x: self._data_offset_x + len(self._time_axis)])
00156
00157 self.replot()
00158
00159 def rescale(self):
00160 y_num_ticks = self.height() / 40
00161 y_lower_limit = self._canvas_offset_y - (self._canvas_display_height / 2)
00162 y_upper_limit = self._canvas_offset_y + (self._canvas_display_height / 2)
00163
00164
00165 y_delta = y_upper_limit - y_lower_limit
00166 exponent = int(math.log10(y_delta))
00167 presicion = -(exponent - 2)
00168 y_step_size = round(y_delta / y_num_ticks, presicion)
00169
00170 self.setAxisScale(Qwt.QwtPlot.yLeft, y_lower_limit, y_upper_limit, y_step_size)
00171
00172 self.setAxisScale(Qwt.QwtPlot.xBottom, 0, len(self._time_axis))
00173 self.redraw()
00174
00175 def rescale_axis_x(self, delta__x):
00176 new_len = len(self._time_axis) + delta__x
00177 new_len = max(10, min(new_len, self._num_value_saved))
00178 self._time_axis = arange(new_len)
00179 self._data_offset_x = max(0, min(self._data_offset_x, self._num_value_saved - len(self._time_axis)))
00180 self.rescale()
00181
00182 def scale_axis_y(self, max_value):
00183 self._canvas_display_height = max_value
00184 self.rescale()
00185
00186 def move_canvas(self, delta_x, delta_y):
00187 self._data_offset_x += delta_x * len(self._time_axis) / float(self.canvas().width())
00188 self._data_offset_x = max(0, min(self._data_offset_x, self._num_value_saved - len(self._time_axis)))
00189 self._canvas_offset_x += delta_x * self._canvas_display_width / self.canvas().width()
00190 self._canvas_offset_y += delta_y * self._canvas_display_height / self.canvas().height()
00191 self.rescale()
00192
00193 def mousePressEvent(self, event):
00194 self._last_canvas_x = event.x() - self.canvas().x()
00195 self._last_canvas_y = event.y() - self.canvas().y()
00196 self._pressed_canvas_y = event.y() - self.canvas().y()
00197
00198 def mouseMoveEvent(self, event):
00199 canvas_x = event.x() - self.canvas().x()
00200 canvas_y = event.y() - self.canvas().y()
00201 if event.buttons() & Qt.MiddleButton:
00202 delta_x = self._last_canvas_x - canvas_x
00203 delta_y = canvas_y - self._last_canvas_y
00204 self.move_canvas(delta_x, delta_y)
00205 elif event.buttons() & Qt.RightButton:
00206 zoom_factor = max(-0.6, min(0.6, (self._last_canvas_y - canvas_y) / 20.0 / 2.0))
00207 delta_y = (self.canvas().height() / 2.0) - self._pressed_canvas_y
00208 self.move_canvas(0, zoom_factor * delta_y * 1.0225)
00209 self.scale_axis_y(max(0.005, self._canvas_display_height - (zoom_factor * self._canvas_display_height)))
00210 self.rescale_axis_x(self._last_canvas_x - canvas_x)
00211 self._last_canvas_x = canvas_x
00212 self._last_canvas_y = canvas_y
00213
00214 def wheelEvent(self, event):
00215 canvas_y = event.y() - self.canvas().y()
00216 zoom_factor = max(-0.6, min(0.6, (event.delta() / 120) / 6.0))
00217 delta_y = (self.canvas().height() / 2.0) - canvas_y
00218 self.move_canvas(0, zoom_factor * delta_y * 1.0225)
00219 self.scale_axis_y(max(0.0005, self._canvas_display_height - zoom_factor * self._canvas_display_height))
00220
00221
00222 if __name__ == '__main__':
00223 from python_qt_binding.QtGui import QApplication
00224
00225 app = QApplication(sys.argv)
00226 plot = QwtDataPlot()
00227 plot.resize(700, 500)
00228 plot.show()
00229 plot.add_curve(0, '(x/500)^2')
00230 plot.add_curve(1, 'sin(x / 20) * 500')
00231 for i in range(plot._num_value_saved):
00232 plot.update_value(0, (i / 500.0) * (i / 5.0))
00233 plot.update_value(1, math.sin(i / 20.0) * 500)
00234
00235 sys.exit(app.exec_())