Fix: Parameter tree ignores user-set 'expanded' state (#1175)
* Fix: Parameter tree ignores user-set 'expanded' state When setting the 'expanded' state of parameters, this change is not applied in the graphically visible tree. This commit changes that behaviour by adding a clause in `ParameterItem.optsChanged` to react to that. Fixes #1130 * ParameterTree: Add option to synchronize "expanded" state As seen in #1130, there is interest in synchronizing the "expanded" state of `Parameter`s in `ParameterTree`s. As a default, this would lead to users being forced to always have multiple `ParameterTree`s to be expanded in the exact same way. Since that might not be desirable, this commit adds an option to customize whether synchronization of the "expanded" state should happen. * Fix: Sync Parameter options "renamable" and "removable" with ParameterTrees Currently, `Parameter` options `renamable` and `removable` are only considered when building a new `ParameterTree`. This commit makes changes in those options reflected in the corresponding `ParameterItem`s. * ParameterTree: Reflect changes in Parameter option 'tip' * Parameter: When setting "syncExpanded", update "expanded" state directly Co-authored-by: 2xB <2xB@users.noreply.github.com>
This commit is contained in:
parent
03b8385e62
commit
7672b5b725
@ -137,9 +137,12 @@ class Parameter(QtCore.QObject):
|
||||
(default=False)
|
||||
removable If True, the user may remove this Parameter.
|
||||
(default=False)
|
||||
expanded If True, the Parameter will appear expanded when
|
||||
displayed in a ParameterTree (its children will be
|
||||
visible). (default=True)
|
||||
expanded If True, the Parameter will initially be expanded in
|
||||
ParameterTrees: Its children will be visible.
|
||||
(default=True)
|
||||
syncExpanded If True, the `expanded` state of this Parameter is
|
||||
synchronized with all ParameterTrees it is displayed in.
|
||||
(default=False)
|
||||
title (str or None) If specified, then the parameter will be
|
||||
displayed to the user using this string as its name.
|
||||
However, the parameter will still be referred to
|
||||
@ -161,6 +164,7 @@ class Parameter(QtCore.QObject):
|
||||
'removable': False,
|
||||
'strictNaming': False, # forces name to be usable as a python variable
|
||||
'expanded': True,
|
||||
'syncExpanded': False,
|
||||
'title': None,
|
||||
#'limits': None, ## This is a bad plan--each parameter type may have a different data type for limits.
|
||||
}
|
||||
@ -461,7 +465,7 @@ class Parameter(QtCore.QObject):
|
||||
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, enabled, and expanded.
|
||||
default, readonly, removable, renamable, visible, enabled, expanded and syncExpanded.
|
||||
|
||||
See :func:`Parameter.__init__ <pyqtgraph.parametertree.Parameter.__init__>`
|
||||
for more information on default options.
|
||||
|
@ -34,30 +34,20 @@ class ParameterItem(QtGui.QTreeWidgetItem):
|
||||
param.sigOptionsChanged.connect(self.optsChanged)
|
||||
param.sigParentChanged.connect(self.parentChanged)
|
||||
|
||||
opts = param.opts
|
||||
self.updateFlags()
|
||||
|
||||
## flag used internally during name editing
|
||||
self.ignoreNameColumnChange = False
|
||||
|
||||
def updateFlags(self):
|
||||
## called when Parameter opts changed
|
||||
opts = self.param.opts
|
||||
|
||||
## Generate context menu for renaming/removing parameter
|
||||
self.contextMenu = QtGui.QMenu()
|
||||
self.contextMenu.addSeparator()
|
||||
flags = QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled
|
||||
if opts.get('renamable', False):
|
||||
if param.opts.get('title', None) is not None:
|
||||
if opts.get('title', None) is not None:
|
||||
raise Exception("Cannot make parameter with both title != None and renamable == True.")
|
||||
flags |= QtCore.Qt.ItemIsEditable
|
||||
self.contextMenu.addAction('Rename').triggered.connect(self.editName)
|
||||
if opts.get('removable', False):
|
||||
self.contextMenu.addAction("Remove").triggered.connect(self.requestRemove)
|
||||
|
||||
# context menu
|
||||
context = opts.get('context', None)
|
||||
if isinstance(context, list):
|
||||
for name in context:
|
||||
self.contextMenu.addAction(name).triggered.connect(
|
||||
self.contextMenuTriggered(name))
|
||||
elif isinstance(context, dict):
|
||||
for name, title in context.items():
|
||||
self.contextMenu.addAction(title).triggered.connect(
|
||||
self.contextMenuTriggered(name))
|
||||
|
||||
## handle movable / dropEnabled options
|
||||
if opts.get('movable', False):
|
||||
@ -65,9 +55,6 @@ class ParameterItem(QtGui.QTreeWidgetItem):
|
||||
if opts.get('dropEnabled', False):
|
||||
flags |= QtCore.Qt.ItemIsDropEnabled
|
||||
self.setFlags(flags)
|
||||
|
||||
## flag used internally during name editing
|
||||
self.ignoreNameColumnChange = False
|
||||
|
||||
|
||||
def valueChanged(self, param, val):
|
||||
@ -120,7 +107,26 @@ class ParameterItem(QtGui.QTreeWidgetItem):
|
||||
if not self.param.opts.get('removable', False) and not self.param.opts.get('renamable', False)\
|
||||
and "context" not in self.param.opts:
|
||||
return
|
||||
|
||||
|
||||
## Generate context menu for renaming/removing parameter
|
||||
self.contextMenu = QtGui.QMenu() # Put in global name space to prevent garbage collection
|
||||
self.contextMenu.addSeparator()
|
||||
if self.param.opts.get('renamable', False):
|
||||
self.contextMenu.addAction('Rename').triggered.connect(self.editName)
|
||||
if self.param.opts.get('removable', False):
|
||||
self.contextMenu.addAction("Remove").triggered.connect(self.requestRemove)
|
||||
|
||||
# context menu
|
||||
context = opts.get('context', None)
|
||||
if isinstance(context, list):
|
||||
for name in context:
|
||||
self.contextMenu.addAction(name).triggered.connect(
|
||||
self.contextMenuTriggered(name))
|
||||
elif isinstance(context, dict):
|
||||
for name, title in context.items():
|
||||
self.contextMenu.addAction(title).triggered.connect(
|
||||
self.contextMenuTriggered(name))
|
||||
|
||||
self.contextMenu.popup(ev.globalPos())
|
||||
|
||||
def columnChangedEvent(self, col):
|
||||
@ -141,6 +147,10 @@ class ParameterItem(QtGui.QTreeWidgetItem):
|
||||
self.nameChanged(self, newName) ## If the parameter rejects the name change, we need to set it back.
|
||||
finally:
|
||||
self.ignoreNameColumnChange = False
|
||||
|
||||
def expandedChangedEvent(self, expanded):
|
||||
if self.param.opts['syncExpanded']:
|
||||
self.param.setOpts(expanded=expanded)
|
||||
|
||||
def nameChanged(self, param, name):
|
||||
## called when the parameter's name has changed.
|
||||
@ -158,10 +168,22 @@ class ParameterItem(QtGui.QTreeWidgetItem):
|
||||
def optsChanged(self, param, opts):
|
||||
"""Called when any options are changed that are not
|
||||
name, value, default, or limits"""
|
||||
#print opts
|
||||
if 'visible' in opts:
|
||||
self.setHidden(not opts['visible'])
|
||||
|
||||
if 'expanded' in opts:
|
||||
if self.param.opts['syncExpanded']:
|
||||
if self.isExpanded() != opts['expanded']:
|
||||
self.setExpanded(opts['expanded'])
|
||||
|
||||
if 'syncExpanded' in opts:
|
||||
if opts['syncExpanded']:
|
||||
if self.isExpanded() != self.param.opts['expanded']:
|
||||
self.setExpanded(self.param.opts['expanded'])
|
||||
|
||||
self.updateFlags()
|
||||
|
||||
|
||||
def contextMenuTriggered(self, name):
|
||||
def trigger():
|
||||
self.param.contextMenu(name)
|
||||
|
@ -28,6 +28,8 @@ class ParameterTree(TreeWidget):
|
||||
self.header().setResizeMode(QtGui.QHeaderView.ResizeToContents)
|
||||
self.setHeaderHidden(not showHeader)
|
||||
self.itemChanged.connect(self.itemChangedEvent)
|
||||
self.itemExpanded.connect(self.itemExpandedEvent)
|
||||
self.itemCollapsed.connect(self.itemCollapsedEvent)
|
||||
self.lastSel = None
|
||||
self.setRootIsDecorated(False)
|
||||
|
||||
@ -134,6 +136,14 @@ class ParameterTree(TreeWidget):
|
||||
def itemChangedEvent(self, item, col):
|
||||
if hasattr(item, 'columnChangedEvent'):
|
||||
item.columnChangedEvent(col)
|
||||
|
||||
def itemExpandedEvent(self, item):
|
||||
if hasattr(item, 'expandedChangedEvent'):
|
||||
item.expandedChangedEvent(True)
|
||||
|
||||
def itemCollapsedEvent(self, item):
|
||||
if hasattr(item, 'expandedChangedEvent'):
|
||||
item.expandedChangedEvent(False)
|
||||
|
||||
def selectionChanged(self, *args):
|
||||
sel = self.selectedItems()
|
||||
|
@ -44,10 +44,6 @@ class WidgetParameterItem(ParameterItem):
|
||||
self.widget = w
|
||||
self.eventProxy = EventProxy(w, self.widgetEventFilter)
|
||||
|
||||
opts = self.param.opts
|
||||
if 'tip' in opts:
|
||||
w.setToolTip(opts['tip'])
|
||||
|
||||
self.defaultBtn = QtGui.QPushButton()
|
||||
self.defaultBtn.setFixedWidth(20)
|
||||
self.defaultBtn.setFixedHeight(20)
|
||||
@ -73,6 +69,7 @@ class WidgetParameterItem(ParameterItem):
|
||||
w.sigChanging.connect(self.widgetValueChanging)
|
||||
|
||||
## update value shown in widget.
|
||||
opts = self.param.opts
|
||||
if opts.get('value', None) is not None:
|
||||
self.valueChanged(self, opts['value'], force=True)
|
||||
else:
|
||||
@ -80,6 +77,8 @@ class WidgetParameterItem(ParameterItem):
|
||||
self.widgetValueChanged()
|
||||
|
||||
self.updateDefaultBtn()
|
||||
|
||||
self.optsChanged(self.param, self.param.opts)
|
||||
|
||||
def makeWidget(self):
|
||||
"""
|
||||
@ -280,6 +279,9 @@ class WidgetParameterItem(ParameterItem):
|
||||
if isinstance(self.widget, (QtGui.QCheckBox,ColorButton)):
|
||||
self.widget.setEnabled(not opts['readonly'])
|
||||
|
||||
if 'tip' in opts:
|
||||
self.widget.setToolTip(opts['tip'])
|
||||
|
||||
## If widget is a SpinBox, pass options straight through
|
||||
if isinstance(self.widget, SpinBox):
|
||||
# send only options supported by spinbox
|
||||
|
Loading…
Reference in New Issue
Block a user