使用 QSQLTable 模型和 QDataWidget 映射器在 Sqlite 表中插入记录时出现问题

发布于 2025-01-10 21:38:03 字数 2988 浏览 0 评论 0原文

我正在努力处理下面显示的代码,该代码使用带有 QDataWidget 映射器的 QSQLTable 模型从具有 3 列(“codice”(主键,自动增量)、“descrizione”、“peso”)的 SQLite 表加载数据。

我的问题是我定义了一个 QPushButton 来在这样的表中插入新记录,但我无法让它工作。我使用 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_()

I'm struggling with the code shown below that loads data from a SQLite table which has 3 columns ("codice" (primary key, autoincrement), "descrizione", "peso") using QSQLTable model with QDataWidget mapper.

My problem is I defined a QPushButton to insert new record in such table but I'm not able to get it work. I tried different ways using self.model.insertRows or self.model.insertRecord but I was not successfully. I'm not getting any error but the record is not inserted.

For sure there are conceptual errors I can't catch.

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_()

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

你穿错了嫁妆 2025-01-17 21:38:03

问题是默认情况下映射器使用 AutoSubmit submitPolicy()

每当小部件失去焦点时,小部件的当前值就会设置为项目模型。

QSqlTableModel 使用 OnRowChange editStrategy()

当用户选择不同的行时,将应用对行的更改。

另外,您在第一行调用 setData(),而不是 new

结果是,当调用 inserimento_materiale 并插入新行时,映射器的当前索引仍然是前一个(启动时的第一个,由于 toFirst() 或 toPrevious()toNext() 之后的任何其他设置),setData() 在任何情况下都不起作用:

  • 在您的示例中,映射器已经设置了缓存数据由于其AutoSubmit政策,但尚未实际提交到数据库中,并且setData() 返回 False 因为“对于 OnRowChange,仅当没有其他行具有缓存的更改时,索引才可能收到更改”;
  • 即使为 setData() 设置正确的行也不起作用,因为插入新行会导致映射器提交前一行中当前小部件的先前数据,并且不会正确更新新映射器指数;

请注意,您的代码也有一个拼写错误:您尝试使用 self.peso(这是一个小部件)调用 setData()

A可能的解决方案是在执行任何操作之前读取当前的小部件值,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)

The problem is that by default the mapper uses the AutoSubmit submitPolicy():

Whenever a widget loses focus, the widget's current value is set to the item model.

And QSqlTableModel uses the OnRowChange editStrategy():

Changes to a row will be applied when the user selects a different row.

Also, you're calling setData() on the first row, instead of the new one.

The result is that when inserimento_materiale is called and a new row is inserted, the current index of the mapper is still the previous one (the first on startup, due to toFirst() or any other set after toPrevious() or toNext()), setData() will not work in any case:

  • in your example, the mapper has already set the cached data due to its AutoSubmit policy, but it has not been actually submitted in the database, and setData() returns False because "For OnRowChange, an index may receive a change only if no other row has a cached change";
  • even setting the proper row for setData() will not work, as inserting a new row causes the mapper to submit the previous data of the current widgets in the previous row, and will not properly update the new mapper index;

Be aware that your code also has a typo: you're trying to call setData() with self.peso which is a widget.

A possible solution is to read the current widget values before doing anything, revert() the model data (to avoid saving the new data on the previous index), submit the new data to the model and set the new index in the mapper:

    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)

Note that since you have a dedicated submit button, you should change the mapper's submit policy to ManualSubmit, which makes things easier:

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)
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文