Pyqt Qtreewidget Problem mit benutzerdefiniertem Delegierter
-
24-09-2019 - |
Frage
Ich versuche, einfache Immobilien -Editor zu schreiben. Ich habe die PYQT -Klasse automatisch generiert (Workzone im Code unten), und ich muss einige seiner Eigenschaften mit dem PropertyEditor mit dem Delegate -PropertyEditelegate anzeigen/bearbeiten, der benutzerdefinierte Redakteure Coloreditor, Lineeditor usw. verwendet.
Die Hauptidee ist, dass Wortzone weiß, welche Eigenschaften bearbeitet werden müssen und wie und der Eigentumsparnis Wortzone nach solchen Eigenschaften anstreben und QTreewidget mit ihren Werten füllen.
Es gibt jedoch ein Problem: Delegate beginnt nicht mit dem Bearbeiten von Doppelklick oder 'Einzug' oder SMTH. Es wird zur rechten Reihe hinzugefügt, es malt den Gegenstand, aber das ist alles. Als ich den Typ des PropertyEditor -Containers auf QTableWidget wechselte, begann Delegate besser zu funktionieren (aber der Editor in der Ecke des Bildschirms, nicht in der Tabelle)!
Ps. Und eine zusätzliche Frage: Gibt es eine Möglichkeit, einige Delegierte zu Zeilen hinzuzufügen, ohne die Instanzen von ihnen woanders zu speichern (self._delegates in einem Skript), es ist nur hässlich. Methode setItemDelegate akzeptiert Zeiger zum Delegieren, und in C ++ erhält es Eigentum, aber in Pyqt ist es nicht so ... außerdem gibt es beispielsweise kein solches Problem mit SetItem.
Das folgende Skript zeigt das Problem:
# -*- coding: utf-8 -*-
#!/usr/bin/env python
from PyQt4 import QtCore, QtGui
import inspect
class LineEditor(QtGui.QLineEdit):
def __init__(self, name = None, parent = None, slot = None):
QtGui.QLineEdit.__init__(self, parent)
self.textChanged.connect(slot)
self.name = name
@staticmethod
def paintForDelegate(delegate, painter, option, index):
QtGui.QItemDelegate.paint(delegate, painter, option, index)
def get(self):
return str(self.text())
def set(self, val):
self.setText(QtCore.QString.fromUtf8(val))
class ColorEditor(QtGui.QComboBox):
def _populateList(self):
for name in QtGui.QColor.colorNames():
self.addItem(name)
index = self.findText(name)
self.setItemData(index, QtGui.QColor(name), QtCore.Qt.DecorationRole)
def __init__(self, name = None, parent = None, slot = None):
QtGui.QComboBox.__init__(self, parent)
self._populateList()
self.currentIndexChanged.connect(slot)
self.name = QtCore.QString.fromUtf8(name)
@staticmethod
def paintForDelegate(delegate, painter, option, index):
QtGui.QItemDelegate.paint(delegate, painter, option, index)
def get(self):
qColor = QtGui.QColor(self.itemData(self.currentIndex(), QtCore.Qt.DecorationRole))
color = ((qColor.blue() | (qColor.green() << 8)) | (qColor.red() << 16))
return color
def set(self, val):
blue = (val & 255)
green = ((val & 65280) >> 8)
red = ((val & 16711680) >> 16)
color = QtGui.QColor(red, green, blue)
index = self.findData(color, QtCore.Qt.DecorationRole)
self.setCurrentIndex(index)
class PropertyEditorDelegate(QtGui.QItemDelegate):
def __init__(self, object, propName, parent = None):
QtGui.QItemDelegate.__init__(self, parent)
self._object = object
self._propName = propName
def paint(self, painter, option, index):
self._object.paintForDelegate(self._propName, self, painter, option, index)
def createEditor(self, parent, option, index):
return self._object.createEditor(self._propName)
def setEditorData(self, editor, index):
value = index.model().data(index, QtCore.Qt.EditRole)
editor.set(value)
def setModelData(self, editor, model, index):
if index.column() == 0:
model.setData(index, editor.name, QtCore.Qt.EditRole)
else:
model.setData(index, editor.get(), QtCore.Qt.EditRole)
def updateEditorGeometry(self, editor, option, index):
editor.setGeometry(option.rect)
class PropertyEditor(QtGui.QWidget):
def __init__(self, parent = None):
QtGui.QWidget.__init__(self, parent)
self._object = None
self._delegates = []
self._mainLayout = QtGui.QVBoxLayout()
self._mainLayout.setContentsMargins(2, 2, 2, 2)
self._mainLayout.setSpacing(2)
self.setLayout(self._mainLayout)
self._contents = QtGui.QTreeWidget()
self._contents.setColumnCount(2)
self._contents.currentItemChanged.connect(self.printCurrent)
self._mainLayout.addWidget(self._contents)
def printCurrent(self, curr, prev):
print self._contents.currentIndex().row()
print self._contents.currentIndex().column()
print self._contents.itemDelegate(self._contents.currentIndex())._propName
print self._contents.itemDelegate(self._contents.currentIndex())
def object(self):
return self._object
def setObject(self, value):
self._object = value
def isProperty(p):
return isinstance(p, property)
for (name, value) in inspect.getmembers(type(self._object), isProperty):
if self._object.isEditable(name):
item = QtGui.QTreeWidgetItem()
item.setData(0, QtCore.Qt.EditRole, QtCore.QString.fromUtf8(self._object.getPropertyName(name)))
item.setData(1, QtCore.Qt.EditRole, self._object.get(name))
self._contents.addTopLevelItem(item)
self._delegates.append(PropertyEditorDelegate(self._object, name, self._contents))
index = self._contents.indexOfTopLevelItem(item)
self._contents.setItemDelegateForRow(index, self._delegates[index])
class WorkZone(object):
def __init__(self):
self._name = ''
self.currentEditor = None
self.red = 100
self.green = 100
self.blue = 100
self._width = 1
def _getColor(self):
color = ((self.blue | (self.green << 8)) | (self.red << 16))
return color
def _setColor(self, color):
self.blue = (color & 255)
self.green = ((color & 65280) >> 8)
self.red = ((color & 16711680) >> 16)
color = property(_getColor, _setColor)
def currentColorChanged(self, index):
if self.currentEditor is not None:
self.color = self.currentEditor.get()
print self.color
def currentNameChanged(self, newName):
if self.currentEditor is not None:
self.name = self.currentEditor.get()
print self.name
def createEditor(self, prop):
if prop == 'color':
self.currentEditor = ColorEditor('Color', None, self.currentColorChanged)
self.currentEditor.set(self.color)
return self.currentEditor
elif prop == 'name':
self.currentEditor = LineEditor('Name', None, self.currentNameChanged)
self.currentEditor.set(self.name)
return self.currentEditor
else:
return None
def releaseEditor(self):
self.currentEditor = None
def isEditable(self, prop):
if prop == 'color':
return True
elif prop == 'name':
return True
else:
return False
def set(self, prop, val):
if prop == 'color':
self.color = val
elif prop == 'name':
self.name = val
def get(self, prop):
if prop == 'color':
return self.color
elif prop == 'name':
return self.name
def getPropertyName(self, prop):
if prop == 'color':
return 'Color'
elif prop == 'name':
return 'Name'
def paintForDelegate(self, prop, delegate, painter, option, index):
if prop == 'color':
ColorEditor.paintForDelegate(delegate, painter, option, index)
elif prop == 'name':
LineEditor.paintForDelegate(delegate, painter, option, index)
def _setWidth(self, Width):
self._width = Width
def _getWidth(self):
return self._width
width = property(_getWidth, _setWidth)
def _getName(self):
return self._name
def _setName(self, val):
self._name = val
name = property(_getName, _setName)
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
zone = WorkZone()
zone.color = 0
zone.width = 1
propertyEditor = PropertyEditor()
propertyEditor.setObject(zone)
propertyEditor.show()
sys.exit(app.exec_())
Lösung
Am Ende habe ich das Double -Click zum Setzen auf das Editier gemacht, das Element in den Bearbeitungsmodus mit editItem () erzwingen und dann zurückstellen. Der Delegierte selbst behandelt die gesamte Anzeige und Bearbeitung.
# In __init__:
self.tree.itemActivated.connect(self.onDoubleClick)
def onDoubleClick(self, item, index):
"""
The logic will happen in the editor delegate. This is needed to let
the delegate run by making this editable
"""
item.setFlags(QtCore.Qt.ItemIsSelectable |
QtCore.Qt.ItemIsEnabled |
QtCore.Qt.ItemIsEditable)
# Force the item in to edit mode so the delegate picks it up
self.tree.editItem(item, index)
# Set the item back to not editable. The delegate will still do its
# job, but the read-only state will already be set when done!
item.setFlags(QtCore.Qt.ItemIsSelectable |
QtCore.Qt.ItemIsEnabled)
Die ersten SETFLAGs müssen wahrscheinlich nur aufgeführt werden, um zu arbeiten, aber das fühlte sich richtig an.
Andere Tipps
Auf ps.
Aus Dokumentation von PYQT
QabstractItemView.setItemDelegate (self, qabstractItemDelegate)
Legt den Element -Delegieren für diese Ansicht und sein Modell zum Delegieren fest. Dies ist nützlich, wenn Sie die vollständige Kontrolle über die Bearbeitung und Anzeige von Elementen möchten.
Jeder vorhandene Delegierte wird entfernt, aber nicht gelöscht. QabstractItemView übernimmt nicht die Verantwortung für den Delegierten.
Warnung: Sie sollten nicht die gleiche Instanz eines Delegierten zwischen den Ansichten teilen. Dies kann zu einem falschen oder unintuitiven Bearbeitungsverhalten führen, da jede Ansicht, die mit einem bestimmten Delegierten verbunden ist, möglicherweise das Signal von Closeditor () empfangen und versucht, einen bereits geschlossenen Editor zuzugreifen, zu ändern oder zu schließen.
Die Antwort ist wie immer einfach .. %) Standardflags für qtreewidgetItem enthalten nicht qtcore.qt.itemisedable.