可编辑的 QTableView 和 Pandas 不能正常工作
Editable QTableView and Pandas do not work properly
我正在尝试获取一段独立的示例代码,以便在获取可编辑的单元格视图时使用 pandas 和 QTableView。
为此,我正在跟进之前的讨论:
虽然其他讨论中的答案和建议的修改有助于摆脱复选框,但那里讨论的代码仍然不适合我 (python 2.7)。
当我使用下面的代码修改单元格时,单元格中显示的内容是:PtQt4.PtCore.QtVariant object at ...
我使用的包版本是:
Pandas: 0.20.2
Pyside 1.2.4
Qt version: 4.8.4
SIP version: 4.14.4
PyQt version: 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_())
当我切换到 PySide 而不是 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_())
直接的问题是由将未转换的 QVariant
对象传递给基础数据库引起的。最简单的修复方法是将其转换为 python 对象,如下所示:
self._data.iloc[index.row(), index.column()] = value.toPyObject()
但是,这并没有真正解决代码的最基本问题,即您使用的是 Python 和 PyQt 的旧版本。 Qt 不再正式支持 Qt4,过不了多久 Python 和 Python2 也是如此。严格来说,PyQt4 已经是过时的遗留代码 - 所以你不应该将它用于新项目,除非你有 really 这样做的充分理由那(例如向后兼容性)。
如果可以,我强烈建议您尽快将代码移植到 Python3/PyQt5,因为从中长期来看,这会为您省去很多麻烦。但是,如果由于某种原因您不能这样做,并且您想继续使用 Python2/PyQt4,您可以通过将以下内容添加到程序的开头来获得与 PySide 相同的行为:
import sip
sip.setapi('QString', 2)
sip.setapi('QVariant', 2)
from PyQt4 import QtCore, QtGui
这样做之后,PyQt 会自动将所有 QString
和 QVariant
对象转换为普通的 python 数据类型,因此您永远不需要进行任何显式转换(即您可以删除代码中的所有 unicode()
和 toPyObject()
调用。
或者,您也可以将 Python3 与 PyQt4 一起使用,默认情况下它与 PySide 具有相同的行为(因此不需要 setapi
东西)。
我正在尝试获取一段独立的示例代码,以便在获取可编辑的单元格视图时使用 pandas 和 QTableView。
为此,我正在跟进之前的讨论:
虽然其他讨论中的答案和建议的修改有助于摆脱复选框,但那里讨论的代码仍然不适合我 (python 2.7)。
当我使用下面的代码修改单元格时,单元格中显示的内容是:PtQt4.PtCore.QtVariant object at ...
我使用的包版本是:
Pandas: 0.20.2
Pyside 1.2.4
Qt version: 4.8.4
SIP version: 4.14.4 PyQt version: 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_())
当我切换到 PySide 而不是 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_())
直接的问题是由将未转换的 QVariant
对象传递给基础数据库引起的。最简单的修复方法是将其转换为 python 对象,如下所示:
self._data.iloc[index.row(), index.column()] = value.toPyObject()
但是,这并没有真正解决代码的最基本问题,即您使用的是 Python 和 PyQt 的旧版本。 Qt 不再正式支持 Qt4,过不了多久 Python 和 Python2 也是如此。严格来说,PyQt4 已经是过时的遗留代码 - 所以你不应该将它用于新项目,除非你有 really 这样做的充分理由那(例如向后兼容性)。
如果可以,我强烈建议您尽快将代码移植到 Python3/PyQt5,因为从中长期来看,这会为您省去很多麻烦。但是,如果由于某种原因您不能这样做,并且您想继续使用 Python2/PyQt4,您可以通过将以下内容添加到程序的开头来获得与 PySide 相同的行为:
import sip
sip.setapi('QString', 2)
sip.setapi('QVariant', 2)
from PyQt4 import QtCore, QtGui
这样做之后,PyQt 会自动将所有 QString
和 QVariant
对象转换为普通的 python 数据类型,因此您永远不需要进行任何显式转换(即您可以删除代码中的所有 unicode()
和 toPyObject()
调用。
或者,您也可以将 Python3 与 PyQt4 一起使用,默认情况下它与 PySide 具有相同的行为(因此不需要 setapi
东西)。