Editable QTableView and Pandas do not work properly

前端 未结 2 362
隐瞒了意图╮
隐瞒了意图╮ 2021-01-20 15:08

I am trying to get a self-contained piece of example code for using pandas and QTableView while getting editable cell views.

For this I am following up to an earlier

相关标签:
2条回答
  • 2021-01-20 15:32

    It seems to work when I switch to PySide instead of PyQt4:

    import sys
    from PySide import QtCore, QtGui
    import pandas as pd
    Qt = QtCore.Qt
    
        class PandasModelEditable(QtCore.QAbstractTableModel):
        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 or role == QtCore.Qt.EditRole:
                    return unicode(self._data.iloc[index.row(), index.column()])
            return None
    
        def headerData(self, section, orientation, role=QtCore.Qt.DisplayRole):
            if role != QtCore.Qt.DisplayRole:
                return None
            if orientation == QtCore.Qt.Horizontal:
                try:
                    return '%s' % unicode(self._data.columns.tolist()[section])
                except (IndexError,):
                    return unicode()
            elif orientation == QtCore.Qt.Vertical:
                try:
                    return '%s' % unicode(self._data.index.tolist()[section])
                except (IndexError,):
                    return unicode()
    
        def flags(self, index):
            return QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable | \
                   QtCore.Qt.ItemIsEditable
    
        def setData(self, index, value, role=QtCore.Qt.EditRole):
            if index.isValid():
                self._data.iloc[index.row(), index.column()] = value
                if self.data(index, QtCore.Qt.DisplayRole) == value:
                    self.dataChanged.emit(index, index)
                    return True
            return False
    
    
    if __name__ == '__main__':
        application = QtGui.QApplication(sys.argv)
        view = QtGui.QTableView()
        df = pd.DataFrame([[1, 2, 3], [4, 5, 6]], columns=['a', 'b', 'c'], index=['x', 'y'])
    
        model = PandasModelEditable(df)
        view.setModel(model)
    
        view.show()
        sys.exit(application.exec_())
    
    0 讨论(0)
  • 2021-01-20 15:36

    The immediate problem is caused by passing an unconverted QVariant object to the underlying database. The simplest fix is convert it to a python object, like this:

    self._data.iloc[index.row(), index.column()] = value.toPyObject()
    

    However, this doesn't really deal with the most fundamental problem with the code, which is that you are using such old versions of Python and PyQt. Qt does not officially support Qt4 any more, and it won't be long before the same is true for Python and Python2. Strictly speaking, PyQt4 is already obsolete legacy code - so you shouldn't be using it for new projects unless you have a really good reason for doing that (e.g. backwards compatibilty).

    If you can, I would strongly recommend that you port your code to Python3/PyQt5 as soon as possible, as it will save you a lot of hassle in the medium to long term. However, if you cannot do this for some reason, and you want to continue using Python2/PyQt4, you can get the same behaviour as PySide by adding the following to the beginning of your program:

    import sip
    sip.setapi('QString', 2)
    sip.setapi('QVariant', 2)
    from PyQt4 import QtCore, QtGui
    

    After doing this, PyQt will automatically convert all QString and QVariant objects to ordinary python data types, so you will never need to do any explicit conversions (i.e. you can remove all those unicode() and toPyObject() calls in your code).

    Alternatively, you could also use Python3 with PyQt4, which has the same behaviour as PySide by default (so the setapi stuff would not be needed).

    0 讨论(0)
提交回复
热议问题