QtableView单元格在窗体关闭时失去焦点时如何保存正在编辑的数据

how to save data being edited when QtableView cell loses focus on close of form

在 QtableView 中编辑单元格的默认行为中,当用户单击另一个小部件或关闭表单时,编辑将丢失。经过大量谷歌搜索,我找到了一种方法来保存编辑,如果用户在表单中选择另一个小部件,但如果关闭表单,编辑仍然会丢失。博客 post 是 here

我试图从窗体 closeEvent 调用 closeEditor 方法,但它需要两个参数:编辑器和提示。我可以提供 QAbstractItemDelegate.NoHint,但编辑器需要进行编辑的 QlineEdit 对象。我不知道如何为当前正在编辑的单元格提供此信息。

这是当前行为的 gif:

我的问题是如何提供正在编辑的单元格的 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_())

委托编辑器是 QTableView 的子级,因此您可以使用 findChildren 来获取它们,要确保它们不是其他子级,您可以设置一个允许您过滤它们的对象名称:

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