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)
|
(default=False)
|
||||||
removable If True, the user may remove this Parameter.
|
removable If True, the user may remove this Parameter.
|
||||||
(default=False)
|
(default=False)
|
||||||
expanded If True, the Parameter will appear expanded when
|
expanded If True, the Parameter will initially be expanded in
|
||||||
displayed in a ParameterTree (its children will be
|
ParameterTrees: Its children will be visible.
|
||||||
visible). (default=True)
|
(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
|
title (str or None) If specified, then the parameter will be
|
||||||
displayed to the user using this string as its name.
|
displayed to the user using this string as its name.
|
||||||
However, the parameter will still be referred to
|
However, the parameter will still be referred to
|
||||||
@ -161,6 +164,7 @@ class Parameter(QtCore.QObject):
|
|||||||
'removable': False,
|
'removable': False,
|
||||||
'strictNaming': False, # forces name to be usable as a python variable
|
'strictNaming': False, # forces name to be usable as a python variable
|
||||||
'expanded': True,
|
'expanded': True,
|
||||||
|
'syncExpanded': False,
|
||||||
'title': None,
|
'title': None,
|
||||||
#'limits': None, ## This is a bad plan--each parameter type may have a different data type for limits.
|
#'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.
|
Set any arbitrary options on this parameter.
|
||||||
The exact behavior of this function will depend on the parameter type, but
|
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,
|
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__>`
|
See :func:`Parameter.__init__ <pyqtgraph.parametertree.Parameter.__init__>`
|
||||||
for more information on default options.
|
for more information on default options.
|
||||||
|
@ -34,30 +34,20 @@ class ParameterItem(QtGui.QTreeWidgetItem):
|
|||||||
param.sigOptionsChanged.connect(self.optsChanged)
|
param.sigOptionsChanged.connect(self.optsChanged)
|
||||||
param.sigParentChanged.connect(self.parentChanged)
|
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
|
flags = QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled
|
||||||
if opts.get('renamable', False):
|
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.")
|
raise Exception("Cannot make parameter with both title != None and renamable == True.")
|
||||||
flags |= QtCore.Qt.ItemIsEditable
|
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
|
## handle movable / dropEnabled options
|
||||||
if opts.get('movable', False):
|
if opts.get('movable', False):
|
||||||
@ -66,9 +56,6 @@ class ParameterItem(QtGui.QTreeWidgetItem):
|
|||||||
flags |= QtCore.Qt.ItemIsDropEnabled
|
flags |= QtCore.Qt.ItemIsDropEnabled
|
||||||
self.setFlags(flags)
|
self.setFlags(flags)
|
||||||
|
|
||||||
## flag used internally during name editing
|
|
||||||
self.ignoreNameColumnChange = False
|
|
||||||
|
|
||||||
|
|
||||||
def valueChanged(self, param, val):
|
def valueChanged(self, param, val):
|
||||||
## called when the parameter's value has changed
|
## called when the parameter's value has changed
|
||||||
@ -121,6 +108,25 @@ class ParameterItem(QtGui.QTreeWidgetItem):
|
|||||||
and "context" not in self.param.opts:
|
and "context" not in self.param.opts:
|
||||||
return
|
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())
|
self.contextMenu.popup(ev.globalPos())
|
||||||
|
|
||||||
def columnChangedEvent(self, col):
|
def columnChangedEvent(self, col):
|
||||||
@ -142,6 +148,10 @@ class ParameterItem(QtGui.QTreeWidgetItem):
|
|||||||
finally:
|
finally:
|
||||||
self.ignoreNameColumnChange = False
|
self.ignoreNameColumnChange = False
|
||||||
|
|
||||||
|
def expandedChangedEvent(self, expanded):
|
||||||
|
if self.param.opts['syncExpanded']:
|
||||||
|
self.param.setOpts(expanded=expanded)
|
||||||
|
|
||||||
def nameChanged(self, param, name):
|
def nameChanged(self, param, name):
|
||||||
## called when the parameter's name has changed.
|
## called when the parameter's name has changed.
|
||||||
if self.param.opts.get('title', None) is None:
|
if self.param.opts.get('title', None) is None:
|
||||||
@ -158,10 +168,22 @@ class ParameterItem(QtGui.QTreeWidgetItem):
|
|||||||
def optsChanged(self, param, opts):
|
def optsChanged(self, param, opts):
|
||||||
"""Called when any options are changed that are not
|
"""Called when any options are changed that are not
|
||||||
name, value, default, or limits"""
|
name, value, default, or limits"""
|
||||||
#print opts
|
|
||||||
if 'visible' in opts:
|
if 'visible' in opts:
|
||||||
self.setHidden(not opts['visible'])
|
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 contextMenuTriggered(self, name):
|
||||||
def trigger():
|
def trigger():
|
||||||
self.param.contextMenu(name)
|
self.param.contextMenu(name)
|
||||||
|
@ -28,6 +28,8 @@ class ParameterTree(TreeWidget):
|
|||||||
self.header().setResizeMode(QtGui.QHeaderView.ResizeToContents)
|
self.header().setResizeMode(QtGui.QHeaderView.ResizeToContents)
|
||||||
self.setHeaderHidden(not showHeader)
|
self.setHeaderHidden(not showHeader)
|
||||||
self.itemChanged.connect(self.itemChangedEvent)
|
self.itemChanged.connect(self.itemChangedEvent)
|
||||||
|
self.itemExpanded.connect(self.itemExpandedEvent)
|
||||||
|
self.itemCollapsed.connect(self.itemCollapsedEvent)
|
||||||
self.lastSel = None
|
self.lastSel = None
|
||||||
self.setRootIsDecorated(False)
|
self.setRootIsDecorated(False)
|
||||||
|
|
||||||
@ -135,6 +137,14 @@ class ParameterTree(TreeWidget):
|
|||||||
if hasattr(item, 'columnChangedEvent'):
|
if hasattr(item, 'columnChangedEvent'):
|
||||||
item.columnChangedEvent(col)
|
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):
|
def selectionChanged(self, *args):
|
||||||
sel = self.selectedItems()
|
sel = self.selectedItems()
|
||||||
if len(sel) != 1:
|
if len(sel) != 1:
|
||||||
|
@ -44,10 +44,6 @@ class WidgetParameterItem(ParameterItem):
|
|||||||
self.widget = w
|
self.widget = w
|
||||||
self.eventProxy = EventProxy(w, self.widgetEventFilter)
|
self.eventProxy = EventProxy(w, self.widgetEventFilter)
|
||||||
|
|
||||||
opts = self.param.opts
|
|
||||||
if 'tip' in opts:
|
|
||||||
w.setToolTip(opts['tip'])
|
|
||||||
|
|
||||||
self.defaultBtn = QtGui.QPushButton()
|
self.defaultBtn = QtGui.QPushButton()
|
||||||
self.defaultBtn.setFixedWidth(20)
|
self.defaultBtn.setFixedWidth(20)
|
||||||
self.defaultBtn.setFixedHeight(20)
|
self.defaultBtn.setFixedHeight(20)
|
||||||
@ -73,6 +69,7 @@ class WidgetParameterItem(ParameterItem):
|
|||||||
w.sigChanging.connect(self.widgetValueChanging)
|
w.sigChanging.connect(self.widgetValueChanging)
|
||||||
|
|
||||||
## update value shown in widget.
|
## update value shown in widget.
|
||||||
|
opts = self.param.opts
|
||||||
if opts.get('value', None) is not None:
|
if opts.get('value', None) is not None:
|
||||||
self.valueChanged(self, opts['value'], force=True)
|
self.valueChanged(self, opts['value'], force=True)
|
||||||
else:
|
else:
|
||||||
@ -81,6 +78,8 @@ class WidgetParameterItem(ParameterItem):
|
|||||||
|
|
||||||
self.updateDefaultBtn()
|
self.updateDefaultBtn()
|
||||||
|
|
||||||
|
self.optsChanged(self.param, self.param.opts)
|
||||||
|
|
||||||
def makeWidget(self):
|
def makeWidget(self):
|
||||||
"""
|
"""
|
||||||
Return a single widget that should be placed in the second tree column.
|
Return a single widget that should be placed in the second tree column.
|
||||||
@ -280,6 +279,9 @@ class WidgetParameterItem(ParameterItem):
|
|||||||
if isinstance(self.widget, (QtGui.QCheckBox,ColorButton)):
|
if isinstance(self.widget, (QtGui.QCheckBox,ColorButton)):
|
||||||
self.widget.setEnabled(not opts['readonly'])
|
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 widget is a SpinBox, pass options straight through
|
||||||
if isinstance(self.widget, SpinBox):
|
if isinstance(self.widget, SpinBox):
|
||||||
# send only options supported by spinbox
|
# send only options supported by spinbox
|
||||||
|
Loading…
Reference in New Issue
Block a user