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