summaryrefslogtreecommitdiff
path: root/lib/taurus/qt/qtgui/plot/curveStatsDlg.py
diff options
context:
space:
mode:
Diffstat (limited to 'lib/taurus/qt/qtgui/plot/curveStatsDlg.py')
-rw-r--r--lib/taurus/qt/qtgui/plot/curveStatsDlg.py316
1 files changed, 316 insertions, 0 deletions
diff --git a/lib/taurus/qt/qtgui/plot/curveStatsDlg.py b/lib/taurus/qt/qtgui/plot/curveStatsDlg.py
new file mode 100644
index 00000000..997a7f07
--- /dev/null
+++ b/lib/taurus/qt/qtgui/plot/curveStatsDlg.py
@@ -0,0 +1,316 @@
+#!/usr/bin/env python
+
+#############################################################################
+##
+## This file is part of Taurus
+##
+## http://taurus-scada.org
+##
+## Copyright 2011 CELLS / ALBA Synchrotron, Bellaterra, Spain
+##
+## Taurus is free software: you can redistribute it and/or modify
+## it under the terms of the GNU Lesser General Public License as published by
+## the Free Software Foundation, either version 3 of the License, or
+## (at your option) any later version.
+##
+## Taurus is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU Lesser General Public License for more details.
+##
+## You should have received a copy of the GNU Lesser General Public License
+## along with Taurus. If not, see <http://www.gnu.org/licenses/>.
+##
+#############################################################################
+
+"""
+curvesAppearanceChooserDlg.py:
+ A Qt dialog for choosing plot appearance (symbols and lines)
+ for a QwtPlot-derived widget (like Taurusplot)
+"""
+
+from taurus.external.qt import Qt, Qwt5
+from datetime import datetime
+from taurus.qt.qtgui.resource import getIcon, getThemeIcon
+from taurus.qt.qtgui.util.ui import UILoadable
+
+
+@UILoadable(with_ui='ui')
+class CurveStatsDialog(Qt.QDialog):
+ """
+ A dialog for configuring and displaying statistics from curves of a plot
+ """
+
+ statColumns = ('points','min', 'max', 'mean', 'std', 'rms')
+ def __init__(self, parent=None):
+ super(CurveStatsDialog,self).__init__(parent)
+ self.loadUi()
+
+ plot = parent
+ xIsTime = plot.getXIsTime()
+ self.ui.minDTE.setVisible(xIsTime)
+ self.ui.maxDTE.setVisible(xIsTime)
+ self.ui.minSB.setVisible(not xIsTime)
+ self.ui.maxSB.setVisible(not xIsTime)
+
+ self.ui.minSB.setMinimum(float('-inf'))
+ self.ui.minSB.setMaximum(float('inf'))
+ self.ui.maxSB.setMinimum(float('-inf'))
+ self.ui.maxSB.setMaximum(float('inf'))
+
+ icon = getIcon(':/actions/system-search.svg')
+ self.ui.selectMinPB.setIcon(icon)
+ self.ui.selectMaxPB.setIcon(icon)
+
+
+ self.refreshCurves()
+
+ cbs = (self.ui.npointsStatCB, self.ui.minStatCB, self.ui.maxStatCB,
+ self.ui.meanStatCB, self.ui.stdStatCB, self.ui.rmsStatCB)
+ self._checkboxToColMap = dict(zip(cbs, xrange(len(self.statColumns))))
+
+ self.minPicker = Qwt5.QwtPlotPicker(Qwt5.QwtPlot.xBottom,
+ Qwt5.QwtPlot.yLeft,
+ Qwt5.QwtPicker.PointSelection,
+ Qwt5.QwtPicker.VLineRubberBand,
+ Qwt5.QwtPicker.AlwaysOn, plot.canvas())
+
+ self.maxPicker = Qwt5.QwtPlotPicker(Qwt5.QwtPlot.xBottom,
+ Qwt5.QwtPlot.yLeft,
+ Qwt5.QwtPicker.PointSelection,
+ Qwt5.QwtPicker.VLineRubberBand,
+ Qwt5.QwtPicker.AlwaysOn, plot.canvas())
+
+ self.minPicker.setEnabled(False)
+ self.maxPicker.setEnabled(False)
+
+ #initialize min and max display
+ xmin = plot.axisScaleDiv(Qwt5.QwtPlot.xBottom).lowerBound()
+ xmax = plot.axisScaleDiv(Qwt5.QwtPlot.xBottom).upperBound()
+
+ self.minMarker = Qwt5.QwtPlotMarker()
+ self.minMarker.setLineStyle(Qwt5.QwtPlotMarker.VLine)
+ self.minMarker.setLinePen(Qt.QPen(Qt.Qt.green, 3))
+ self.minMarker.setXValue(xmin)
+ self.minMarker.attach(plot)
+ self.minMarker.hide()
+
+ self.maxMarker = Qwt5.QwtPlotMarker()
+ self.maxMarker.setLineStyle(Qwt5.QwtPlotMarker.VLine)
+ self.maxMarker.setLinePen(Qt.QPen(Qt.Qt.red, 3))
+ self.maxMarker.setXValue(xmax)
+ self.maxMarker.attach(plot)
+ self.maxMarker.hide()
+
+ if xIsTime:
+ self.ui.minDTE.setDateTime(self._timestamptToQDateTime(xmin))
+ self.ui.maxDTE.setDateTime(self._timestamptToQDateTime(xmax))
+ else:
+ self.ui.minSB.setValue(xmin)
+ self.ui.maxSB.setValue(xmax)
+
+ refreshAction = Qt.QAction(getThemeIcon('view-refresh'), "Refresh available curves", self.ui.statsTW)
+ refreshAction.setShortcut(Qt.Qt.Key_F5)
+ self.connect(refreshAction, Qt.SIGNAL("triggered()"), self.refreshCurves)
+ self.ui.statsTW.addAction(refreshAction)
+
+ #connections
+ for cb in cbs:
+ self.connect(cb, Qt.SIGNAL('toggled(bool)'), self.onStatToggled)
+ self.connect(self.ui.calculatePB, Qt.SIGNAL('clicked()'),self.onCalculate)
+ self.connect(self.ui.selectMinPB, Qt.SIGNAL('clicked()'),self.onSelectMin)
+ self.connect(self.ui.selectMaxPB, Qt.SIGNAL('clicked()'),self.onSelectMax)
+ self.connect(self.minPicker, Qt.SIGNAL('selected(QwtDoublePoint)'), self.minSelected)
+ self.connect(self.maxPicker, Qt.SIGNAL('selected(QwtDoublePoint)'), self.maxSelected)
+ self.connect(self.ui.minSB, Qt.SIGNAL('valueChanged(double)'), self.onMinChanged)
+ self.connect(self.ui.minDTE, Qt.SIGNAL('dateTimeChanged(QDateTime)'), self.onMinChanged)
+ self.connect(self.ui.maxSB, Qt.SIGNAL('valueChanged(double)'), self.onMaxChanged)
+ self.connect(self.ui.maxDTE, Qt.SIGNAL('dateTimeChanged(QDateTime)'), self.onMaxChanged)
+ self.connect(self.ui.minCB, Qt.SIGNAL('toggled(bool)'), self.onMinChanged)
+ self.connect(self.ui.maxCB, Qt.SIGNAL('toggled(bool)'), self.onMaxChanged)
+
+ def _timestamptToQDateTime(self, ts):
+ dt = datetime.fromtimestamp(ts)
+ return Qt.QDateTime(dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second, dt.microsecond/1000)
+
+ def _QDateTimeToTimestamp(self, qdt):
+ return qdt.toTime_t() + qdt.time().msec()/1000.
+
+ def onSelectMin(self):
+ '''slot called when the user clicks on the selectMin button'''
+ plot = self.parent()
+ plot.getZoomers()[0].setEnabled(False)
+ plot.canvas().setCursor(Qt.Qt.SplitHCursor)
+ self.minPicker.setEnabled(True)
+ self.hide()
+
+ def minSelected(self, pos):
+ '''slot called when the user has selected a min value from the plot'''
+ self.show()
+ self.ui.minCB.setChecked(True)
+ self.minPicker.setEnabled(False)
+ plot = self.parent()
+ xmin = pos.x()
+ if plot.getXIsTime():
+ self.ui.minDTE.setDateTime(self._timestamptToQDateTime(xmin)) #this triggers a call to onMinChanged()
+ else:
+ self.ui.minSB.setValue(xmin) #this triggers a call to onMinChanged()
+ self.restorePlot(keepMarkers=True)
+
+ def onMinChanged(self, x):
+ '''slot called when the min value is changed'''
+ plot = self.parent()
+ if isinstance(x, bool):
+ self.minMarker.setVisible(x)
+ else:
+ if isinstance(x, Qt.QDateTime):
+ x = self._QDateTimeToTimestamp(x)
+ self.minMarker.setXValue(x)
+ plot.replot()
+
+ def onSelectMax(self):
+ '''slot called when the user clicks on the selectMax button'''
+ self.minPicker.setEnabled(False)
+ plot = self.parent()
+ plot.getZoomers()[0].setEnabled(False)
+ plot.canvas().setCursor(Qt.Qt.SplitHCursor)
+ self.maxPicker.setEnabled(True)
+ self.hide()
+
+ def maxSelected(self, pos):
+ '''slot called when the user has selected a min value from the plot'''
+ self.show()
+ self.ui.maxCB.setChecked(True)
+ self.maxPicker.setEnabled(False)
+ plot = self.parent()
+ xmax = pos.x()
+ if plot.getXIsTime():
+ self.ui.maxDTE.setDateTime(self._timestamptToQDateTime(xmax)) #this triggers a call to onMaxChanged()
+ else:
+ self.ui.maxSB.setValue(xmax) #this triggers a call to onMaxChanged()
+ self.restorePlot(keepMarkers=True)
+
+ def onMaxChanged(self, x):
+ '''slot called when the max value is changed'''
+ plot = self.parent()
+ if isinstance(x, bool):
+ self.maxMarker.setVisible(x)
+ else:
+ if isinstance(x, Qt.QDateTime):
+ x = self._QDateTimeToTimestamp(x)
+ self.maxMarker.setXValue(x)
+ plot.replot()
+
+ def onStatToggled(self, checked):
+ '''slot called when any of the stat checkboxes is toggled'''
+ cb = self.sender()
+ col = self._checkboxToColMap[cb]
+ if checked:
+ self.ui.statsTW.showColumn(col)
+ else:
+ self.ui.statsTW.hideColumn(col)
+
+ def refreshCurves(self):
+ '''resets the table re-reading the curves from the plot'''
+ plot = self.parent()
+ self.ui.statsTW.clear()
+ self.ui.statsTW.setColumnCount(len(self.statColumns))
+ self.ui.statsTW.setHorizontalHeaderLabels(self.statColumns)
+ self.curveNames = plot.getCurveNamesSorted()
+ self.ui.statsTW.setRowCount(len(self.curveNames))
+ for row,name in enumerate(self.curveNames):
+ title = plot.getCurveTitle(name)
+ item = Qt.QTableWidgetItem(title)
+ self.ui.statsTW.setVerticalHeaderItem(row, item)
+
+ def getSelectedRows(self):
+ '''returns a list of row numbers corresponding to the selected rows of the table'''
+ selected = []
+ for rg in self.ui.statsTW.selectedRanges():
+ for row in xrange(rg.topRow(), rg.topRow()+rg.rowCount()):
+ selected.append(row)
+ return selected
+
+ def onCalculate(self):
+ '''
+ slot called when the calculate button is pressed. Performs the
+ calculation of stats in the current limits for the currently selected
+ curves (or for all if none selected) and fills the table.
+ '''
+ plot = self.parent()
+ table = self.ui.statsTW
+
+ xmin,xmax = None,None
+ if self.ui.minCB.isChecked():
+ if plot.getXIsTime():
+ xmin = self.ui.minDTE.dateTime().toTime_t() + self.ui.minDTE.time().msec()/1000.
+ else:
+ xmin = self.ui.minSB.value()
+ if self.ui.maxCB.isChecked():
+ if plot.getXIsTime():
+ xmax = self.ui.maxDTE.dateTime().toTime_t() + self.ui.maxDTE.time().msec()/1000.
+ else:
+ xmax = self.ui.maxSB.value()
+ limits = xmin,xmax
+
+ selectedRows = self.getSelectedRows()
+ if len(selectedRows) == 0:
+ selectedRows = range(len(self.curveNames))
+ selectedCurves = [self.curveNames[i] for i in selectedRows]
+ statsdict = plot.getCurveStats(limits=limits, curveNames=selectedCurves)
+
+ for row, name in zip(selectedRows,selectedCurves):
+ stats = statsdict.get(name, {})
+
+ minxy = stats.get('min')
+ if minxy is None:
+ text = "---"
+ elif plot.xIsTime:
+ text = "t=%s \ny=%g"%(datetime.fromtimestamp(minxy[0]).isoformat(),minxy[1])
+ else:
+ text = "x=%g \ny=%g"%minxy
+ table.setItem(row,self.statColumns.index('min'), Qt.QTableWidgetItem(text))
+
+ maxxy = stats.get('max')
+ if maxxy is None:
+ text = "---"
+ elif plot.xIsTime:
+ text = "t=%s \ny=%g"%(datetime.fromtimestamp(maxxy[0]).isoformat(),maxxy[1])
+ else:
+ text = "x=%g \ny=%g"%maxxy
+ table.setItem(row,self.statColumns.index('max'), Qt.QTableWidgetItem(text))
+
+ for s in ('points', 'mean', 'std', 'rms'):
+ r = stats.get(s)
+ if r is None:
+ text = "---"
+ else:
+ text = "%g"%r
+ table.setItem(row, self.statColumns.index(s), Qt.QTableWidgetItem(text))
+ table.resizeColumnsToContents()
+
+ def restorePlot(self, keepMarkers=False):
+ '''leaves the parent plot in its original state'''
+ plot = self.parent()
+ plot.canvas().setCursor(Qt.Qt.CrossCursor)
+ plot.getZoomers()[0].setEnabled(plot.getAllowZoomers())
+ if not keepMarkers:
+ self.minMarker.detach()
+ self.maxMarker.detach()
+ plot.replot()
+
+ def closeEvent(self, event):
+ '''See :meth:`Qwidget.closeEvent`'''
+ self.restorePlot()
+ self.emit(Qt.SIGNAL('closed'))
+
+ def showEvent(self, event):
+ '''See :meth:`Qwidget.showEvent`'''
+ plot = self.parent()
+ self.minMarker.attach(plot)
+ self.maxMarker.attach(plot)
+ plot.replot()
+
+
+
+