Commit 237625a4 authored by Luke Campagnola's avatar Luke Campagnola
Browse files

Parametertree updates:

  - Fixed up parametertree.saveState() and restoreState() methods
  - Updated parametertree documentation
parents e62af590 1c94d1b8
......@@ -39,6 +39,7 @@ examples = OrderedDict([
('Widgets', OrderedDict([
('PlotWidget', 'PlotWidget.py'),
('SpinBox', 'SpinBox.py'),
('ConsoleWidget', 'ConsoleWidget.py'),
('TreeWidget', 'TreeWidget.py'),
('DataTreeWidget', 'DataTreeWidget.py'),
('GradientWidget', 'GradientWidget.py'),
......
......@@ -81,7 +81,6 @@ class TextParameterItem(pTypes.WidgetParameterItem):
return self.textBox
class TextParameter(Parameter):
type = 'text'
itemClass = TextParameterItem
registerParameterType('text', TextParameter)
......@@ -151,7 +150,9 @@ t2.setParameters(p, showTop=False)
t2.show()
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.
......
......@@ -14,14 +14,39 @@ def registerParameterType(name, cls, override=False):
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
- Optionally, a custom widget may be specified for a property
- Any number of extra columns may be added for other purposes
- Any values may be reset to a default value
- Parameters may be grouped / nested
- Parameter may be subclassed to provide customized behavior.
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.
## can also carry UI hints (slider vs spinbox, etc.)
......@@ -70,6 +95,7 @@ class Parameter(QtCore.QObject):
QtCore.QObject.__init__(self)
self.opts = {
'type': None,
'readonly': False,
'visible': True,
'enabled': True,
......@@ -130,6 +156,9 @@ class Parameter(QtCore.QObject):
self.sigNameChanged.emit(self, name)
return name
def type(self):
return self.opts['type']
def childPath(self, child):
"""
Return the path of parameter names from self to child.
......@@ -171,29 +200,102 @@ class Parameter(QtCore.QObject):
return vals
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['children'] = {ch.name(): ch.saveState() for ch in self}
state['children'] = [ch.saveState() for ch in self]
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):
"""Return the default value for this parameter."""
return self.opts['default']
def setDefault(self, val):
"""Set the default value for this parameter."""
if self.opts['default'] == val:
return
self.opts['default'] = val
self.sigDefaultChanged.emit(self, val)
def setToDefault(self):
"""Set this parameter's value to the default."""
if self.hasDefault():
self.setValue(self.defaultValue())
def hasDefault(self):
"""Returns True if this parameter has a default value."""
return 'default' in self.opts
def valueIsDefault(self):
"""Returns True if this parameter's value is equal to the default value."""
return self.value() == self.defaultValue()
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:
return
self.opts['limits'] = limits
......@@ -201,10 +303,20 @@ class Parameter(QtCore.QObject):
return limits
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)
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()
for k in opts:
if k == 'value':
......@@ -275,6 +387,7 @@ class Parameter(QtCore.QObject):
return child
def removeChild(self, child):
"""Remove a child parameter."""
name = child.name()
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))
......@@ -286,22 +399,27 @@ class Parameter(QtCore.QObject):
child.sigTreeStateChanged.disconnect(self.treeStateChanged)
def clearChildren(self):
"""Remove all child parameters."""
for ch in self.childs[:]:
self.removeChild(ch)
def children(self):
"""Return a list of this parameter's children."""
## warning -- this overrides QObject.children
return self.childs[:]
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.sigParentChanged.emit(self, parent)
def parent(self):
"""Return the parent of this parameter."""
return self._parent
def remove(self):
"""Remove self from parent's child list"""
"""Remove this parameter from its parent's child list"""
parent = self.parent()
if parent is None:
raise Exception("Cannot remove; no parent.")
......@@ -327,13 +445,21 @@ class Parameter(QtCore.QObject):
yield ch
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):
names = (names,)
return self.param(*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):
names = (names,)
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))
def __getattr__(self, attr):
## Leaving this undocumented because I might like to remove it in the future..
#print type(self), attr
if attr in self.names:
return self.param(attr)
......@@ -373,9 +500,12 @@ class Parameter(QtCore.QObject):
self.items[item] = None
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)
def show(self, s=True):
"""Show this parameter. """
self.opts['visible'] = s
self.sigOptionsChanged.emit(self, {'visible': s})
......
......@@ -366,7 +366,6 @@ class GroupParameter(Parameter):
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.
"""
type = 'group'
itemClass = GroupParameterItem
def addNew(self, typ=None):
......@@ -457,7 +456,6 @@ class ListParameterItem(WidgetParameterItem):
class ListParameter(Parameter):
type = 'list'
itemClass = ListParameterItem
def __init__(self, **opts):
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment