) the name of the attributes to
consider for the merge. If None, all the attributes
will be merged
:param conflict: (callable) a function that takes 2 objects (having a
different attribute) and returns a value that solves
the conflict. If None is given, any conflicting
attribute will be set to CONFLICT.
:return: (CurveAppearanceProperties) merged properties
"""
n = len(plist)
if n < 1:
raise ValueError("plist must contain at least 1 member")
plist = copy.deepcopy(plist)
if n == 1:
return plist[0]
if attributes is None:
attributes = [
"sStyle",
"sSize",
"sColor",
"sFill",
"lStyle",
"lWidth",
"lColor",
"cStyle",
"cFill",
"y2",
"title",
]
if conflict is None:
conflict = CurveAppearanceProperties.inConflict_CONFLICT
p = CurveAppearanceProperties()
for a in attributes:
alist = [p.__getattribute__(a) for p in plist]
p.__setattr__(a, alist[0])
for ai in alist[1:]:
if alist[0] != ai:
# print "MERGING:",a,alist[0],ai,conflict(alist[0],ai)
p.__setattr__(a, conflict(alist[0], ai))
break
return p
def deserialize_opts(opts):
"""
Deserialize opts dict to pass it to a PlotDataItem
:param opts: (dict) serialized properties (as the output of
:meth:`deserialize_opts`)
:return: (dict) deserialized properties (acceptable by PlotDataItem)
"""
# pen property
if opts["pen"] is not None:
opts["pen"] = _unmarshallingQPainter(opts, "pen", "pen")
# shadowPen property
if opts["shadowPen"] is not None:
opts["shadowPen"] = _unmarshallingQPainter(opts, "shadowPen", "pen")
# symbolPen property
if opts["symbolPen"] is not None:
opts["symbolPen"] = _unmarshallingQPainter(opts, "symbolPen", "pen")
# fillBrush property
if opts["fillBrush"] is not None:
opts["fillBrush"] = _unmarshallingQPainter(opts, "fillBrush", "brush")
# symbolBrush property
if opts["symbolBrush"] is not None:
opts["symbolBrush"] = _unmarshallingQPainter(
opts, "symbolBrush", "brush"
)
return opts
def serialize_opts(opts):
"""
Serialize all properties from PlotDataItem.
:param opts: (dict) PlotDataItem opts (may include non-serializable
objects)
:return: (dict) serialized properties (can be pickled)
"""
# pen property (QPen object)
if opts["pen"] is not None:
_marshallingQPainter(opts, "pen", "pen")
# shadowPen property (QPen object)
if opts["shadowPen"] is not None:
_marshallingQPainter(opts, "shadowPen", "pen")
# symbolPen property (QPen object)
if opts["symbolPen"] is not None:
_marshallingQPainter(opts, "symbolPen", "pen")
# fillBrush property (QBrush object)
if opts["fillBrush"] is not None:
_marshallingQPainter(opts, "fillBrush", "brush")
# symbolBrush property (QBrush object)
if opts["symbolBrush"] is not None:
_marshallingQPainter(opts, "symbolBrush", "brush")
return opts
def _marshallingQPainter(opts, prop_name, qPainter):
if qPainter == "pen":
painter = pyqtgraph.mkPen(opts[prop_name])
opts[prop_name + "_width"] = painter.width()
opts[prop_name + "_dash"] = painter.dashPattern()
opts[prop_name + "_cosmetic"] = painter.isCosmetic()
elif qPainter == "brush":
painter = pyqtgraph.mkBrush(opts[prop_name])
else:
return
color = pyqtgraph.colorStr(painter.color())
opts[prop_name] = color
opts[prop_name + "_style"] = painter.style()
def _unmarshallingQPainter(opts, prop_name, qPainter):
color = opts[prop_name]
style = opts[prop_name + "_style"]
del opts[prop_name + "_style"]
if qPainter == "pen":
width = opts[prop_name + "_width"]
dash = opts[prop_name + "_dash"]
cosmetic = opts[prop_name + "_cosmetic"]
del opts[prop_name + "_width"]
del opts[prop_name + "_dash"]
del opts[prop_name + "_cosmetic"]
painter = pyqtgraph.mkPen(
color=color, style=style, width=width, dash=dash, cosmetic=cosmetic
)
elif qPainter == "brush":
painter = pyqtgraph.mkBrush(color=color)
painter.setStyle(style)
else:
return
return painter
taurus-pyqtgraph-0.3.1/taurus_pyqtgraph/curvesmodel.py 0000664 0000000 0000000 00000045451 13550077441 0023416 0 ustar 00root root 0000000 0000000 #!/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 .
##
#############################################################################
"""
curvesmodel Model and view for new CurveItem configuration
.. warning:: this is Work-in-progress. The API may change.
Do not rely on current API of this module
"""
from __future__ import print_function
from future.utils import string_types
from builtins import bytes
__all__ = ["TaurusCurveItemTableModel", "TaurusItemConf", "TaurusItemConfDlg"]
import copy
from taurus.external.qt import Qt
import taurus
from taurus.core import TaurusElementType
from taurus.qt.qtcore.mimetypes import (
TAURUS_MODEL_LIST_MIME_TYPE,
TAURUS_ATTR_MIME_TYPE,
)
from taurus.qt.qtgui.util.ui import UILoadable
from taurus.qt.qtgui.panel import TaurusModelSelector
# columns:
NUMCOLS = 3
X, Y, TITLE = list(range(NUMCOLS))
SRC_ROLE = Qt.Qt.UserRole + 1
class Component(object):
def __init__(self, src):
self.display = ""
self.icon = Qt.QIcon()
self.ok = True
self.processSrc(src)
def processSrc(self, src):
"""
processes the src and sets the values of display, icon and ok
attributes
"""
if src is None:
self.display, self.icon, self.ok = "", Qt.QIcon(), True
return
src = str(src).strip()
# empty
if src == "":
self.display, self.icon, self.ok = "", Qt.QIcon(), True
return
# for taurus attributes
if taurus.isValidName(src, etypes=[TaurusElementType.Attribute]):
self.display, self.icon, self.ok = (
src,
Qt.QIcon("logos:taurus.png"),
True,
)
return
# if not caught before, it is unsupported
self.display, self.icon, self.ok = (
src,
Qt.QIcon.fromTheme("dialog-warning"),
False,
)
class TaurusItemConf(object):
"""An object to hold an item of the TaurusCurveItemTableModel"""
def __init__(self, YModel=None, XModel=None, name=None):
self.x = Component(XModel)
self.y = Component(YModel)
self.xModel = XModel
self.yModel = YModel
self.curveLabel = name
def __repr__(self):
ret = "TaurusItemConf(xModel='%s', yModel='%s')" % (
self.xModel,
self.yModel,
)
return ret
class TaurusCurveItemTableModel(Qt.QAbstractTableModel):
""" A Qt data model for describing curves"""
dataChanged = Qt.pyqtSignal("QModelIndex", "QModelIndex")
def __init__(self, taurusItems=None):
super(TaurusCurveItemTableModel, self).__init__()
self.ncolumns = NUMCOLS
self.taurusItems = list(taurusItems)
def dumpData(self):
return copy.copy(self.taurusItems)
def rowCount(self, index=Qt.QModelIndex()):
return len(self.taurusItems)
def columnCount(self, index=Qt.QModelIndex()):
return self.ncolumns
def swapItems(self, index1, index2):
""" swap the items described by index1 and index2 in the list"""
r1, r2 = index1.row(), index2.row()
items = self.taurusItems
self.layoutAboutToBeChanged.emit()
items[r1], items[r2] = items[r2], items[r1]
self.dataChanged.emit(index1, index2)
self.layoutChanged.emit()
def data(self, index, role=Qt.Qt.DisplayRole):
if not index.isValid() or not (0 <= index.row() < self.rowCount()):
return None
row = index.row()
column = index.column()
# Display Role
if role == Qt.Qt.DisplayRole:
if column == X:
return str(self.taurusItems[row].x.display)
elif column == Y:
return str(self.taurusItems[row].y.display)
elif column == TITLE:
return str(self.taurusItems[row].curveLabel)
else:
return None
elif role == Qt.Qt.DecorationRole:
if column == X:
return self.taurusItems[row].x.icon
elif column == Y:
return self.taurusItems[row].y.icon
else:
return None
elif role == Qt.Qt.TextColorRole:
if column == X:
Qt.QColor(self.taurusItems[row].x.ok and "green" or "red")
elif column == Y:
Qt.QColor(self.taurusItems[row].y.ok and "green" or "red")
else:
return None
elif role == SRC_ROLE:
if column == X:
return str(self.taurusItems[row].xModel)
elif column == Y:
return str(self.taurusItems[row].yModel)
else:
return None
elif role == Qt.Qt.ToolTipRole:
if column == X:
return str(self.taurusItems[row].xModel)
elif column == Y:
return str(self.taurusItems[row].yModel)
else:
return None
if role == Qt.Qt.EditRole:
if column == X:
return str(self.taurusItems[row].xModel)
elif column == Y:
return str(self.taurusItems[row].yModel)
elif column == TITLE:
return str(self.taurusItems[row].curveLabel)
else:
return None
return None
def headerData(self, section, orientation, role=Qt.Qt.DisplayRole):
if role == Qt.Qt.TextAlignmentRole:
if orientation == Qt.Qt.Horizontal:
return int(Qt.Qt.AlignLeft | Qt.Qt.AlignVCenter)
return int(Qt.Qt.AlignRight | Qt.Qt.AlignVCenter)
if role != Qt.Qt.DisplayRole:
return None
# So this is DisplayRole...
if orientation == Qt.Qt.Horizontal:
if section == X:
return "X source"
elif section == Y:
return "Y Source"
elif section == TITLE:
return "Title"
return None
else:
return str(section + 1)
def flags(self, index):
# use this to set the editable flag when fix is selected
if not index.isValid():
return Qt.Qt.ItemIsEnabled
column = index.column()
if column in (X, Y):
return Qt.Qt.ItemFlags(
Qt.Qt.ItemIsEnabled
| Qt.Qt.ItemIsEditable
| Qt.Qt.ItemIsDragEnabled
| Qt.Qt.ItemIsDropEnabled
| Qt.Qt.ItemIsSelectable
)
elif column == TITLE:
return Qt.Qt.ItemFlags(
Qt.Qt.ItemIsEnabled
| Qt.Qt.ItemIsEditable
| Qt.Qt.ItemIsDragEnabled
)
return Qt.Qt.ItemFlags(
Qt.Qt.ItemIsEnabled
| Qt.Qt.ItemIsEditable
| Qt.Qt.ItemIsDragEnabled
)
def setData(self, index, value=None, role=Qt.Qt.EditRole):
if index.isValid() and (0 <= index.row() < self.rowCount()):
row = index.row()
curve = self.taurusItems[row]
column = index.column()
if column == X:
curve.xModel = value
curve.x.processSrc(value)
elif column == Y:
curve.yModel = value
curve.y.processSrc(value)
elif column == TITLE:
curve.curveLabel = value
self.dataChanged.emit(index, index)
return True
return False
def insertRows(self, position=None, rows=1, parentindex=None):
if position is None:
position = self.rowCount()
if parentindex is None:
parentindex = Qt.QModelIndex()
self.beginInsertRows(parentindex, position, position + rows - 1)
slice = [TaurusItemConf() for i in range(rows)]
self.taurusItems = (
self.taurusItems[:position] + slice + self.taurusItems[position:]
)
self.endInsertRows()
return True
def removeRows(self, position, rows=1, parentindex=None):
if parentindex is None:
parentindex = Qt.QModelIndex()
self.beginResetModel()
self.beginRemoveRows(parentindex, position, position + rows - 1)
self.taurusItems = (
self.taurusItems[:position] + self.taurusItems[position + rows :]
)
self.endRemoveRows()
self.endResetModel()
return True
def clearAll(self):
self.removeRows(0, self.rowCount())
def mimeTypes(self):
result = list(Qt.QAbstractTableModel.mimeTypes(self))
result += [TAURUS_ATTR_MIME_TYPE, "text/plain"]
return result
def dropMimeData(self, data, action, row, column, parent):
if row == -1:
if parent.isValid():
row = parent.row()
else:
row = parent.rowCount()
if column == -1:
if parent.isValid():
column = parent.column()
else:
column = parent.columnCount()
if data.hasFormat(TAURUS_ATTR_MIME_TYPE):
model = bytes(data.data(TAURUS_ATTR_MIME_TYPE)).decode("utf-8")
self.setData(self.index(row, column), value=model)
return True
elif data.hasFormat(TAURUS_MODEL_LIST_MIME_TYPE):
d = bytes(data.data(TAURUS_MODEL_LIST_MIME_TYPE))
models = d.decode("utf-8").split()
if len(models) == 1:
self.setData(self.index(row, column), value=models[0])
return True
else:
self.insertRows(row, len(models))
for i, m in enumerate(models):
self.setData(self.index(row + i, column), value=m)
return True
elif data.hasText():
self.setData(self.index(row, column), data.text())
return True
return False
def mimeData(self, indexes):
mimedata = Qt.QAbstractTableModel.mimeData(self, indexes)
if len(indexes) == 1:
# mimedata.setData(TAURUS_ATTR_MIME_TYPE, data)
data = self.data(indexes[0], role=SRC_ROLE)
mimedata.setText(data)
return mimedata
# mimedata.setData()
@UILoadable(with_ui="ui")
class TaurusItemConfDlg(Qt.QWidget):
""" A configuration dialog for creating new CurveItems.
Provides a TaurusModelBrowser for Taurus models and an editable
table for the sources and title of data
"""
dataChanged = Qt.pyqtSignal("QModelIndex", "QModelIndex")
applied = Qt.pyqtSignal()
def __init__(self, parent=None, taurusItemsConf=None, showXcol=True):
super(TaurusItemConfDlg, self).__init__(parent)
self.loadUi()
self._showXcol = showXcol
if taurusItemsConf is None:
taurusItemsConf = [
TaurusItemConf(YModel=None, XModel=None, name=None)
]
# @todo: The action for this button is not yet implemented
self.ui.reloadBT.setEnabled(False)
self.model = TaurusCurveItemTableModel(taurusItemsConf)
self._toolbar = Qt.QToolBar("Selector toolbar")
self.ui.horizontalLayout_2.addWidget(self._toolbar)
self._toolbar.setIconSize(Qt.QSize(16, 16))
self._toolbar.addAction(
Qt.QIcon.fromTheme("list-add"), "Add row", self._onAddAction
)
self._removeAction = self._toolbar.addAction(
Qt.QIcon.fromTheme("list-remove"),
"Remove selected row",
self._onRemoveThisAction,
)
self._removeAllAction = self._toolbar.addAction(
Qt.QIcon.fromTheme("edit-clear"),
"Remove all rows",
self._onClearAll,
)
self._moveUpAction = self._toolbar.addAction(
Qt.QIcon.fromTheme("go-up"),
"Move up the row",
self._onMoveUpAction,
)
self._moveDownAction = self._toolbar.addAction(
Qt.QIcon.fromTheme("go-down"),
"Move down the row",
self._onMoveDownAction,
)
table = self.ui.curvesTable
table.setModel(self.model)
table.setColumnHidden(X, not self._showXcol)
selectionmodel = table.selectionModel()
selectionmodel.selectionChanged.connect(self._onSelectionChanged)
# -------------------------------------------------------------------
# I get "UnboundLocalError: local variable 'taurus' referenced before
# assignment" if I don't import taurus again here
# TODO: check if this workaround is really needed
import taurus # noqa
# -------------------------------------------------------------------
modelSelector = TaurusModelSelector(parent=self)
self.ui.verticalLayout.addWidget(modelSelector)
# Connections
self.ui.applyBT.clicked.connect(self.onApply)
self.ui.reloadBT.clicked.connect(self.onReload)
self.ui.cancelBT.clicked.connect(self.close)
self.ui.curvesTable.customContextMenuRequested.connect(
self.onTableContextMenu
)
modelSelector.modelsAdded.connect(self.onModelsAdded)
def onTableContextMenu(self, pos):
index = self.ui.curvesTable.indexAt(pos)
row = index.row()
menu = Qt.QMenu(self.ui.curvesTable)
if row >= 0:
menu.addAction(
Qt.QIcon.fromTheme("list-remove"),
"Remove this curve",
self._onRemoveThisAction,
)
menu.addAction(
Qt.QIcon.fromTheme("edit-clear"), "Clear all", self.model.clearAll
)
menu.addAction(
Qt.QIcon.fromTheme("list-add"),
"Add new row",
self.model.insertRows,
)
menu.exec_(Qt.QCursor.pos())
def _onSelectionChanged(self):
""" Modify the status of the actions"""
selected = self.ui.curvesTable.selectedIndexes()
rows = []
for item in selected:
if item.row() not in rows:
rows.append(item.row())
lrows = len(rows)
row = self.ui.curvesTable.currentIndex().row()
isLastElem = row == self.model.rowCount() - 1
isFirstElem = row == 0
self._removeAction.setEnabled(lrows == 1)
self._moveUpAction.setEnabled(lrows == 1 and not isFirstElem)
self._moveDownAction.setEnabled(lrows == 1 and not isLastElem)
def _onAddAction(self):
""" Add a new row"""
self.model.insertRows()
self._removeAllAction.setEnabled(True)
def _onRemoveThisAction(self):
""" Remove the selected row"""
row = self.ui.curvesTable.currentIndex().row()
self.model.removeRows(row)
if self.model.rowCount() == 0:
self._removeAllAction.setEnabled(False)
def _onClearAll(self):
""" Remove all rows"""
self.model.clearAll()
self._removeAction.setEnabled(False)
self._moveUpAction.setEnabled(False)
self._moveDownAction.setEnabled(False)
self._removeAllAction.setEnabled(False)
def _onMoveUpAction(self):
""" Move up action swap the selected row with the previous one"""
i1 = self.ui.curvesTable.currentIndex()
i2 = self.ui.curvesTable.model().index(i1.row() - 1, 0)
self.__commitAndCloseEditor(i1)
self.model.swapItems(i1, i2)
self.ui.curvesTable.setCurrentIndex(i2)
def _onMoveDownAction(self):
""" Move down action swap the selected row with the next one"""
i1 = self.ui.curvesTable.currentIndex()
i2 = self.ui.curvesTable.model().index(i1.row() + 1, 0)
self.__commitAndCloseEditor(i1)
self.model.swapItems(i1, i2)
self.ui.curvesTable.setCurrentIndex(i2)
def __commitAndCloseEditor(self, idx):
"""if an editor is open, commit the data and close it before moving
:param idx: qmodel index
"""
w = self.ui.curvesTable.indexWidget(idx)
if w is not None:
self.ui.curvesTable.commitData(w)
self.ui.curvesTable.closePersistentEditor(idx)
def onModelsAdded(self, models):
nmodels = len(models)
rowcount = self.model.rowCount()
self.model.insertRows(rowcount, nmodels)
for i, m in enumerate(models):
if isinstance(m, string_types):
modelx, modely = None, m
else:
modelx, modely = m
if modelx is not None:
self.model.setData(
self.model.index(rowcount + i, X), value=modelx
)
self.model.setData(self.model.index(rowcount + i, Y), value=modely)
title = self.model.data(
self.model.index(rowcount + i, Y)
) # the display data
# print type(title), title
self.model.setData(
self.model.index(rowcount + i, TITLE), value=title
)
def getItemConfs(self):
return self.model.dumpData()
@staticmethod
def showDlg(parent=None, taurusItemConf=None, showXCol=True):
"""
Static method that launches a modal dialog containing a
TaurusItemConfDlg.
For the parameters, see :class:`TaurusItemConfDlg`
:return: (list,bool) Returns a models,ok tuple
models is a list of models.
ok is True if the dialog was accepted (by clicking on the
"update" button) and False otherwise
"""
dlg = Qt.QDialog(parent)
dlg.setWindowTitle("Curves Selection")
layout = Qt.QVBoxLayout()
w = TaurusItemConfDlg(
parent=parent, taurusItemsConf=taurusItemConf, showXcol=showXCol
)
layout.addWidget(w)
dlg.setLayout(layout)
w.applied.connect(dlg.accept)
w.ui.cancelBT.clicked.connect(dlg.close)
dlg.exec_()
return w.getItemConfs(), (dlg.result() == dlg.Accepted)
def onApply(self):
self.applied.emit()
def onReload(self):
# TODO
print("RELOAD!!! (todo)")
if __name__ == "__main__":
from taurus.qt.qtgui.application import TaurusApplication
import sys
app = TaurusApplication(cmd_line_parser=None)
TaurusItemConfDlg.showDlg()
sys.exit(app.exec_())
taurus-pyqtgraph-0.3.1/taurus_pyqtgraph/curvespropertiestool.py 0000664 0000000 0000000 00000011234 13550077441 0025400 0 ustar 00root root 0000000 0000000 #!/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 .
##
#############################################################################
__all__ = ["CurvesPropertiesTool"]
from taurus.external.qt import QtGui, Qt
from taurus.external.qt import QtCore
from taurus_pyqtgraph.curveproperties import (
CurvePropAdapter,
CurvesAppearanceChooser,
)
import pyqtgraph
class CurvesPropertiesTool(QtGui.QAction):
"""
This tool inserts an action in the menu of the :class:`pyqtgraph.PlotItem`
to which it is attached to show a dialog for editing curve properties.
It is implemented as an Action, and provides a method to attach it to a
PlotItem.
"""
def __init__(self, parent=None):
QtGui.QAction.__init__(self, "Plot configuration", parent)
self.triggered.connect(self._onTriggered)
self.plot_item = None
self.Y2Axis = None
def attachToPlotItem(self, plot_item, y2=None):
"""
Use this method to add this tool to a plot
:param plot_item: (PlotItem)
:param y2: (Y2ViewBox) instance of the Y2Viewbox attached to plot_item
if the axis change controls are to be used
"""
self.plot_item = plot_item
menu = plot_item.getViewBox().menu
menu.addAction(self)
self.Y2Axis = y2
def _onTriggered(self):
data_items = self.plot_item.listDataItems()
# checks in all ViewBoxes from plot_item,
# looking for a data_items (Curves).
for item in self.plot_item.scene().items():
if isinstance(item, pyqtgraph.ViewBox):
for data in item.addedItems:
if data not in data_items:
data_items.append(data)
# The dialog will ignore curves that define `._UImodifiable=False`
modifiable_items = []
for item in data_items:
if getattr(item, "_UImodifiable", True):
modifiable_items.append(item)
# It is necessary a CurvePropAdapter object for 'translate'
# the PlotDataItem properties into generic form given for the dialog
curvePropAdapter = CurvePropAdapter(
modifiable_items, self.plot_item, self.Y2Axis
)
curves = curvePropAdapter.getCurveProperties()
dlg = Qt.QDialog(parent=self.parent())
dlg.setWindowTitle("Plot Configuration")
layout = Qt.QVBoxLayout()
w = CurvesAppearanceChooser(
parent=dlg,
curvePropDict=curves,
showButtons=True,
Y2Axis=self.Y2Axis,
curvePropAdapter=curvePropAdapter,
)
layout.addWidget(w)
dlg.setLayout(layout)
dlg.exec_()
if __name__ == "__main__":
import sys
import numpy
import pyqtgraph as pg
from taurus.qt.qtgui.tpg import TaurusPlotDataItem
from taurus.qt.qtgui.application import TaurusApplication
app = TaurusApplication()
# a standard pyqtgraph plot_item
w = pg.PlotWidget()
# add legend to the plot, for that we have to give a name to plot items
w.addLegend()
# add a Y2 axis
from taurus.qt.qtgui.tpg import Y2ViewBox
y2ViewBox = Y2ViewBox()
y2ViewBox.attachToPlotItem(w.getPlotItem())
# adding a regular data item (non-taurus)
c1 = pg.PlotDataItem(
name="st plot",
pen=dict(color="y", width=3, style=QtCore.Qt.DashLine),
fillLevel=0.3,
fillBrush="g",
)
c1.setData(numpy.arange(300) / 300.0)
w.addItem(c1)
# adding a taurus data item
c2 = TaurusPlotDataItem(
name="st2 plot", pen="r", symbol="o", symbolSize=10
)
c2.setModel("sys/tg_test/1/wave")
w.addItem(c2)
# attach tool to plot item of the PlotWidget
tool = CurvesPropertiesTool()
tool.attachToPlotItem(w.getPlotItem(), y2=y2ViewBox)
w.show()
# directly trigger the tool
tool.trigger()
sys.exit(app.exec_())
taurus-pyqtgraph-0.3.1/taurus_pyqtgraph/datainspectortool.py 0000664 0000000 0000000 00000020733 13550077441 0024620 0 ustar 00root root 0000000 0000000 #!/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 .
##
#############################################################################
from datetime import datetime
import numpy
from taurus.external.qt import Qt
from taurus.qt.qtcore.configuration import BaseConfigurableClass
from taurus_pyqtgraph import DateAxisItem
from pyqtgraph import SignalProxy, InfiniteLine, TextItem, ScatterPlotItem
class DataInspectorLine(InfiniteLine):
"""
DataInspectorLine provides a moveable vertical line item that shows labels
containing the coordinates of the points of existing curves it touches.
It provides a method to attach it to a PlotItem.
.. todo:: for now it only works on the main viewbox.
"""
# TODO: support more than 1 viewbox (e.g. y2axis).
# TODO: modify anchor of labels so that they are plotted on the left if
# they do not fit in the view
def __init__(
self,
date_format="%Y-%m-%d %H:%M:%S",
y_format="%0.4f",
trigger_point_size=10,
):
super(DataInspectorLine, self).__init__(angle=90, movable=True)
self._labels = []
self._plot_item = None
self.y_format = y_format
self.trigger_point_size = trigger_point_size
self.date_format = date_format
self._label_style = "background-color: #35393C;"
self.sigPositionChanged.connect(self._inspect)
self._highlights = ScatterPlotItem(
pos=(),
symbol="s",
brush="35393C88",
pxMode=True,
size=trigger_point_size,
)
# hack to make the CurvesPropertiesTool ignore the highlight points
self._highlights._UImodifiable = False
def _inspect(self):
"""
Slot to re inspector line movemethe mouse move event, and perform
the action on the plot.
:param evt: mouse event
"""
xpos = self.pos().x()
x_px_size, _ = self.getViewBox().viewPixelSize()
self._removeLabels()
points = []
# iterate over the existing curves
for c in self._plot_item.curves:
if c is self._highlights:
continue
if c.xData is not None:
# find the index of the closest point of this curve
adiff = numpy.abs(c.xData - xpos)
idx = numpy.argmin(adiff)
# only add a label if the line touches the symbol
tolerance = 0.5 * max(1, c.opts["symbolSize"]) * x_px_size
if adiff[idx] < tolerance:
points.append((c.xData[idx], c.yData[idx]))
self._createLabels(points)
def _createLabels(self, points):
for x, y in points:
# create label at x,y
_x = self._getXValue(x)
_y = self._getYValue(y)
text_item = TextItem()
text_item.setPos(x, y)
text_item.setHtml(
(
" "
+ "x={} "
+ "y={} "
+ "
"
).format(self._label_style, _x, _y)
)
self._labels.append(text_item)
self._plot_item.addItem(text_item, ignoreBounds=True)
# Add "highlight" marker at each point
self._highlights.setData(pos=points)
def _getXValue(self, x):
"""
Helper method converting x value to time if necessary
:param x: current x value
:return: time or normal x value (depends of the x axis type)
"""
x_axis = self._plot_item.getAxis("bottom")
if isinstance(x_axis, DateAxisItem):
return self._timestampToDateTime(x)
else:
return x
def _getYValue(self, y):
return str(self.y_format % y)
def _timestampToDateTime(self, timestamp):
"""
Method used to caste the timestamp from the curve to date
in proper format (%Y-%m-%d %H:%M:%S)
:param timestamp: selected timestamp from curve
"""
return datetime.utcfromtimestamp(timestamp).strftime(self.date_format)
def _removeLabels(self):
# remove existing texts
for item in self._labels:
self.getViewBox().removeItem(item)
self._labels = []
# remove existing highlights
self._highlights.setData(pos=())
def attachToPlotItem(self, plot_item):
"""
Method to attach :class:`DataInspectorLine` to the plot
:param plot: to attach
"""
self._plot_item = plot_item
self._plot_item.addItem(self, ignoreBounds=True)
self._plot_item.addItem(self._highlights, ignoreBounds=True)
def dettach(self):
"""
Method use to detach the class:`DataInspectorLine` from the plot
"""
self._removeLabels()
self._plot_item.removeItem(self._highlights)
self._plot_item.removeItem(self)
self._plot_item = None
class DataInspectorTool(Qt.QWidgetAction, BaseConfigurableClass):
"""
This tool inserts an action in the menu of the :class:`pyqtgraph.PlotItem`
to which it is attached. When activated, the data inspection mode is
entered (a :class:`DataInspectorLine` is added and it follows the mouse,
allowing the user to inspect the coordinates of existing curves).
It is implemented as an Action, and provides a method to attach it to a
PlotItem.
"""
def __init__(self, parent=None):
BaseConfigurableClass.__init__(self)
Qt.QWidgetAction.__init__(self, parent)
self._cb = Qt.QCheckBox()
self._cb.setText("Data Inspector")
self._cb.toggled.connect(self._onToggled)
self.setDefaultWidget(self._cb)
self.plot_item = None
self.enable = False
self.data_inspector = DataInspectorLine()
self.registerConfigProperty(self.isChecked, self.setChecked, "checked")
def attachToPlotItem(self, plot_item):
"""
Use this method to add this tool to a plot
:param plot_item: (PlotItem)
:param y2: (Y2ViewBox) instance of the Y2Viewbox attached to plot_item
if the axis change controls are to be used
"""
self.plot_item = plot_item
menu = plot_item.getViewBox().menu
menu.addAction(self)
def _onToggled(self):
if not self.enable:
self.data_inspector.attachToPlotItem(self.plot_item)
# Signal Proxy which connect the movement of the mouse with
# the onMoved method in the data inspector object
self.proxy = SignalProxy(
self.plot_item.scene().sigMouseMoved,
rateLimit=60,
slot=self._followMouse,
)
self.enable = True
# auto-close the menu so that the user can start inspecting
self.plot_item.getViewBox().menu.close()
else:
self.proxy.disconnect()
self.data_inspector.dettach()
self.enable = False
def _followMouse(self, evt):
mouse_pos = evt[0]
inspector_x = self.plot_item.vb.mapSceneToView(mouse_pos).x()
self.data_inspector.setPos(inspector_x)
if __name__ == "__main__":
from taurus.qt.qtgui.application import TaurusApplication
import pyqtgraph as pg
import sys
app = TaurusApplication()
w = pg.PlotWidget(title="[hint: enable inspector mode from context menu]")
w.plot(y=numpy.arange(4), pen="r", symbol="o")
w.plot(y=numpy.random.rand(4), pen="c")
w.plot(
x=numpy.linspace(0, 3, 40),
y=1 + numpy.random.rand(40),
pen="w",
symbol="+",
)
t = DataInspectorTool(w)
t.attachToPlotItem(w.getPlotItem())
w.show()
sys.exit(app.exec_())
taurus-pyqtgraph-0.3.1/taurus_pyqtgraph/dateaxisitem.py 0000664 0000000 0000000 00000017541 13550077441 0023546 0 ustar 00root root 0000000 0000000 #!/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 .
##
#############################################################################
"""
This module provides date-time aware axis
"""
__all__ = ["DateAxisItem"]
# -------------------------------------------------------------------------
# There is a conflict problem with PyQt versions. Pyqtgraph imports his own
# library of PyQt, and Taurus too. So we have to import Qt from own version
# first as a workaround for forcing our own (as a workaround)
from taurus.external.qt import Qt # noqa
# -------------------------------------------------------------------------
import numpy
from pyqtgraph import AxisItem
from datetime import datetime, timedelta
from time import mktime
class DateAxisItem(AxisItem):
"""
A tool that provides a date-time aware axis. It is implemented as an
AxisItem that interpretes positions as unix timestamps (i.e. seconds
since 1970).
The labels and the tick positions are dynamically adjusted depending
on the range.
It provides a :meth:`attachToPlotItem` method to add it to a given
PlotItem
"""
# TODO: Document this class and methods
# Max width in pixels reserved for each label in axis
_pxLabelWidth = 80
def __init__(self, *args, **kwargs):
AxisItem.__init__(self, *args, **kwargs)
self._oldAxis = None
def tickValues(self, minVal, maxVal, size):
"""
Reimplemented from PlotItem to adjust to the range and to force
the ticks at "round" positions in the context of time units instead of
rounding in a decimal base
"""
maxMajSteps = int(size // self._pxLabelWidth)
dt1 = datetime.fromtimestamp(minVal)
dt2 = datetime.fromtimestamp(maxVal)
dx = maxVal - minVal
majticks = []
if dx > 63072001: # 3600s*24*(365+366) = 2 years (count leap year)
d = timedelta(days=366)
for y in range(dt1.year + 1, dt2.year):
dt = datetime(year=y, month=1, day=1)
majticks.append(mktime(dt.timetuple()))
elif dx > 5270400: # 3600s*24*61 = 61 days
d = timedelta(days=31)
dt = (
dt1.replace(day=1, hour=0, minute=0, second=0, microsecond=0)
+ d
)
while dt < dt2:
# make sure that we are on day 1 (even if always sum 31 days)
dt = dt.replace(day=1)
majticks.append(mktime(dt.timetuple()))
dt += d
elif dx > 172800: # 3600s24*2 = 2 days
d = timedelta(days=1)
dt = dt1.replace(hour=0, minute=0, second=0, microsecond=0) + d
while dt < dt2:
majticks.append(mktime(dt.timetuple()))
dt += d
elif dx > 7200: # 3600s*2 = 2hours
d = timedelta(hours=1)
dt = dt1.replace(minute=0, second=0, microsecond=0) + d
while dt < dt2:
majticks.append(mktime(dt.timetuple()))
dt += d
elif dx > 1200: # 60s*20 = 20 minutes
d = timedelta(minutes=10)
dt = (
dt1.replace(
minute=(dt1.minute // 10) * 10, second=0, microsecond=0
)
+ d
)
while dt < dt2:
majticks.append(mktime(dt.timetuple()))
dt += d
elif dx > 120: # 60s*2 = 2 minutes
d = timedelta(minutes=1)
dt = dt1.replace(second=0, microsecond=0) + d
while dt < dt2:
majticks.append(mktime(dt.timetuple()))
dt += d
elif dx > 20: # 20s
d = timedelta(seconds=10)
dt = dt1.replace(second=(dt1.second // 10) * 10, microsecond=0) + d
while dt < dt2:
majticks.append(mktime(dt.timetuple()))
dt += d
elif dx > 2: # 2s
d = timedelta(seconds=1)
majticks = list(range(int(minVal), int(maxVal)))
else: # <2s , use standard implementation from parent
return AxisItem.tickValues(self, minVal, maxVal, size)
# print("majticks >: ", majticks)
L = len(majticks)
if L > maxMajSteps:
if maxMajSteps == 0:
majticks = []
else:
majticks = majticks[:: int(numpy.ceil(float(L) / maxMajSteps))]
# print("majticks <: ", majticks)
# print "----------------------------"
return [(d.total_seconds(), majticks)]
def tickStrings(self, values, scale, spacing):
"""Reimplemented from PlotItem to adjust to the range"""
ret = []
if not values:
return []
# rng = max(values)-min(values)
# print('values: ', values)
# print('scale: ', scale)
# print('spacing: ', spacing)
if spacing >= 31622400: # = timedelta(days=366).total_seconds
fmt = "%Y"
elif spacing >= 2678400: # = timedelta(days=31).total_seconds
fmt = "%Y %b"
elif spacing >= 86400: # = timedelta(days = 1).total_seconds
fmt = "%b/%d"
elif spacing >= 3600: # = timedelta(hours=1).total_seconds
fmt = "%b/%d-%Hh"
elif spacing >= 600: # = timedelta(minutes=10).total_seconds
fmt = "%H:%M"
elif spacing >= 60: # = timedelta(minutes=1).total_seconds
fmt = "%H:%M"
elif spacing >= 10: # 10 s
fmt = "%H:%M:%S"
elif spacing >= 1: # 1s
fmt = "%H:%M:%S"
else:
# less than 2s (show microseconds)
# fmt = '%S.%f"'
fmt = "[+%fms]" # explicitly relative to last second
for x in values:
try:
t = datetime.fromtimestamp(x)
ret.append(t.strftime(fmt))
except ValueError: # Windows can't handle dates before 1970
ret.append("")
return ret
def attachToPlotItem(self, plotItem):
"""Add this axis to the given PlotItem
:param plotItem: (PlotItem)
"""
self.setParentItem(plotItem)
viewBox = plotItem.getViewBox()
self.linkToView(viewBox)
self._oldAxis = plotItem.axes[self.orientation]["item"]
self._oldAxis.hide()
plotItem.axes[self.orientation]["item"] = self
pos = plotItem.axes[self.orientation]["pos"]
plotItem.layout.addItem(self, *pos)
self.setZValue(-1000)
def detachFromPlotItem(self):
"""Remove this axis from its attached PlotItem
(not yet implemented)
"""
pass # TODO
if __name__ == "__main__":
import sys
import pyqtgraph as pg
from taurus.qt.qtgui.application import TaurusApplication
from taurus.qt.qtgui.tpg import TaurusPlotDataItem
app = TaurusApplication()
# a standard pyqtgraph plot_item
w = pg.PlotWidget()
axis = DateAxisItem(orientation="bottom")
axis.attachToPlotItem(w.getPlotItem())
# adding a taurus data item
c2 = TaurusPlotDataItem()
w.addItem(c2)
w.show()
sys.exit(app.exec_())
taurus-pyqtgraph-0.3.1/taurus_pyqtgraph/examples/ 0000775 0000000 0000000 00000000000 13550077441 0022321 5 ustar 00root root 0000000 0000000 taurus-pyqtgraph-0.3.1/taurus_pyqtgraph/examples/__init__.py 0000664 0000000 0000000 00000001763 13550077441 0024441 0 ustar 00root root 0000000 0000000 #!/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 .
##
#############################################################################
"""Examples / recipes for usage of taurus with pyqtgraph"""
taurus-pyqtgraph-0.3.1/taurus_pyqtgraph/examples/axislabels.py 0000664 0000000 0000000 00000002730 13550077441 0025024 0 ustar 00root root 0000000 0000000 #!/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 .
##
#############################################################################
"""
This is an example of how to assign specific labels to arbitrary positions
of a given axis using AxisItem.setTicks().
(Pure Qt)
"""
import sys
from pyqtgraph.Qt import QtGui
import pyqtgraph as pg
import numpy as np
if __name__ == "__main__":
app = QtGui.QApplication([])
w = pg.PlotWidget()
# define a list of position,label tuples
ticks = [list(zip((1, 2, 3, 7, 8), ("a", "b", "c", "d", "e")))]
xax = w.getAxis("bottom")
xax.setTicks(ticks)
w.plot(np.arange(9))
w.show()
sys.exit(app.exec_())
taurus-pyqtgraph-0.3.1/taurus_pyqtgraph/examples/legendExample.py 0000664 0000000 0000000 00000004266 13550077441 0025455 0 ustar 00root root 0000000 0000000 #!/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 .
##
#############################################################################
"""
Example on how to use a separate widget (LegendItem) for the legend of a plot.
(Pure Qt)
"""
import pyqtgraph as pg
from pyqtgraph.Qt import QtGui
import sys
if __name__ == "__main__":
app = QtGui.QApplication([])
# instantiate the main plot
plt = pg.PlotWidget()
plt.setWindowTitle("pyqtgraph example: PLOT")
# instantiate a graphics view to contain the legend
gv = QtGui.QGraphicsView(QtGui.QGraphicsScene())
gv.setWindowTitle("pyqtgraph example: Legend")
gv.setBackgroundBrush(QtGui.QBrush(QtGui.QColor("black")))
legend = pg.LegendItem(size=(100, 60), offset=(70, 30))
gv.scene().addItem(legend)
# create 3 curves
c1 = plt.plot(
[1, 3, 2, 4],
pen="r",
symbol="o",
symbolPen="r",
symbolBrush=0.5,
name="red plot",
)
c2 = plt.plot(
[2, 1, 4, 3],
pen="g",
fillLevel=0,
fillBrush=(255, 255, 255, 30),
name="green plot",
)
c3 = plt.plot(list(range(7)), pen="c", fillLevel=0)
# add the **named** curves to the legend
for dataitem in plt.getPlotItem().listDataItems():
if dataitem.name():
legend.addItem(dataitem, dataitem.name())
plt.show()
gv.show()
sys.exit(app.exec_())
taurus-pyqtgraph-0.3.1/taurus_pyqtgraph/examples/taurusplotdataitem.py 0000664 0000000 0000000 00000003410 13550077441 0026624 0 ustar 00root root 0000000 0000000 #!/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 .
##
#############################################################################
"""Example on using a tpg.TaurusPlotDataItem on a pure pyqtgraph plot"""
if __name__ == "__main__":
import sys
import numpy
from taurus.qt.qtgui.application import TaurusApplication
from taurus.qt.qtgui.tpg import TaurusPlotDataItem
import pyqtgraph as pg
app = TaurusApplication()
# a standard pyqtgraph plot_item
w = pg.PlotWidget()
# add legend to the plot, for that we have to give a name to plot items
w.addLegend()
# add a regular data item (non-taurus)
c1 = pg.PlotDataItem(name="pg item", pen="b", fillLevel=0, brush="c")
c1.setData(numpy.linspace(0, 2, 250))
w.addItem(c1)
# add a taurus data item
c2 = TaurusPlotDataItem(name="taurus item", pen="r", symbol="o")
c2.setModel('eval:Quantity(rand(256),"m")')
w.addItem(c2)
w.show()
sys.exit(app.exec_())
taurus-pyqtgraph-0.3.1/taurus_pyqtgraph/examples/taurustrendset.py 0000664 0000000 0000000 00000004033 13550077441 0025767 0 ustar 00root root 0000000 0000000 #!/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 .
##
#############################################################################
"""Example on using a tpg.TaurusTrendSet and some related tools
on a pure pyqtgraph plot"""
if __name__ == "__main__":
import sys
from taurus.qt.qtgui.application import TaurusApplication
from taurus.qt.qtgui.tpg import (
TaurusTrendSet,
DateAxisItem,
XAutoPanTool,
ForcedReadTool,
)
import pyqtgraph as pg
from taurus.core.taurusmanager import TaurusManager
taurusM = TaurusManager()
taurusM.changeDefaultPollingPeriod(1000) # ms
app = TaurusApplication()
# Add a date-time X axis
axis = DateAxisItem(orientation="bottom")
w = pg.PlotWidget()
axis.attachToPlotItem(w.getPlotItem())
# Add the auto-pan ("oscilloscope mode") tool
autopan = XAutoPanTool()
autopan.attachToPlotItem(w.getPlotItem())
# Add Forced-read tool
fr = ForcedReadTool(w, period=1000)
fr.attachToPlotItem(w.getPlotItem())
# add legend the legend tool
w.addLegend()
# adding a taurus data item...
c2 = TaurusTrendSet(name="foo")
c2.setModel("eval:rand(2)")
w.addItem(c2)
w.show()
sys.exit(app.exec_())
taurus-pyqtgraph-0.3.1/taurus_pyqtgraph/examples/y2axis.py 0000664 0000000 0000000 00000003652 13550077441 0024120 0 ustar 00root root 0000000 0000000 #!/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 .
##
#############################################################################
"""Example on using a tpg.Y2ViewBox to provide a secondary Y axis"""
from PyQt5 import Qt
import pyqtgraph as pg
import numpy
from taurus.qt.qtgui.tpg import Y2ViewBox, CurvesPropertiesTool
if __name__ == "__main__":
import sys
app = Qt.QApplication([])
w = pg.PlotWidget()
# add Y2 viewbox (provides a ViewBox associated to bottom & right axes)
y2 = Y2ViewBox()
y2.attachToPlotItem(w.getPlotItem())
# add a data item to Y1 (just as you would normally)
c1 = pg.PlotDataItem(name="c1", pen="c")
c1.setData(y=numpy.linspace(0, 20, 250))
w.addItem(c1)
# add a data item to Y2 (similar, but adding it to the secondary ViewBox!)
c2 = pg.PlotDataItem(name="c2", pen="y")
c2.setData(y=numpy.random.rand(250))
y2.addItem(c2) # <- note that it is y2, not w !
# (optional) add CurvesPropertiesTool to switch curves between Y1 and Y2
t = CurvesPropertiesTool()
t.attachToPlotItem(w.getPlotItem(), y2=y2)
w.show()
sys.exit(app.exec_())
taurus-pyqtgraph-0.3.1/taurus_pyqtgraph/forcedreadtool.py 0000664 0000000 0000000 00000014140 13550077441 0024051 0 ustar 00root root 0000000 0000000 #!/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 .
##
#############################################################################
__all__ = ["ForcedReadTool"]
from taurus.external.qt import QtGui, QtCore
from taurus.qt.qtcore.configuration.configuration import BaseConfigurableClass
class ForcedReadTool(QtGui.QWidgetAction, BaseConfigurableClass):
"""
This tool provides a menu option to control the "Forced Read" period of
Plot data items that implement a `setForcedReadPeriod` method
(see, e.g. :meth:`TaurusTrendSet.setForcedReadPeriod`).
The force-read feature consists on forcing periodic attribute reads for
those attributes being plotted with a :class:`TaurusTrendSet` object.
This allows to force plotting periodical updates even for attributes
for which the taurus polling is not enabled.
Note that this is done at the widget level and therefore does not affect
the rate of arrival of events for other widgets connected to the same
attributes
This tool inserts an action with a spinbox and emits a `valueChanged`
signal whenever the value is changed.
The connection between the data items and this tool can be done manually
(by connecting to the `valueChanged` signal or automatically, if
:meth:`autoconnect()` is `True` (default). The autoconnection feature works
by discovering the compliant data items that share associated to the
plot_item.
This tool is implemented as an Action, and provides a method to attach it
to a :class:`pyqtgraph.PlotItem`
"""
valueChanged = QtCore.pyqtSignal(int)
def __init__(
self, parent=None, period=0, text="Forced read", autoconnect=True
):
BaseConfigurableClass.__init__(self)
QtGui.QWidgetAction.__init__(self, parent)
# defining the widget
self._w = QtGui.QWidget()
self._w.setLayout(QtGui.QHBoxLayout())
tt = "Period between forced readings.\nSet to 0 to disable"
self._w.setToolTip(tt)
self._label = QtGui.QLabel(text)
self._w.layout().addWidget(self._label)
self._sb = QtGui.QSpinBox()
self._w.layout().addWidget(self._sb)
self._sb.setRange(0, 604800000)
self._sb.setValue(period)
self._sb.setSingleStep(500)
self._sb.setSuffix(" ms")
self._sb.setSpecialValueText("disabled")
self._autoconnect = autoconnect
self.setDefaultWidget(self._w)
# register config properties
self.registerConfigProperty(self.period, self.setPeriod, "period")
self.registerConfigProperty(
self.autoconnect, self.setAutoconnect, "autoconnect"
)
# internal conections
self._sb.valueChanged[int].connect(self._onValueChanged)
def _onValueChanged(self, period):
"""emit valueChanged and update all associated trendsets (if
self.autoconnect=True
"""
self.valueChanged.emit(period)
if self.autoconnect() and self.plot_item is not None:
for item in self.plot_item.listDataItems():
if hasattr(item, "setForcedReadPeriod"):
item.setForcedReadPeriod(period)
def attachToPlotItem(self, plot_item):
"""Use this method to add this tool to a plot
:param plot_item: (PlotItem)
"""
menu = plot_item.getViewBox().menu
menu.addAction(self)
self.plot_item = plot_item
# force an update of period for connected trendsets
self._onValueChanged(self.period())
def autoconnect(self):
"""Returns autoconnect state
:return: (bool)
"""
return self._autoconnect
def setAutoconnect(self, autoconnect):
"""Set autoconnect state. If True, the tool will autodetect trendsets
associated to the plot item and will call setForcedReadPeriod
on each of them for each change. If False, it will only emit a
valueChanged signal and only those connected to it will be notified
of changes
:param autoconnect: (bool)
"""
self._autoconnect = autoconnect
def period(self):
"""Returns the current period value (in ms)
:return: (int)
"""
return self._sb.value()
def setPeriod(self, value):
"""Change the period value. Use 0 for disabling
:param period: (int) period in ms
"""
self._sb.setValue(value)
if __name__ == "__main__":
import taurus
taurus.setLogLevel(taurus.Debug)
import sys
from taurus.qt.qtgui.application import TaurusApplication
from taurus.qt.qtgui.tpg import TaurusTrendSet, DateAxisItem
import pyqtgraph as pg
# from taurus.qt.qtgui.tpg import ForcedReadTool
app = TaurusApplication()
w = pg.PlotWidget()
axis = DateAxisItem(orientation="bottom")
w = pg.PlotWidget()
axis.attachToPlotItem(w.getPlotItem())
# test adding the curve before the tool
ts1 = TaurusTrendSet(name="before", symbol="o")
ts1.setModel("eval:rand()+1")
w.addItem(ts1)
fr = ForcedReadTool(w, period=1000)
fr.attachToPlotItem(w.getPlotItem())
# test adding the curve after the tool
ts2 = TaurusTrendSet(name="after", symbol="+")
ts2.setModel("eval:rand()")
w.addItem(ts2)
w.show()
ret = app.exec_()
import pprint
pprint.pprint(fr.createConfig())
sys.exit(ret)
taurus-pyqtgraph-0.3.1/taurus_pyqtgraph/legendtool.py 0000664 0000000 0000000 00000004553 13550077441 0023220 0 ustar 00root root 0000000 0000000 #!/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 .
##
#############################################################################
__all__ = ["PlotLegendTool"]
from taurus.external.qt import QtGui
from taurus.qt.qtcore.configuration.configuration import BaseConfigurableClass
class PlotLegendTool(QtGui.QWidgetAction, BaseConfigurableClass):
"""
This tool adds a legend to the PlotItem to which it is attached, and it
inserts a checkable menu action for showing/hiding the legend.
Implementation note: this is implemented as a QWidgetAction+QCheckBox
instead of a checkable QAction to avoid closing the menu when toggling it
"""
def __init__(self, parent=None):
BaseConfigurableClass.__init__(self)
QtGui.QWidgetAction.__init__(self, parent)
self._cb = QtGui.QCheckBox()
self._cb.setText("Show legend")
self.setDefaultWidget(self._cb)
self.registerConfigProperty(
self._cb.isChecked, self._cb.setChecked, "checked"
)
# TODO: register config prop for legend position
self._cb.toggled.connect(self._onToggled)
self._legend = None
def attachToPlotItem(self, plotItem):
"""
Use this method to add this tool to a plot
:param plot_item: (PlotItem)
"""
self._legend = plotItem.addLegend()
self._cb.setChecked(True)
menu = plotItem.getViewBox().menu
menu.addAction(self)
def _onToggled(self, checked):
if checked:
self._legend.show()
else:
self._legend.hide()
taurus-pyqtgraph-0.3.1/taurus_pyqtgraph/plot.py 0000664 0000000 0000000 00000024163 13550077441 0022041 0 ustar 00root root 0000000 0000000 #!/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 .
##
#############################################################################
from __future__ import absolute_import
__all__ = ["TaurusPlot"]
from future.utils import string_types
import copy
from taurus.external.qt import QtGui, Qt
from taurus.core.util.containers import LoopList
from taurus.qt.qtcore.configuration import BaseConfigurableClass
from pyqtgraph import PlotWidget
from .curvespropertiestool import CurvesPropertiesTool
from .taurusmodelchoosertool import TaurusXYModelChooserTool
from .legendtool import PlotLegendTool
from .datainspectortool import DataInspectorTool
from .taurusplotdataitem import TaurusPlotDataItem
from .y2axis import Y2ViewBox
CURVE_COLORS = [
Qt.QPen(Qt.Qt.red),
Qt.QPen(Qt.Qt.blue),
Qt.QPen(Qt.Qt.green),
Qt.QPen(Qt.Qt.magenta),
Qt.QPen(Qt.Qt.cyan),
Qt.QPen(Qt.Qt.yellow),
Qt.QPen(Qt.Qt.white),
]
class TaurusPlot(PlotWidget, BaseConfigurableClass):
"""
TaurusPlot is a general widget for plotting 1D data sets. It is an extended
taurus-aware version of :class:`pyqtgraph.PlotWidget`.
Apart from all the features already available in a regulat PlotWidget,
TaurusPlot incorporates the following tools/features:
- Secondary Y axis (right axis)
- A plot configuration dialog, and save/restore configuration
facilities
- A menu option for adding/removing models
- A menu option for showing/hiding the legend
- Automatic color change of curves for newly added models
"""
def __init__(self, parent=None, **kwargs):
if Qt.QT_VERSION < 0x050000:
# Workaround for issue when using super with pyqt<5
BaseConfigurableClass.__init__(self)
PlotWidget.__init__(self, parent=parent, **kwargs)
else:
super(TaurusPlot, self).__init__(parent=None, **kwargs)
# set up cyclic color generator
self._curveColors = LoopList(CURVE_COLORS)
self._curveColors.setCurrentIndex(-1)
# add save & retrieve configuration actions
menu = self.getPlotItem().getViewBox().menu
saveConfigAction = QtGui.QAction("Save configuration", menu)
saveConfigAction.triggered.connect(self.saveConfigFile)
menu.addAction(saveConfigAction)
loadConfigAction = QtGui.QAction("Retrieve saved configuration", menu)
loadConfigAction.triggered.connect(self.loadConfigFile)
menu.addAction(loadConfigAction)
self.registerConfigProperty(self._getState, self.restoreState, "state")
# add legend tool
legend_tool = PlotLegendTool(self)
legend_tool.attachToPlotItem(self.getPlotItem())
# add model chooser
self._model_chooser_tool = TaurusXYModelChooserTool(self)
self._model_chooser_tool.attachToPlotItem(
self.getPlotItem(), self, self._curveColors
)
# add Y2 axis
self._y2 = Y2ViewBox()
self._y2.attachToPlotItem(self.getPlotItem())
# add plot configuration dialog
cprop_tool = CurvesPropertiesTool(self)
cprop_tool.attachToPlotItem(self.getPlotItem(), y2=self._y2)
# add a data inspector
inspector_tool = DataInspectorTool(self)
inspector_tool.attachToPlotItem(self.getPlotItem())
# Register config properties
self.registerConfigDelegate(self._y2, "Y2Axis")
self.registerConfigDelegate(legend_tool, "legend")
self.registerConfigDelegate(inspector_tool, "inspector")
# --------------------------------------------------------------------
# workaround for bug in pyqtgraph v<=0.10.0, already fixed in
# https://github.com/pyqtgraph/pyqtgraph/commit/52754d4859
# TODO: remove this once pyqtgraph v>0.10 is released
def __getattr__(self, item):
try:
return PlotWidget.__getattr__(self, item)
except NameError:
raise AttributeError(
"{} has no attribute {}".format(self.__class__.__name__, item)
)
# --------------------------------------------------------------------
def setModel(self, names):
"""Reimplemented to delegate to the """
# support passing a string in names
if isinstance(names, string_types):
names = [names]
self._model_chooser_tool.updateModels(names)
def addModels(self, names):
"""Reimplemented to delegate to the """
# support passing a string in names
if isinstance(names, string_types):
names = [names]
self._model_chooser_tool.addModels(names)
def createConfig(self, allowUnpickable=False):
"""
Reimplemented from BaseConfigurableClass to manage the config
properties of the curves attached to this plot
"""
try:
# Temporarily register curves as delegates
tmpreg = []
curve_list = self.getPlotItem().listDataItems()
for idx, curve in enumerate(curve_list):
if isinstance(curve, TaurusPlotDataItem):
name = "__TaurusPlotDataItem_%d__" % idx
tmpreg.append(name)
self.registerConfigDelegate(curve, name)
configdict = copy.deepcopy(
BaseConfigurableClass.createConfig(
self, allowUnpickable=allowUnpickable
)
)
finally:
# Ensure that temporary delegates are unregistered
for n in tmpreg:
self.unregisterConfigurableItem(n, raiseOnError=False)
return configdict
def applyConfig(self, configdict, depth=None):
"""
Reimplemented from BaseConfigurableClass to manage the config
properties of the curves attached to this plot
"""
try:
# Temporarily register curves as delegates
tmpreg = []
curves = []
for name in configdict["__orderedConfigNames__"]:
if name.startswith("__TaurusPlotDataItem_"):
# Instantiate empty TaurusPlotDataItem
curve = TaurusPlotDataItem()
curves.append(curve)
self.registerConfigDelegate(curve, name)
tmpreg.append(name)
# remove the curves from the second axis (Y2) for avoid dups
self._y2.clearItems()
BaseConfigurableClass.applyConfig(
self, configdict=configdict, depth=depth
)
# keep a dict of existing curves (to use it for avoiding dups)
currentCurves = dict()
for curve in self.getPlotItem().listDataItems():
if isinstance(curve, TaurusPlotDataItem):
currentCurves[curve.getFullModelNames()] = curve
# remove curves that exists in currentCurves, also remove from
# the legend (avoid duplicates)
for curve in curves:
c = currentCurves.get(curve.getFullModelNames(), None)
if c is not None:
self.getPlotItem().legend.removeItem(c.name())
self.getPlotItem().removeItem(c)
# Add to plot **after** their configuration has been applied
for curve in curves:
# First we add all the curves in self. This way the plotItem
# can keeps a list of dataItems (plotItem.listDataItems())
self.addItem(curve)
# Add curves to Y2 axis, when the curve configurations
# have been applied.
# Ideally, the Y2ViewBox class must handle the action of adding
# curves to itself, but we want add the curves when they are
# restored with all their properties.
if curve.getFullModelNames() in self._y2.getCurves():
self.getPlotItem().getViewBox().removeItem(curve)
self._y2.addItem(curve)
finally:
# Ensure that temporary delegates are unregistered
for n in tmpreg:
self.unregisterConfigurableItem(n, raiseOnError=False)
def _getState(self):
"""Same as PlotWidget.saveState but removing viewRange conf to force
a refresh with targetRange when loading
"""
state = copy.deepcopy(self.saveState())
# remove viewRange conf
del state["view"]["viewRange"]
return state
def plot_main(
models=(),
config_file=None,
x_axis_mode="n",
demo=False,
window_name="TaurusPlot (pg)",
):
"""Launch a TaurusPlot"""
import sys
from taurus.qt.qtgui.application import TaurusApplication
app = TaurusApplication(cmd_line_parser=None, app_name="taurusplot(pg)")
w = TaurusPlot()
# w.loadConfigFile('tmp/TaurusPlot.pck')
w.setWindowTitle(window_name)
if demo:
models = list(models)
models.extend(["eval:rand(100)", "eval:0.5*sqrt(arange(100))"])
if x_axis_mode.lower() == "t":
from taurus.qt.qtgui.tpg import DateAxisItem
axis = DateAxisItem(orientation="bottom")
axis.attachToPlotItem(w.getPlotItem())
if config_file is not None:
w.loadConfigFile(config_file)
if models:
w.setModel(models)
w.show()
ret = app.exec_()
# import pprint
# pprint.pprint(w.createConfig())
sys.exit(ret)
if __name__ == "__main__":
plot_main()
taurus-pyqtgraph-0.3.1/taurus_pyqtgraph/taurusimageitem.py 0000664 0000000 0000000 00000004232 13550077441 0024263 0 ustar 00root root 0000000 0000000 #!/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 .
##
#############################################################################
__all__ = ["TaurusImageItem"]
import sys
from taurus.qt.qtgui.application import TaurusApplication
from taurus.qt.qtgui.base import TaurusBaseComponent
from pyqtgraph import ImageItem
class TaurusImageItem(ImageItem, TaurusBaseComponent):
"""
Displays 2D and 3D image data
"""
# TODO: clear image if .setModel(None)
def __init__(self, *args, **kwargs):
ImageItem.__init__(self, *args, **kwargs)
TaurusBaseComponent.__init__(self, "TaurusImageItem")
def handleEvent(self, evt_src, evt_type, evt_val):
try:
data = evt_val.rvalue
self.setImage(data)
except Exception as e:
self.warning("Exception in handleEvent: %s", e)
if __name__ == "__main__":
import pyqtgraph as pg
app = TaurusApplication()
plot_widget = pg.PlotWidget()
plot_item = plot_widget.getPlotItem()
image_item = TaurusImageItem()
# Add taurus 2D image data
image_item.setModel("eval:randint(0,256,(16,16))")
# add TarusImageItem to a PlotItem
plot_item.addItem(image_item)
# show or hide axis from the plot
plot_item.showAxis("left", show=True)
plot_item.showAxis("bottom", show=True)
plot_widget.show()
sys.exit(app.exec_())
taurus-pyqtgraph-0.3.1/taurus_pyqtgraph/taurusmodelchoosertool.py 0000664 0000000 0000000 00000043367 13550077441 0025717 0 ustar 00root root 0000000 0000000 #!/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 .
##
#############################################################################
__all__ = ["TaurusModelChooserTool", "TaurusImgModelChooserTool"]
from builtins import bytes
from future.utils import string_types
from taurus.external.qt import Qt
from taurus.core import TaurusElementType
from taurus.qt.qtgui.panel import TaurusModelChooser
from taurus_pyqtgraph.taurusimageitem import TaurusImageItem
from taurus_pyqtgraph.taurusplotdataitem import TaurusPlotDataItem
from taurus_pyqtgraph.curvesmodel import TaurusItemConf, TaurusItemConfDlg
import taurus
from collections import OrderedDict
from taurus.qt.qtcore.mimetypes import (
TAURUS_MODEL_LIST_MIME_TYPE,
TAURUS_ATTR_MIME_TYPE,
)
class TaurusModelChooserTool(Qt.QAction):
"""
This tool inserts an action in the menu of the :class:`pyqtgraph.PlotItem`
to which it is attached to show choosing taurus models to be shown.
It is implemented as an Action, and provides a method to attach it to a
PlotItem.
"""
def __init__(self, parent=None, itemClass=None):
Qt.QAction.__init__(self, "Model selection", parent)
self.triggered.connect(self._onTriggered)
self.plot_item = None
self.legend = None
if itemClass is None:
itemClass = TaurusPlotDataItem
self.itemClass = itemClass
def attachToPlotItem(self, plot_item, parentWidget=None):
"""
Use this method to add this tool to a plot
:param plot_item: (PlotItem)
"""
self.plot_item = plot_item
if self.plot_item.legend is not None:
self.legend = self.plot_item.legend
menu = self.plot_item.getViewBox().menu
menu.addAction(self)
self.setParent(parentWidget or menu)
def _onTriggered(self):
currentModelNames = []
for item in self.plot_item.items:
if isinstance(item, self.itemClass):
currentModelNames.append(item.getFullModelName())
names, ok = TaurusModelChooser.modelChooserDlg(
selectables=[TaurusElementType.Attribute],
listedModels=currentModelNames,
)
if ok:
self.updateModels(names)
def updateModels(self, names):
"""Accepts a list of model names and updates the data items of class
`itemClass` (provided in the constructor) attached to the plot.
It creates and removes items if needed, and enforces the z-order
according to that given in the `models` list
"""
# from names, construct an ordered dict with k=fullname, v=modelObj
models = OrderedDict()
for n in names:
m = taurus.Attribute(n)
models[m.getFullName()] = m
# construct a dict and a list for current models and names
currentModelItems = dict()
currentModelNames = []
for item in self.plot_item.items:
if isinstance(item, self.itemClass):
fullname = item.getFullModelName()
currentModelNames.append(fullname)
currentModelItems[fullname] = item
# remove existing curves from plot (but not discarding the object)
# so that they can be re-added later in the correct z-order
for k, v in currentModelItems.items():
# v.getViewBox().removeItem(v) # TODO: maybe needed for Y2
self.plot_item.removeItem(v)
# -------------------------------------------------
# Workaround for bug in pyqtgraph 0.10.0
# (which is fixed in pyqtgraph's commit ee0ea5669)
# TODO: remove this lines when pyqtgraph > 0.10.0 is released
if self.legend is not None:
self.legend.removeItem(v.name())
# -------------------------------------------------
# Add all curves (creating those that did not exist previously)
# respecting the z-order
for modelName, model in models.items():
if modelName in currentModelNames:
item = currentModelItems[modelName]
self.plot_item.addItem(item)
# item.getViewBox().addItem(item) # TODO: maybe needed for Y2
else:
# TODO support labels
item = self.itemClass(name=model.getSimpleName())
item.setModel(modelName)
self.plot_item.addItem(item)
# self.plot_item.enableAutoRange() # TODO: Why? remove?
def setParent(self, parent):
"""Reimplement setParent to add an event filter"""
Qt.QAction.setParent(self, parent)
if parent is not None:
parent.installEventFilter(self)
def _dropMimeData(self, data):
"""Method to process the dropped MimeData"""
ymodels = []
if data.hasFormat(TAURUS_ATTR_MIME_TYPE):
m = bytes(data.data(TAURUS_ATTR_MIME_TYPE)).decode("utf-8")
ymodels.append(m)
elif data.hasFormat(TAURUS_MODEL_LIST_MIME_TYPE):
ymodels = (
bytes(data.data(TAURUS_MODEL_LIST_MIME_TYPE))
.decode("utf-8")
.split()
)
elif data.hasText():
ymodels.append(data.text())
# Add models
current = []
for item in self.plot_item.items:
if isinstance(item, self.itemClass):
current.append(item.getFullModelName())
self.updateModels(current + ymodels)
return True
def eventFilter(self, source, event):
"""
Reimplementation of eventFilter to delegate parent's drag and drop
events to TaurusModelChooserTool
"""
if source is self.parent():
if event.type() == Qt.QEvent.DragEnter:
event.acceptProposedAction()
return True
if event.type() == Qt.QEvent.Drop:
event.acceptProposedAction()
return self._dropMimeData(event.mimeData())
return self.parent().eventFilter(source, event)
class TaurusImgModelChooserTool(Qt.QAction):
"""
This tool inserts an action in the menu of the :class:`pyqtgraph.PlotItem`
to which it is attached for choosing a 2D taurus model to be shown.
It is implemented as an Action, and provides a method to attach it to a
PlotItem.
"""
# TODO: merge this with TaurusModelChooserTool (or use a common base)
def __init__(self, parent=None):
Qt.QAction.__init__(self, parent)
self._plot_item = None
def attachToPlotItem(self, plot_item):
"""
Use this method to add this tool to a plot
:param plot_item: (PlotItem)
"""
self._plot_item = plot_item
view = plot_item.getViewBox()
menu = view.menu
model_chooser = Qt.QAction("Model selection", menu)
model_chooser.triggered.connect(self._onTriggered)
menu.addAction(model_chooser)
def _onTriggered(self):
imageItem = None
for item in self._plot_item.items:
if isinstance(item, TaurusImageItem):
imageItem = item
break
if imageItem is None:
imageItem = TaurusImageItem()
modelName = imageItem.getFullModelName()
if modelName is None:
listedModels = []
else:
listedModels = [modelName]
res, ok = TaurusModelChooser.modelChooserDlg(
selectables=[TaurusElementType.Attribute],
singleModel=True,
listedModels=listedModels,
)
if ok:
if res:
model = res[0]
else:
model = None
imageItem.setModel(model)
class TaurusXYModelChooserTool(Qt.QAction):
"""
(Work-in-Progress)
This tool inserts an action in the menu of the :class:`pyqtgraph.PlotItem`
to which it is attached for choosing X and Y 1D taurus models of the curves
to be shown.
It is implemented as an Action, and provides a method to attach it to a
PlotItem.
It only deals with the subgroup of plot data items of the type defined by
`self.ItemClass` (which defaults to :class:`TaurusPlotDataItem`)
"""
# TODO: This class is WIP.
def __init__(self, parent=None, itemClass=None):
Qt.QAction.__init__(self, "Model selection", parent)
self.triggered.connect(self._onTriggered)
self.plot_item = None
self.legend = None
self._curveColors = None
if itemClass is None:
itemClass = TaurusPlotDataItem
self.itemClass = itemClass
def setParent(self, parent):
"""Reimplement setParent to add an event filter"""
Qt.QAction.setParent(self, parent)
if parent is not None:
parent.installEventFilter(self)
def attachToPlotItem(
self, plot_item, parentWidget=None, curve_colors=None
):
"""
Use this method to add this tool to a plot
:param plot_item: (PlotItem)
.. warning:: this is Work-in-progress. The API may change.
Do not rely on current signature of this method
"""
# TODO: Check if we can simplify the signature (remove keyword args)
self.plot_item = plot_item
self._curveColors = curve_colors
if self.plot_item.legend is not None:
self.legend = self.plot_item.legend
menu = self.plot_item.getViewBox().menu
menu.addAction(self)
self.setParent(parentWidget or menu)
def _onTriggered(self):
oldconfs = self._getTaurusPlotDataItemConfigs().values()
newconfs, ok = TaurusItemConfDlg.showDlg(
parent=self.parent(), taurusItemConf=oldconfs
)
if ok:
xy_names = [(c.xModel, c.yModel) for c in newconfs]
self.updateModels(xy_names)
# TODO: apply configurations too
def _getTaurusPlotDataItemConfigs(self):
"""Get all the TaurusItemConf of the existing TaurusPlotDataItems
Returns an ordered dict whose keys are (xfullname, yfullname)
and whose values are the corresponding item config class
"""
itemconfigs = OrderedDict()
for curve in self.plot_item.listDataItems():
if isinstance(curve, self.itemClass):
xmodel, ymodel = curve.getFullModelNames()
c = TaurusItemConf(
YModel=ymodel, XModel=xmodel, name=curve.name()
)
itemconfigs[(xmodel, ymodel)] = c
return itemconfigs
def _dropMimeData(self, data):
"""Method to process the dropped MimeData"""
ymodels = []
if data.hasFormat(TAURUS_ATTR_MIME_TYPE):
m = bytes(data.data(TAURUS_ATTR_MIME_TYPE)).decode("utf-8")
ymodels.append(m)
elif data.hasFormat(TAURUS_MODEL_LIST_MIME_TYPE):
ymodels = (
bytes(data.data(TAURUS_MODEL_LIST_MIME_TYPE))
.decode("utf-8")
.split()
)
elif data.hasText():
ymodels.append(data.text())
xmodels = [None] * len(ymodels)
self.addModels(list(zip(xmodels, ymodels)))
return True
def eventFilter(self, source, event):
"""
Reimplementation of eventFilter to delegate parent's drag and drop
events to TaurusXYModelChooserTool
"""
if source is self.parent():
if event.type() == Qt.QEvent.DragEnter:
event.acceptProposedAction()
return True
if event.type() == Qt.QEvent.Drop:
event.acceptProposedAction()
return self._dropMimeData(event.mimeData())
return self.parent().eventFilter(source, event)
def getModelNames(self):
"""
Get the x and y model names for the data items of type
defined by `self.itemClass` present in the plot item to which
this tool is attached
"""
return list(self._getTaurusPlotDataItemConfigs().keys())
def addModels(self, xy_names):
"""Add new items with the given x and y model pairs. Those given model
pairs that are already present will not be altered or duplicated (e.g.
the z-order of the corresponding curve will not be modified for in case
of adding duplicates)
"""
current = self.getModelNames()
self.updateModels(current + xy_names)
def updateModels(self, xy_names):
"""
Update the current plot item list with the given configuration items
"""
mainViewBox = self.plot_item.getViewBox()
# Remove existing taurus curves from the plot (but keep the item object
# and a reference to their viewbox so that they can be readded
# later on if needed.
currentModelItems = OrderedDict()
_currentCurves = list(self.plot_item.listDataItems())
for curve in _currentCurves:
if isinstance(curve, self.itemClass):
xname, yname = curve.getFullModelNames()
viewbox = curve.getViewBox()
# store curve and current viewbox for later use
currentModelItems[(xname, yname)] = curve, viewbox
# remove the curve
self.plot_item.removeItem(curve)
# if viewbox is not mainViewBox: # TODO: do we need this?
# viewbox.removeItem(curve)
if self.legend is not None:
self.legend.removeItem(curve.name())
# Add only the curves defined in xy_names (reusing existing ones and
# creating those that did not exist) in the desired z-order
_already_added = []
for xy_name in xy_names:
# each member of xy_names can be yname or a (xname, yname) tuple
if isinstance(xy_name, string_types):
xname, yname = None, xy_name
else:
xname, yname = xy_name
# make sure that fullnames are used
try:
if xname is not None:
xmodel = taurus.Attribute(xname)
xname = xmodel.getFullName()
ymodel = taurus.Attribute(yname)
yname = ymodel.getFullName()
except Exception as e:
from taurus import warning
warning("Problem adding %r: %r", (xname, yname), e)
continue
# do not add it again if we already added it (avoid duplications)
if (xname, yname) in _already_added:
continue
_already_added.append((xname, yname))
# if the item already existed, re-use it
if (xname, yname) in currentModelItems:
item, viewbox = currentModelItems[(xname, yname)]
self.plot_item.addItem(item)
if viewbox is not mainViewBox:
# if the curve was originally associated to a viewbox
# other than the main one, we should move it there after
# re-adding it to the plot_item
mainViewBox.removeItem(item)
viewbox.addItem(item)
# if it is a new curve, create it and add it to the main plot_item
else:
item = self.itemClass(
xModel=xname, yModel=yname, name=ymodel.getSimpleName()
)
if self._curveColors is not None:
item.setPen(self._curveColors.next().color())
self.plot_item.addItem(item)
def _demo_ModelChooser():
import sys
import numpy
import pyqtgraph as pg
from taurus.qt.qtgui.tpg import TaurusModelChooserTool
from taurus.qt.qtgui.application import TaurusApplication
from taurus.qt.qtgui.tpg import TaurusPlotDataItem
app = TaurusApplication()
# a standard pyqtgraph plot_item
w = pg.PlotWidget()
# add legend to the plot, for that we have to give a name to plot items
w.addLegend()
# adding a regular data item (non-taurus)
c1 = pg.PlotDataItem(name="st plot", pen="b", fillLevel=0, brush="c")
c1.setData(numpy.arange(300) / 300.0)
w.addItem(c1)
# adding a taurus data item
c2 = TaurusPlotDataItem(name="st2 plot", pen="r", symbol="o")
c2.setModel("eval:rand(222)")
w.addItem(c2)
# attach to plot item
tool = TaurusModelChooserTool(itemClass=TaurusPlotDataItem)
tool.attachToPlotItem(w.getPlotItem())
w.show()
tool.trigger()
sys.exit(app.exec_())
def _demo_ModelChooserImage():
import sys
from taurus.qt.qtgui.tpg import TaurusImgModelChooserTool, TaurusImageItem
from taurus.qt.qtgui.application import TaurusApplication
import pyqtgraph as pg
app = TaurusApplication()
w = pg.PlotWidget()
img = TaurusImageItem()
# Add taurus 2D image data
img.setModel("eval:rand(256,256)")
w.addItem(img)
w.showAxis("left", show=False)
w.showAxis("bottom", show=False)
tool = TaurusImgModelChooserTool()
tool.attachToPlotItem(w.getPlotItem())
w.show()
tool.trigger()
sys.exit(app.exec_())
if __name__ == "__main__":
_demo_ModelChooser()
# _demo_ModelChooserImage()
taurus-pyqtgraph-0.3.1/taurus_pyqtgraph/taurusplotdataitem.py 0000664 0000000 0000000 00000013203 13550077441 0025007 0 ustar 00root root 0000000 0000000 #!/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 .
##
#############################################################################
__all__ = ["TaurusPlotDataItem"]
import copy
from taurus import Attribute
from taurus.core import TaurusEventType
from taurus.qt.qtgui.base import TaurusBaseComponent
from pyqtgraph import PlotDataItem
class TaurusPlotDataItem(PlotDataItem, TaurusBaseComponent):
"""A taurus-ified PlotDataItem"""
def __init__(self, *args, **kwargs):
"""
Accepts same args and kwargs as PlotDataItem, plus:
:param xModel: (str) Taurus model name for abscissas values.
Default=None
:param yModel: (str) Taurus model name for ordinate values.
Default=None
"""
xModel = kwargs.pop("xModel", None)
yModel = kwargs.pop("yModel", None)
PlotDataItem.__init__(self, *args, **kwargs)
TaurusBaseComponent.__init__(self, "TaurusBaseComponent")
self._x = None
self._y = None
self.xModel = None
if xModel is not None:
self.setXModel(xModel)
if yModel is not None:
self.setModel(yModel)
self.registerConfigProperty(self.getOpts, self.setOpts, "opts")
self.setModelInConfig(True)
self.registerConfigProperty(
self.getXModelName, self.setXModel, "XModel"
)
def setXModel(self, xModel):
if self.xModel is not None:
self.xModel.removeListener(self)
if not xModel:
self.xModel = None
return
self.xModel = Attribute(xModel)
self.xModel.addListener(self)
def getXModelName(self):
if self.xModel is None:
return None
else:
return self.xModel.getFullName()
def handleEvent(self, evt_src, evt_type, evt_value):
if evt_type not in (TaurusEventType.Change, TaurusEventType.Periodic):
return
yModel = self.getModelObj()
if yModel == evt_src:
if yModel is not None:
self._y = evt_value.rvalue
else:
self._y = None
if self.xModel == evt_src:
if self.xModel is not None:
self._x = evt_value.rvalue
else:
self._x = None
try:
self.setData(x=self._x, y=self._y)
except Exception as e:
self.debug("Could not set data. Reason: %r", e)
def getOpts(self):
from taurus.qt.qtgui.tpg import serialize_opts
return serialize_opts(copy.copy(self.opts))
def setOpts(self, opts):
# creates QPainters (QPen or QBrush) from a pickle loaded file
# for adapt the serialized objects into PlotDataItem properties
from taurus.qt.qtgui.tpg import deserialize_opts
self.opts = deserialize_opts(opts)
# This is a workaround for the following pyqtgraph's bug:
# https://github.com/pyqtgraph/pyqtgraph/issues/531
if opts["connect"] == "all":
self.opts["connect"] = "all"
elif opts["connect"] == "pairs":
self.opts["connect"] = "pairs"
elif opts["connect"] == "finite":
self.opts["connect"] = "finite"
def getFullModelNames(self):
return (self.getXModelName(), self.getFullModelName())
if __name__ == "__main__":
import sys
import numpy
import pyqtgraph as pg
from taurus.qt.qtgui.application import TaurusApplication
# from taurus.qt.qtgui.tpg import TaurusPlotDataItem
from taurus.external.qt import Qt
from taurus.qt.qtgui.tpg import TaurusModelChooserTool
app = TaurusApplication()
# a standard pyqtgraph plot_item
w = pg.PlotWidget()
# add legend to the plot, for that we have to give a name to plot items
w.addLegend()
# adding a regular data item (non-taurus)
c1 = pg.PlotDataItem(name="st plot", pen="b", fillLevel=0, brush="c")
c1.setData(numpy.arange(300) / 300.0)
w.addItem(c1)
pen = pg.mkPen(color="r", style=4)
brush = pg.mkBrush(color="b")
brush.setStyle(3)
# adding a taurus data item
# c2 = TaurusPlotDataItem(name='st2 plot', pen='r', symbol='o')
c2 = TaurusPlotDataItem(pen=pen, name="foo")
# c2 = TaurusPlotDataItem()
# c2.loadConfigFile('tmp/conf.cfg')
c2.setModel('eval:Quantity(rand(256),"m")')
# c2.setModel('sys/tg_test/1/wave')
# c2.setModel(None)
# c2.setXModel(None)
# c2.setXModel('eval:Quantity(rand(256),"m")')
w.addItem(c2)
# ...and remove it after a while
def rem():
w.removeItem(c2)
Qt.QTimer.singleShot(2000, rem)
modelchooser = TaurusModelChooserTool(itemClass=TaurusPlotDataItem)
modelchooser.attachToPlotItem(w.getPlotItem())
w.show()
res = app.exec_()
# config = c2.createConfig()
# print config
# c2.saveConfigFile('tmp/conf.cfg')
sys.exit(res)
taurus-pyqtgraph-0.3.1/taurus_pyqtgraph/taurustrendset.py 0000664 0000000 0000000 00000035360 13550077441 0024160 0 ustar 00root root 0000000 0000000 #!/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 .
##
#############################################################################
__all__ = ["TaurusTrendSet"]
"""This provides the pyqtgraph implementation of :class:`TaurusTrendSet`"""
import copy
import numpy
from taurus.core import TaurusEventType, TaurusTimeVal
from taurus.qt.qtgui.base import TaurusBaseComponent
from taurus.core.util.containers import ArrayBuffer, LoopList
from taurus.external.qt import Qt
from pyqtgraph import PlotDataItem
from taurus_pyqtgraph.forcedreadtool import ForcedReadTool
import taurus
CURVE_COLORS = [
Qt.QPen(Qt.Qt.red),
Qt.QPen(Qt.Qt.blue),
Qt.QPen(Qt.Qt.green),
Qt.QPen(Qt.Qt.magenta),
Qt.QPen(Qt.Qt.cyan),
Qt.QPen(Qt.Qt.yellow),
Qt.QPen(Qt.Qt.white),
]
class TaurusTrendSet(PlotDataItem, TaurusBaseComponent):
"""
A PlotDataItem for displaying trend curve(s) associated to a
TaurusAttribute. The TaurusTrendSet itself does not contain any data,
but acts as a manager that dynamically adds/removes curve(s) (other
PlotDataItems) to its associated plot.
If the attribute is a scalar, the Trend Set generates only one curve
representing the evolution of the value of the attribute. If the attribute
is an array, as many curves as the attribute size are created,
each representing the evolution of the value of a component of the array.
When an event is received, all curves belonging to a TaurusTrendSet
are updated.
TaurusTrendSet can be considered used as a container of (sorted) curves.
As such, the curves contained by it can be accessed by index::
ts = TaurusTrendSet('eval:rand(3)')
# (...) wait for a Taurus Event arriving so that the curves are created
ncurves = len(ts) # ncurves will be 3 (assuming the event arrived)
curve0 = ts[0] # you can access the curve by index
Note that internally each curve is a :class:`pyqtgraph.PlotDataItem` (i.e.,
it is not aware of events by itself, but it relies on the TaurusTrendSet
object to update its values)
"""
def __init__(self, *args, **kwargs):
PlotDataItem.__init__(self, *args, **kwargs)
TaurusBaseComponent.__init__(self, "TaurusBaseComponent")
self._UImodifiable = False
self._maxBufferSize = 65536 # (=2**16, i.e., 64K events))
self._xBuffer = None
self._yBuffer = None
self._curveColors = LoopList(CURVE_COLORS)
self._args = args
self._kwargs = kwargs
self._curves = []
self._timer = Qt.QTimer()
self._timer.timeout.connect(self._forceRead)
self._legend = None
# register config properties
self.setModelInConfig(True)
self.registerConfigProperty(
self._getCurvesOpts, self._setCurvesOpts, "opts"
)
# TODO: store forceReadPeriod config
# TODO: store _maxBufferSize config
def name(self):
"""Reimplemented from PlotDataItem to avoid having the ts itself added
to legends.
.. seealso:: :meth:`basename`
"""
return None
def base_name(self):
"""Returns the name of the trendset, which is used as a prefix for
constructing the associated curves names
.. seealso:: :meth:`name`
"""
return PlotDataItem.name(self)
def __getitem__(self, k):
return self._curves[k]
def __len__(self):
return len(self._curves)
def __contains__(self, k):
return k in self._curves
def setModel(self, name):
"""Reimplemented from :meth:`TaurusBaseComponent.setModel`"""
TaurusBaseComponent.setModel(self, name)
# force a read to ensure that the curves are created
self._forceRead()
def _initBuffers(self, ntrends):
"""initializes new x and y buffers"""
self._yBuffer = ArrayBuffer(
numpy.zeros((min(128, self._maxBufferSize), ntrends), dtype="d"),
maxSize=self._maxBufferSize,
)
self._xBuffer = ArrayBuffer(
(numpy.zeros(min(128, self._maxBufferSize), dtype="d")),
maxSize=self._maxBufferSize,
)
def _initCurves(self, ntrends):
""" Initializes new curves """
# self._removeFromLegend(self._legend)
self._curves = []
self._curveColors.setCurrentIndex(-1)
a = self._args
kw = self._kwargs.copy()
base_name = (
self.base_name()
or taurus.Attribute(self.getModel()).getSimpleName()
)
for i in range(ntrends):
subname = "%s[%i]" % (base_name, i)
kw["name"] = subname
curve = PlotDataItem(*a, **kw)
if "pen" not in kw:
curve.setPen(self._curveColors.next().color())
self._curves.append(curve)
self._updateViewBox()
def _addToLegend(self, legend):
# ------------------------------------------------------------------
# In theory, TaurusTrendSet only uses viewBox.addItem to add its
# sub-curves to the plot. In theory this should not add the curves
# to the legend, and therefore we should do it here.
# But somewhere the curves are already being added to the legend, and
# if we re-add them here we get duplicated legend entries
# TODO: Find where are the curves being added to the legend
pass
# if legend is None:
# return
# for c in self._curves:
# legend.addItem(c, c.name())
# -------------------------------------------------------------------
def _removeFromLegend(self, legend):
if legend is None:
return
for c in self._curves:
legend.removeItem(c.name())
def _updateViewBox(self):
"""Add/remove the "extra" curves from the viewbox if needed"""
if self._curves:
viewBox = self.getViewBox()
self.forgetViewBox()
for curve in self._curves:
curve.forgetViewBox()
curve_viewBox = curve.getViewBox()
if curve_viewBox is not None:
curve_viewBox.removeItem(curve)
if viewBox is not None:
viewBox.addItem(curve)
def _updateBuffers(self, evt_value):
"""Update the x and y buffers with the new data. If the new data is
not compatible with the existing buffers, the buffers are reset
"""
# TODO: we use .magnitude below to avoid issue #509 in pint
# https://github.com/hgrecco/pint/issues/509
ntrends = numpy.size(evt_value.rvalue.magnitude)
if not self._isDataCompatible(evt_value, ntrends):
self._initBuffers(ntrends)
self._yUnits = evt_value.rvalue.units
self._initCurves(ntrends)
try:
self._yBuffer.append(evt_value.rvalue.to(self._yUnits).magnitude)
except Exception as e:
self.warning(
"Problem updating buffer Y (%s):%s", evt_value.rvalue, e
)
evt_value = None
try:
self._xBuffer.append(evt_value.time.totime())
except Exception as e:
self.warning("Problem updating buffer X (%s):%s", evt_value, e)
return self._xBuffer.contents(), self._yBuffer.contents()
def _isDataCompatible(self, evt_value, ntrends):
"""
Check that the new evt_value is compatible with the current data in the
buffers. Check shape and unit compatibility.
"""
if self._xBuffer is None or self._yBuffer is None:
return False
rvalue = evt_value.rvalue
if rvalue.dimensionality != self._yUnits.dimensionality:
return False
current_trends = numpy.prod(self._yBuffer.contents().shape[1:])
if ntrends != current_trends:
return False
return True
def _addData(self, x, y):
for i, curve in enumerate(self._curves):
curve.setData(x=x, y=y[:, i])
def handleEvent(self, evt_src, evt_type, evt_value):
"""Reimplementation of :meth:`TaurusBaseComponent.handleEvent`"""
# model = evt_src if evt_src is not None else self.getModelObj()
# TODO: support boolean values from evt_value.rvalue
if evt_value is None or evt_value.rvalue is None:
self.info("Invalid value. Ignoring.")
return
else:
try:
xValues, yValues = self._updateBuffers(evt_value)
except Exception:
# TODO: handle dropped events see: TaurusTrend._onDroppedEvent
raise
self._addData(xValues, yValues)
def parentChanged(self):
"""Reimplementation of :meth:`PlotDataItem.parentChanged` to handle
the change of the containing viewbox
"""
PlotDataItem.parentChanged(self)
self._updateViewBox()
# update legend if needed
try:
legend = self.getViewWidget().getPlotItem().legend
except Exception:
legend = None
if legend is not self._legend:
self._removeFromLegend(self._legend)
self._addToLegend(legend)
self._legend = legend
# Set period from ForcedReadTool (if found)
try:
for a in self.getViewBox().menu.actions():
if isinstance(a, ForcedReadTool) and a.autoconnect():
self.setForcedReadPeriod(a.period())
break
except Exception as e:
self.debug("cannot set period from ForcedReadTool: %r", e)
@property
def forcedReadPeriod(self):
"""Returns the forced reading period (in ms). A value <= 0 indicates
that the forced reading is disabled
"""
return self._timer.interval()
def setForcedReadPeriod(self, period):
"""
Forces periodic reading of the subscribed attribute in order to show
new points even if no events are received.
It will create fake events as needed with the read value.
It will also block the plotting of regular events when period > 0.
:param period: (int) period in milliseconds. Use period<=0 to stop the
forced periodic reading
"""
# stop the timer and remove the __ONLY_OWN_EVENTS filter
self._timer.stop()
filters = self.getEventFilters()
if self.__ONLY_OWN_EVENTS in filters:
filters.remove(self.__ONLY_OWN_EVENTS)
self.setEventFilters(filters)
# if period is positive, set the filter and start
if period > 0:
self.insertEventFilter(self.__ONLY_OWN_EVENTS)
self._timer.start(period)
def _forceRead(self, cache=False):
"""Forces a read of the associated attribute.
:param cache: (bool) If True, the reading will be done with cache=True
but the timestamp of the resulting event will be replaced
by the current time. If False, no cache will be used at
all.
"""
value = self.getModelValueObj(cache=cache)
if cache and value is not None:
value = copy.copy(value)
value.time = TaurusTimeVal.now()
self.fireEvent(self, TaurusEventType.Periodic, value)
def __ONLY_OWN_EVENTS(self, s, t, v):
"""An event filter that rejects all events except those that originate
from this object
"""
if s is self:
return s, t, v
else:
return None
def _getCurvesOpts(self):
"""returns a list of serialized opts (one for each curve)"""
from taurus.qt.qtgui.tpg import serialize_opts
return [serialize_opts(copy.copy(c.opts)) for c in self._curves]
def _setCurvesOpts(self, all_opts):
"""restore options to curves"""
# If no curves are yet created, force a read to create them
if not self._curves:
self._forceRead(cache=True)
# Check consistency in the number of curves
if len(self._curves) != len(all_opts):
self.warning(
"Cannot apply curve options (mismatch in curves number)"
)
return
from taurus.qt.qtgui.tpg import deserialize_opts
for c, opts in zip(self._curves, all_opts):
c.opts = deserialize_opts(opts)
# This is a workaround for the following pyqtgraph's bug:
# https://github.com/pyqtgraph/pyqtgraph/issues/531
if opts["connect"] == "all":
c.opts["connect"] = "all"
elif opts["connect"] == "pairs":
c.opts["connect"] = "pairs"
elif opts["connect"] == "finite":
c.opts["connect"] = "finite"
if __name__ == "__main__":
import sys
import pyqtgraph as pg
from taurus.qt.qtgui.application import TaurusApplication
from taurus.qt.qtgui.tpg import (
# TaurusTrendSet,
DateAxisItem,
XAutoPanTool,
# TaurusModelChooserTool,
CurvesPropertiesTool,
)
from taurus.core.taurusmanager import TaurusManager
taurusM = TaurusManager()
taurusM.changeDefaultPollingPeriod(1000) # ms
app = TaurusApplication()
# a standard pyqtgraph plot_item
axis = DateAxisItem(orientation="bottom")
w = pg.PlotWidget()
axis.attachToPlotItem(w.getPlotItem())
cp = CurvesPropertiesTool()
cp.attachToPlotItem(w.getPlotItem())
autopan = XAutoPanTool()
autopan.attachToPlotItem(w.getPlotItem())
# add legend to the plot, for that we have to give a name to plot items
w.addLegend()
# adding a taurus data item...
c2 = TaurusTrendSet(name="foo")
c2.setModel("eval:rand(2)")
# c2.setForcedReadPeriod(500)
w.addItem(c2)
# ...and remove it after a while
def rem():
w.removeItem(c2)
Qt.QTimer.singleShot(2000, rem)
# modelchooser = TaurusModelChooserTool(itemClass=TaurusTrendSet)
# modelchooser.attachToPlotItem(w.getPlotItem())
w.show()
ret = app.exec_()
# import pprint
# pprint.pprint(c2.createConfig())
sys.exit(ret)
taurus-pyqtgraph-0.3.1/taurus_pyqtgraph/trend.py 0000664 0000000 0000000 00000024645 13550077441 0022204 0 ustar 00root root 0000000 0000000 #!/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 .
##
#############################################################################
from __future__ import absolute_import
__all__ = ["TaurusTrend"]
from future.utils import string_types
import copy
from taurus.external.qt import QtGui, Qt
from taurus.core.util.containers import LoopList
from taurus.qt.qtcore.configuration import BaseConfigurableClass
from pyqtgraph import PlotWidget
from .curvespropertiestool import CurvesPropertiesTool
from .dateaxisitem import DateAxisItem
from .legendtool import PlotLegendTool
from .forcedreadtool import ForcedReadTool
from .datainspectortool import DataInspectorTool
from .taurusmodelchoosertool import TaurusModelChooserTool
from .taurustrendset import TaurusTrendSet
from .y2axis import Y2ViewBox
from .autopantool import XAutoPanTool
CURVE_COLORS = [
Qt.QPen(Qt.Qt.red),
Qt.QPen(Qt.Qt.blue),
Qt.QPen(Qt.Qt.green),
Qt.QPen(Qt.Qt.magenta),
Qt.QPen(Qt.Qt.cyan),
Qt.QPen(Qt.Qt.yellow),
Qt.QPen(Qt.Qt.white),
]
class TaurusTrend(PlotWidget, BaseConfigurableClass):
"""
TaurusTrend is a general widget for plotting the evolution of a value
over time. It is an extended taurus-aware version of
:class:`pyqtgraph.PlotWidget`.
Apart from all the features already available in a regulat PlotWidget,
TaurusTrend incorporates the following tools/features:
- Secondary Y axis (right axis)
- Time X axis
- A plot configuration dialog, and save/restore configuration
facilities
- A menu option for adding/removing taurus models
- A menu option for showing/hiding the legend
- Automatic color change of curves for newly added models
"""
def __init__(self, parent=None, **kwargs):
if Qt.QT_VERSION < 0x050000:
# Workaround for issue when using super with pyqt<5
BaseConfigurableClass.__init__(self)
PlotWidget.__init__(self, parent=parent, **kwargs)
else:
super(TaurusTrend, self).__init__(parent=parent, **kwargs)
# set up cyclic color generator
self._curveColors = LoopList(CURVE_COLORS)
self._curveColors.setCurrentIndex(-1)
plot_item = self.getPlotItem()
menu = plot_item.getViewBox().menu
# add save & retrieve configuration actions
saveConfigAction = QtGui.QAction("Save configuration", menu)
saveConfigAction.triggered.connect(self.saveConfigFile)
menu.addAction(saveConfigAction)
loadConfigAction = QtGui.QAction("Retrieve saved configuration", menu)
loadConfigAction.triggered.connect(self.loadConfigFile)
menu.addAction(loadConfigAction)
self.registerConfigProperty(self._getState, self.restoreState, "state")
# add legend tool
legend_tool = PlotLegendTool(self)
legend_tool.attachToPlotItem(plot_item)
# add model chooser
self._model_chooser_tool = TaurusModelChooserTool(
self, itemClass=TaurusTrendSet
)
self._model_chooser_tool.attachToPlotItem(plot_item, self)
# add Y2 axis
self._y2 = Y2ViewBox()
self._y2.attachToPlotItem(plot_item)
# Add time X axis
axis = DateAxisItem(orientation="bottom")
axis.attachToPlotItem(plot_item)
# add plot configuration dialog
cprop_tool = CurvesPropertiesTool(self)
cprop_tool.attachToPlotItem(plot_item, y2=self._y2)
# add data inspector widget
inspector_tool = DataInspectorTool(self)
inspector_tool.attachToPlotItem(self.getPlotItem())
# add force read tool
fr_tool = ForcedReadTool(self)
fr_tool.attachToPlotItem(self.getPlotItem())
# Add the auto-pan ("oscilloscope mode") tool
autopan = XAutoPanTool()
autopan.attachToPlotItem(self.getPlotItem())
# Register config properties
self.registerConfigDelegate(self._y2, "Y2Axis")
self.registerConfigDelegate(legend_tool, "legend")
self.registerConfigDelegate(fr_tool, "forceread")
self.registerConfigDelegate(inspector_tool, "inspector")
# --------------------------------------------------------------------
# workaround for bug in pyqtgraph v<=0.10.0, already fixed in
# https://github.com/pyqtgraph/pyqtgraph/commit/52754d4859
# TODO: remove this once pyqtgraph v>0.10 is released
def __getattr__(self, item):
try:
return PlotWidget.__getattr__(self, item)
except NameError:
raise AttributeError(
"{} has no attribute {}".format(self.__class__.__name__, item)
)
# --------------------------------------------------------------------
def setModel(self, names):
"""Set a list of models"""
# support passing a string in names instead of a sequence
if isinstance(names, string_types):
names = [names]
self._model_chooser_tool.updateModels(names or [])
def createConfig(self, allowUnpickable=False):
"""
Reimplemented from BaseConfigurableClass to manage the config
properties of the trendsets attached to this plot
"""
try:
# Temporarily register trendsets as delegates
tmpreg = []
data_items = self.getPlotItem().listDataItems()
for idx, item in enumerate(data_items):
if isinstance(item, TaurusTrendSet):
name = "__TaurusTrendSet_%d__" % idx
tmpreg.append(name)
self.registerConfigDelegate(item, name)
configdict = copy.deepcopy(
BaseConfigurableClass.createConfig(
self, allowUnpickable=allowUnpickable
)
)
finally:
# Ensure that temporary delegates are unregistered
for n in tmpreg:
self.unregisterConfigurableItem(n, raiseOnError=False)
return configdict
def applyConfig(self, configdict, depth=None):
"""
Reimplemented from BaseConfigurableClass to manage the config
properties of the trendsets attached to this plot
"""
try:
# Temporarily register trendsets as delegates
tmpreg = []
tsets = []
for name in configdict["__orderedConfigNames__"]:
if name.startswith("__TaurusTrendSet_"):
# Instantiate empty TaurusTrendSet
tset = TaurusTrendSet()
tsets.append(tset)
self.registerConfigDelegate(tset, name)
tmpreg.append(name)
# remove the trendsets from the second axis (Y2) to avoid dups
self._y2.clearItems()
BaseConfigurableClass.applyConfig(
self, configdict=configdict, depth=depth
)
plot_item = self.getPlotItem()
# keep a dict of existing trendsets (to use it for avoiding dups)
currentTrendSets = dict()
curveNames = []
for tset in plot_item.listDataItems():
if isinstance(tset, TaurusTrendSet):
currentTrendSets[tset.getFullModelName()] = tset
curveNames.extend([c.name for c in tset])
# remove trendsets that exists in currentTrendSets from plot
# (to avoid duplicates). Also remove curves from the legend
for tset in tsets:
ts = currentTrendSets.get(tset.getFullModelName(), None)
if ts is not None:
plot_item.removeItem(ts)
# Add to plot **after** their configuration has been applied
for tset in tsets:
# First we add all the trendsets to self. This way the plotItem
# can keep a list of dataItems (PlotItem.listDataItems())
self.addItem(tset)
# Add trendsets to Y2 axis, when the trendset configurations
# have been applied.
# Ideally, the Y2ViewBox class must handle the action of adding
# trendsets to itself, but we want add the trendsets when they
# are restored with all their properties.
if tset.getFullModelName() in self._y2.getCurves():
plot_item.getViewBox().removeItem(tset)
self._y2.addItem(tset)
finally:
# Ensure that temporary delegates are unregistered
for n in tmpreg:
self.unregisterConfigurableItem(n, raiseOnError=False)
def _getState(self):
"""Same as PlotWidget.saveState but removing viewRange conf to force
a refresh with targetRange when loading
"""
state = copy.deepcopy(self.saveState())
# remove viewRange conf
del state["view"]["viewRange"]
return state
def trend_main(
models=(), config_file=None, demo=False, window_name="TaurusTrend (pg)"
):
"""Launch a TaurusTrend"""
import sys
from taurus.qt.qtgui.application import TaurusApplication
app = TaurusApplication(cmd_line_parser=None, app_name="taurustrend(pg)")
w = TaurusTrend()
w.setWindowTitle(window_name)
# config_file = 'tmp/TaurusTrend.pck'
if demo:
models = list(models)
models.extend(["eval:rand()", "eval:1+rand(2)"])
if config_file is not None:
w.loadConfigFile(config_file)
if models:
w.setModel(models)
w.show()
ret = app.exec_()
# w.saveConfigFile('tmp/TaurusTrend.pck')
# import pprint
# pprint.pprint(w.createConfig())
sys.exit(ret)
if __name__ == "__main__":
trend_main(models=("eval:rand()", "sys/tg_test/1/ampli"))
taurus-pyqtgraph-0.3.1/taurus_pyqtgraph/ui/ 0000775 0000000 0000000 00000000000 13550077441 0021120 5 ustar 00root root 0000000 0000000 taurus-pyqtgraph-0.3.1/taurus_pyqtgraph/ui/CurvesAppearanceChooser.ui 0000664 0000000 0000000 00000026264 13550077441 0026243 0 ustar 00root root 0000000 0000000
curvesAppearanceChooserDlg
0
0
808
205
0
0
16777215
205
Form
-
-
0
0
QAbstractItemView::ExtendedSelection
false
-
-
&Reset
-
&Apply
-
Line
-
0
0
&Area Fill
-
S&tyle
lStyleCB
-
0
0
--
-1
10
1
-
0
0
-99.989999999999995
-
<html><head><meta name="qrichtext" content="1" /><style type="text/css">
p, li { white-space: pre-wrap; }
</style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;">
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Style of the pen used to connect the points.</p></body></html>
-
Mode
-
C&olor
lColorCB
-
&Width
lWidthSB
-
<html><head><meta name="qrichtext" content="1" /><style type="text/css">
p, li { white-space: pre-wrap; }
</style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;">
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Connector mode: how the data points are connected (steps, straight lines,...)</p></body></html>
-
-
Symbols
-
&Style
sStyleCB
-
Si&ze
sSizeSB
-
-
--
-1
10
1
3
-
&Color
sColorCB
-
Qt::Vertical
111
16
-
&Fill
false
-
-
Other
-
Curve Title(s)...
-
Assign to axis
-
<html><head><meta name="qrichtext" content="1" /><style type="text/css">
p, li { white-space: pre-wrap; }
</style></head><body style=" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal;">
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Assign selected curves to Y1 (left axis)</p></body></html>
Y1
false
-
<html><head><meta name="qrichtext" content="1" /><style type="text/css">
p, li { white-space: pre-wrap; }
</style></head><body style=" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal;">
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Assign selected curves to Y2 (right axis)</p></body></html>
Y2
false
-
0
0
Background fill...
taurus-pyqtgraph-0.3.1/taurus_pyqtgraph/ui/TaurusItemConfDlg.ui 0000664 0000000 0000000 00000006340 13550077441 0025021 0 ustar 00root root 0000000 0000000
TaurusItemConfDlg
0
0
850
495
Form
-
Qt::Horizontal
Sources of data
-
Contents & appearance
-
-
400
0
Qt::CustomContextMenu
QAbstractItemView::DragDrop
-
-
Qt::Horizontal
40
20
-
-
Qt::Horizontal
40
20
-
Reload
-
Cancel
-
Apply
taurus-pyqtgraph-0.3.1/taurus_pyqtgraph/y2axis.py 0000664 0000000 0000000 00000017527 13550077441 0022310 0 ustar 00root root 0000000 0000000 #!/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 .
##
#############################################################################
__all__ = ["Y2ViewBox"]
from pyqtgraph import ViewBox, PlotItem
from taurus.qt.qtcore.configuration.configuration import BaseConfigurableClass
def _PlotItem_addItem(self, item, *args, **kwargs):
"""replacement for `PlotItem.addItem` that Y2Axis will use to monkey-patch
the original one
"""
PlotItem.addItem(self, item, *args, **kwargs)
if hasattr(item, "setLogMode"):
item.setLogMode(
self.getAxis("bottom").logMode, self.getAxis("left").logMode
)
class Y2ViewBox(ViewBox, BaseConfigurableClass):
"""
A tool that inserts a secondary Y axis to a plot item (see
:meth:`attachToPlotItem`).
It is implemented as a :class:`pyqtgraph.ViewBox` and provides methods to
add and remove :class:`pyqtgraph.PlotDataItem` objects to it.
"""
def __init__(self, *args, **kwargs):
BaseConfigurableClass.__init__(self)
ViewBox.__init__(self, *args, **kwargs)
self.registerConfigProperty(self.getCurves, self.setCurves, "Y2Curves")
self.registerConfigProperty(self._getState, self.setState, "viewState")
self._isAttached = False
self.plotItem = None
self._curvesModelNames = []
def attachToPlotItem(self, plot_item):
"""Use this method to add this axis to a plot
:param plot_item: (PlotItem)
"""
if self._isAttached:
return # TODO: log a message it's already attached
self._isAttached = True
mainViewBox = plot_item.getViewBox()
mainViewBox.sigResized.connect(self._updateViews)
self.plotItem = plot_item
# add axis-independent actions for logarithmic scale
self._addLogAxisActions()
# disable the standard (custom view-unfriendly) log actions
self.plotItem.ctrl.logXCheck.setEnabled(False)
self.plotItem.ctrl.logYCheck.setEnabled(False)
# monkey-patch the addItem method of the PlotItem
from types import MethodType
self.plotItem.addItem = MethodType(_PlotItem_addItem, self.plotItem)
def _updateViews(self, viewBox):
self.setGeometry(viewBox.sceneBoundingRect())
self.linkedViewChanged(viewBox, self.XAxis)
def removeItem(self, item):
"""Reimplemented from :class:`pyqtgraph.ViewBox`"""
ViewBox.removeItem(self, item)
# when last curve is removed from self (axis Y2), we must remove the
# axis from scene and hide the axis.
if len(self.addedItems) < 1:
self.plotItem.scene().removeItem(self)
self.plotItem.hideAxis("right")
if hasattr(item, "getFullModelNames"):
self._curvesModelNames.remove(item.getFullModelNames())
def addItem(self, item, ignoreBounds=False):
"""Reimplemented from :class:`pyqtgraph.ViewBox`"""
ViewBox.addItem(self, item, ignoreBounds=ignoreBounds)
if len(self.addedItems) == 1:
# when the first curve is added to self (axis Y2), we must
# add Y2 to main scene(), show the axis and link X axis to self.
self.plotItem.showAxis("right")
self.plotItem.scene().addItem(self)
self.plotItem.getAxis("right").linkToView(self)
self.setXLink(self.plotItem)
# set the item log mode to match this view:
if hasattr(item, "setLogMode"):
item.setLogMode(
self.plotItem.getAxis("bottom").logMode,
self.plotItem.getAxis("right").logMode,
)
if hasattr(item, "getFullModelNames") and (
len(self.addedItems) > 0
and item.getFullModelNames() not in self._curvesModelNames
):
self._curvesModelNames.append(item.getFullModelNames())
def getCurves(self):
"""Returns the curve model names of curves associated to the Y2 axis.
:return: (list) List of tuples of model names (xModelName, yModelName)
from each curve in this view
"""
return self._curvesModelNames
def setCurves(self, curves):
"""Sets the curve names associated to the Y2 axis (but does not
create/remove any curve.
"""
self._curvesModelNames = curves
def _getState(self):
"""Same as ViewBox.getState but removing viewRange conf to force
a refresh with targetRange when loading
"""
state = self.getState(copy=True)
del state["viewRange"]
return state
def clearItems(self):
"""Remove the added items"""
for c in self.addedItems:
self.removeItem(c)
def _addLogAxisActions(self):
# insert & connect actions Log Scale Actions
# X (bottom)
menu = self.plotItem.getViewBox().menu.axes[0]
action = menu.addAction("Log scale")
action.setCheckable(True)
action.setChecked(self.plotItem.getAxis("bottom").logMode)
action.setParent(menu)
action.toggled.connect(self._onXLogToggled)
self.menu.axes[0].addAction(action) # Add same action to X2 menu too
# Y1 (left)
menu = self.plotItem.getViewBox().menu.axes[1]
action = menu.addAction("Log scale")
action.setCheckable(True)
action.setChecked(self.plotItem.getAxis("left").logMode)
action.setParent(menu)
action.toggled.connect(self._onY1LogToggled)
# Y2 (right)
menu = self.menu.axes[1]
action = menu.addAction("Log scale")
action.setCheckable(True)
action.setChecked(self.plotItem.getAxis("right").logMode)
action.setParent(menu)
action.toggled.connect(self._onY2LogToggled)
def _onXLogToggled(self, checked):
logx = checked
# set log mode for items of main viewbox
logy = self.plotItem.getAxis("left").logMode
for i in self.plotItem.getViewBox().addedItems:
if hasattr(i, "setLogMode"):
i.setLogMode(logx, logy)
# set log mode for items of Y2 viewbox
logy = self.plotItem.getAxis("right").logMode
for i in self.addedItems:
if hasattr(i, "setLogMode"):
i.setLogMode(logx, logy)
# set log mode for the bottom axis
self.plotItem.getAxis("bottom").setLogMode(checked)
def _onY1LogToggled(self, checked):
# set log mode for items of main viewbox
logx = self.plotItem.getAxis("bottom").logMode
logy = checked
for i in self.plotItem.getViewBox().addedItems:
if hasattr(i, "setLogMode"):
i.setLogMode(logx, logy)
# set log mode for the left axis
self.plotItem.getAxis("left").setLogMode(checked)
def _onY2LogToggled(self, checked):
# set log mode for items of Y2 viewbox
logx = self.plotItem.getAxis("bottom").logMode
logy = checked
for i in self.addedItems:
if hasattr(i, "setLogMode"):
i.setLogMode(logx, logy)
# set log mode for the right axis
self.plotItem.getAxis("right").setLogMode(checked)
taurus-pyqtgraph-0.3.1/tests/ 0000775 0000000 0000000 00000000000 13550077441 0016223 5 ustar 00root root 0000000 0000000 taurus-pyqtgraph-0.3.1/tests/test_taurus_pyqtgraph.py 0000664 0000000 0000000 00000001124 13550077441 0023254 0 ustar 00root root 0000000 0000000 #!/usr/bin/env python
# -*- coding: utf-8 -*-
"""Tests for `taurus_pyqtgraph` package."""
from click.testing import CliRunner
def test_smoke():
import taurus_pyqtgraph # noqa
def test_command_line_interface():
"""Test the CLI."""
from taurus_pyqtgraph import cli
runner = CliRunner()
result = runner.invoke(cli.tpg)
assert result.exit_code == 0
assert "Taurus-pyqtgraph related commands" in result.output
help_result = runner.invoke(cli.tpg, ["--help"])
assert help_result.exit_code == 0
assert "Show this message and exit." in help_result.output
taurus-pyqtgraph-0.3.1/tox.ini 0000664 0000000 0000000 00000000606 13550077441 0016376 0 ustar 00root root 0000000 0000000 [tox]
envlist = py35, py36, py37, flake8
[travis]
python =
3.7: py37
3.6: py36
3.5: py35
[testenv:flake8]
basepython = python
deps = flake8
commands = flake8 taurus_pyqtgraph
[testenv]
setenv =
PYTHONPATH = {toxinidir}
deps =
-r{toxinidir}/requirements_dev.txt
-r{toxinidir}/requirements.txt
commands =
pip install -U pip
py.test --basetemp={envtmpdir}