PyQt - 使用 pandas DataFrame 在 QAbstractTableModel (QTableView) 中加载 SQL - 在 GUI 中编辑数据

tgr*_*dje 6 python pyqt qtableview python-2.7 pandas

我对 python 和使用都很陌生WinPython-32bit-2.7.10.3(包括QTDesigner 4.8.7。我正在尝试使用 QtableViews 编写一个接口,以便在两个独立的项目上使用 sqlite 数据库。

\n\n

该算法大致如下:
\n- 连接到数据库并将数据转换为pandas.DataFrame
\n- 将 DataFrame 转换为 QAbstractTableModel
\n- 将 QAbstractTableModel 应用到tableview.model
\n- 加载对话框

\n\n

我没有得到相同的行为,具体取决于用于创建数据帧的 sql:\n给定一个 SQL 表“参数”,具有 3 个字段(LIBELLE 作为 varchar,VALEUR 作为 varchar,TEST 作为布尔值),尝试的 sql 是:

\n\n
    \n
  • \'从参数中选择 LIBELLE AS "Param\xc3\xa8tre", VALEUR AS "Valeur"\'.encode("utf-8")
  • \n
  • \'SELECT * FROM 参数\'.encode("utf-8")
  • \n
\n\n

通过第一个请求,我可以编辑表格视图内的数据。对于第二个请求,我可以选择一个“单元格”,对其进行编辑,但是当我提交版本(按 Enter 键)时,数据将设置回其第一个值。

\n\n

在搜索时,我发现无论“任何”值是什么,setData 代码的这一行都不起作用:

\n\n
self._data.values[index.row()][index.column()] = "anything"\n
Run Code Online (Sandbox Code Playgroud)\n\n

您可以通过删除主代码中第27行开头的#字符来测试sql源的发生率。

\n\n

我已将代码截断到严格的最低限度(非常接近我的第一个项目的原始工作代码),并且我感到非常困惑。如果有人有想法,那就太好了!

\n\n

谢谢

\n\n

PS:我后来发布了代码,但我还没有找到加入的方法sqlite.db...如果有人可以指导我,我会很高兴添加它;与此同时,我已经在我的google.drive上加入了该地段的整个 zip

\n\n
\n\n

编辑#2:

\n\n

仍然无法理解那里出了什么问题,但我刚刚发现一旦加载数据就无法将数据提交到模型。我很确定这是我问题的核心,并随后更新了问题和标题。

\n\n
\n\n

主要代码:

\n\n
#\xc2\xad*\xc2\xadcoding: utf\xc2\xad8 \xc2\xad*\xc2\xad\n\nfrom PyQt4 import QtCore, QtGui\n\nimport os,sys\nfrom parametrage import Ui_WinParam\nfrom PANDAS_TO_PYQT import PandasModel\n\nimport pandas as pd\nimport sqlite3\n\nclass window_parametreur(QtGui.QDialog, Ui_WinParam):\n    def __init__(self, dataframemodel, parent=None):        \n        QtGui.QDialog.__init__(self, parent)\n        # Set up the user interface from Designer.\n        self.ui = Ui_WinParam()\n        self.ui.setupUi(self)\n        self.setModal(True)        \n        self.ui.tableView.setModel(dataframemodel)\n        self.ui.tableView.resizeColumnsToContents()\n\ndef OpenParametreur(self, db_path):\n\n    #connecting to database and getting datas as pandas.dataframe\n    con = sqlite3.connect(db_path)\n    strSQL = u\'SELECT LIBELLE AS "Param\xc3\xa8tre", VALEUR AS "Valeur" FROM parametres\'.encode("utf-8")\n    #strSQL = u\'SELECT * FROM parametres\'.encode("utf-8")\n    data = pd.read_sql_query(strSQL, con)\n    con.close()\n\n    #converting to QtCore.QAbstractTableModel\n    model = PandasModel(data)\n\n    #loading dialog\n    self.f=window_parametreur(model)   \n    self.f.exec_()\n\nif __name__=="__main__":\n    a=QtGui.QApplication(sys.argv)\n    f=QtGui.QMainWindow()\n    print OpenParametreur(f, ".\\SQLiteDataBase.db")\n
Run Code Online (Sandbox Code Playgroud)\n\n

“PANDAS_TO_PYQT.py”的代码,被调用以将 pandas.dataframe 转换为 QtCore.QAbstractTableModel

\n\n
#\xc2\xad*\xc2\xadcoding: utf\xc2\xad8 \xc2\xad*\xc2\xad\n\nfrom PyQt4 import QtCore, QtGui\n\nclass PandasModel(QtCore.QAbstractTableModel):\n    def __init__(self, data, parent=None):\n        QtCore.QAbstractTableModel.__init__(self, parent)\n        self._data = data\n\n    def rowCount(self, parent=None):\n        return len(self._data.values)\n\n    def columnCount(self, parent=None):\n        return self._data.columns.size\n\n    def data(self, index, role=QtCore.Qt.DisplayRole):\n        if index.isValid():\n            if role == QtCore.Qt.DisplayRole or role == QtCore.Qt.EditRole:\n                return QtCore.QVariant(unicode(\n                    self._data.values[index.row()][index.column()]))\n        return QtCore.QVariant()\n\n    def headerData(self, section, orientation, role=QtCore.Qt.DisplayRole):\n        if role != QtCore.Qt.DisplayRole:\n            return None\n        if orientation == QtCore.Qt.Horizontal:\n            try:\n                return \'%s\' % unicode(self._data.columns.tolist()[section], encoding="utf-8")\n            except (IndexError, ):\n                return QtCore.QVariant()\n        elif orientation == QtCore.Qt.Vertical:\n            try:\n                return \'%s\' % self._data.index.tolist()[section]\n            except (IndexError, ):\n                return QtCore.QVariant()\n\n    def flags(self, index):\n        return QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEditable\n\n    def setData(self, index, value, role=QtCore.Qt.EditRole):\n        if index.isValid():\n\n            print "data set with keyboard : " + value.toByteArray().data().decode("latin1")\n            self._data.values[index.row()][index.column()] = "anything"\n            print "data committed : " +self._data.values[index.row()][index.column()]\n\n            self.dataChanged.emit(index, index)\n            return True\n        return QtCore.QVariant()\n
Run Code Online (Sandbox Code Playgroud)\n\n

parametrage.py 的代码,由 QtDesigner 创建,包含对话框源:

\n\n
# -*- coding: utf-8 -*-\n\n# Form implementation generated from reading ui file \'parametrage.ui\'\n#\n# Created by: PyQt4 UI code generator 4.11.4\n#\n# WARNING! All changes made in this file will be lost!\n\nfrom PyQt4 import QtCore, QtGui\n\ntry:\n    _fromUtf8 = QtCore.QString.fromUtf8\nexcept AttributeError:\n    def _fromUtf8(s):\n        return s\n\ntry:\n    _encoding = QtGui.QApplication.UnicodeUTF8\n    def _translate(context, text, disambig):\n        return QtGui.QApplication.translate(context, text, disambig, _encoding)\nexcept AttributeError:\n    def _translate(context, text, disambig):\n        return QtGui.QApplication.translate(context, text, disambig)\n\nclass Ui_WinParam(object):\n    def setupUi(self, WinParam):\n        WinParam.setObjectName(_fromUtf8("WinParam"))\n        WinParam.resize(608, 279)\n        icon = QtGui.QIcon()\n        icon.addPixmap(QtGui.QPixmap(_fromUtf8("../../pictures/EAUX.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off)\n        WinParam.setWindowIcon(icon)\n        self.gridLayout = QtGui.QGridLayout(WinParam)\n        self.gridLayout.setObjectName(_fromUtf8("gridLayout"))\n        self.ButtonBox = QtGui.QDialogButtonBox(WinParam)\n        self.ButtonBox.setOrientation(QtCore.Qt.Horizontal)\n        self.ButtonBox.setStandardButtons(QtGui.QDialogButtonBox.Cancel|QtGui.QDialogButtonBox.Ok)\n        self.ButtonBox.setCenterButtons(True)\n        self.ButtonBox.setObjectName(_fromUtf8("ButtonBox"))\n        self.gridLayout.addWidget(self.ButtonBox, 1, 0, 1, 1)\n        self.tableView = QtGui.QTableView(WinParam)\n        self.tableView.setEditTriggers(QtGui.QAbstractItemView.DoubleClicked)\n        self.tableView.setSortingEnabled(False)\n        self.tableView.setObjectName(_fromUtf8("tableView"))\n        self.gridLayout.addWidget(self.tableView, 0, 0, 1, 1)\n\n        self.retranslateUi(WinParam)\n        QtCore.QObject.connect(self.ButtonBox, QtCore.SIGNAL(_fromUtf8("accepted()")), WinParam.accept)\n        QtCore.QObject.connect(self.ButtonBox, QtCore.SIGNAL(_fromUtf8("rejected()")), WinParam.reject)\n        QtCore.QMetaObject.connectSlotsByName(WinParam)\n\n    def retranslateUi(self, WinParam):\n        WinParam.setWindowTitle(_translate("WinParam", "Param\xc3\xa9trage", None))\n\n\nif __name__ == "__main__":\n    import sys\n    app = QtGui.QApplication(sys.argv)\n    WinParam = QtGui.QDialog()\n    ui = Ui_WinParam()\n    ui.setupUi(WinParam)\n    WinParam.show()\n    sys.exit(app.exec_())\n
Run Code Online (Sandbox Code Playgroud)\n

tgr*_*dje 3

我终于想通了...但我仍然不知道为什么 pandas 仅通过更改 SQL 请求就可以不同地工作(必须是 read_sql_query 过程中的某些内容...)

\n\n

为了让课程正常工作,我必须更改“PANDAS_TO_PYQT.py”的代码,替换

\n\n
self._data.values[index.row()][index.column()]\n
Run Code Online (Sandbox Code Playgroud)\n\n

经过

\n\n
self._data.iloc[index.row(),index.column()]\n
Run Code Online (Sandbox Code Playgroud)\n\n

在 setData 和 data 函数中。

\n\n

不知何故,pandas 似乎在这个过程中生成了数据帧的副本(对于那些寻找解释的人,请转到doc)。

\n\n

因此,将数据帧转换为 QAbstractTableModel 的正确类代码是:

\n\n
#\xc2\xad*\xc2\xadcoding: utf\xc2\xad8 \xc2\xad*\xc2\xad\n\nfrom PyQt4 import QtCore, QtGui\n\nclass PandasModel(QtCore.QAbstractTableModel):\n    def __init__(self, data, parent=None):\n        QtCore.QAbstractTableModel.__init__(self, parent)\n        self._data = data\n\n    def rowCount(self, parent=None):\n        return len(self._data.values)\n\n    def columnCount(self, parent=None):\n        return self._data.columns.size\n\n    def data(self, index, role=QtCore.Qt.DisplayRole):\n        if index.isValid():\n            if role == QtCore.Qt.DisplayRole or role == QtCore.Qt.EditRole:\n                return QtCore.QVariant(unicode(\n                    self._data.iloc[index.row(),index.column()]))\n        return QtCore.QVariant()\n\n    def headerData(self, section, orientation, role=QtCore.Qt.DisplayRole):\n        if role != QtCore.Qt.DisplayRole:\n            return None\n        if orientation == QtCore.Qt.Horizontal:\n            try:\n                return \'%s\' % unicode(self._data.columns.tolist()[section], encoding="utf-8")\n            except (IndexError, ):\n                return QtCore.QVariant()\n        elif orientation == QtCore.Qt.Vertical:\n            try:\n                return \'%s\' % self._data.index.tolist()[section]\n            except (IndexError, ):\n                return QtCore.QVariant()\n\n    def flags(self, index):\n        return QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEditable\n\n    def setData(self, index, value, role=QtCore.Qt.EditRole):\n        if index.isValid():\n            self._data.iloc[index.row(),index.column()] = value.toByteArray().data().decode("latin1")\n            if self.data(index,QtCore.Qt.DisplayRole) == value.toByteArray().data().decode("latin1"):\n                self.dataChanged.emit(index, index)\n                return True\n        return QtCore.QVariant()\n
Run Code Online (Sandbox Code Playgroud)\n