qtableview 无法显示新插入的行
Posted
技术标签:
【中文标题】qtableview 无法显示新插入的行【英文标题】:qtableview fails to display the newly inserted row 【发布时间】:2021-09-12 13:13:05 【问题描述】:使用QtableView + QSqlTableModel组合时,插入新行后QtableView无法正确显示插入行。
id 字段被隐藏。 EditStrategy 是 OnFieldChange。在模型的末尾有一个按钮可以插入一个新的空行。在用户填写完所有字段并按回车键或单击其他行后,预期的行为是将行提交到数据库并且视图保留新行。但是QSqlTableModel成功将数据插入到数据库表中,但是tableview新插入的行变成了空,并且字段不可编辑。
如果 id 字段未隐藏并且明确提供,则行为将更改为所需的行为。然而,如果 id 未隐藏且未手动填充,则该行为再次失败,如上所述。
为什么会这样?
这是最小的可重现代码。您将不得不更改数据库配置文件。并使用自动增量 id 连接到表:
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import QModelIndex
from PyQt5.QtSql import QSqlDatabase, QSqlTableModel
from PyQt5.QtWidgets import QPushButton
class Ui_main(object):
def setupUi(self, main):
main.setObjectName("main")
main.resize(781, 652)
main.setContextMenuPolicy(QtCore.Qt.NoContextMenu)
self.verticalLayoutWidget = QtWidgets.QWidget(main)
self.verticalLayoutWidget.setGeometry(QtCore.QRect(10, 10, 761, 631))
self.verticalLayoutWidget.setObjectName("verticalLayoutWidget")
self.verticalLayout = QtWidgets.QVBoxLayout(self.verticalLayoutWidget)
self.verticalLayout.setContentsMargins(0, 0, 0, 0)
self.verticalLayout.setObjectName("verticalLayout")
# Replace values with your database configurations
# But the table you refer to must have autoincremented primary key
database = QSqlDatabase.addDatabase('QPSQL')
database.setHostName('localhost')
database.setPort(5432)
database.setDatabaseName('some_database')
database.setUserName('some_user')
database.setPassword('some_password')
database.open()
button_add = QPushButton("AddRow")
button_add.clicked.connect(self.addRow)
self.verticalLayout.addWidget(button_add)
self.tableView = QtWidgets.QTableView(self.verticalLayoutWidget)
self.tableView.setObjectName("tableView")
self.tableView.verticalHeader().setVisible(False)
self.verticalLayout.addWidget(self.tableView)
self.table_model = QSqlTableModel(self.tableView, database)
table_name = 'some_table'
self.table_model.setTable(table_name)
self.table_model.setEditStrategy(QSqlTableModel.OnFieldChange)
self.table_model.select()
self.tableView.setModel(self.table_model)
self.tableView.hideColumn(0)
self.retranslateUi(main)
QtCore.QMetaObject.connectSlotsByName(main)
def retranslateUi(self, main):
_translate = QtCore.QCoreApplication.translate
main.setWindowTitle(_translate("main", "main"))
def addRow(self):
count = self.table_model.rowCount(QModelIndex())
self.table_model.insertRow(count)
self.tableView.scrollToBottom()
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
main_window = QtWidgets.QWidget()
window = Ui_main()
window.setupUi(main_window)
main_window.show()
sys.exit(app.exec_())
更新:
我现在使用的临时解决方法是从获取的表中查找 max id 并将 max_id + 1 附加到新插入的行中。因此,该行在提交到数据库时具有一个 id。但是,这似乎不是正确的方式,因为数据库自己处理主键...
更新 2: 这是我的数据库表的样子:
设计:
book_of_accounts: table
+ columns
id: bigint NN default nextval('book_of_accounts_id_seq'::regclass)
code: varchar(256) NN
name: varchar(1024) NN
account_type: varchar(256) NN
quantitative: boolean NN
monetary: boolean NN
subconoto_1: varchar(256)
subconoto_2: varchar(256)
subconoto_3: varchar(256)
+ indices
book_of_accounts_pkey: unique (id)
bookofaccounts_code: unique (code)
bookofaccounts_name: index (name)
+ keys
book_of_accounts_pkey: PK (id)
SQL
create table if not exists book_of_accounts
(
id bigserial not null
constraint book_of_accounts_pkey
primary key,
code varchar(256) not null,
name varchar(1024) not null,
account_type varchar(256) not null,
quantitative boolean not null,
monetary boolean not null,
subconoto_1 varchar(256),
subconoto_2 varchar(256),
subconoto_3 varchar(256)
);
alter table book_of_accounts owner to hamlet;
create unique index if not exists bookofaccounts_code
on book_of_accounts (code);
create index if not exists bookofaccounts_name
on book_of_accounts (name);
【问题讨论】:
能否展示一下您创建表格的说明(我想知道存在哪些类型的字段以及每个字段的特点) @eyllanesc 我已根据您的要求更新了我的问题。 你试过insertRecord()而不是insertRow()吗?另外,我会在每次更新后添加 table.show()。 @s0mbre 在这一点上,问题不在于让它工作。我有解决这个问题的方法。但我想知道这种风格/代码有什么问题?为什么会出现这个特殊问题?回答您的问题,是的,我尝试了 insertRecord 的空记录,然后由用户填充。它给出了相同的行为。 【参考方案1】:好的,所以问题似乎是(引自Qt docs):
注意:为了防止仅将部分初始化的行插入到数据库中,OnFieldChange 将像 OnRowChange 一样处理新插入的行。
要使表格更新并显示新插入的行,您可以在数据更改时调用模型的submitAll()
方法。
完整的更新代码(更新在 cmets 中标记):
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import QModelIndex
from PyQt5.QtSql import QSqlDatabase, QSqlTableModel
from PyQt5.QtWidgets import QPushButton
class Ui_main(object):
def setupUi(self, main):
main.setObjectName("main")
main.resize(781, 652)
main.setContextMenuPolicy(QtCore.Qt.NoContextMenu)
self.verticalLayoutWidget = QtWidgets.QWidget(main)
self.verticalLayoutWidget.setGeometry(QtCore.QRect(10, 10, 761, 631))
self.verticalLayoutWidget.setObjectName("verticalLayoutWidget")
self.verticalLayout = QtWidgets.QVBoxLayout(self.verticalLayoutWidget)
self.verticalLayout.setContentsMargins(0, 0, 0, 0)
self.verticalLayout.setObjectName("verticalLayout")
# Replace values with your database configurations
# But the table you refer to must have autoincremented primary key
database = QSqlDatabase.addDatabase('QPSQL')
database.setHostName('localhost')
database.setPort(5432)
database.setDatabaseName('dbname')
database.setUserName('postgres')
database.setPassword('password')
database.open()
button_add = QPushButton("AddRow")
button_add.clicked.connect(self.addRow)
self.verticalLayout.addWidget(button_add)
self.tableView = QtWidgets.QTableView(self.verticalLayoutWidget)
self.tableView.setObjectName("tableView")
self.tableView.verticalHeader().setVisible(False)
self.verticalLayout.addWidget(self.tableView)
self.table_model = QSqlTableModel(self.tableView, database)
table_name = 'units'
self.table_model.setTable(table_name)
self.table_model.setEditStrategy(QSqlTableModel.OnFieldChange)
self.table_model.select()
## ========== ADDED ========== ##
self.table_model.sort(0, 0) # DEFAULT SORT TO SEE NEW ITEMS AT END
self.table_model.dataChanged.connect(self.on_dataChanged) # CONNECT DATA CHANGE SIGNAL TO CUSTOM SLOT
## =========================== ##
self.tableView.setModel(self.table_model)
self.tableView.hideColumn(0)
self.retranslateUi(main)
QtCore.QMetaObject.connectSlotsByName(main)
def retranslateUi(self, main):
_translate = QtCore.QCoreApplication.translate
main.setWindowTitle(_translate("main", "main"))
def addRow(self):
count = self.table_model.rowCount(QModelIndex())
self.table_model.insertRow(count)
self.tableView.scrollToBottom()
## ========== ADDED ========== ##
# SLOT FIRED WHEN DATA CHANGES
def on_dataChanged(self, topLeft, bottomRight, roles):
self.table_model.submitAll() # COMMIT TO DB
self.table_model.select() # RESELECT FROM DB
self.table_model.sort(0, 0) # SORT
## =========================== ##
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
main_window = QtWidgets.QWidget()
window = Ui_main()
window.setupUi(main_window)
main_window.show()
sys.exit(app.exec_())
【讨论】:
此代码尝试在任何字段更改时提交该行。提交失败,因为在其他一些字段上存在 Not Null 约束。而且我认为问题不在于模型的 EditStrategy,因为模型本身的行为符合预期。它成功将数据提交到数据库。问题在于 TableView 无法正确显示新插入的行。 好吧,我已经在我的数据库 (Postgres) 上测试了这段代码,它就是这样工作的。没有submitAll()
,它不起作用。
您对任何列是否有 Not null 约束?我已经在我的表上尝试了你的代码(答案中提供的 postgresql 模式作为更新 2),但它没有解决问题。
是的,我没有空:sql CREATE TABLE public.units ( id int4 NOT NULL GENERATED ALWAYS AS IDENTITY, val varchar(256) NOT NULL, "search" tsvector NULL, CONSTRAINT units_pkey PRIMARY KEY (id), CONSTRAINT units_unique UNIQUE (val) INCLUDE (val) );
以上是关于qtableview 无法显示新插入的行的主要内容,如果未能解决你的问题,请参考以下文章