3

In my gui file I create a QTableView as follows (this is the part that is automatically generated by Qt Designer):

self.pnl_results = QtGui.QTableView(self.tab_3)
font = QtGui.QFont()
font.setPointSize(7)
self.pnl_results.setFont(font)
self.pnl_results.setFrameShape(QtGui.QFrame.StyledPanel)
self.pnl_results.setFrameShadow(QtGui.QFrame.Sunken)
self.pnl_results.setEditTriggers(QtGui.QAbstractItemView.AllEditTriggers)
self.pnl_results.setShowGrid(True)
self.pnl_results.setSortingEnabled(True)
self.pnl_results.setCornerButtonEnabled(True)
self.pnl_results.setObjectName("pnl_results")

I then define a model that enables me to link the pandas dataframe to the QTableView:

class PandasModel(QtCore.QAbstractTableModel):
    """
    Class to populate a table view with a pandas dataframe
    """

    def __init__(self, data, parent=None):
        QtCore.QAbstractTableModel.__init__(self, parent)
        self._data = data

    def rowCount(self, parent=None):
        return len(self._data.values)

    def columnCount(self, parent=None):
        return self._data.columns.size

    def data(self, index, role=QtCore.Qt.DisplayRole):
        if index.isValid():
            if role == QtCore.Qt.DisplayRole:
                return str(self._data.values[index.row()][index.column()])
        return None

    def headerData(self, col, orientation, role):
        if orientation == QtCore.Qt.Horizontal and role == QtCore.Qt.DisplayRole:
            return self._data.columns[col]
        return None

    def setData(self, index, value, role):
        if not index.isValid():
            return False
        if role != QtCore.Qt.EditRole:
            return False
        row = index.row()
        if row < 0 or row >= len(self._data.values):
            return False
        column = index.column()
        if column < 0 or column >= self._data.columns.size:
            return False
        self._data.values[row][column] = value
        self.dataChanged.emit(index, index)
        return True

    def flags(self, index):
        flags = super(self.__class__,self).flags(index)
        flags |= QtCore.Qt.ItemIsEditable
        flags |= QtCore.Qt.ItemIsSelectable
        flags |= QtCore.Qt.ItemIsEnabled
        flags |= QtCore.Qt.ItemIsDragEnabled
        flags |= QtCore.Qt.ItemIsDropEnabled
        return flags

and finally a add my pandas dataframe (df) to the model:

model = PandasModel(df)
self.ui.pnl_results.setModel(model)

This correctly displays my pandas dataframe in the QTableView. However, for some reason when I edit the fileds the return to their original values (and also once I edit the field it is starting as empty). How can I make it editable and then write the reults back to the pandas dataframe?

ekhumoro
  • 115,249
  • 20
  • 229
  • 336
Nickpick
  • 6,163
  • 16
  • 65
  • 116

2 Answers2

5

Your model lacks setData method. The default implementation from QtCore.QAbstractTableModel does nothing and returns False. You need to implement this method in your model to make its items editable. If df is the actual container storing the data, you should simply change the value of the item stored in the container in setData method. It could look like this:

def setData(self, index, value, role):
    if not index.isValid():
        return False
    if role != QtCore.Qt.EditRole:
        return False
    row = index.row()
    if row < 0 or row >= len(self._data.values):
        return False
    column = index.column()
    if column < 0 or column >= self._data.columns.size:
        return False
    self._data.values[row][column] = value
    self.dataChanged.emit(index, index)
    return True

You also need to implement flags method to return a value containing QtCore.Qt.ItemIsEditable.

Dmitry
  • 3,063
  • 2
  • 22
  • 32
  • could you make an example how the setData method would have to look like? – Nickpick Dec 17 '16 at 09:40
  • Ok, added the example implementation. – Dmitry Dec 17 '16 at 10:45
  • I have altered my question to the latest version, the problem is that the edited fileds don't seem to propagate to the dataframe and once I hit enter the values are returned to the original ones – Nickpick Dec 17 '16 at 16:36
  • It appears this next problem is specific to that "pandas dataframe" thing. In the related questions I found [this](http://stackoverflow.com/a/39971773/1217285) note. Try this and see if t helps. – Dmitry Dec 17 '16 at 17:36
  • @Dimitry: the model in the link seems to work, just not sure why it has checkboxes in every field. How can I get rid of them? is there any flag for that? – Nickpick Dec 19 '16 at 16:55
  • Could you please upload a screenshot with what you observe somewhere? I'm not sure what kind of checkboxes you mean. – Dmitry Dec 19 '16 at 19:18
  • I've posted a new question here: http://stackoverflow.com/questions/41232314/pandas-df-in-editable-qtableview-flags-remove-check-boxes – Nickpick Dec 19 '16 at 22:54
  • Great, this works flawlessly for me, however what would i have to do to have index? – U13-Forward Aug 03 '19 at 10:43
  • Well, you can create one using model's `index` method and pass the desired row and column there. – Dmitry Aug 03 '19 at 10:46
  • @Dmitry I am not an expert in PyQt, so could you please show an example? – U13-Forward Aug 03 '19 at 10:52
  • @Dmitry That said, should I ask a question? – U13-Forward Aug 03 '19 at 10:54
  • I would suggest to read QAbdtractItemModel's documentation instead of asking it as a separate question. For table models it's a really simple thing. I'm not in front of a computer and won't be until tomorrow so I'm not in position to give a detailed example. But I am sure you can find enough similar examples around here. – Dmitry Aug 03 '19 at 10:58
  • @Dmitry I looked in the documentation however I still can't solve it. – U13-Forward Aug 03 '19 at 11:08
  • @Dmitry Sorry to ask you again, when the tableview is edited how can i edit the dataframe too? – U13-Forward Aug 05 '19 at 06:04
  • @Dmitry I posted a question: https://stackoverflow.com/questions/57353056/how-do-i-edit-the-actual-dataframe-when-i-edit-the-qtableview-data-pyqt5 – U13-Forward Aug 05 '19 at 06:14
0
    def setData(self, index, value, role=Qt.EditRole):
    if index.isValid():
        if role == Qt.EditRole:
            self._data.iat[index.row(),index.column()] = value
            self.dataChanged.emit(index, index)
            return True
    return False

self._data.values[row][column] = value

didn't work for me, but changing with iat worked

tCot
  • 307
  • 2
  • 7