使用 QSQLTable 模型和 QDataWidget 映射器在 Sqlite table 中插入记录时出现问题
problem inserting record in Sqlite table using QSQLTable model and QDataWidget mapper
我正在努力处理下面显示的代码,该代码使用 QSQLTable 从具有 3 列(“codice”(主键,自动增量)、“descrizione”、“peso”)的 SQLite table 加载数据带有 QDataWidget 映射器的模型。
我的问题是我定义了一个 QPushButton 以在 table 中插入新记录,但我无法让它工作。我尝试了使用 self.model.insertRows 或 self.model.insertRecord 的不同方法,但没有成功。我没有收到任何错误,但没有插入记录。
确实存在我无法理解的概念错误。
import sys
from PyQt5.QtCore import QSize, Qt
from PyQt5.QtSql import QSqlDatabase, QSqlTableModel
from PyQt5.QtWidgets import (
QApplication,
QComboBox,
QDataWidgetMapper,
QDoubleSpinBox,
QFormLayout,
QHBoxLayout,
QLabel,
QLineEdit,
QMainWindow,
QPushButton,
QSpinBox,
QTableView,
QVBoxLayout,
QWidget,
)
from connect_SQLITE import Database
db=Database.con
db.open()
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
layout = QVBoxLayout()
form = QFormLayout()
self.codice = QSpinBox()
self.codice.setRange(0, 2147483647)
self.codice.setDisabled(True)
self.descrizione = QLineEdit()
self.peso = QDoubleSpinBox()
self.peso.setDecimals(5)
self.peso.setRange(0, 9999.99999)
self.peso.setSingleStep(0.01)
form.addRow(QLabel("Codice Materiale"), self.codice)
form.addRow(QLabel("Nome Materiale"), self.descrizione)
form.addRow(QLabel("Peso specifico Materiale (Kg/dm3)"), self.peso)
self.model = QSqlTableModel(db=db)
self.mapper = QDataWidgetMapper()
self.mapper.setModel(self.model)
self.mapper.addMapping(self.codice, 0)
self.mapper.addMapping(self.descrizione, 1)
self.mapper.addMapping(self.peso, 2)
self.model.setTable("Prova")
self.model.select()
self.mapper.toFirst()
# tag::controls[]
self.setMinimumSize(QSize(400, 400))
controls = QHBoxLayout()
prev_rec = QPushButton("Precedente")
prev_rec.clicked.connect(self.mapper.toPrevious)
next_rec = QPushButton("Successivo")
next_rec.clicked.connect(self.mapper.toNext)
ins_rec = QPushButton("Inserimento")
ins_rec.clicked.connect(self.inserimento_materiale)
save_rec = QPushButton("Salvataggio modifiche")
save_rec.clicked.connect(self.mapper.submit)
controls.addWidget(prev_rec)
controls.addWidget(next_rec)
controls.addWidget(ins_rec)
controls.addWidget(save_rec)
layout.addLayout(form)
layout.addLayout(controls)
widget = QWidget()
widget.setLayout(layout)
self.setCentralWidget(widget)
# end::controls[]
def inserimento_materiale(self):
self.model.insertRows(self.model.rowCount(), 1)
self.model.setData(self.model.index(0,1), self.descrizione.text())
self.model.setData(self.model.index(0,2), self.peso)
self.model.submit()
app = QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec_()
问题是映射器默认使用 AutoSubmit
submitPolicy()
:
Whenever a widget loses focus, the widget's current value is set to the item model.
而 QSqlTableModel 使用 OnRowChange
editStrategy()
:
Changes to a row will be applied when the user selects a different row.
此外,您在第一行调用 setData()
,而不是 new。
结果是当inserimento_materiale
被调用并插入新行时,mapper的当前索引仍然是之前的索引(启动时第一个,由于toFirst()
或任何toPrevious()
或 toNext()
之后的其他设置),setData()
在任何情况下都不起作用:
- 在你的例子中,映射器由于其
AutoSubmit
策略已经设置了缓存数据,但尚未实际提交到数据库中,并且setData()
returns False
因为“对于 OnRowChange,只有当其他行没有缓存更改时,索引才可能接收更改”;
- 即使为
setData()
设置正确的行也不起作用,因为插入新行会导致映射器提交前一行中当前小部件的先前数据,并且不会正确更新新映射器索引;
请注意,您的代码也有拼写错误:您正在尝试使用 self.peso
调用 setData()
,这是一个小部件。
一个可能的解决方案是在执行任何操作之前读取当前小部件值,revert()
模型数据(以避免将新数据保存在先前的索引中),将新数据提交给模型并在映射器中设置新索引:
def inserimento_materiale(self):
descrizione = self.descrizione.text()
peso = self.peso.value()
self.model.revert()
row = self.model.rowCount()
self.model.insertRow(row)
self.model.setData(self.model.index(row, 1), descrizione)
self.model.setData(self.model.index(row, 2), peso)
self.model.submitAll()
self.mapper.setCurrentIndex(row)
请注意,由于您有一个专用的提交按钮,您应该将映射器的提交策略更改为 ManualSubmit
,这会使事情变得更容易:
class MainWindow(QMainWindow):
def __init__(self):
# ...
self.mapper.setSubmitPolicy(self.mapper.ManualSubmit)
def inserimento_materiale(self):
row = self.model.rowCount()
self.model.insertRow(row)
self.model.setData(self.model.index(row, 1), self.descrizione.text())
self.model.setData(self.model.index(row, 2), self.peso.value())
self.model.submitAll()
self.mapper.setCurrentIndex(row)
我正在努力处理下面显示的代码,该代码使用 QSQLTable 从具有 3 列(“codice”(主键,自动增量)、“descrizione”、“peso”)的 SQLite table 加载数据带有 QDataWidget 映射器的模型。
我的问题是我定义了一个 QPushButton 以在 table 中插入新记录,但我无法让它工作。我尝试了使用 self.model.insertRows 或 self.model.insertRecord 的不同方法,但没有成功。我没有收到任何错误,但没有插入记录。
确实存在我无法理解的概念错误。
import sys
from PyQt5.QtCore import QSize, Qt
from PyQt5.QtSql import QSqlDatabase, QSqlTableModel
from PyQt5.QtWidgets import (
QApplication,
QComboBox,
QDataWidgetMapper,
QDoubleSpinBox,
QFormLayout,
QHBoxLayout,
QLabel,
QLineEdit,
QMainWindow,
QPushButton,
QSpinBox,
QTableView,
QVBoxLayout,
QWidget,
)
from connect_SQLITE import Database
db=Database.con
db.open()
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
layout = QVBoxLayout()
form = QFormLayout()
self.codice = QSpinBox()
self.codice.setRange(0, 2147483647)
self.codice.setDisabled(True)
self.descrizione = QLineEdit()
self.peso = QDoubleSpinBox()
self.peso.setDecimals(5)
self.peso.setRange(0, 9999.99999)
self.peso.setSingleStep(0.01)
form.addRow(QLabel("Codice Materiale"), self.codice)
form.addRow(QLabel("Nome Materiale"), self.descrizione)
form.addRow(QLabel("Peso specifico Materiale (Kg/dm3)"), self.peso)
self.model = QSqlTableModel(db=db)
self.mapper = QDataWidgetMapper()
self.mapper.setModel(self.model)
self.mapper.addMapping(self.codice, 0)
self.mapper.addMapping(self.descrizione, 1)
self.mapper.addMapping(self.peso, 2)
self.model.setTable("Prova")
self.model.select()
self.mapper.toFirst()
# tag::controls[]
self.setMinimumSize(QSize(400, 400))
controls = QHBoxLayout()
prev_rec = QPushButton("Precedente")
prev_rec.clicked.connect(self.mapper.toPrevious)
next_rec = QPushButton("Successivo")
next_rec.clicked.connect(self.mapper.toNext)
ins_rec = QPushButton("Inserimento")
ins_rec.clicked.connect(self.inserimento_materiale)
save_rec = QPushButton("Salvataggio modifiche")
save_rec.clicked.connect(self.mapper.submit)
controls.addWidget(prev_rec)
controls.addWidget(next_rec)
controls.addWidget(ins_rec)
controls.addWidget(save_rec)
layout.addLayout(form)
layout.addLayout(controls)
widget = QWidget()
widget.setLayout(layout)
self.setCentralWidget(widget)
# end::controls[]
def inserimento_materiale(self):
self.model.insertRows(self.model.rowCount(), 1)
self.model.setData(self.model.index(0,1), self.descrizione.text())
self.model.setData(self.model.index(0,2), self.peso)
self.model.submit()
app = QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec_()
问题是映射器默认使用 AutoSubmit
submitPolicy()
:
Whenever a widget loses focus, the widget's current value is set to the item model.
而 QSqlTableModel 使用 OnRowChange
editStrategy()
:
Changes to a row will be applied when the user selects a different row.
此外,您在第一行调用 setData()
,而不是 new。
结果是当inserimento_materiale
被调用并插入新行时,mapper的当前索引仍然是之前的索引(启动时第一个,由于toFirst()
或任何toPrevious()
或 toNext()
之后的其他设置),setData()
在任何情况下都不起作用:
- 在你的例子中,映射器由于其
AutoSubmit
策略已经设置了缓存数据,但尚未实际提交到数据库中,并且setData()
returnsFalse
因为“对于 OnRowChange,只有当其他行没有缓存更改时,索引才可能接收更改”; - 即使为
setData()
设置正确的行也不起作用,因为插入新行会导致映射器提交前一行中当前小部件的先前数据,并且不会正确更新新映射器索引;
请注意,您的代码也有拼写错误:您正在尝试使用 self.peso
调用 setData()
,这是一个小部件。
一个可能的解决方案是在执行任何操作之前读取当前小部件值,revert()
模型数据(以避免将新数据保存在先前的索引中),将新数据提交给模型并在映射器中设置新索引:
def inserimento_materiale(self):
descrizione = self.descrizione.text()
peso = self.peso.value()
self.model.revert()
row = self.model.rowCount()
self.model.insertRow(row)
self.model.setData(self.model.index(row, 1), descrizione)
self.model.setData(self.model.index(row, 2), peso)
self.model.submitAll()
self.mapper.setCurrentIndex(row)
请注意,由于您有一个专用的提交按钮,您应该将映射器的提交策略更改为 ManualSubmit
,这会使事情变得更容易:
class MainWindow(QMainWindow):
def __init__(self):
# ...
self.mapper.setSubmitPolicy(self.mapper.ManualSubmit)
def inserimento_materiale(self):
row = self.model.rowCount()
self.model.insertRow(row)
self.model.setData(self.model.index(row, 1), self.descrizione.text())
self.model.setData(self.model.index(row, 2), self.peso.value())
self.model.submitAll()
self.mapper.setCurrentIndex(row)