# -*- coding: utf-8 *-*
#import numpy as np
#from copy import deepcopy
import sys
#import scipy
from diaGrabber import _utils
from ._plot import _plot
from pyqtgraph.Qt import QtGui, QtCore
import numpy as np
import pyqtgraph as pg
import pyqtgraph.dockarea as pgDock
import pyqtgraph.parametertree.parameterTypes as pTypes
from pyqtgraph.parametertree import Parameter, ParameterTree, ParameterItem, registerParameterType
[docs]class interactive(object):
def __init__(self):
self.hasInteractiveModus = True
[docs] def plot(self, matrixClass, **kwargs):
'''
Optional kwargs ("keyword arguments") are:
================ =========================================
Keyword Description
================ =========================================
*interactive* bool: [True, False]
*merge* list: instances of mergeDimension of the source
*basis* list: instances of basisDimension of the source
*fps* float: frames per seconds
*enableAutoRange* list: ["x", "y"]
*showType* string: ["together", "widget"]
*colorTheme* string: ["default", "bright"]
artists
*windowSize* list: [size_x, size_y] e.g. **[1000,600]**
*showPreferences* bool: [True, False]
================ =========================================
'''
self.matrixClass = matrixClass
self.sortMatrix = self.matrixClass.sortMatrix
self.mergeMatrix = self.matrixClass.mergeMatrix
self._merge_dim = self.matrixClass._merge_dim
self._basis_dim = self.matrixClass._basis_dim
self.nDim = self.matrixClass.nDim
self.nMerge = self.matrixClass.nMerge
#standard
show_merge = range(self.nMerge)#show all merge-dim
show_basis = range(self.nDim)#show all basis-dim
interactive = False
fps = 20
self.windowSize = [1000,600]
self.enableAutoRange = []
self.showType = "together"
colorTheme = "default"
self.build_preferences_dock = True
#individual
for key in kwargs:
if key == "interactive":
interactive = bool(kwargs[key])
elif key == "merge":
show_merge = []
for merge in kwargs[key]:
##proove for mergeDim fehlt
show_merge.append(merge._mergeIndex)
self.nMerge = len(show_merge)
elif key == "basis":
show_basis = []
for basis in kwargs[key]:
##proove for mergeDim fehlt
show_basis.append(basis._basisIndex)
self.nDim = len(show_basis)
elif key == "fps":
fps = kwargs[key]
elif key == "enableAutoRange":
self.enableAutoRange = kwargs[key]
elif key == "showType":
self.showType = kwargs[key]
elif key == "colorTheme":
colorTheme = kwargs[key]
elif key == "windowSize":
self.windowSize = list( int(kwargs[key][0]), int(kwargs[key][1]) )
elif key == "showPreferences":
self.build_preferences_dock = bool(kwargs[key])
self.setColorTheme(colorTheme)
self.end_readOut = False
self.wait_ms = 1000/fps #frames per second
##create qt-window
app = QtGui.QApplication([])
win = QtGui.QMainWindow()
#win = pg.GraphicsWindow(title="Interactive Plot")
win.resize(self.windowSize[0],self.windowSize[1])
self.area = pgDock.DockArea()
win.setCentralWidget(self.area)
self.docks = []
plot_size_x = int(self.windowSize[0]*0.1)#int(windowSize[0]*(2/3))
if self.showType == "together":
self.docks.append(pgDock.Dock("together", size=(plot_size_x, 1)))
self.area.addDock(self.docks[-1])
elif self.showType == "widged":
for m in range(self.nMerge):
self.docks.append(pgDock.Dock("widget %s" %m, size=(plot_size_x, 1)))
#for m in range(self.nMerge):
self.area.addDock(self.docks[-1])
else:
sys.exit("ERROR: showType invalid")
if self.nDim > 1:
#set axis name and range
#=========
yAxis = pg.AxisItem("left")##y
#yScale = self._basis_dim[1].resolution/self._basis_dim[1]
#yAxis.setScale(0.001)
yAxis.setLabel(units = self._basis_dim[1].name)
xAxis = pg.AxisItem("bottom")##y
xAxis.setLabel(units = self._basis_dim[0].name)
#yAxis.setRange(self._basis_dim[1].include_from_to[1],self._basis_dim[1].include_from_to[0])
#self.plot = pg.ImageView(view=pg.PlotItem(title="testtest", axisItems = {"left": yAxis, "bottom": xAxis}))
#self.plot = win.addItem(pg.ImageView())
self.plot = win.addViewBox(lockAspect=True)
self.img = pg.ImageItem()
self.plot.addItem(self.img)
self.plot.autoRange()
else:#simple 2d-plot - no imag
yAxis = []#pg.AxisItem("left")##y
xAxis = []#pg.AxisItem("bottom")##y
#xAxis.setLabel(units = self._basis_dim[0].name)
self.plots = []
self.curves = []
colorList = ['r', 'g', 'b', 'c', 'm', 'y', 'k', 'w']
if self.showType == "together":
yAxis.append(pg.AxisItem("left"))##y
xAxis.append(pg.AxisItem("bottom"))##y
xAxis[-1].setLabel(units = self._basis_dim[0].name)
self.plots.append(pg.PlotWidget(axisItems = {"left": yAxis[0], "bottom": xAxis[0]}))
docks[0].addWidget(self.plots[0])
#self.plot = win.addPlot(title="Updating plot", axisItems = {"left": yAxis, "bottom": xAxis})
self.plots[0].enableAutoRange('xy', False)
if self.nMerge > 1:#if there are more plots draw a legend
self.plots[0].addLegend()
else:
yAxis[0].setLabel(units = self._merge_dim[0].name)
#create all plots and ink in range of the colorlist
for i in show_merge:
self.curves.append(self.plots[0].plot(pen=colorList[i%len(colorList)], symbol='+', name=self._merge_dim[i].name))
elif self.showType == "widged":
for n,m in enumerate(show_merge):
yAxis.append(pg.AxisItem("left", pen=self.ticks_color))##y
xAxis.append(pg.AxisItem("bottom", pen=self.ticks_color))##y
labelStyle = {'color': self.label_color}
xAxis[-1].setLabel(units = self._basis_dim[0].name, **labelStyle)
self.plots.append(pg.PlotWidget(axisItems = {"left": yAxis[-1], "bottom": xAxis[-1]}))
self.docks[n].addWidget(self.plots[-1])
self.plots[-1].enableAutoRange('xy', False)
yAxis[-1].setLabel(units = self._merge_dim[m].name, **labelStyle)
self.curves.append(self.plots[-1].plot(pen=colorList[n%len(colorList)], symbol='+', symbolPen=self.ticks_color, name=self._merge_dim[m].name))
self.plots[-1].setBackground(self.bg_color)
else:
sys.exit(NotImplemented)
if self.build_preferences_dock:
self.buildPreferencesDock()
win.show()
def updateValues():
#lineWise updating data
done_readOut = self.matrixClass._fillInteractive(self.end_readOut)
if done_readOut or done_readOut == None: #done_readOut = None, if the readoutprocess doesnt func
app.quit()#close the window
def updatePlot():
##########################################
if self.nDim == 1:
basis_extract = self.sortMatrix[show_basis[0]][self._basis_dim[show_basis[0]]._plot_range]
for n,m in enumerate(show_merge):
merge_extract = self.mergeMatrix[m][self._basis_dim[show_basis[0]]._plot_range]
self.curves[n].setData(basis_extract, merge_extract )
for p in self.plots:
if "x" in self.enableAutoRange:
p.setXRange(self._basis_dim[show_basis[0]]._include_from_to[0],self._basis_dim[show_basis[0]]._include_from_to[1])
if "y" in self.enableAutoRange:
##get min, max values for all merge-dims
#miny = self._merge_dim[0]._include_from_to[0]
#maxy = self._merge_dim[0]._include_from_to[1]
#for i in range(1,self.nMerge):
#if self._merge_dim[i]._include_from_to[0] < miny:
#miny = self._merge_dim[i]._include_from_to[0]
#if self._merge_dim[i]._include_from_to[1] > maxy:
#maxy = self._merge_dim[i]._include_from_to[1]
#print miny,maxy
#self.plot.setYRange(miny,maxy)
p.enableAutoRange('y', True)
elif self.nDim == 2:
sys.exit("mehrere merge noch nicht drinne für image")
merge_extract = self.mergeMatrix[self._basis_dim[0]._plot_range][self._basis_dim[1]._plot_range]
self.img.setImage(_utils.nanToZeros(merge_extract))
else:
sys.exit(NotImplemented)
if interactive:
#set timer
timerValues = QtCore.QTimer()
#get new basis-merge-values
timerValues.timeout.connect(updateValues)
timerValues.start(0)
timerPlot = QtCore.QTimer()
timerPlot.timeout.connect(updatePlot)
timerPlot.start(self.wait_ms)
if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
QtGui.QApplication.instance().exec_()
[docs] def setColorTheme(self, colorTheme):
'''choose the colortheme for the plot-output, possible values are:
* "default"
* "bright"
'''
if colorTheme == "default" or colorTheme == "":
self.ticks_color = (200,200,200)
self.label_color = '#fff'
self.bg_color = (0,0,0)
elif colorTheme == "bright":
self.ticks_color = (30,30,30)
self.label_color = '#000'
self.bg_color = 'w'
[docs] def buildPreferencesDock(self):
## test subclassing parameters
## This parameter automatically generates two child parameters which are always reciprocals of each other
class ComplexParameter(pTypes.GroupParameter):
def __init__(self, **opts):
opts['type'] = 'bool'
opts['value'] = True
pTypes.GroupParameter.__init__(self, **opts)
self.addChild({'name': 'A = 1/B', 'type': 'float', 'value': 7, 'suffix': 'Hz', 'siPrefix': True})
self.addChild({'name': 'B = 1/A', 'type': 'float', 'value': 1/7., 'suffix': 's', 'siPrefix': True})
self.a = self.param('A = 1/B')
self.b = self.param('B = 1/A')
self.a.sigValueChanged.connect(self.aChanged)
self.b.sigValueChanged.connect(self.bChanged)
def aChanged(self):
self.b.setValue(1.0 / self.a.value(), blockSignal=self.bChanged)
def bChanged(self):
self.a.setValue(1.0 / self.b.value(), blockSignal=self.aChanged)
## test add/remove
## this group includes a menu allowing the user to add new parameters into its child list
class ScalableGroup(pTypes.GroupParameter):
def __init__(self, **opts):
opts['type'] = 'group'
opts['addText'] = "Add"
opts['addList'] = ['str', 'float', 'int']
pTypes.GroupParameter.__init__(self, **opts)
def addNew(self, typ):
val = {
'str': '',
'float': 0.0,
'int': 0
}[typ]
self.addChild(dict(name="ScalableParam %d" % (len(self.childs)+1), type=typ, value=val, removable=True, renamable=True))
params = [
{'name': 'Basic parameter data types', 'type': 'group', 'children': [
{'name': 'Integer', 'type': 'int', 'value': 10},
{'name': 'Float', 'type': 'float', 'value': 10.5, 'step': 0.1},
{'name': 'String', 'type': 'str', 'value': "hi"},
{'name': 'List', 'type': 'list', 'values': [1,2,3], 'value': 2},
{'name': 'Named List', 'type': 'list', 'values': {"one": 1, "two": 2, "three": 3}, 'value': 2},
{'name': 'Boolean', 'type': 'bool', 'value': True, 'tip': "This is a checkbox"},
{'name': 'Color', 'type': 'color', 'value': "FF0", 'tip': "This is a color button"},
{'name': 'Gradient', 'type': 'colormap'},
{'name': 'Subgroup', 'type': 'group', 'children': [
{'name': 'Sub-param 1', 'type': 'int', 'value': 10},
{'name': 'Sub-param 2', 'type': 'float', 'value': 1.2e6},
]},
{'name': 'Text Parameter', 'type': 'text', 'value': 'Some text...'},
{'name': 'Action Parameter', 'type': 'action'},
]},
{'name': 'Numerical Parameter Options', 'type': 'group', 'children': [
{'name': 'Units + SI prefix', 'type': 'float', 'value': 1.2e-6, 'step': 1e-6, 'siPrefix': True, 'suffix': 'V'},
{'name': 'Limits (min=7;max=15)', 'type': 'int', 'value': 11, 'limits': (7, 15), 'default': -6},
{'name': 'DEC stepping', 'type': 'float', 'value': 1.2e6, 'dec': True, 'step': 1, 'siPrefix': True, 'suffix': 'Hz'},
]},
{'name': 'Save/Restore functionality', 'type': 'group', 'children': [
{'name': 'Save State', 'type': 'action'},
{'name': 'Restore State', 'type': 'action', 'children': [
{'name': 'Add missing items', 'type': 'bool', 'value': True},
{'name': 'Remove extra items', 'type': 'bool', 'value': True},
]},
]},
{'name': 'Extra Parameter Options', 'type': 'group', 'children': [
{'name': 'Read-only', 'type': 'float', 'value': 1.2e6, 'siPrefix': True, 'suffix': 'Hz', 'readonly': True},
{'name': 'Renamable', 'type': 'float', 'value': 1.2e6, 'siPrefix': True, 'suffix': 'Hz', 'renamable': True},
{'name': 'Removable', 'type': 'float', 'value': 1.2e6, 'siPrefix': True, 'suffix': 'Hz', 'removable': True},
]},
ComplexParameter(name='Custom parameter group (reciprocal values)'),
ScalableGroup(name="Expandable Parameter Group", children=[
{'name': 'ScalableParam 1', 'type': 'str', 'value': "default param 1"},
{'name': 'ScalableParam 2', 'type': 'str', 'value': "default param 2"},
]),
]
## Create tree of Parameter objects
p = Parameter.create(name='params', type='group', children=params)
## If anything changes in the tree, print a message
def change(param, changes):
print("tree changes:")
for param, change, data in changes:
path = p.childPath(param)
if path is not None:
childName = '.'.join(path)
else:
childName = param.name()
print(' parameter: %s'% childName)
print(' change: %s'% change)
print(' data: %s'% str(data))
print(' ----------')
p.sigTreeStateChanged.connect(change)
def save():
global state
state = p.saveState()
def restore():
global state
add = p['Save/Restore functionality', 'Restore State', 'Add missing items']
rem = p['Save/Restore functionality', 'Restore State', 'Remove extra items']
p.restoreState(state, addChildren=add, removeChildren=rem)
p.param('Save/Restore functionality', 'Save State').sigActivated.connect(save)
p.param('Save/Restore functionality', 'Restore State').sigActivated.connect(restore)
## Create ParameterTree widget,
t = ParameterTree()
t.setParameters(p, showTop=False)
#t.show() #only nec. when allone
#t.resize(400,800)
#create new dock
self.docks.append( pgDock.Dock("Preferences", size=(1, 1)) )
#append parameterTree-instance to dock
self.docks[-1].addWidget(t)
# add dock to area
self.area.addDock(self.docks[-1], 'left')
## test save/restore
s = p.saveState()
p.restoreState(s)