如何在关闭表单时保存 QTableView 单元格失去焦点时编辑的数据?

3
在默认情况下,编辑 QtableView 中的单元格时,当用户单击其他小部件或关闭表单时,所做的编辑将丢失。经过大量搜索,我找到了一种方法,可以在用户选择表单中的另一个小部件时保存编辑内容,但是如果关闭表单,则仍会丢失编辑内容。博客文章在这里
我试图从表单的closeEvent中调用closeEditor方法,但它需要两个参数:editor和hint。 我可以提供QAbstractItemDelegate.NoHint,但编辑器需要正在进行编辑的QlineEdit对象。 我不知道如何为当前正在编辑的单元格提供此对象。
以下是当前行为的GIF:enter image description here 我的问题是如何提供正在编辑的单元格的QlineEdit?
import sys
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtSql import *
from PyQt5.QtWidgets import *
from phones import *

class Main(QMainWindow):

    def __init__(self, parent=None):

        QMainWindow.__init__(self, parent)
        
        self.resize(490, 998)
        self.layoutWidget = QWidget(self)
        self.layoutWidget.setObjectName("layoutWidget")
        self.verticalLayout = QtWidgets.QVBoxLayout(self.layoutWidget)
        self.horizontalLayout_7 = QtWidgets.QHBoxLayout()
        self.new_phone = QtWidgets.QPushButton(self.layoutWidget)
        self.new_phone.setObjectName("new_phone")
        self.new_phone.setText("New Phone")
        self.horizontalLayout_7.addWidget(self.new_phone)
        self.delete_phone = QtWidgets.QPushButton(self.layoutWidget)
        self.delete_phone.setObjectName("delete_phone")
        self.delete_phone.setText("Delete phone")
        self.horizontalLayout_7.addWidget(self.delete_phone)
        self.verticalLayout.addLayout(self.horizontalLayout_7)
        
        self.phone_view = Syn_tableview()
        
        self.verticalLayout.addWidget(self.phone_view)
        self.cont_id = '9'
        self.setCentralWidget(self.layoutWidget)
        self.new_phone.clicked.connect(self.add_phone)
        self.populate_phones()

    def populate_phones(self):
        self.phone_model = QSqlTableModel(self)
        self.phone_model.setTable("contact_phones")
        self.phone_model.setFilter("contact_id='{0}'".format(self.cont_id))
        self.phone_model.select()

        self.phone_view.setModel(self.phone_model)  
        self.phone_view.resizeColumnsToContents()
         
    def add_phone(self):
        self.phone_model.submitAll()
        self.phone_model.setEditStrategy(QSqlTableModel.OnManualSubmit)
        row = self.phone_model.rowCount()
        record = self.phone_model.record()
        record.setGenerated('id', False)            #primary key
        record.setValue('contact_id', self.cont_id) #foreign key
        self.phone_model.insertRecord(row, record)
        phone_index_edit = self.phone_model.index(row, self.phone_model.fieldIndex('phone_number'))
        self.phone_view.edit(phone_index_edit)

    def closeEvent(self, event):
        submit = self.phone_model.submitAll()
        
        #This is the problem 
        self.phone_view.closeEditor("QLineEdit", QAbstractItemDelegate.NoHint)
        
    
class Syn_tableview(QTableView):
    def __init__(self, *args, **kwargs):
        QTableView.__init__(self, *args, **kwargs)    
        
    def closeEditor(self, editor, hint):
        if hint == QAbstractItemDelegate.NoHint:
            QTableView.closeEditor(self, editor,
                QAbstractItemDelegate.SubmitModelCache)


if __name__=="__main__":
    app=QApplication(sys.argv)
    db = QSqlDatabase.addDatabase("QPSQL");
    db.setHostName(server)
    db.setDatabaseName(database)
    db.setUserName(user)
    db.setPassword(pword)
    myapp = Main()
    myapp.show()
    sys.exit(app.exec_())

嗯,你的解释让我感到困惑,你能给一个实际的例子来说明你想要得到什么吗?比如我们可以使用QLineEdit,假设用户输入了“SO”,然后按下了另一个项目,那么这个“SO”应该怎么处理呢?如果按下了初始要显示的项目呢? - eyllanesc
嗨@eyllanesc。例如,用户在新记录的单元格中输入电话号码。那是他们需要添加的最后一条数据。他们没有按回车键,而是通过单击QMainWindow上的关闭窗口按钮(X)来关闭窗口。目前,电话号码尚未保存。我无法捕获单元格何时有编辑。 - Dkellygb
我正在尝试在closeEvent中放置逻辑,以确定QtableView中是否有任何脏单元格并保存它们。 - Dkellygb
那么您希望在关闭窗口时保存QTableView编辑器中的信息,还有哪些情况需要保存呢? - eyllanesc
是的,Syn_tableview类的closeEditor方法确保如果用户单击另一个小部件,则信息将被保存。只有在关闭表单时,我才会遇到保存数据的问题。 - Dkellygb
1个回答

3

委托编辑器是QTableView的子项,您可以使用findChildren获取它们。为了确保它们不是其他子项,您可以设置一个objectName来过滤它们:

import sys
from PyQt5 import QtCore, QtSql, QtWidgets


def create_connection():
    db = QtSql.QSqlDatabase.addDatabase("QPSQL")
    # FIXME
    db.setHostName("server")
    db.setDatabaseName("database")
    db.setUserName("user")
    db.setPassword("pword")
    if not db.open():
        print(db.lastError().text())
        return False
    return True


class Syn_Delegate(QtWidgets.QStyledItemDelegate):
    def createEditor(self, parent, option, index):
        editor = super(Syn_Delegate, self).createEditor(parent, option, index)
        if isinstance(editor, QtWidgets.QWidget):
            editor.setObjectName("syn_editor")
        return editor


class Syn_Tableview(QtWidgets.QTableView):
    def closeEditor(self, editor, hint):
        if hint == QtWidgets.QAbstractItemDelegate.NoHint:
            hint = QtWidgets.QAbstractItemDelegate.SubmitModelCache
        super(Syn_Tableview, self).closeEditor(editor, hint)


class MainWindow(QtWidgets.QMainWindow):
    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent)

        self.new_phone = QtWidgets.QPushButton(self.tr("New Phone"))
        self.delete_phone = QtWidgets.QPushButton(self.tr("Delete phone"))
        self.phone_view = Syn_Tableview()
        self.phone_model = QtSql.QSqlTableModel()
        self.phone_model.setEditStrategy(QtSql.QSqlTableModel.OnManualSubmit)
        self.phone_view.setModel(self.phone_model)
        self.phone_view.resizeColumnsToContents()
        delegate = Syn_Delegate(self)
        self.phone_view.setItemDelegate(delegate)

        central_widget = QtWidgets.QWidget()
        self.setCentralWidget(central_widget)

        lay = QtWidgets.QGridLayout(central_widget)
        lay.addWidget(self.new_phone, 0, 0)
        lay.addWidget(self.delete_phone, 0, 1)
        lay.addWidget(self.phone_view, 1, 0, 1, 2)

        self._contact_id = "9"

        self.populate_phones()

        self.new_phone.clicked.connect(self.add_phone)

    @property
    def contact_id(self):
        return self._contact_id

    def populate_phones(self):
        self.phone_model.setTable("contact_phones")
        self.phone_model.setFilter("contact_id='{0}'".format(self.contact_id))
        self.phone_model.select()

    @QtCore.pyqtSlot()
    def add_phone(self):
        self.phone_model.submitAll()

        row = self.phone_model.rowCount()
        record = self.phone_model.record()
        record.setGenerated("id", False)  # primary key
        record.setValue("contact_id", self.contact_id)  # foreign key
        self.phone_model.insertRecord(row, record)

        phone_index_edit = self.phone_model.index(
            row, self.phone_model.fieldIndex("phone_number")
        )
        if phone_index_edit.isValid():
            self.phone_view.edit(phone_index_edit)

    def closeEvent(self, event):
        for editor in self.phone_view.findChildren(QtWidgets.QWidget, "syn_editor"):
            self.phone_view.commitData(editor)
        submit = self.phone_model.submitAll()
        super().closeEvent(event)


def main():
    app = QtWidgets.QApplication(sys.argv)

    if not create_connection():
        sys.exit(-1)

    w = MainWindow()
    w.show()
    w.resize(640, 480)

    ret = sys.exit(app.exec_())

    sys.exit(ret)


if __name__ == "__main__":
    main()

嗨eyllanesc,非常感谢您的帮助。我运行了您的代码,只调整了数据库凭据并调整了QMainWindow的大小。不幸的是,结果是一样的。当您直接从编辑中关闭窗口时,编辑内容会丢失。 - Dkellygb
我上面添加的gif使用了这个答案中的代码。 - Dkellygb

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接