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_())