python pandas model pyqt4 qtableview

python - Editable QTableView y pandas no funcionan correctamente



model pyqt4 (2)

Estoy tratando de obtener un código de ejemplo autónomo para usar pandas y QTableView mientras obtengo vistas de celda editables.

Para esto estoy siguiendo una discusión anterior: Pandas df en QTableView editable: eliminar casillas de verificación

Si bien la respuesta y las modificaciones propuestas en esa otra discusión ayudan a deshacerse de las casillas de verificación, el código discutido allí todavía no funciona para mí (Python 2.7).

Cuando modifico una celda usando el siguiente código, el contenido que se muestra en la celda es: Objeto PtQt4.PtCore.QtVariant en ...

Las versiones de paquete que uso son:

Pandas: 0.20.2
Pyside 1.2.4
Versión de Qt: 4.8.4
Versión SIP: versión 4.14.4 PyQt: 4.10

import sys from PyQt4 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_())


El problema inmediato se produce al pasar un objeto QVariant no convertido a la base de datos subyacente. La solución más simple es convertirlo en un objeto python, como este:

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

Sin embargo, esto realmente no trata el problema más fundamental con el código, que es que estás usando versiones anteriores de Python y PyQt. Qt ya no es compatible oficialmente con Qt4, y no pasará mucho tiempo antes de que lo mismo sea cierto para Python y Python2. Estrictamente hablando, PyQt4 ya es código heredado obsoleto, por lo que no debería usarlo para nuevos proyectos a menos que tenga una buena razón para hacerlo (por ejemplo, compatibilidad hacia atrás).

Si puede, le recomiendo encarecidamente que transmita su código a Python3 / PyQt5 tan pronto como sea posible, ya que le ahorrará muchos problemas a medio y largo plazo. Sin embargo, si no puede hacer esto por algún motivo, y desea continuar usando Python2 / PyQt4, puede obtener el mismo comportamiento que PySide agregando lo siguiente al comienzo de su programa:

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

Después de hacer esto, PyQt convertirá automáticamente todos los objetos QString y QVariant en tipos de datos python ordinarios, por lo que nunca tendrá que realizar conversiones explícitas (es decir, puede eliminar todas las llamadas unicode() y toPyObject() en su código).

Alternativamente, también podría usar Python3 con PyQt4, que tiene el mismo comportamiento que PySide por defecto (por lo que las cosas de setapi no serían necesarias).


Parece que funciona cuando cambio a PySide en lugar de 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_())