Merge pull request #586 from acq4/parametertree-updates
Parametertree updates
This commit is contained in:
commit
7d992a56ee
@ -162,7 +162,11 @@ class Parameter(QtCore.QObject):
|
|||||||
'title': None,
|
'title': None,
|
||||||
#'limits': None, ## This is a bad plan--each parameter type may have a different data type for limits.
|
#'limits': None, ## This is a bad plan--each parameter type may have a different data type for limits.
|
||||||
}
|
}
|
||||||
|
value = opts.get('value', None)
|
||||||
|
name = opts.get('name', None)
|
||||||
self.opts.update(opts)
|
self.opts.update(opts)
|
||||||
|
self.opts['value'] = None # will be set later.
|
||||||
|
self.opts['name'] = None
|
||||||
|
|
||||||
self.childs = []
|
self.childs = []
|
||||||
self.names = {} ## map name:child
|
self.names = {} ## map name:child
|
||||||
@ -172,17 +176,19 @@ class Parameter(QtCore.QObject):
|
|||||||
self.blockTreeChangeEmit = 0
|
self.blockTreeChangeEmit = 0
|
||||||
#self.monitoringChildren = False ## prevent calling monitorChildren more than once
|
#self.monitoringChildren = False ## prevent calling monitorChildren more than once
|
||||||
|
|
||||||
if 'value' not in self.opts:
|
if not isinstance(name, basestring):
|
||||||
self.opts['value'] = None
|
|
||||||
|
|
||||||
if 'name' not in self.opts or not isinstance(self.opts['name'], basestring):
|
|
||||||
raise Exception("Parameter must have a string name specified in opts.")
|
raise Exception("Parameter must have a string name specified in opts.")
|
||||||
self.setName(opts['name'])
|
self.setName(name)
|
||||||
|
|
||||||
self.addChildren(self.opts.get('children', []))
|
self.addChildren(self.opts.get('children', []))
|
||||||
|
|
||||||
if 'value' in self.opts and 'default' not in self.opts:
|
self.opts['value'] = None
|
||||||
self.opts['default'] = self.opts['value']
|
if value is not None:
|
||||||
|
self.setValue(value)
|
||||||
|
|
||||||
|
if 'default' not in self.opts:
|
||||||
|
self.opts['default'] = None
|
||||||
|
self.setDefault(self.opts['value'])
|
||||||
|
|
||||||
## Connect all state changed signals to the general sigStateChanged
|
## Connect all state changed signals to the general sigStateChanged
|
||||||
self.sigValueChanged.connect(lambda param, data: self.emitStateChanged('value', data))
|
self.sigValueChanged.connect(lambda param, data: self.emitStateChanged('value', data))
|
||||||
@ -647,15 +653,16 @@ class Parameter(QtCore.QObject):
|
|||||||
"""Return a child parameter.
|
"""Return a child parameter.
|
||||||
Accepts the name of the child or a tuple (path, to, child)
|
Accepts the name of the child or a tuple (path, to, child)
|
||||||
|
|
||||||
Added in version 0.9.9. Ealier versions used the 'param' method, which is still
|
Added in version 0.9.9. Earlier versions used the 'param' method, which is still
|
||||||
implemented for backward compatibility."""
|
implemented for backward compatibility.
|
||||||
|
"""
|
||||||
try:
|
try:
|
||||||
param = self.names[names[0]]
|
param = self.names[names[0]]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
raise Exception("Parameter %s has no child named %s" % (self.name(), names[0]))
|
raise KeyError("Parameter %s has no child named %s" % (self.name(), names[0]))
|
||||||
|
|
||||||
if len(names) > 1:
|
if len(names) > 1:
|
||||||
return param.param(*names[1:])
|
return param.child(*names[1:])
|
||||||
else:
|
else:
|
||||||
return param
|
return param
|
||||||
|
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
from ..pgcollections import OrderedDict
|
from ..pgcollections import OrderedDict
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
import copy
|
||||||
|
|
||||||
|
|
||||||
class SystemSolver(object):
|
class SystemSolver(object):
|
||||||
"""
|
"""
|
||||||
@ -73,6 +75,12 @@ class SystemSolver(object):
|
|||||||
self.__dict__['_currentGets'] = set()
|
self.__dict__['_currentGets'] = set()
|
||||||
self.reset()
|
self.reset()
|
||||||
|
|
||||||
|
def copy(self):
|
||||||
|
sys = type(self)()
|
||||||
|
sys.__dict__['_vars'] = copy.deepcopy(self.__dict__['_vars'])
|
||||||
|
sys.__dict__['_currentGets'] = copy.deepcopy(self.__dict__['_currentGets'])
|
||||||
|
return sys
|
||||||
|
|
||||||
def reset(self):
|
def reset(self):
|
||||||
"""
|
"""
|
||||||
Reset all variables in the solver to their default state.
|
Reset all variables in the solver to their default state.
|
||||||
@ -167,6 +175,16 @@ class SystemSolver(object):
|
|||||||
elif constraint == 'fixed':
|
elif constraint == 'fixed':
|
||||||
if 'f' not in var[3]:
|
if 'f' not in var[3]:
|
||||||
raise TypeError("Fixed constraints not allowed for '%s'" % name)
|
raise TypeError("Fixed constraints not allowed for '%s'" % name)
|
||||||
|
# This is nice, but not reliable because sometimes there is 1 DOF but we set 2
|
||||||
|
# values simultaneously.
|
||||||
|
# if var[2] is None:
|
||||||
|
# try:
|
||||||
|
# self.get(name)
|
||||||
|
# # has already been computed by the system; adding a fixed constraint
|
||||||
|
# # would overspecify the system.
|
||||||
|
# raise ValueError("Cannot fix parameter '%s'; system would become overconstrained." % name)
|
||||||
|
# except RuntimeError:
|
||||||
|
# pass
|
||||||
var[2] = constraint
|
var[2] = constraint
|
||||||
elif isinstance(constraint, tuple):
|
elif isinstance(constraint, tuple):
|
||||||
if 'r' not in var[3]:
|
if 'r' not in var[3]:
|
||||||
@ -177,7 +195,7 @@ class SystemSolver(object):
|
|||||||
raise TypeError("constraint must be None, True, 'fixed', or tuple. (got %s)" % constraint)
|
raise TypeError("constraint must be None, True, 'fixed', or tuple. (got %s)" % constraint)
|
||||||
|
|
||||||
# type checking / massaging
|
# type checking / massaging
|
||||||
if var[1] is np.ndarray:
|
if var[1] is np.ndarray and value is not None:
|
||||||
value = np.array(value, dtype=float)
|
value = np.array(value, dtype=float)
|
||||||
elif var[1] in (int, float, tuple) and value is not None:
|
elif var[1] in (int, float, tuple) and value is not None:
|
||||||
value = var[1](value)
|
value = var[1](value)
|
||||||
@ -187,7 +205,7 @@ class SystemSolver(object):
|
|||||||
raise ValueError("Setting %s = %s violates constraint %s" % (name, value, var[2]))
|
raise ValueError("Setting %s = %s violates constraint %s" % (name, value, var[2]))
|
||||||
|
|
||||||
# invalidate other dependent values
|
# invalidate other dependent values
|
||||||
if var[0] is not None:
|
if var[0] is not None or value is None:
|
||||||
# todo: we can make this more clever..(and might need to)
|
# todo: we can make this more clever..(and might need to)
|
||||||
# we just know that a value of None cannot have dependencies
|
# we just know that a value of None cannot have dependencies
|
||||||
# (because if anyone else had asked for this value, it wouldn't be
|
# (because if anyone else had asked for this value, it wouldn't be
|
||||||
@ -237,6 +255,31 @@ class SystemSolver(object):
|
|||||||
for k in self._vars:
|
for k in self._vars:
|
||||||
getattr(self, k)
|
getattr(self, k)
|
||||||
|
|
||||||
|
def checkOverconstraint(self):
|
||||||
|
"""Check whether the system is overconstrained. If so, return the name of
|
||||||
|
the first overconstrained parameter.
|
||||||
|
|
||||||
|
Overconstraints occur when any fixed parameter can be successfully computed by the system.
|
||||||
|
(Ideally, all parameters are either fixed by the user or constrained by the
|
||||||
|
system, but never both).
|
||||||
|
"""
|
||||||
|
for k,v in self._vars.items():
|
||||||
|
if v[2] == 'fixed' and 'n' in v[3]:
|
||||||
|
oldval = v[:]
|
||||||
|
self.set(k, None, None)
|
||||||
|
try:
|
||||||
|
self.get(k)
|
||||||
|
return k
|
||||||
|
except RuntimeError:
|
||||||
|
pass
|
||||||
|
finally:
|
||||||
|
self._vars[k] = oldval
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
state = OrderedDict()
|
state = OrderedDict()
|
||||||
for name, var in self._vars.items():
|
for name, var in self._vars.items():
|
||||||
|
@ -463,11 +463,14 @@ class GroupParameter(Parameter):
|
|||||||
"""
|
"""
|
||||||
itemClass = GroupParameterItem
|
itemClass = GroupParameterItem
|
||||||
|
|
||||||
|
sigAddNew = QtCore.Signal(object, object) # self, type
|
||||||
|
|
||||||
def addNew(self, typ=None):
|
def addNew(self, typ=None):
|
||||||
"""
|
"""
|
||||||
This method is called when the user has requested to add a new item to the group.
|
This method is called when the user has requested to add a new item to the group.
|
||||||
|
By default, it emits ``sigAddNew(self, typ)``.
|
||||||
"""
|
"""
|
||||||
raise Exception("Must override this function in subclass.")
|
self.sigAddNew.emit(self, typ)
|
||||||
|
|
||||||
def setAddList(self, vals):
|
def setAddList(self, vals):
|
||||||
"""Change the list of options available for the user to add to the group."""
|
"""Change the list of options available for the user to add to the group."""
|
||||||
@ -605,6 +608,7 @@ class ActionParameterItem(ParameterItem):
|
|||||||
ParameterItem.__init__(self, param, depth)
|
ParameterItem.__init__(self, param, depth)
|
||||||
self.layoutWidget = QtGui.QWidget()
|
self.layoutWidget = QtGui.QWidget()
|
||||||
self.layout = QtGui.QHBoxLayout()
|
self.layout = QtGui.QHBoxLayout()
|
||||||
|
self.layout.setContentsMargins(0, 0, 0, 0)
|
||||||
self.layoutWidget.setLayout(self.layout)
|
self.layoutWidget.setLayout(self.layout)
|
||||||
self.button = QtGui.QPushButton(param.name())
|
self.button = QtGui.QPushButton(param.name())
|
||||||
#self.layout.addSpacing(100)
|
#self.layout.addSpacing(100)
|
||||||
|
Loading…
Reference in New Issue
Block a user