Python qt: delegates

From wikinotes

Delegates are assigned to a row/column in a model and handle custom painting styles within it. The classic example is a model that contains a star rating of 0-5. Dragging across the field will change the number of assigned stars, and a star will be painted for each.


WARNING:

Apparently, I have two pages dedicated to this. See Qt modelview: delegates

Techniques

createEditor on events other than doubleclick

https://forum.qt.io/topic/16213/qpushbutton-in-a-qtableview/3

Change for entire model

You can change how the entire model handles clicks

model = QtWidgets.QStandardItemModel()
model.setEditTriggers(QtWidgets.QAbstractItemView.SelectedClicked)

custom handling of click events

Alternatively, you can be more precise with a custom view (any QAbstractItemView subclass).

Connect clicked, and you can trigger starting the editor early if a QStandardItem matches your desired conditions.

class MyTreeView(QtWidgets.QTreeView):
    def __init__(self, *args, **kwargs):
        super(MyTreeView, self).__init__(*args, **kwargs)
        self.clicked.connect(self._handle_clicked)

    def _handle_clicked(self, modelindex):
        if modelindex.row() == 2:
            self.edit(modelindex)

Events/Signals

Delegates do not give you a information like mouseover, arrowup, etc. using signals.

See https://stackoverflow.com/questions/11183354/how-to-use-custom-widget-with-events-signals-as-qstyleditemdelegates

Delegate-Editor Examples

Modified Editor Widget

Here we are creating a delegate so that when a user edits a QListView, instead of the default behaviour, their text-editor will be a QLineEdit with a QIntValidator.

non-default editor

class LineEditorDelegate( QtWidgets.QStyledItemDelegate ):
    """ replaces the default editor with a QLineEdit (which can have a QValidator) """
    def createEditor(self, parent, option, index ):
        editor = QtWidgets.QLineEdit(parent=parent)
        editor.setValidator(QtGui.QIntValidator())
        editor.editingFinished.connect(self.commitAndCloseEditor)

        return editor

    def updateEditorGeometry(self, editor, option, index ):
        editor.setGeometry( option.rect )

    def commitAndCloseEditor(self):
        editor = self.sender()
        self.commitData.emit( editor )


listwid  = QtWidgets.QListWidget()
delegate = ListWidget_LineEdit_Delegate()
listwid.setItemDelegate(delegate)

Custom Window Editor Widget

Here, we are spawning a new custom window that manages edits to the modelitem.

There is nothing special about the editor widget, most of it's special sauce is programmed into the delegate.

delegate methods that control editor
createEditor create the widget you will be using as your editor
updateEditorGeometry where do you want the window?
setEditorData(editor, index) load the current modeldata into the editor
setModelData write editor data into the model
closeEditor handle the actual deletion of editor widget, now that we're through with it.

custom editor widget

class DatetimeDelegate(QtWidgets.QStyledItemDelegate):
    def __init__(self, *args, **kwargs):
        super(DatetimeDelegate, self).__init__(*args, **kwargs)
        self._pixmap = QtGui.QPixmap('myfile.png')

    def paint(self, painter, option, index):
        """ Paints your pixmap (what is seen within table/list/tree
        """
        QtWidgets.QApplication.style().drawItemPixmap(
            painter,
            option.rect,
            QtCore.Qt.AlignCenter,
            self._pixmap,
        )

    def createEditor(self, parent, option, index):
        editor = DatetimeChooserEditor(index)
        return editor

    def updateEditorGeometry(self, editor, option, index):
        """ Renders the widget at the center of the screen
        """
        qapp = QtWidgets.QApplication.instance()
        desktop = qapp.desktop()
        screen = desktop.screen()

        screen_rect = screen.rect()
        editor_rect = editor.rect()

        centerscreen_pos = screen_rect.center() - editor_rect.center()
        editor.move(centerscreen_pos)

    def setEditorData(self, editor, index):
        """ loads fields in the editor
        """
        if not isinstance(editor, ToolLockEditor):
            return super(DatetimeDelegate, self).setEditorData(editor, index)

        editor.load_from_modeldata(index.data())

    def setModelData(self, editor, model, index):
        """ Runs after editor is closed. Used to update the model.
        """
        result = editor.result()

        item = model.itemFromIndex(index)
        item.setText(result)

    def closeEditor(self, editor):
        editor.deleteLater()


class DateChooserEditor(QtWidgets.QDialog):
    """
    Your Editor can be a normal widget, just make sure to ``close()``
    instead of ``deleteLater()`` so that your delegate still has access
    to it's information.
    """
    def __init__(self):
        # embed a calendarwidget

    def load_from_modeldata(self, modeldata):
        # load date into calendarwidget

    def result(self):
        return self._result