QSqlTableModel数据编辑(子类化)问题

QSqlTableModel data edit (subclassing) problem

我在尝试用 Python 3.7 和 PyQt5 编写一个小应用程序时遇到了麻烦。

我是 Python 的新手,我对 C++ 的了解接近于 0(零)。
最后,我的项目将有一个 Tableview,其模型是链接到 QSlite 数据库的 QsqlTableModel table。

即table由三列组成。我有兴趣只编辑具有 0/1 值的第二列(索引 1)。在本专栏中,我想作为委托(但使用永久编辑器)放置一个切换按钮,如 以更新布尔值(最后,应用程序将类似于 ToDo 列表)。

但问题刚开始就出现了。我能够填充模型并且视图正确显示来自 table 的数据,格式正确(仅用于测试目的),但是当我尝试编辑数据时,它不会更改模型中的数据编辑行中的所有值都消失了,我在行 header 中得到感叹号。

这仅适用于我重新实现数据方法。如果我只重新实现标志方法一切正常。

我确定我犯了一个愚蠢的错误,但我找不到问题所在...

这是我的代码。

main.py

from MyClass import MyConnection, TableView, Model
from PyQt5 import QtWidgets, QtCore, QtGui
import sys


if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    MyConnection('data22.db')
    model = Model('tabella_di_test')
    table = TableView(model)
    table.setSizeAdjustPolicy(QtWidgets.QAbstractScrollArea.AdjustToContents)
    table.show()
    sys.exit(app.exec())

MyClass.py

from PyQt5.QtSql import QSqlDatabase, QSqlTableModel
from PyQt5.QtWidgets import QTableView, QItemDelegate
from PyQt5.QtCore import Qt, QModelIndex, QRect, QAbstractTableModel, QVariant
from PyQt5.QtGui import QFont, QPainter, QPixmap
import typing
import sys

COL_DEL = 1 #column that receive delegate in the future
COL_ICON = 2


class MyConnection:
    def __init__(self, name):
        super(MyConnection, self).__init__()
        self.db = QSqlDatabase().addDatabase("QSQLITE")
        self.db.setDatabaseName(name)
        if self.db.open():
            print("Connection Opened...")
        else:
            sys.exit("Errore nella connessione al DB...")


class TableView(QTableView):
    def __init__(self, model):
        super(TableView, self).__init__()
        self.setAlternatingRowColors(True)
        self.setModel(model)


class Model(QSqlTableModel):
    def __init__(self, name):
        super(Model, self).__init__()
        self.setTable(name)
        self.select()
        print(self.rowCount())

    def flags(self, index: QModelIndex):
        if not index.isValid():
            print('Flags ==> Index is not valid!')
            return QVariant()
        if index.column() == COL_DEL:
            print('Flags ==> Index({},{}) '.format(index.row(), index.column()))
            return Qt.NoItemFlags | Qt.ItemIsEditable | Qt.ItemIsEnabled | Qt.ItemIsSelectable
        else:
            return Qt.NoItemFlags | Qt.ItemIsEnabled

    def data(self, index: QModelIndex, role=None) -> typing.Any:
        if not index.isValid():
            print('Index is not valid!')
            return QVariant()
        if role == Qt.TextAlignmentRole:
            return Qt.AlignCenter
        if role == Qt.DisplayRole:
            return super(Model, self).data(index, role)
        if role == Qt.FontRole and index.column() == COL_DEL:
            font = QFont()
            font.setBold(True)
            font.setPixelSize(16)
            return font


class Delegate(QItemDelegate):
    def __init__(self):
        super(Delegate, self).__init__()

不需要为这种情况实现自定义模型,只需一个带有自定义委托的 QSqlTableModel 就足够了:

import os

from PyQt5 import QtCore, QtGui, QtWidgets, QtSql

CURRENT_DIR = os.path.dirname(os.path.realpath(__file__))
COL_DEL = 1


def create_connection(name):
    db = QtSql.QSqlDatabase.addDatabase("QSQLITE")
    db.setDatabaseName(name)
    if db.open():
        print("Connection Opened...")
        return True
    print("Errore nella connessione al DB...")
    return False


class MySwitch(QtWidgets.QPushButton):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.setCheckable(True)
        self.setMinimumWidth(66)
        self.setMinimumHeight(22)

    def paintEvent(self, event):
        label = "ON" if self.isChecked() else "OFF"
        bg_color = (
            QtGui.QColor(QtCore.Qt.green)
            if self.isChecked()
            else QtGui.QColor(QtCore.Qt.red)
        )

        radius = 10
        width = 32
        center = self.rect().center()

        painter = QtGui.QPainter(self)
        painter.setRenderHint(QtGui.QPainter.Antialiasing)
        painter.translate(center)
        painter.setBrush(QtGui.QColor(0, 0, 0))

        pen = QtGui.QPen(QtCore.Qt.black)
        pen.setWidth(2)
        painter.setPen(pen)

        painter.drawRoundedRect(
            QtCore.QRect(-width, -radius, 2 * width, 2 * radius), radius, radius
        )
        painter.setBrush(QtGui.QBrush(bg_color))
        sw_rect = QtCore.QRect(-radius, -radius, width + radius, 2 * radius)
        if not self.isChecked():
            sw_rect.moveLeft(-width)
        painter.drawRoundedRect(sw_rect, radius, radius)
        painter.drawText(sw_rect, QtCore.Qt.AlignCenter, label)


class CustomDelegate(QtWidgets.QStyledItemDelegate):
    def initStyleOption(self, option, index):
        super().initStyleOption(option, index)
        font = QtGui.QFont()
        font.setBold(True)
        font.setPixelSize(16)
        option.font = font
        option.displayAlignment = QtCore.Qt.AlignCenter

    def paint(self, painter, option, index):
        if (
            isinstance(option.widget, QtWidgets.QAbstractItemView)
            and index.column() == COL_DEL
        ):
            option.widget.openPersistentEditor(index)
            return
        super().paint(painter, option, index)

    def createEditor(self, parent, option, index):
        if index.column() == COL_DEL:
            editor = MySwitch(parent)
            editor.toggled.connect(lambda *args, e=editor: self.commitData.emit(e))
            return editor

    def setModelData(self, editor, model, index):
        if index.column() == COL_DEL:
            value = 1 if editor.isChecked() else 0
            model.setData(index, value)
        else:
            super().setModelData(editor, model, index)

    def setEditorData(self, editor, index):
        if index.column() == COL_DEL:
            value = bool(index.data())
            editor.setChecked(value)
        else:
            super().setEditorData(editor, index)


if __name__ == "__main__":
    import sys

    app = QtWidgets.QApplication(sys.argv)

    if not create_connection(os.path.join(CURRENT_DIR, "data22.db")):
        sys.exit(-1)

    w = QtWidgets.QTableView()
    delegate = CustomDelegate(w)
    w.setItemDelegate(delegate)
    model = QtSql.QSqlTableModel()
    model.setEditStrategy(QtSql.QSqlTableModel.OnFieldChange)
    model.setTable("tabella_di_test")
    model.select()
    w.setModel(model)
    w.resize(640, 480)
    w.show()

    sys.exit(app.exec_())