#!/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/>.
##
#############################################################################
"""
taurustrend.py: Generic trend widget for Taurus
"""
__all__ = ["TaurusTrend2DDialog"]
from guiqwt.plot import ImageDialog
from taurus.external.qt import Qt
import taurus.core
from taurus.qt.qtgui.base import TaurusBaseWidget
from taurus.qt.qtgui.extra_guiqwt.image import TaurusTrend2DItem
from taurus.qt.qtgui.extra_guiqwt.tools import (TaurusModelChooserTool,
TimeAxisTool, AutoScrollTool)
[docs]class TaurusTrend2DDialog(ImageDialog, TaurusBaseWidget):
"""
This is a widget for displaying trends from 1D Taurus attributes (i.e.,
representing the variation over time of a 1D array). Sometimes this kind of
plots are also known as "spectrograms".
The widget shows a 3D plot (Z represented with colors) where the values in
the 1D array are plotted in the Y-Z plane and are stacked along the X axis.
"""
_modifiableByUser = True
def __init__(self, parent=None, designMode=False, toolbar=True,
stackMode='deltatime', buffersize=512, options=None, **kwargs):
"""see :class:`guiqwt.plot.ImageDialog` for other valid initialization
parameters"""
defaultOptions = dict(lock_aspect_ratio=False)
if options is not None:
defaultOptions.update(options)
ImageDialog.__init__(self, parent=parent, toolbar=toolbar,
options=defaultOptions, **kwargs)
TaurusBaseWidget.__init__(self, "TaurusTrend2DDialog")
self.trendItem = None
self.buffersize = buffersize
self._useArchiving = False
self._stackMode = stackMode
self.setStackMode(stackMode)
self.setWindowFlags(Qt.Qt.Widget)
# add some tools
for toolklass in (TaurusModelChooserTool, AutoScrollTool):
self.add_tool(toolklass)
self.get_tool(TaurusModelChooserTool).singleModel = True
self.setModifiableByUser(self._modifiableByUser)
self.setContextMenuPolicy(Qt.Qt.CustomContextMenu)
self.registerConfigDelegate(self.get_tool(AutoScrollTool))
[docs] def keyPressEvent(self, event):
if(event.key() == Qt.Qt.Key_Escape):
event.ignore()
else:
ImageDialog.keyPressEvent(self, event)
[docs] def setStackMode(self, mode):
"""set the type of stack to be used. This determines how X values are
interpreted:
- as timestamps ('datetime')
- as time deltas ('timedelta')
- as event numbers ('event')
:param mode:(one of 'datetime', 'timedelta' or 'event')
"""
mode = str(mode)
if mode == 'datetime':
self.add_tool(TimeAxisTool)
timetool = self.get_tool(TimeAxisTool)
timetool.set_scale_y_t(True)
elif mode == 'deltatime':
from taurus.qt.qtgui.plot import DeltaTimeScaleEngine
plot = self.get_plot()
DeltaTimeScaleEngine.enableInAxis(plot, plot.xBottom, rotation=-45)
elif mode == 'event':
plot = self.get_plot()
scaleEngine = plot.axisScaleEngine(plot.xBottom)
if hasattr(scaleEngine, 'disableInAxis'):
scaleEngine.disableInAxis(plot, plot.xBottom)
else:
self.error('Unknown stack mode "%s"' % repr(mode))
return
self._stackMode = mode
if hasattr(self.trendItem, 'stackMode'):
self.trendItem.stackMode = mode
[docs] def getStackMode(self):
return self._stackMode
[docs] def resetStackMode(self):
self.setStackMode('datetime')
[docs] def getModelClass(self):
"""reimplemented from :class:`TaurusBaseWidget`"""
return taurus.core.taurusattribute.TaurusAttribute
[docs] def setModel(self, model):
"""reimplemented from :class:`TaurusBaseWidget`"""
plot = self.get_plot()
if self.trendItem is not None:
plot.del_item(self.trendItem)
self.trendItem = TaurusTrend2DItem(
stackMode=self.getStackMode(), buffersize=self.buffersize)
self.trendItem.setModel(model)
plot.add_item(self.trendItem)
self.trendItem.set_readonly(not self.isModifiableByUser())
plot.set_axis_title(plot.colormap_axis, 'value')
plot.set_axis_unit('left', 'index')
try:
plot.set_axis_title(
'left', self.trendItem.getModelObj().getSimpleName())
except:
self.debug('cannot set title for left axis')
self.traceback()
try:
unit = self.trendItem.getModelObj().getConfig().getUnit() or ''
plot.set_axis_unit(plot.colormap_axis, unit)
except:
self.debug('cannot set units for colormap axis')
self.traceback()
self.trendItem.dataChanged.connect(self.update_cross_sections)
[docs] def getModel(self):
"""reimplemented from :class:`TaurusBaseWidget`"""
if self.trendItem is None:
return None
else:
return self.trendItem.getModel()
[docs] def setUseArchiving(self, enable):
"""enables/disables looking up in the archiver for data stored before
the Trend was started
:param enable: (bool) if True, archiving values will be used if
available
"""
if not self._stackMode == 'datetime':
self.info('ignoring setUseArchiving. Reason: not in X time scale')
self._useArchiving = enable
[docs] def getUseArchiving(self):
"""whether TaurusTrend is looking for data in the archiver when needed
:return: (bool)
.. seealso:: :meth:`setUseArchiving`
"""
return self._useArchiving
[docs] def resetUseArchiving(self):
"""Same as setUseArchiving(False)"""
self.setUseArchiving(False)
[docs] def setMaxDataBufferSize(self, maxSize):
"""sets the maximum number of events that will be stacked
:param maxSize: (int) the maximum limit
.. seealso:: :class:`TaurusTrendSet`
"""
if self.trendItem is not None:
self.trendItem.setBufferSize(maxSize)
self.buffersize = maxSize
[docs] def getMaxDataBufferSize(self):
"""returns the maximum number of events that can be plotted in the trend
:return: (int)
"""
return self.buffersize
[docs] def resetMaxDataBufferSize(self):
"""Same as setMaxDataBufferSize(512) (i.e. 512 events)"""
self.setMaxDataBufferSize(512)
@classmethod
[docs] def getQtDesignerPluginInfo(cls):
"""reimplemented from :class:`TaurusBaseWidget`"""
ret = TaurusBaseWidget.getQtDesignerPluginInfo()
ret['module'] = 'taurus.qt.qtgui.plot'
ret['group'] = 'Taurus Display'
ret['icon'] = 'designer:qwtplot.png'
return ret
[docs] def setModifiableByUser(self, modifiable):
"""reimplemented from :class:`TaurusBaseWidget`"""
self.get_tool(TaurusModelChooserTool).action.setEnabled(modifiable)
self.get_plot().set_items_readonly(not modifiable)
TaurusBaseWidget.setModifiableByUser(self, modifiable)
model = Qt.pyqtProperty("QString", getModel, setModel,
TaurusBaseWidget.resetModel)
# @todo uncomment this when archiving is supported
useArchiving = Qt.pyqtProperty("bool", getUseArchiving, setUseArchiving,
resetUseArchiving)
maxDataBufferSize = Qt.pyqtProperty("int", getMaxDataBufferSize,
setMaxDataBufferSize,
resetMaxDataBufferSize)
stackMode = Qt.pyqtProperty("QString", getStackMode, setStackMode,
resetStackMode)
modifiableByUser = Qt.pyqtProperty("bool",
TaurusBaseWidget.isModifiableByUser,
setModifiableByUser,
TaurusBaseWidget.resetModifiableByUser)
def taurusTrend2DMain():
from taurus.qt.qtgui.application import TaurusApplication
import taurus.core
import sys
# prepare options
parser = taurus.core.util.argparse.get_taurus_parser()
parser.set_usage("%prog [options] <model>")
parser.set_description('a Taurus application for plotting trends of ' +
'arrays (aka "spectrograms")')
parser.add_option("-x", "--x-axis-mode", dest="x_axis_mode", default='d',
metavar="t|d|e",
help=("interpret X values as timestamps (t), " +
"time deltas (d) or event numbers (e). " +
"Accepted values: t|d|e")
)
parser.add_option("-b", "--buffer", dest="max_buffer_size", default='512',
help=("maximum number of values to be stacked " +
"(when reached, the oldest values will be " +
"discarded)")
)
parser.add_option("-a", "--use-archiving",
action="store_true", dest="use_archiving", default=False)
parser.add_option("--demo", action="store_true", dest="demo",
default=False, help="show a demo of the widget")
parser.add_option("--window-name", dest="window_name",
default="Taurus Trend 2D", help="Name of the window")
app = TaurusApplication(cmd_line_parser=parser, app_name="Taurus Trend 2D",
app_version=taurus.Release.version)
args = app.get_command_line_args()
options = app.get_command_line_options()
# check & process options
stackModeMap = dict(t='datetime', d='deltatime', e='event')
if options.x_axis_mode.lower() not in stackModeMap:
parser.print_help(sys.stderr)
sys.exit(1)
stackMode = stackModeMap[options.x_axis_mode.lower()]
if options.demo:
args.append('eval:x=linspace(0,3,40);t=rand();sin(x+t)')
w = TaurusTrend2DDialog(stackMode=stackMode, wintitle=options.window_name,
buffersize=int(options.max_buffer_size))
# set archiving
if options.use_archiving:
raise NotImplementedError('Archiving support is not yet implemented')
w.setUseArchiving(True)
# set model
if len(args) == 1:
w.setModel(args[0])
else:
parser.print_help(sys.stderr)
sys.exit(1)
w.show()
sys.exit(app.exec_())
if __name__ == "__main__":
taurusTrend2DMain()