The framwork to be used: PyQt4.
The concept provided by Qt4 is to use delegates. These allow the developer to modify the behaviour from each cell in a table. The problem is that delegates only can handle one input element, not multiple.
My solution:
An sbstract delegate which enherits from the QItemDelegate.
The paint function has to be overwritten, since if not the default presentation is also painted additional to your custom presentation. Resulting in two elements painted on top of each other. Here I just skip the default presentation for elements in the second column, since I do not want use custom delegates for the labels in the the column 0 (my field attribute labels).
If the data in the cell changes the setModelData function is called. Additionally to calling the parent function I am emitting a signal that the data changed. This function is overwritten by delegates consisting of multiple input elements but is used by elements with one input element.
class AbstractDfDelegate(QtGui.QItemDelegate):The use case mentioned in the beginning is the extendable list. It accepts default values which are used as the input for the list element.
def paint(self, parent, option, index):
if index.column() == 1:
pass
else:
QtGui.QItemDelegate.paint(self, parent, option, index)
def setModelData(self, editor, model, index):
QtGui.QItemDelegate.setModelData(self, editor, model, index)
model.emit(QtCore.SIGNAL("itemChanged(QStandardItem)"), model.itemFromIndex(index))
class ListDelegate(AbstractDfDelegate):
def __init__(self, defaults, parent=None):
self._defaults = defaults
AbstractDfDelegate.__init__(self, parent)
First the input elements are defined. One list element and a text editor. They are displayed in a vertical layout which again is used by a QWidget, which is also the widget we use as editor from now on.
def createEditor(self, parent, option, index):The setModelData function of the abstract delegate is overwritten. The selected values of the list are collected and the text of the text field is added if there is any. The resulting value is then set manually in the model. Again after collecting the value a signal is emitted that the model was updated.
if index.column() == 1:
self._index = index
self._listBox = QtGui.QListView()
self._listBox.setSelectionMode(QtGui.QListWidget.MultiSelection)
self._listBox.setModel(QtGui.QStringListModel(self._defaults))
self._listBox.clearSelection()
self._listBox.setMinimumHeight(100)
self._listBox.setMaximumHeight(100)
self._textEditor = QtGui.QLineEdit()
if QtCore.Qt.NoItemFlags == self._index.flags():
self._listBox.setEnabled(False)
self._textEditor.setEnabled(False)
self.connect(self._listBox.selectionModel(), QtCore.SIGNAL("selectionChanged(QItemSelection,QItemSelection)"), self.setModelData)
self.connect(self._textEditor, QtCore.SIGNAL("textChanged(QString)"), self.setModelData)
vLayout = QtGui.QVBoxLayout(parent)
vLayout.addWidget(self._listBox)
vLayout.addWidget(self._textEditor)
vLayout.setMargin(0)
widget = QtGui.QWidget(parent)
widget.setLayout(vLayout)
return widget
def setModelData(self, *args):The last function is the setEditorData function which presents the value of the model in the editor. Therefore the value is split up. To decide which values are from the list the values have to be compared with the default values. All remaining values are presented in the text box.
values = list()
for y in self._listBox.selectedIndexes():
values.append(unicode(y.data().toString().trimmed()))
additionalText = unicode(self._textEditor.text().trimmed()).replace(",", "\,")
if len(additionalText) > 0:
values.append(additionalText)
self._index.model().setData(self._index, values)
self._index.model().emit(QtCore.SIGNAL("itemChanged(QStandardItem)"), self._index.model().itemFromIndex(self._index))
def setEditorData(self, editor, index):The signal emitted by the setModelData function is connected to a slot function handling the change as well as the table model:
if index.column() == 1 and not editor is None:
items = index.model().data(index).toStringList()
additionalItems = list()
for item in items:
if item in self._defaults:
i = self._defaults.index(item)
self._listBox.selectionModel().select(self._listBox.model().index(i, 0), QtGui.QItemSelectionModel.Select)
else:
additionalItems.append(str(item))
self._textEditor.setText(",".join(additionalItems))
else:
return AbstractDfDelegate.setEditorData(self, editor, index)
self.connect(self.tableView.model(), QtCore.SIGNAL("itemChanged(QStandardItem)"), self.__propertyTableValueChangedSlot)In the slot function I then extract the name and the value of the property and can do with those what I need to do:
def __propertyTableValueChangedSlot(self, item):
""" Slot to handle value changes. """
propertyName = unicode(item.model().item(item.row(), item.column()-1).text())
propertyValue = item.index().data().toPyObject()
Keine Kommentare:
Kommentar veröffentlichen