From 0f910c45d1338f47aaaad021790dc199fafd16f6 Mon Sep 17 00:00:00 2001 From: Luke Campagnola Date: Fri, 29 Sep 2017 08:54:33 -0700 Subject: [PATCH 1/7] Make parameter name,value inint args go through setValue and setName --- pyqtgraph/parametertree/Parameter.py | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/pyqtgraph/parametertree/Parameter.py b/pyqtgraph/parametertree/Parameter.py index 4ca80ffe..d48fee57 100644 --- a/pyqtgraph/parametertree/Parameter.py +++ b/pyqtgraph/parametertree/Parameter.py @@ -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)) From bf31a5ba99ca11cb75c50492bff281bb125aa09a Mon Sep 17 00:00:00 2001 From: Luke Campagnola Date: Fri, 29 Sep 2017 08:55:36 -0700 Subject: [PATCH 2/7] Parameter.child raises KeyError if requested child name does not exist --- pyqtgraph/parametertree/Parameter.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/pyqtgraph/parametertree/Parameter.py b/pyqtgraph/parametertree/Parameter.py index d48fee57..e28085bf 100644 --- a/pyqtgraph/parametertree/Parameter.py +++ b/pyqtgraph/parametertree/Parameter.py @@ -653,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) From 09b8e662b17307eee22b969de5cbe23ef134cc04 Mon Sep 17 00:00:00 2001 From: Luke Campagnola Date: Fri, 29 Sep 2017 08:56:28 -0700 Subject: [PATCH 3/7] systemsolver: minor fixes --- pyqtgraph/parametertree/SystemSolver.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pyqtgraph/parametertree/SystemSolver.py b/pyqtgraph/parametertree/SystemSolver.py index 24e35e9a..c804d50a 100644 --- a/pyqtgraph/parametertree/SystemSolver.py +++ b/pyqtgraph/parametertree/SystemSolver.py @@ -177,7 +177,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 +185,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 From eb1b7fc8bb07669bc0140678f09c1b934a507bbb Mon Sep 17 00:00:00 2001 From: Luke Campagnola Date: Fri, 29 Sep 2017 08:56:44 -0700 Subject: [PATCH 4/7] add systemsolver copy method --- pyqtgraph/parametertree/SystemSolver.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/pyqtgraph/parametertree/SystemSolver.py b/pyqtgraph/parametertree/SystemSolver.py index c804d50a..ffdabfae 100644 --- a/pyqtgraph/parametertree/SystemSolver.py +++ b/pyqtgraph/parametertree/SystemSolver.py @@ -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. From 2754427b25d172dff2d73199a470ab4d99b06709 Mon Sep 17 00:00:00 2001 From: Luke Campagnola Date: Fri, 29 Sep 2017 08:58:00 -0700 Subject: [PATCH 5/7] systemsolver: add method for checking constraints / DOF --- pyqtgraph/parametertree/SystemSolver.py | 37 ++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/pyqtgraph/parametertree/SystemSolver.py b/pyqtgraph/parametertree/SystemSolver.py index ffdabfae..b1d4256a 100644 --- a/pyqtgraph/parametertree/SystemSolver.py +++ b/pyqtgraph/parametertree/SystemSolver.py @@ -175,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]: @@ -245,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(): @@ -386,4 +421,4 @@ if __name__ == '__main__': camera.solve() print(camera.saveState()) - \ No newline at end of file + From ce7594b6972ec6b840829d7e306aad7a2fc13c50 Mon Sep 17 00:00:00 2001 From: Luke Campagnola Date: Fri, 29 Sep 2017 08:59:14 -0700 Subject: [PATCH 6/7] Add GroupParameter.sigAddNew signal --- pyqtgraph/parametertree/parameterTypes.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pyqtgraph/parametertree/parameterTypes.py b/pyqtgraph/parametertree/parameterTypes.py index 8c1e587d..d75dbba0 100644 --- a/pyqtgraph/parametertree/parameterTypes.py +++ b/pyqtgraph/parametertree/parameterTypes.py @@ -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.""" From 812a65461d8e1f7f5b6a2507031530ec31246d2b Mon Sep 17 00:00:00 2001 From: Luke Campagnola Date: Fri, 29 Sep 2017 08:59:37 -0700 Subject: [PATCH 7/7] action parameter minor ui adjustment --- pyqtgraph/parametertree/parameterTypes.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pyqtgraph/parametertree/parameterTypes.py b/pyqtgraph/parametertree/parameterTypes.py index d75dbba0..d137410d 100644 --- a/pyqtgraph/parametertree/parameterTypes.py +++ b/pyqtgraph/parametertree/parameterTypes.py @@ -608,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)