PyQt5编程(45)—使用数据库(11)
4.4 使用关联委托
PyQt5编程(44)的程序有一个缺点,在添加新条目或编辑现有记录时,如果类别的输入不是代号而是“耗材”、“存储介质”时,所作的修改是不会被保存。因为该字段在数据库表中为整型字段,只能输入整数。
为了改变这种情况,我们将被一个被称为连接(connected)的特殊委托(delegate)。它能够第一表中执行搜索,提取id字段内容并将其存储到第二个表的字段中。
关联的委托功能由QSqlRelationalDelegate类实现。 继承层次结构如下:
QObject - QAbstractltemDelegate - QltemDelegate - QSqlRelationalDelegate
使用方法:
1. 以要展示的组件(如,TableView)作参数创建QSqlRelationalDelegate类实例;
2.调用setItemDelegate()、setItemDelegateForColumn()或setItemDelegateForRow()。
PyQt5中有个BUG, 在修改使用委托的某列值,并将焦点移到另一记录时,有时会显示id而非显示指定值的情况。要绕开此BUG,需要按如下操作:
1.把展示组件(TableView)的编辑模式设置为onManualSubmit;
2.处理dataChanged信号,调用submit()或submitAll()来实现修改的数据存储到数据库。
下面是 PyQt5编程(44)中的例子使用关联委托后的代码:
from PyQt5 import QtCore, QtWidgets, QtSql
import sys
def addRecord():
stm.insertRow(stm.rowCount())
def delRecord():
stm.removeRow(tv.currentIndex().row())
stm.select()
app = QtWidgets.QApplication(sys.argv)
window = QtWidgets.QWidget()
window.setWindowTitle("QRelationalSqlTableModel")
con = QtSql.QSqlDatabase.addDatabase('QSQLITE')
con.setDatabaseName('data.sqlite')
con.open()
stm = QtSql.QSqlRelationalTableModel(parent = window)
stm.setEditStrategy(QtSql.QSqlTableModel.OnManualSubmit)
stm.setTable('good')
stm.setSort(2, QtCore.Qt.AscendingOrder)
将good表中的category字段设置为到category表的链接
stm.setRelation(3, QtSql.QSqlRelation('category', 'id', 'catname'))
stm.select()
stm.setHeaderData(1, QtCore.Qt.Horizontal, '名称')
stm.setHeaderData(2, QtCore.Qt.Horizontal, '数量')
stm.setHeaderData(3, QtCore.Qt.Horizontal, '类别')
stm.dataChanged.connect(stm.submitAll)
vbox = QtWidgets.QVBoxLayout()
tv = QtWidgets.QTableView()
tv.setModel(stm)
tv.setItemDelegateForColumn(3, QtSql.QSqlRelationalDelegate(tv))
tv.hideColumn(0)
tv.setColumnWidth(1, 150)
tv.setColumnWidth(2, 60)
tv.setColumnWidth(3, 150)
vbox.addWidget(tv)
btnAdd = QtWidgets.QPushButton("添加记录(&A)")
btnAdd.clicked.connect(addRecord)
vbox.addWidget(btnAdd)
btnDel = QtWidgets.QPushButton("删除记录(&A)")
btnDel.clicked.connect(delRecord)
vbox.addWidget(btnDel)
window.setLayout(vbox)
window.resize(430, 250)
window.show()
sys.exit(app.exec_())