1# -*- coding: utf-8 -*-
2from .. import functions as fn
3from ..Qt import QtCore
4import weakref, re
5from collections import OrderedDict
6from .ParameterItem import ParameterItem
7import warnings
8
9PARAM_TYPES = {}
10PARAM_NAMES = {}
11
12_PARAM_ITEM_TYPES = {}
13
14def registerParameterItemType(name, itemCls, parameterCls=None, override=False):
15    """
16    Similar to :func:`registerParameterType`, but works on ParameterItems. This is useful for Parameters where the
17    `itemClass` does all the heavy lifting, and a redundant Parameter class must be defined just to house `itemClass`.
18    Instead, use `registerParameterItemType`. If this should belong to a subclass of `Parameter`, specify which one
19    in `parameterCls`.
20    """
21    global _PARAM_ITEM_TYPES
22    if name in _PARAM_ITEM_TYPES and not override:
23        raise Exception("Parameter item type '%s' already exists (use override=True to replace)" % name)
24
25    parameterCls = parameterCls or Parameter
26    _PARAM_ITEM_TYPES[name] = itemCls
27    registerParameterType(name, parameterCls, override)
28
29
30def registerParameterType(name, cls, override=False):
31    """Register a parameter type in the parametertree system.
32
33    This enables construction of custom Parameter classes by name in
34    :meth:`~pyqtgraph.parametertree.Parameter.create`.
35    """
36    global PARAM_TYPES
37    if name in PARAM_TYPES and not override:
38        raise Exception("Parameter type '%s' already exists (use override=True to replace)" % name)
39    PARAM_TYPES[name] = cls
40    PARAM_NAMES[cls] = name
41
42def __reload__(old):
43    PARAM_TYPES.update(old.get('PARAM_TYPES', {}))
44    PARAM_NAMES.update(old.get('PARAM_NAMES', {}))
45
46class Parameter(QtCore.QObject):
47    """
48    A Parameter is the basic unit of data in a parameter tree. Each parameter has
49    a name, a type, a value, and several other properties that modify the behavior of the
50    Parameter. Parameters may have parent / child / sibling relationships to construct
51    organized hierarchies. Parameters generally do not have any inherent GUI or visual
52    interpretation; instead they manage ParameterItem instances which take care of
53    display and user interaction.
54
55    Note: It is fairly uncommon to use the Parameter class directly; mostly you
56    will use subclasses which provide specialized type and data handling. The static
57    pethod Parameter.create(...) is an easy way to generate instances of these subclasses.
58
59    For more Parameter types, see ParameterTree.parameterTypes module.
60
61    ===================================  =========================================================
62    **Signals:**
63    sigStateChanged(self, change, info)  Emitted when anything changes about this parameter at
64                                         all.
65                                         The second argument is a string indicating what changed
66                                         ('value', 'childAdded', etc..)
67                                         The third argument can be any extra information about
68                                         the change
69    sigTreeStateChanged(self, changes)   Emitted when any child in the tree changes state
70                                         (but only if monitorChildren() is called)
71                                         the format of *changes* is [(param, change, info), ...]
72    sigValueChanged(self, value)         Emitted when value is finished changing
73    sigValueChanging(self, value)        Emitted immediately for all value changes,
74                                         including during editing.
75    sigChildAdded(self, child, index)    Emitted when a child is added
76    sigChildRemoved(self, child)         Emitted when a child is removed
77    sigRemoved(self)                     Emitted when this parameter is removed
78    sigParentChanged(self, parent)       Emitted when this parameter's parent has changed
79    sigLimitsChanged(self, limits)       Emitted when this parameter's limits have changed
80    sigDefaultChanged(self, default)     Emitted when this parameter's default value has changed
81    sigNameChanged(self, name)           Emitted when this parameter's name has changed
82    sigOptionsChanged(self, opts)        Emitted when any of this parameter's options have changed
83    sigContextMenu(self, name)           Emitted when a context menu was clicked
84    ===================================  =========================================================
85    """
86    ## name, type, limits, etc.
87    ## can also carry UI hints (slider vs spinbox, etc.)
88
89    itemClass = None
90
91    sigValueChanged = QtCore.Signal(object, object)  ## self, value   emitted when value is finished being edited
92    sigValueChanging = QtCore.Signal(object, object)  ## self, value  emitted as value is being edited
93
94    sigChildAdded = QtCore.Signal(object, object, object)  ## self, child, index
95    sigChildRemoved = QtCore.Signal(object, object)  ## self, child
96    sigRemoved = QtCore.Signal(object) ## self
97    sigParentChanged = QtCore.Signal(object, object)  ## self, parent
98    sigLimitsChanged = QtCore.Signal(object, object)  ## self, limits
99    sigDefaultChanged = QtCore.Signal(object, object)  ## self, default
100    sigNameChanged = QtCore.Signal(object, object)  ## self, name
101    sigOptionsChanged = QtCore.Signal(object, object)  ## self, {opt:val, ...}
102
103    ## Emitted when anything changes about this parameter at all.
104    ## The second argument is a string indicating what changed ('value', 'childAdded', etc..)
105    ## The third argument can be any extra information about the change
106    sigStateChanged = QtCore.Signal(object, object, object) ## self, change, info
107
108    ## emitted when any child in the tree changes state
109    ## (but only if monitorChildren() is called)
110    sigTreeStateChanged = QtCore.Signal(object, object)  # self, changes
111                                                         # changes = [(param, change, info), ...]
112    sigContextMenu = QtCore.Signal(object, object)       # self, name
113
114    # bad planning.
115    #def __new__(cls, *args, **opts):
116        #try:
117            #cls = PARAM_TYPES[opts['type']]
118        #except KeyError:
119            #pass
120        #return QtCore.QObject.__new__(cls, *args, **opts)
121
122    @staticmethod
123    def create(**opts):
124        """
125        Static method that creates a new Parameter (or subclass) instance using
126        opts['type'] to select the appropriate class.
127
128        All options are passed directly to the new Parameter's __init__ method.
129        Use registerParameterType() to add new class types.
130        """
131        typ = opts.get('type', None)
132        if typ is None:
133            cls = Parameter
134        else:
135            cls = PARAM_TYPES[opts['type']]
136        return cls(**opts)
137
138    def __init__(self, **opts):
139        """
140        Initialize a Parameter object. Although it is rare to directly create a
141        Parameter instance, the options available to this method are also allowed
142        by most Parameter subclasses.
143
144        =======================      =========================================================
145        **Keyword Arguments:**
146        name                         The name to give this Parameter. This is the name that
147                                     will appear in the left-most column of a ParameterTree
148                                     for this Parameter.
149        value                        The value to initially assign to this Parameter.
150        default                      The default value for this Parameter (most Parameters
151                                     provide an option to 'reset to default').
152        children                     A list of children for this Parameter. Children
153                                     may be given either as a Parameter instance or as a
154                                     dictionary to pass to Parameter.create(). In this way,
155                                     it is possible to specify complex hierarchies of
156                                     Parameters from a single nested data structure.
157        readonly                     If True, the user will not be allowed to edit this
158                                     Parameter. (default=False)
159        enabled                      If False, any widget(s) for this parameter will appear
160                                     disabled. (default=True)
161        visible                      If False, the Parameter will not appear when displayed
162                                     in a ParameterTree. (default=True)
163        renamable                    If True, the user may rename this Parameter.
164                                     (default=False)
165        removable                    If True, the user may remove this Parameter.
166                                     (default=False)
167        expanded                     If True, the Parameter will initially be expanded in
168                                     ParameterTrees: Its children will be visible.
169                                     (default=True)
170        syncExpanded                 If True, the `expanded` state of this Parameter is
171                                     synchronized with all ParameterTrees it is displayed in.
172                                     (default=False)
173        title                        (str or None) If specified, then the parameter will be
174                                     displayed to the user using this string as its name.
175                                     However, the parameter will still be referred to
176                                     internally using the *name* specified above. Note that
177                                     this option is not compatible with renamable=True.
178                                     (default=None; added in version 0.9.9)
179        =======================      =========================================================
180        """
181
182
183        QtCore.QObject.__init__(self)
184
185        self.opts = {
186            'type': None,
187            'readonly': False,
188            'visible': True,
189            'enabled': True,
190            'renamable': False,
191            'removable': False,
192            'strictNaming': False,  # forces name to be usable as a python variable
193            'expanded': True,
194            'syncExpanded': False,
195            'title': None,
196            #'limits': None,  ## This is a bad plan--each parameter type may have a different data type for limits.
197        }
198        value = opts.get('value', None)
199        name = opts.get('name', None)
200        self.opts.update(opts)
201        self.opts['value'] = None  # will be set later.
202        self.opts['name'] = None
203
204        self.childs = []
205        self.names = {}   ## map name:child
206        self.items = weakref.WeakKeyDictionary()  ## keeps track of tree items representing this parameter
207        self._parent = None
208        self.treeStateChanges = []  ## cache of tree state changes to be delivered on next emit
209        self.blockTreeChangeEmit = 0
210        #self.monitoringChildren = False  ## prevent calling monitorChildren more than once
211
212        if not isinstance(name, str):
213            raise Exception("Parameter must have a string name specified in opts.")
214        self.setName(name)
215
216        self.addChildren(self.opts.pop('children', []))
217
218        if value is not None:
219            self.setValue(value)
220
221        if 'default' not in self.opts:
222            self.opts['default'] = None
223            self.setDefault(self.opts['value'])
224
225        ## Connect all state changed signals to the general sigStateChanged
226        self.sigValueChanged.connect(self._emitValueChanged)
227        self.sigChildAdded.connect(self._emitChildAddedChanged)
228        self.sigChildRemoved.connect(self._emitChildRemovedChanged)
229        self.sigParentChanged.connect(self._emitParentChanged)
230        self.sigLimitsChanged.connect(self._emitLimitsChanged)
231        self.sigDefaultChanged.connect(self._emitDefaultChanged)
232        self.sigNameChanged.connect(self._emitNameChanged)
233        self.sigOptionsChanged.connect(self._emitOptionsChanged)
234        self.sigContextMenu.connect(self._emitContextMenuChanged)
235
236
237        #self.watchParam(self)  ## emit treechange signals if our own state changes
238
239    def name(self):
240        """Return the name of this Parameter."""
241        return self.opts['name']
242
243    def title(self):
244        """Return the title of this Parameter.
245
246        By default, the title is the same as the name unless it has been explicitly specified
247        otherwise."""
248        title = self.opts.get('title', None)
249        if title is None:
250            title = self.name()
251        return title
252
253    def contextMenu(self, name):
254        """"A context menu entry was clicked"""
255        self.sigContextMenu.emit(self, name)
256
257    def setName(self, name):
258        """Attempt to change the name of this parameter; return the actual name.
259        (The parameter may reject the name change or automatically pick a different name)"""
260        if self.opts['strictNaming']:
261            if len(name) < 1 or re.search(r'\W', name) or re.match(r'\d', name[0]):
262                raise Exception("Parameter name '%s' is invalid. (Must contain only alphanumeric and underscore characters and may not start with a number)" % name)
263        parent = self.parent()
264        if parent is not None:
265            name = parent._renameChild(self, name)  ## first ask parent if it's ok to rename
266        if self.opts['name'] != name:
267            self.opts['name'] = name
268            self.sigNameChanged.emit(self, name)
269        return name
270
271    def type(self):
272        """Return the type string for this Parameter."""
273        return self.opts['type']
274
275    def isType(self, typ):
276        """
277        Return True if this parameter type matches the name *typ*.
278        This can occur either of two ways:
279
280          - If self.type() == *typ*
281          - If this parameter's class is registered with the name *typ*
282        """
283        if self.type() == typ:
284            return True
285        global PARAM_TYPES
286        cls = PARAM_TYPES.get(typ, None)
287        if cls is None:
288            raise Exception("Type name '%s' is not registered." % str(typ))
289        return self.__class__ is cls
290
291    def childPath(self, child):
292        """
293        Return the path of parameter names from self to child.
294        If child is not a (grand)child of self, return None.
295        """
296        path = []
297        while child is not self:
298            path.insert(0, child.name())
299            child = child.parent()
300            if child is None:
301                return None
302        return path
303
304    def setValue(self, value, blockSignal=None):
305        """
306        Set the value of this Parameter; return the actual value that was set.
307        (this may be different from the value that was requested)
308        """
309        try:
310            if blockSignal is not None:
311                self.sigValueChanged.disconnect(blockSignal)
312            value = self._interpretValue(value)
313            if fn.eq(self.opts['value'], value):
314                return value
315            self.opts['value'] = value
316            self.sigValueChanged.emit(self, value)  # value might change after signal is received by tree item
317        finally:
318            if blockSignal is not None:
319                self.sigValueChanged.connect(blockSignal)
320
321        return self.opts['value']
322
323    def _interpretValue(self, v):
324        return v
325
326    def value(self):
327        """
328        Return the value of this Parameter.
329        """
330        return self.opts['value']
331
332    def getValues(self):
333        """Return a tree of all values that are children of this parameter"""
334        vals = OrderedDict()
335        for ch in self:
336            vals[ch.name()] = (ch.value(), ch.getValues())
337        return vals
338
339    def saveState(self, filter=None):
340        """
341        Return a structure representing the entire state of the parameter tree.
342        The tree state may be restored from this structure using restoreState().
343
344        If *filter* is set to 'user', then only user-settable data will be included in the
345        returned state.
346        """
347        if filter is None:
348            state = self.opts.copy()
349            if state['type'] is None:
350                global PARAM_NAMES
351                state['type'] = PARAM_NAMES.get(type(self), None)
352        elif filter == 'user':
353            state = {'value': self.value()}
354        else:
355            raise ValueError("Unrecognized filter argument: '%s'" % filter)
356
357        ch = OrderedDict([(ch.name(), ch.saveState(filter=filter)) for ch in self])
358        if len(ch) > 0:
359            state['children'] = ch
360        return state
361
362    def restoreState(self, state, recursive=True, addChildren=True, removeChildren=True, blockSignals=True):
363        """
364        Restore the state of this parameter and its children from a structure generated using saveState()
365        If recursive is True, then attempt to restore the state of child parameters as well.
366        If addChildren is True, then any children which are referenced in the state object will be
367        created if they do not already exist.
368        If removeChildren is True, then any children which are not referenced in the state object will
369        be removed.
370        If blockSignals is True, no signals will be emitted until the tree has been completely restored.
371        This prevents signal handlers from responding to a partially-rebuilt network.
372        """
373        state = state.copy()
374        childState = state.pop('children', [])
375
376        ## list of children may be stored either as list or dict.
377        if isinstance(childState, dict):
378            cs = []
379            for k,v in childState.items():
380                cs.append(v.copy())
381                cs[-1].setdefault('name', k)
382            childState = cs
383
384        if blockSignals:
385            self.blockTreeChangeSignal()
386
387        try:
388            self.setOpts(**state)
389
390            if not recursive:
391                return
392
393            ptr = 0  ## pointer to first child that has not been restored yet
394            foundChilds = set()
395            #print "==============", self.name()
396
397            for ch in childState:
398                name = ch['name']
399                #typ = ch.get('type', None)
400                #print('child: %s, %s' % (self.name()+'.'+name, typ))
401
402                ## First, see if there is already a child with this name
403                gotChild = False
404                for i, ch2 in enumerate(self.childs[ptr:]):
405                    #print "  ", ch2.name(), ch2.type()
406                    if ch2.name() != name: # or not ch2.isType(typ):
407                        continue
408                    gotChild = True
409                    #print "    found it"
410                    if i != 0:  ## move parameter to next position
411                        #self.removeChild(ch2)
412                        self.insertChild(ptr, ch2)
413                        #print "  moved to position", ptr
414                    ch2.restoreState(ch, recursive=recursive, addChildren=addChildren, removeChildren=removeChildren)
415                    foundChilds.add(ch2)
416
417                    break
418
419                if not gotChild:
420                    if not addChildren:
421                        #print "  ignored child"
422                        continue
423                    #print "    created new"
424                    ch2 = Parameter.create(**ch)
425                    self.insertChild(ptr, ch2)
426                    foundChilds.add(ch2)
427
428                ptr += 1
429
430            if removeChildren:
431                for ch in self.childs[:]:
432                    if ch not in foundChilds:
433                        #print "  remove:", ch
434                        self.removeChild(ch)
435        finally:
436            if blockSignals:
437                self.unblockTreeChangeSignal()
438
439
440
441    def defaultValue(self):
442        """Return the default value for this parameter."""
443        return self.opts['default']
444
445    def setDefault(self, val):
446        """Set the default value for this parameter."""
447        if self.opts['default'] == val:
448            return
449        self.opts['default'] = val
450        self.sigDefaultChanged.emit(self, val)
451
452    def setToDefault(self):
453        """Set this parameter's value to the default."""
454        if self.hasDefault():
455            self.setValue(self.defaultValue())
456
457    def hasDefault(self):
458        """Returns True if this parameter has a default value."""
459        return self.opts['default'] is not None
460
461    def valueIsDefault(self):
462        """Returns True if this parameter's value is equal to the default value."""
463        return fn.eq(self.value(), self.defaultValue())
464
465    def setLimits(self, limits):
466        """Set limits on the acceptable values for this parameter.
467        The format of limits depends on the type of the parameter and
468        some parameters do not make use of limits at all."""
469        if 'limits' in self.opts and fn.eq(self.opts['limits'], limits):
470            return
471        self.opts['limits'] = limits
472        self.sigLimitsChanged.emit(self, limits)
473        return limits
474
475    def writable(self):
476        """
477        Returns True if this parameter's value can be changed by the user.
478        Note that the value of the parameter can *always* be changed by
479        calling setValue().
480        """
481        return not self.readonly()
482
483    def setWritable(self, writable=True):
484        """Set whether this Parameter should be editable by the user. (This is
485        exactly the opposite of setReadonly)."""
486        self.setOpts(readonly=not writable)
487
488    def readonly(self):
489        """
490        Return True if this parameter is read-only. (this is the opposite of writable())
491        """
492        return self.opts.get('readonly', False)
493
494    def setReadonly(self, readonly=True):
495        """Set whether this Parameter's value may be edited by the user
496        (this is the opposite of setWritable())."""
497        self.setOpts(readonly=readonly)
498
499    def setOpts(self, **opts):
500        """
501        Set any arbitrary options on this parameter.
502        The exact behavior of this function will depend on the parameter type, but
503        most parameters will accept a common set of options: value, name, limits,
504        default, readonly, removable, renamable, visible, enabled, expanded and syncExpanded.
505
506        See :func:`Parameter.__init__ <pyqtgraph.parametertree.Parameter.__init__>`
507        for more information on default options.
508        """
509        changed = OrderedDict()
510        for k in opts:
511            if k == 'value':
512                self.setValue(opts[k])
513            elif k == 'name':
514                self.setName(opts[k])
515            elif k == 'limits':
516                self.setLimits(opts[k])
517            elif k == 'default':
518                self.setDefault(opts[k])
519            elif k not in self.opts or not fn.eq(self.opts[k], opts[k]):
520                self.opts[k] = opts[k]
521                changed[k] = opts[k]
522
523        if len(changed) > 0:
524            self.sigOptionsChanged.emit(self, changed)
525
526    def emitStateChanged(self, changeDesc, data):
527        ## Emits stateChanged signal and
528        ## requests emission of new treeStateChanged signal
529        self.sigStateChanged.emit(self, changeDesc, data)
530        #self.treeStateChanged(self, changeDesc, data)
531        self.treeStateChanges.append((self, changeDesc, data))
532        self.emitTreeChanges()
533
534    def _emitValueChanged(self, param, data):
535        self.emitStateChanged("value", data)
536
537    def _emitChildAddedChanged(self, param, *data):
538        self.emitStateChanged("childAdded", data)
539
540    def _emitChildRemovedChanged(self, param, data):
541        self.emitStateChanged("childRemoved", data)
542
543    def _emitParentChanged(self, param, data):
544        self.emitStateChanged("parent", data)
545
546    def _emitLimitsChanged(self, param, data):
547        self.emitStateChanged("limits", data)
548
549    def _emitDefaultChanged(self, param, data):
550        self.emitStateChanged("default", data)
551
552    def _emitNameChanged(self, param, data):
553        self.emitStateChanged("name", data)
554
555    def _emitOptionsChanged(self, param, data):
556        self.emitStateChanged("options", data)
557
558    def _emitContextMenuChanged(self, param, data):
559        self.emitStateChanged("contextMenu", data)
560
561    def makeTreeItem(self, depth):
562        """
563        Return a TreeWidgetItem suitable for displaying/controlling the content of
564        this parameter. This is called automatically when a ParameterTree attempts
565        to display this Parameter.
566        Most subclasses will want to override this function.
567        """
568        # Default to user-specified itemClass. If not present, check for a registered item class. Finally,
569        # revert to ParameterItem if both fail
570        itemClass = self.itemClass or _PARAM_ITEM_TYPES.get(self.opts['type'], ParameterItem)
571        return itemClass(self, depth)
572
573
574    def addChild(self, child, autoIncrementName=None):
575        """
576        Add another parameter to the end of this parameter's child list.
577
578        See insertChild() for a description of the *autoIncrementName*
579        argument.
580        """
581        return self.insertChild(len(self.childs), child, autoIncrementName=autoIncrementName)
582
583    def addChildren(self, children):
584        """
585        Add a list or dict of children to this parameter. This method calls
586        addChild once for each value in *children*.
587        """
588        ## If children was specified as dict, then assume keys are the names.
589        if isinstance(children, dict):
590            ch2 = []
591            for name, opts in children.items():
592                if isinstance(opts, dict) and 'name' not in opts:
593                    opts = opts.copy()
594                    opts['name'] = name
595                ch2.append(opts)
596            children = ch2
597
598        for chOpts in children:
599            #print self, "Add child:", type(chOpts), id(chOpts)
600            self.addChild(chOpts)
601
602
603    def insertChild(self, pos, child, autoIncrementName=None):
604        """
605        Insert a new child at pos.
606        If pos is a Parameter, then insert at the position of that Parameter.
607        If child is a dict, then a parameter is constructed using
608        :func:`Parameter.create <pyqtgraph.parametertree.Parameter.create>`.
609
610        By default, the child's 'autoIncrementName' option determines whether
611        the name will be adjusted to avoid prior name collisions. This
612        behavior may be overridden by specifying the *autoIncrementName*
613        argument. This argument was added in version 0.9.9.
614        """
615        if isinstance(child, dict):
616            child = Parameter.create(**child)
617
618        name = child.name()
619        if name in self.names and child is not self.names[name]:
620            if autoIncrementName is True or (autoIncrementName is None and child.opts.get('autoIncrementName', False)):
621                name = self.incrementName(name)
622                child.setName(name)
623            else:
624                raise Exception("Already have child named %s" % str(name))
625        if isinstance(pos, Parameter):
626            pos = self.childs.index(pos)
627
628        with self.treeChangeBlocker():
629            if child.parent() is not None:
630                child.remove()
631
632            self.names[name] = child
633            self.childs.insert(pos, child)
634
635            child.parentChanged(self)
636            child.sigTreeStateChanged.connect(self.treeStateChanged)
637            self.sigChildAdded.emit(self, child, pos)
638        return child
639
640    def removeChild(self, child):
641        """Remove a child parameter."""
642        name = child.name()
643        if name not in self.names or self.names[name] is not child:
644            raise Exception("Parameter %s is not my child; can't remove." % str(child))
645        del self.names[name]
646        self.childs.pop(self.childs.index(child))
647        child.parentChanged(None)
648        try:
649            child.sigTreeStateChanged.disconnect(self.treeStateChanged)
650        except (TypeError, RuntimeError):  ## already disconnected
651            pass
652        self.sigChildRemoved.emit(self, child)
653
654    def clearChildren(self):
655        """Remove all child parameters."""
656        for ch in self.childs[:]:
657            self.removeChild(ch)
658
659    def children(self):
660        """Return a list of this parameter's children.
661        Warning: this overrides QObject.children
662        """
663        return self.childs[:]
664
665    def hasChildren(self):
666        """Return True if this Parameter has children."""
667        return len(self.childs) > 0
668
669    def parentChanged(self, parent):
670        """This method is called when the parameter's parent has changed.
671        It may be useful to extend this method in subclasses."""
672        self._parent = parent
673        self.sigParentChanged.emit(self, parent)
674
675    def parent(self):
676        """Return the parent of this parameter."""
677        return self._parent
678
679    def remove(self):
680        """Remove this parameter from its parent's child list"""
681        parent = self.parent()
682        if parent is None:
683            raise Exception("Cannot remove; no parent.")
684        parent.removeChild(self)
685        self.sigRemoved.emit(self)
686
687    def incrementName(self, name):
688        ## return an unused name by adding a number to the name given
689        base, num = re.match(r'(.*)(\d*)', name).groups()
690        numLen = len(num)
691        if numLen == 0:
692            num = 2
693            numLen = 1
694        else:
695            num = int(num)
696        while True:
697            newName = base + ("%%0%dd"%numLen) % num
698            if newName not in self.names:
699                return newName
700            num += 1
701
702    def __iter__(self):
703        for ch in self.childs:
704            yield ch
705
706    def __getitem__(self, names):
707        """Get the value of a child parameter. The name may also be a tuple giving
708        the path to a sub-parameter::
709
710            value = param[('child', 'grandchild')]
711        """
712        if not isinstance(names, tuple):
713            names = (names,)
714        return self.param(*names).value()
715
716    def __setitem__(self, names, value):
717        """Set the value of a child parameter. The name may also be a tuple giving
718        the path to a sub-parameter::
719
720            param[('child', 'grandchild')] = value
721        """
722        if isinstance(names, str):
723            names = (names,)
724        return self.param(*names).setValue(value)
725
726    def keys(self):
727        return self.names
728
729    def child(self, *names):
730        """Return a child parameter.
731        Accepts the name of the child or a tuple (path, to, child)
732
733        Added in version 0.9.9. Earlier versions used the 'param' method, which is still
734        implemented for backward compatibility.
735        """
736        try:
737            param = self.names[names[0]]
738        except KeyError:
739            raise KeyError("Parameter %s has no child named %s" % (self.name(), names[0]))
740
741        if len(names) > 1:
742            return param.child(*names[1:])
743        else:
744            return param
745
746    def param(self, *names):
747        # for backward compatibility.
748        return self.child(*names)
749
750    def __repr__(self):
751        return "<%s '%s' at 0x%x>" % (self.__class__.__name__, self.name(), id(self))
752
753    def __getattr__(self, attr):
754        ## Leaving this undocumented because I might like to remove it in the future..
755        #print type(self), attr
756        warnings.warn(
757            'Use of Parameter.subParam is deprecated and will be removed in 0.13 '
758            'Use Parameter.param(name) instead.',
759            DeprecationWarning, stacklevel=2
760        )
761        if 'names' not in self.__dict__:
762            raise AttributeError(attr)
763        if attr in self.names:
764            import traceback
765            traceback.print_stack()
766            print("Warning: Use of Parameter.subParam is deprecated. Use Parameter.param(name) instead.")
767            return self.param(attr)
768        else:
769            raise AttributeError(attr)
770
771    def _renameChild(self, child, name):
772        ## Only to be called from Parameter.rename
773        if name in self.names:
774            return child.name()
775        self.names[name] = child
776        del self.names[child.name()]
777        return name
778
779    def registerItem(self, item):
780        self.items[item] = None
781
782    def hide(self):
783        """Hide this parameter. It and its children will no longer be visible in any ParameterTree
784        widgets it is connected to."""
785        self.show(False)
786
787    def show(self, s=True):
788        """Show this parameter. """
789        self.opts['visible'] = s
790        self.sigOptionsChanged.emit(self, {'visible': s})
791
792
793    def treeChangeBlocker(self):
794        """
795        Return an object that can be used to temporarily block and accumulate
796        sigTreeStateChanged signals. This is meant to be used when numerous changes are
797        about to be made to the tree and only one change signal should be
798        emitted at the end.
799
800        Example::
801
802            with param.treeChangeBlocker():
803                param.addChild(...)
804                param.removeChild(...)
805                param.setValue(...)
806        """
807        return SignalBlocker(self.blockTreeChangeSignal, self.unblockTreeChangeSignal)
808
809    def blockTreeChangeSignal(self):
810        """
811        Used to temporarily block and accumulate tree change signals.
812        *You must remember to unblock*, so it is advisable to use treeChangeBlocker() instead.
813        """
814        self.blockTreeChangeEmit += 1
815
816    def unblockTreeChangeSignal(self):
817        """Unblocks enission of sigTreeStateChanged and flushes the changes out through a single signal."""
818        self.blockTreeChangeEmit -= 1
819        self.emitTreeChanges()
820
821
822    def treeStateChanged(self, param, changes):
823        """
824        Called when the state of any sub-parameter has changed.
825
826        ==============  ================================================================
827        **Arguments:**
828        param           The immediate child whose tree state has changed.
829                        note that the change may have originated from a grandchild.
830        changes         List of tuples describing all changes that have been made
831                        in this event: (param, changeDescr, data)
832        ==============  ================================================================
833
834        This function can be extended to react to tree state changes.
835        """
836        self.treeStateChanges.extend(changes)
837        self.emitTreeChanges()
838
839    def emitTreeChanges(self):
840        if self.blockTreeChangeEmit == 0:
841            changes = self.treeStateChanges
842            self.treeStateChanges = []
843            if len(changes) > 0:
844                self.sigTreeStateChanged.emit(self, changes)
845
846
847class SignalBlocker(object):
848    def __init__(self, enterFn, exitFn):
849        self.enterFn = enterFn
850        self.exitFn = exitFn
851
852    def __enter__(self):
853        self.enterFn()
854
855    def __exit__(self, exc_type, exc_value, tb):
856        self.exitFn()
857
858
859
860