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,
|
||||
#'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['value'] = None # will be set later.
|
||||
self.opts['name'] = None
|
||||
|
||||
self.childs = []
|
||||
self.names = {} ## map name:child
|
||||
@ -172,17 +176,19 @@ class Parameter(QtCore.QObject):
|
||||
self.blockTreeChangeEmit = 0
|
||||
#self.monitoringChildren = False ## prevent calling monitorChildren more than once
|
||||
|
||||
if 'value' not in self.opts:
|
||||
self.opts['value'] = None
|
||||
|
||||
if 'name' not in self.opts or not isinstance(self.opts['name'], basestring):
|
||||
if not isinstance(name, basestring):
|
||||
raise Exception("Parameter must have a string name specified in opts.")
|
||||
self.setName(opts['name'])
|
||||
self.setName(name)
|
||||
|
||||
self.addChildren(self.opts.get('children', []))
|
||||
|
||||
if 'value' in self.opts and 'default' not in self.opts:
|
||||
self.opts['default'] = self.opts['value']
|
||||
|
||||
self.opts['value'] = None
|
||||
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
|
||||
self.sigValueChanged.connect(lambda param, data: self.emitStateChanged('value', data))
|
||||
@ -647,18 +653,19 @@ class Parameter(QtCore.QObject):
|
||||
"""Return a child parameter.
|
||||
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
|
||||
implemented for backward compatibility."""
|
||||
Added in version 0.9.9. Earlier versions used the 'param' method, which is still
|
||||
implemented for backward compatibility.
|
||||
"""
|
||||
try:
|
||||
param = self.names[names[0]]
|
||||
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:
|
||||
return param.param(*names[1:])
|
||||
return param.child(*names[1:])
|
||||
else:
|
||||
return param
|
||||
|
||||
|
||||
def param(self, *names):
|
||||
# for backward compatibility.
|
||||
return self.child(*names)
|
||||
|
@ -1,5 +1,7 @@
|
||||
from ..pgcollections import OrderedDict
|
||||
import numpy as np
|
||||
import copy
|
||||
|
||||
|
||||
class SystemSolver(object):
|
||||
"""
|
||||
@ -73,6 +75,12 @@ class SystemSolver(object):
|
||||
self.__dict__['_currentGets'] = set()
|
||||
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):
|
||||
"""
|
||||
Reset all variables in the solver to their default state.
|
||||
@ -167,6 +175,16 @@ class SystemSolver(object):
|
||||
elif constraint == 'fixed':
|
||||
if 'f' not in var[3]:
|
||||
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
|
||||
elif isinstance(constraint, tuple):
|
||||
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)
|
||||
|
||||
# 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)
|
||||
elif var[1] in (int, float, tuple) and value is not None:
|
||||
value = var[1](value)
|
||||
@ -185,9 +203,9 @@ class SystemSolver(object):
|
||||
# constraint checks
|
||||
if constraint is True and not self.check_constraint(name, value):
|
||||
raise ValueError("Setting %s = %s violates constraint %s" % (name, value, var[2]))
|
||||
|
||||
|
||||
# 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)
|
||||
# we just know that a value of None cannot have dependencies
|
||||
# (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:
|
||||
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):
|
||||
state = OrderedDict()
|
||||
for name, var in self._vars.items():
|
||||
@ -378,4 +421,4 @@ if __name__ == '__main__':
|
||||
|
||||
camera.solve()
|
||||
print(camera.saveState())
|
||||
|
||||
|
||||
|
@ -462,12 +462,15 @@ class GroupParameter(Parameter):
|
||||
instead of a button.
|
||||
"""
|
||||
itemClass = GroupParameterItem
|
||||
|
||||
sigAddNew = QtCore.Signal(object, object) # self, type
|
||||
|
||||
def addNew(self, typ=None):
|
||||
"""
|
||||
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):
|
||||
"""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)
|
||||
self.layoutWidget = QtGui.QWidget()
|
||||
self.layout = QtGui.QHBoxLayout()
|
||||
self.layout.setContentsMargins(0, 0, 0, 0)
|
||||
self.layoutWidget.setLayout(self.layout)
|
||||
self.button = QtGui.QPushButton(param.name())
|
||||
#self.layout.addSpacing(100)
|
||||
|
Loading…
Reference in New Issue
Block a user