Reputation: 535
I am looking for the clean way to use a multi line text edit in the my QStyledItemDelegate. The createEditor implementation is pretty straight forward by returning a QTextEdit instance
def createEditor(self, parent, option, index):
return QtGui.QTextEdit("Some text")
But setModelData
expects a Edit Widget derived from QWidget
as a parameter instead of QTextEdits
base QScrollArea
. The Qt Documentation also tells me (at least in the PyQt Doc) that the setModelData
function tries to get the data from the QWidget UserData field. But without having an Edit Widget derived from QWidget
there is no option in setting the data. Currently it throws a AttributeError
because it can't find text()
on the editor.
Is there some proven way to use a non-QWidget editor? Or am I just missing some Widget to do that?
Currently I quick fixed the issue by instanciating a QLineEdit
with the QTextEdit
data from toPlainText()
and passing this to setModelData
. Very Hacky!! I could also use duck typing and just implement a text()
method on a QTextEdit
derivate. But still not a nice way, isn't it? What is a way to do this in C++?
Upvotes: 3
Views: 3196
Reputation: 120568
The minimum requirements for an item-delegate are very simple (see the Model/View Overview for more details). Just create a subclass of QStyledItemDelegate and reimplement the methods createEditor
, setEditorData
, and setModelData
:
class Delegate(QStyledItemDelegate):
def createEditor(self, parent, options, index):
return QtGui.QTextEdit(parent)
def setEditorData(self, editor, index):
editor.setText(index.data())
def setModelData(self, editor, model, index):
model.setData(index, editor.toPlainText())
With this approach, it's easy to support several different types of editor via the same delegate. For example, an isinstance
check could be used to decide which type of editor to use. Below is a basic demo that shows how to do this:
import random
from PyQt5 import QtCore, QtGui, QtWidgets
class ItemDelegate(QtWidgets.QStyledItemDelegate):
def createEditor(self, parent, options, index):
column = index.column()
if column == 0:
return QtWidgets.QTextEdit(parent)
elif column == 1:
return QtWidgets.QComboBox(parent)
elif column == 2:
return QtWidgets.QSpinBox(parent)
def setEditorData(self, editor, index):
if isinstance(editor, QtWidgets.QTextEdit):
editor.setText(index.data())
elif isinstance(editor, QtWidgets.QComboBox):
editor.addItems('Red Blue Green'.split())
editor.setCurrentIndex(editor.findText(index.data()))
elif isinstance(editor, QtWidgets.QSpinBox):
editor.setValue(int(index.data()))
def setModelData(self, editor, model, index):
if isinstance(editor, QtWidgets.QTextEdit):
model.setData(index, editor.toPlainText())
elif isinstance(editor, QtWidgets.QComboBox):
model.setData(index, editor.currentText())
elif isinstance(editor, QtWidgets.QSpinBox):
model.setData(index, editor.value())
class Window(QtWidgets.QWidget):
def __init__(self):
super().__init__()
self.table = QtWidgets.QTableWidget(5, 3)
self.table.setItemDelegate(ItemDelegate(self))
layout = QtWidgets.QVBoxLayout(self)
layout.addWidget(self.table)
colours = 'Red Blue Green'.split()
for row in range(5):
for column in range(4):
item = QtWidgets.QTableWidgetItem()
if column == 1:
item.setData(QtCore.Qt.EditRole, random.choice(colours))
elif column == 2:
item.setData(QtCore.Qt.EditRole, random.randint(0, 99))
self.table.setItem(row, column, item)
if __name__ == '__main__':
app = QtWidgets.QApplication(['Test'])
window = Window()
window.setGeometry(600, 100, 375, 225)
window.show()
app.exec()
Upvotes: 7
Reputation: 535
So I took some time and digged deeper into the problem. There are in fact multiple cleaner solutions:
Implementing a Property
I got it to work properly by implementing a QProperty on a subclass of QTextEdit
which adds the text
property as USER property to the object. Here is my code:
class DelegatableTextEdit(QtGui.QTextEdit):
@pyqtProperty(str, user=True)
def text(self):
return self.toPlainText()
@text.setter
def text(self, text):
self.setText(text)
Using the Factory
There seems to be an way to solve this by using the Delegates setDefaultFactory()
method which then takes a QItemEditorFactory
which has registered a custom editor creator by using the registerEditor()
function. You can register a custom implementation of the QItemEditorCreatorBase
class there which has a overriden createWidget
method (and if needed a valuePropertyName
function as well). But I haven't tried this one yet
I decided to take the first solution which only needs the custom QTextEdit
and the overriden createEditor()
function. Both should work in C++
Upvotes: 1