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_())
我在尝试用 Python 3.7 和 PyQt5 编写一个小应用程序时遇到了麻烦。
我是 Python 的新手,我对 C++ 的了解接近于 0(零)。
最后,我的项目将有一个 Tableview,其模型是链接到 QSlite 数据库的 QsqlTableModel table。
即table由三列组成。我有兴趣只编辑具有 0/1 值的第二列(索引 1)。在本专栏中,我想作为委托(但使用永久编辑器)放置一个切换按钮,如
但问题刚开始就出现了。我能够填充模型并且视图正确显示来自 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_())