Parametertree updates:

- Fixed up parametertree.saveState() and restoreState() methods
  - Updated parametertree documentation
This commit is contained in:
Luke Campagnola 2012-08-17 16:17:44 -04:00
commit 237625a48d
4 changed files with 141 additions and 11 deletions

View File

@ -39,6 +39,7 @@ examples = OrderedDict([
('Widgets', OrderedDict([ ('Widgets', OrderedDict([
('PlotWidget', 'PlotWidget.py'), ('PlotWidget', 'PlotWidget.py'),
('SpinBox', 'SpinBox.py'), ('SpinBox', 'SpinBox.py'),
('ConsoleWidget', 'ConsoleWidget.py'),
('TreeWidget', 'TreeWidget.py'), ('TreeWidget', 'TreeWidget.py'),
('DataTreeWidget', 'DataTreeWidget.py'), ('DataTreeWidget', 'DataTreeWidget.py'),
('GradientWidget', 'GradientWidget.py'), ('GradientWidget', 'GradientWidget.py'),

View File

@ -81,7 +81,6 @@ class TextParameterItem(pTypes.WidgetParameterItem):
return self.textBox return self.textBox
class TextParameter(Parameter): class TextParameter(Parameter):
type = 'text'
itemClass = TextParameterItem itemClass = TextParameterItem
registerParameterType('text', TextParameter) registerParameterType('text', TextParameter)
@ -151,7 +150,9 @@ t2.setParameters(p, showTop=False)
t2.show() t2.show()
t2.resize(400,600) t2.resize(400,600)
## test save/restore
s = p.saveState()
p.restoreState(s)
## Start Qt event loop unless running in interactive mode or using pyside. ## Start Qt event loop unless running in interactive mode or using pyside.

View File

@ -14,14 +14,39 @@ def registerParameterType(name, cls, override=False):
class Parameter(QtCore.QObject): class Parameter(QtCore.QObject):
"""Tree of name=value pairs (modifiable or not) """
Tree of name=value pairs (modifiable or not)
- Value may be integer, float, string, bool, color, or list selection - Value may be integer, float, string, bool, color, or list selection
- Optionally, a custom widget may be specified for a property - Optionally, a custom widget may be specified for a property
- Any number of extra columns may be added for other purposes - Any number of extra columns may be added for other purposes
- Any values may be reset to a default value - Any values may be reset to a default value
- Parameters may be grouped / nested - Parameters may be grouped / nested
- Parameter may be subclassed to provide customized behavior.
For more Parameter types, see ParameterTree.parameterTypes module. For more Parameter types, see ParameterTree.parameterTypes module.
=================================== =========================================================
**Signals:**
sigStateChanged(self, change, info) Emitted when anything changes about this parameter at
all.
The second argument is a string indicating what changed
('value', 'childAdded', etc..)
The third argument can be any extra information about
the change
sigTreeStateChanged(self, changes) Emitted when any child in the tree changes state
(but only if monitorChildren() is called)
the format of *changes* is [(param, change, info), ...]
sigValueChanged(self, value) Emitted when value is finished changing
sigValueChanging(self, value) Emitted immediately for all value changes,
including during editing.
sigChildAdded(self, child, index) Emitted when a child is added
sigChildRemoved(self, child) Emitted when a child is removed
sigParentChanged(self, parent) Emitted when this parameter's parent has changed
sigLimitsChanged(self, limits) Emitted when this parameter's limits have changed
sigDefaultChanged(self, default) Emitted when this parameter's default value has changed
sigNameChanged(self, name) Emitted when this parameter's name has changed
sigOptionsChanged(self, opts) Emitted when any of this parameter's options have changed
=================================== =========================================================
""" """
## name, type, limits, etc. ## name, type, limits, etc.
## can also carry UI hints (slider vs spinbox, etc.) ## can also carry UI hints (slider vs spinbox, etc.)
@ -70,6 +95,7 @@ class Parameter(QtCore.QObject):
QtCore.QObject.__init__(self) QtCore.QObject.__init__(self)
self.opts = { self.opts = {
'type': None,
'readonly': False, 'readonly': False,
'visible': True, 'visible': True,
'enabled': True, 'enabled': True,
@ -130,6 +156,9 @@ class Parameter(QtCore.QObject):
self.sigNameChanged.emit(self, name) self.sigNameChanged.emit(self, name)
return name return name
def type(self):
return self.opts['type']
def childPath(self, child): def childPath(self, child):
""" """
Return the path of parameter names from self to child. Return the path of parameter names from self to child.
@ -171,29 +200,102 @@ class Parameter(QtCore.QObject):
return vals return vals
def saveState(self): def saveState(self):
"""Return a structure representing the entire state of the parameter tree.""" """
Return a structure representing the entire state of the parameter tree.
The tree state may be restored from this structure using restoreState()
"""
state = self.opts.copy() state = self.opts.copy()
state['children'] = {ch.name(): ch.saveState() for ch in self} state['children'] = [ch.saveState() for ch in self]
return state return state
def restoreState(self, state, recursive=True, addChildren=True, removeChildren=True):
"""
Restore the state of this parameter and its children from a structure generated using saveState()
If recursive is True, then attempt to restore the state of child parameters as well.
If addChildren is True, then any children which are referenced in the state object will be
created if they do not already exist.
If removeChildren is True, then any children which are not referenced in the state object will
be removed.
"""
childState = state.get('children', [])
self.setOpts(**state)
if not recursive:
return
ptr = 0 ## pointer to first child that has not been restored yet
foundChilds = set()
#print "==============", self.name()
for ch in childState:
name = ch['name']
typ = ch['type']
#print('child: %s, %s' % (self.name()+'.'+name, typ))
## First, see if there is already a child with this name and type
gotChild = False
for i, ch2 in enumerate(self.childs[ptr:]):
#print ch2, ch2.name, ch2.type
if ch2.name() != name or ch2.type() != typ:
continue
gotChild = True
#print " found it"
if i != 0: ## move parameter to next position
self.removeChild(ch2)
self.insertChild(ptr, ch2)
#print " moved to position", ptr
ch2.restoreState(ch, recursive=recursive, addChildren=addChildren, removeChildren=removeChildren)
foundChilds.add(ch2)
break
if not gotChild:
if not addChildren:
#print " ignored child"
continue
#print " created new"
ch2 = Parameter.create(**ch)
self.insertChild(ptr, ch2)
foundChilds.add(ch2)
ptr += 1
if removeChildren:
for ch in self:
if ch not in foundChilds:
#print " remove:", ch
self.removeChild(ch)
def defaultValue(self): def defaultValue(self):
"""Return the default value for this parameter."""
return self.opts['default'] return self.opts['default']
def setDefault(self, val): def setDefault(self, val):
"""Set the default value for this parameter."""
if self.opts['default'] == val:
return
self.opts['default'] = val self.opts['default'] = val
self.sigDefaultChanged.emit(self, val) self.sigDefaultChanged.emit(self, val)
def setToDefault(self): def setToDefault(self):
"""Set this parameter's value to the default."""
if self.hasDefault(): if self.hasDefault():
self.setValue(self.defaultValue()) self.setValue(self.defaultValue())
def hasDefault(self): def hasDefault(self):
"""Returns True if this parameter has a default value."""
return 'default' in self.opts return 'default' in self.opts
def valueIsDefault(self): def valueIsDefault(self):
"""Returns True if this parameter's value is equal to the default value."""
return self.value() == self.defaultValue() return self.value() == self.defaultValue()
def setLimits(self, limits): def setLimits(self, limits):
"""Set limits on the acceptable values for this parameter.
The format of limits depends on the type of the parameter and
some parameters do not make use of limits at all."""
if 'limits' in self.opts and self.opts['limits'] == limits: if 'limits' in self.opts and self.opts['limits'] == limits:
return return
self.opts['limits'] = limits self.opts['limits'] = limits
@ -201,10 +303,20 @@ class Parameter(QtCore.QObject):
return limits return limits
def writable(self): def writable(self):
"""
Returns True if this parameter's value can be changed by the user.
Note that the value of the parameter can *always* be changed by
calling setValue().
"""
return not self.opts.get('readonly', False) return not self.opts.get('readonly', False)
def setOpts(self, **opts): def setOpts(self, **opts):
"""For setting any arbitrary options.""" """
Set any arbitrary options on this parameter.
The exact behavior of this function will depend on the parameter type, but
most parameters will accept a common set of options: value, name, limits,
default, readonly, removable, renamable, visible, and enabled.
"""
changed = collections.OrderedDict() changed = collections.OrderedDict()
for k in opts: for k in opts:
if k == 'value': if k == 'value':
@ -275,6 +387,7 @@ class Parameter(QtCore.QObject):
return child return child
def removeChild(self, child): def removeChild(self, child):
"""Remove a child parameter."""
name = child.name() name = child.name()
if name not in self.names or self.names[name] is not child: if name not in self.names or self.names[name] is not child:
raise Exception("Parameter %s is not my child; can't remove." % str(child)) raise Exception("Parameter %s is not my child; can't remove." % str(child))
@ -286,22 +399,27 @@ class Parameter(QtCore.QObject):
child.sigTreeStateChanged.disconnect(self.treeStateChanged) child.sigTreeStateChanged.disconnect(self.treeStateChanged)
def clearChildren(self): def clearChildren(self):
"""Remove all child parameters."""
for ch in self.childs[:]: for ch in self.childs[:]:
self.removeChild(ch) self.removeChild(ch)
def children(self): def children(self):
"""Return a list of this parameter's children."""
## warning -- this overrides QObject.children ## warning -- this overrides QObject.children
return self.childs[:] return self.childs[:]
def parentChanged(self, parent): def parentChanged(self, parent):
"""This method is called when the parameter's parent has changed.
It may be useful to extend this method in subclasses."""
self._parent = parent self._parent = parent
self.sigParentChanged.emit(self, parent) self.sigParentChanged.emit(self, parent)
def parent(self): def parent(self):
"""Return the parent of this parameter."""
return self._parent return self._parent
def remove(self): def remove(self):
"""Remove self from parent's child list""" """Remove this parameter from its parent's child list"""
parent = self.parent() parent = self.parent()
if parent is None: if parent is None:
raise Exception("Cannot remove; no parent.") raise Exception("Cannot remove; no parent.")
@ -327,13 +445,21 @@ class Parameter(QtCore.QObject):
yield ch yield ch
def __getitem__(self, names): def __getitem__(self, names):
"""Get the value of a child parameter""" """Get the value of a child parameter. The name may also be a tuple giving
the path to a sub-parameter::
value = param[('child', 'grandchild')]
"""
if not isinstance(names, tuple): if not isinstance(names, tuple):
names = (names,) names = (names,)
return self.param(*names).value() return self.param(*names).value()
def __setitem__(self, names, value): def __setitem__(self, names, value):
"""Set the value of a child parameter""" """Set the value of a child parameter. The name may also be a tuple giving
the path to a sub-parameter::
param[('child', 'grandchild')] = value
"""
if isinstance(names, basestring): if isinstance(names, basestring):
names = (names,) names = (names,)
return self.param(*names).setValue(value) return self.param(*names).setValue(value)
@ -355,6 +481,7 @@ class Parameter(QtCore.QObject):
return "<%s '%s' at 0x%x>" % (self.__class__.__name__, self.name(), id(self)) return "<%s '%s' at 0x%x>" % (self.__class__.__name__, self.name(), id(self))
def __getattr__(self, attr): def __getattr__(self, attr):
## Leaving this undocumented because I might like to remove it in the future..
#print type(self), attr #print type(self), attr
if attr in self.names: if attr in self.names:
return self.param(attr) return self.param(attr)
@ -373,9 +500,12 @@ class Parameter(QtCore.QObject):
self.items[item] = None self.items[item] = None
def hide(self): def hide(self):
"""Hide this parameter. It and its children will no longer be visible in any ParameterTree
widgets it is connected to."""
self.show(False) self.show(False)
def show(self, s=True): def show(self, s=True):
"""Show this parameter. """
self.opts['visible'] = s self.opts['visible'] = s
self.sigOptionsChanged.emit(self, {'visible': s}) self.sigOptionsChanged.emit(self, {'visible': s})

View File

@ -366,7 +366,6 @@ class GroupParameter(Parameter):
of child parameters. It also provides a simple mechanism for displaying a button or combo of child parameters. It also provides a simple mechanism for displaying a button or combo
that can be used to add new parameters to the group. that can be used to add new parameters to the group.
""" """
type = 'group'
itemClass = GroupParameterItem itemClass = GroupParameterItem
def addNew(self, typ=None): def addNew(self, typ=None):
@ -457,7 +456,6 @@ class ListParameterItem(WidgetParameterItem):
class ListParameter(Parameter): class ListParameter(Parameter):
type = 'list'
itemClass = ListParameterItem itemClass = ListParameterItem
def __init__(self, **opts): def __init__(self, **opts):