julian
julian

Reputation: 59

PushButton in Qt Model/View Table

I'm writing a custom TableModel in PyQt5, inheriting from QtCore.QAbstractTableModel. I want my Table to have one column with CheckBoxes only, no text, and one column with a PushButton in each row.

I tried to return a QPushButton object in the data method for the Qt.Display role, but apparently this is not possible.

So my question is: Can I implement in the Model itself that it returns certain widgets for certain cells? In my opinion, that is the job of the model, but how do I achieve this?

My second question would be how I have to assign the slots so that I know from which of the buttons (from which row) the action occurred?

Upvotes: 2

Views: 2259

Answers (1)

eyllanesc
eyllanesc

Reputation: 243973

Explanation:

Can I implement in the Model itself that it returns certain widgets for certain cells? In my opinion, that is the job of the model, but how do I achieve this?

It could be that the model provides the widgets but it is not common. In general, the model provides the information that will be displayed through a delegate, and is the delegate that can provide widgets. There is also the alternative of setting widgets on the unlinked view of the model using the setIndexWidget method.

Considering the first case, the solution is to implement a delegate.

how I have to assign the slots so that I know from which of the buttons (from which row) the action occurred?

In general the widgets that are used as editors so you must update the model, and then the model can notify other elements through the dataChanged signal as for example if the user wrote a text or changed the status of a checkbox, but in the case of the clicked not notify a permanent change but temporary. Considering this, the delegate could present a signal.

Solution:

from PyQt5 import QtCore, QtGui, QtWidgets


class PushButtonDelegate(QtWidgets.QStyledItemDelegate):
    clicked = QtCore.pyqtSignal(QtCore.QModelIndex)

    def paint(self, painter, option, index):
        if (
            isinstance(self.parent(), QtWidgets.QAbstractItemView)
            and self.parent().model() is index.model()
        ):
            self.parent().openPersistentEditor(index)

    def createEditor(self, parent, option, index):
        button = QtWidgets.QPushButton(parent)
        button.clicked.connect(lambda *args, ix=index: self.clicked.emit(ix))
        return button

    def setEditorData(self, editor, index):
        editor.setText(index.data(QtCore.Qt.DisplayRole))

    def setModelData(self, editor, model, index):
        pass


if __name__ == "__main__":
    import sys

    app = QtWidgets.QApplication(sys.argv)

    w = QtWidgets.QTableView()
    model = QtGui.QStandardItemModel(0, 2)
    for i in range(10):
        it1 = QtGui.QStandardItem()
        it1.setData(QtCore.Qt.Checked, QtCore.Qt.CheckStateRole)
        it1.setFlags(it1.flags() | QtCore.Qt.ItemIsUserCheckable)
        it2 = QtGui.QStandardItem()
        it2.setData("text-{}".format(i), QtCore.Qt.DisplayRole)
        model.appendRow([it1, it2])

    # pass the view as parent
    delegate = PushButtonDelegate(w)
    w.setItemDelegateForColumn(1, delegate)

    def on_clicked(index):
        print(index.data())

    delegate.clicked.connect(on_clicked)

    w.setModel(model)
    w.show()
    sys.exit(app.exec_())

Upvotes: 5

Related Questions