1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220
|
from ..Qt import QtCore, QtGui, QtWidgets
translate = QtCore.QCoreApplication.translate
class ParameterItem(QtWidgets.QTreeWidgetItem):
"""
Abstract ParameterTree item.
Used to represent the state of a Parameter from within a ParameterTree.
- Sets first column of item to name
- generates context menu if item is renamable or removable
- handles child added / removed events
- provides virtual functions for handling changes from parameter
For more ParameterItem types, see ParameterTree.parameterTypes module.
"""
def __init__(self, param, depth=0):
QtWidgets.QTreeWidgetItem.__init__(self, [param.title(), ''])
self.param = param
self.param.registerItem(self) ## let parameter know this item is connected to it (for debugging)
self.depth = depth
param.sigValueChanged.connect(self.valueChanged)
param.sigChildAdded.connect(self.childAdded)
param.sigChildRemoved.connect(self.childRemoved)
param.sigNameChanged.connect(self.nameChanged)
param.sigLimitsChanged.connect(self.limitsChanged)
param.sigDefaultChanged.connect(self.defaultChanged)
param.sigOptionsChanged.connect(self.optsChanged)
param.sigParentChanged.connect(self.parentChanged)
self.updateFlags()
## flag used internally during name editing
self.ignoreNameColumnChange = False
def updateFlags(self):
## called when Parameter opts changed
opts = self.param.opts
flags = QtCore.Qt.ItemFlag.ItemIsSelectable | QtCore.Qt.ItemFlag.ItemIsEnabled
if opts.get('renamable', False):
if opts.get('title', None) is not None:
raise Exception("Cannot make parameter with both title != None and renamable == True.")
flags |= QtCore.Qt.ItemFlag.ItemIsEditable
## handle movable / dropEnabled options
if opts.get('movable', False):
flags |= QtCore.Qt.ItemFlag.ItemIsDragEnabled
if opts.get('dropEnabled', False):
flags |= QtCore.Qt.ItemFlag.ItemIsDropEnabled
self.setFlags(flags)
def valueChanged(self, param, val):
## called when the parameter's value has changed
pass
def isFocusable(self):
"""Return True if this item should be included in the tab-focus order"""
return False
def setFocus(self):
"""Give input focus to this item.
Can be reimplemented to display editor widgets, etc.
"""
pass
def focusNext(self, forward=True):
"""Give focus to the next (or previous) focusable item in the parameter tree"""
self.treeWidget().focusNext(self, forward=forward)
def treeWidgetChanged(self):
"""Called when this item is added or removed from a tree.
Expansion, visibility, and column widgets must all be configured AFTER
the item is added to a tree, not during __init__.
"""
self.setHidden(not self.param.opts.get('visible', True))
self.setExpanded(self.param.opts.get('expanded', True))
def childAdded(self, param, child, pos):
item = child.makeTreeItem(depth=self.depth+1)
self.insertChild(pos, item)
item.treeWidgetChanged()
for i, ch in enumerate(child):
item.childAdded(child, ch, i)
def childRemoved(self, param, child):
for i in range(self.childCount()):
item = self.child(i)
if item.param is child:
self.takeChild(i)
break
def parentChanged(self, param, parent):
## called when the parameter's parent has changed.
pass
def contextMenuEvent(self, ev):
opts = self.param.opts
if not opts.get('removable', False) and not opts.get('renamable', False)\
and "context" not in opts:
return
## Generate context menu for renaming/removing parameter
self.contextMenu = QtWidgets.QMenu() # Put in global name space to prevent garbage collection
self.contextMenu.addSeparator()
if opts.get('renamable', False):
self.contextMenu.addAction(translate("ParameterItem", 'Rename')).triggered.connect(self.editName)
if opts.get('removable', False):
self.contextMenu.addAction(translate("ParameterItem", "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):
"""Called when the text in a column has been edited (or otherwise changed).
By default, we only use changes to column 0 to rename the parameter.
"""
if col == 0 and (self.param.opts.get('title', None) is None):
if self.ignoreNameColumnChange:
return
try:
newName = self.param.setName(self.text(col))
except Exception:
self.setText(0, self.param.name())
raise
try:
self.ignoreNameColumnChange = True
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.
if self.param.opts.get('title', None) is None:
self.titleChanged()
def titleChanged(self):
# called when the user-visble title has changed (either opts['title'], or name if title is None)
title = self.param.title()
# This makes sure that items without a title or the title 'params' remain invisible
if not title or title == 'params':
return
self.setText(0, title)
fm = QtGui.QFontMetrics(self.font(0))
textFlags = QtCore.Qt.TextFlag.TextSingleLine
size = fm.size(textFlags, self.text(0))
size.setHeight(int(size.height() * 1.35))
size.setWidth(int(size.width() * 1.15))
self.setSizeHint(0, size)
def limitsChanged(self, param, limits):
"""Called when the parameter's limits have changed"""
pass
def defaultChanged(self, param, default):
"""Called when the parameter's default value has changed"""
pass
def optsChanged(self, param, opts):
"""Called when any options are changed that are not
name, value, default, or limits"""
if 'visible' in opts:
self.setHidden(not opts['visible'])
if 'expanded' in opts:
if self.isExpanded() != opts['expanded']:
self.setExpanded(opts['expanded'])
if 'title' in opts:
self.titleChanged()
self.updateFlags()
def contextMenuTriggered(self, name):
def trigger():
self.param.contextMenu(name)
return trigger
def editName(self):
self.treeWidget().editItem(self, 0)
def selected(self, sel):
"""Called when this item has been selected (sel=True) OR deselected (sel=False)"""
pass
def requestRemove(self):
## called when remove is selected from the context menu.
## we need to delay removal until the action is complete
## since destroying the menu in mid-action will cause a crash.
QtCore.QTimer.singleShot(0, self.param.remove)
## for python 3 support, we need to redefine hash and eq methods.
def __hash__(self):
return id(self)
def __eq__(self, x):
return x is self
|