PYQT 使用 QSqlRelationalTableModel 和 QtableView 添加新记录

PYQT Add new record using QSqlRelationalTableModel and QtableView

我正在尝试向我的 QtableView 添加一个新行,从而向 Postgresql 数据库添加一条记录。这个想法是添加记录并允许用户以如下形式就地编辑它:

添加行似乎没问题,但当我尝试编辑它时,垂直 header 显示一个感叹号,我设置数据的两个字段消失了。

问题是,如何为就地编辑添加一行?如果该行已经存在,我可以毫无问题地对其进行编辑。

from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtSql import *
from PyQt5.QtWidgets import *
import sys
from datetime import datetime, timedelta, time

#form modules
from main import *


class Main(QMainWindow):

    def __init__(self, parent=None):
        QMainWindow.__init__(self, parent)
        self.ui = Ui_Nextock()
        self.ui.setupUi(self)
        currentdate = QDate.currentDate()
        self.ui.weekending.setDate(currentdate.addDays(6-currentdate.dayOfWeek()))
        #self.ui.weekending.setDate(QDate(2017, 6, 10))

        self.emp_model = QSqlRelationalTableModel(self)
        self.emp_model.setTable('employees')
        self.emp_model.setSort(int(self.emp_model.fieldIndex("empid")), Qt.AscendingOrder)
        self.emp_model.setRelation(self.emp_model.fieldIndex("department"), QSqlRelation('departments', 'department', 'department'))
        self.emp_model.setFilter("termination_date IS NULL")
        self.emp_model.select()
        self.ui.department.setModel(self.emp_model.relationModel(self.emp_model.fieldIndex("department")))
        self.emp_mapper = QDataWidgetMapper(self)
        self.emp_mapper.setSubmitPolicy(QDataWidgetMapper.ManualSubmit)
        self.emp_mapper.setModel(self.emp_model)
        self.emp_mapper.addMapping(self.ui.title, self.emp_model.fieldIndex("salutation"))
        self.emp_mapper.addMapping(self.ui.first_name, self.emp_model.fieldIndex("first_name"))
        self.emp_mapper.addMapping(self.ui.last_name, self.emp_model.fieldIndex("surname"))
        self.emp_mapper.addMapping(self.ui.address, self.emp_model.fieldIndex("Address"))
        self.emp_mapper.addMapping(self.ui.email, self.emp_model.fieldIndex('email'))
        self.emp_mapper.addMapping(self.ui.phone, self.emp_model.fieldIndex('phone'))
        self.emp_mapper.addMapping(self.ui.mobile, self.emp_model.fieldIndex('mobile'))
        self.emp_mapper.addMapping(self.ui.birthdate, self.emp_model.fieldIndex('birth_date'))
        self.emp_mapper.addMapping(self.ui.start_date, self.emp_model.fieldIndex('start_date'))
        self.emp_mapper.addMapping(self.ui.contracted_hours, self.emp_model.fieldIndex('contracted_hours'))
        self.emp_mapper.addMapping(self.ui.payrate, self.emp_model.fieldIndex('pay_rate'))
        self.emp_mapper.addMapping(self.ui.username, self.emp_model.fieldIndex('loginid'))
        self.emp_mapper.addMapping(self.ui.password, self.emp_model.fieldIndex('pwd'))
        self.emp_mapper.addMapping(self.ui.department, self.emp_model.fieldIndex('department'))
        self.emp_mapper.setItemDelegate(QSqlRelationalDelegate(self.emp_mapper))
        self.emp_mapper.toFirst()

        self.hours_model = Tc_relational_model(self)
        self.hours_model.setTable('emphours')
        self.hours_model.setSort(self.hours_model.fieldIndex('id'), Qt.AscendingOrder)
        self.hours_model.setRelation(self.hours_model.fieldIndex("department"), QSqlRelation('departments', 'department', 'department'))
        self.hours_model.setRelation(self.hours_model.fieldIndex("paytype"), QSqlRelation('pay_types', 'pay_type', 'pay_type'))
        self.hours_model.select()
        self.hours_view = self.ui.emp_hours
        self.hours_view.setModel(self.hours_model)
        self.hours_view.setItemDelegate(QSqlRelationalDelegate(self.hours_view))
        self.hours_view.setColumnHidden(self.hours_model.fieldIndex('id'), True)
        self.hours_view.setColumnHidden(self.hours_model.fieldIndex('empyear'), True)
        self.hours_view.setColumnHidden(self.hours_model.fieldIndex('empweek'), True)
        self.hours_view.setColumnHidden(self.hours_model.fieldIndex('empmonth'), True)
        self.hours_view.setEditTriggers(QAbstractItemView.AllEditTriggers)
        self.hours_model.dataChanged.connect(self.dta_chng)

        self.ui.weekending.dateChanged.connect(lambda: self.record_changed(self.emp_mapper.currentIndex()))
        self.ui.new_line.clicked.connect(self.new_line)


        self.ui.current_index.setText('Record ' + str(self.emp_mapper.currentIndex()+1) + ' of ' + str(self.emp_model.rowCount()) + ' records')

    def dta_chng(self):
        print(self.hours_model.lastError().text())


    def new_line(self):
        row =  self.hours_model.rowCount()
        self.hours_model.insertRow(row)
        self.idx_empid = QModelIndex(self.hours_model.index(row, self.hours_model.fieldIndex('empid')))
        self.hours_model.setData(self.idx_empid, int(self.ui.emp_id.text()), Qt.EditRole)
        self.idx_weekending = QModelIndex(self.hours_model.index(row, self.hours_model.fieldIndex('weekending')))
        #self.hours_model.setData(self.idx_weekending, self.ui.weekending.date(), Qt.EditRole)
        print('In new_line')


    def record_changed(self, index):
        self.emp_mapper.submit()
        if index >= 0:
            total_hours = timedelta(0)
            the_id = self.emp_model.record(index).value('empid')
            self.hours_model.setFilter("empid = '{0}' and weekending = '{1}'".format(the_id,
                                                                                     self.ui.weekending.date().toString(Qt.ISODate)))
            self.ui.name_label.setText(self.emp_model.record(index).value('surname')
                                       + ", " + self.emp_model.record(index).value('first_name'))
            self.ui.terminationdate.setText(self.emp_model.record(index).value('termination_date').toString("dd/MM/yyyy"))
            self.ui.emp_id.setText(str(self.emp_model.record(index).value('empid')))

            for row in range(self.hours_model.rowCount()):
                #elapsed = self.data(self.index(index.row(), 9)).toPyDateTime() - self.data(self.index(index.row(), 8)).toPyDateTime()
                elapsed = self.hours_model.index(row, 9).data().toPyDateTime() - self.hours_model.index(row, 8).data().toPyDateTime()
                if elapsed.total_seconds() > 0:
                    total_hours += elapsed

            self.ui.total_hours.setText(str(total_hours))

        else:
            self.hours_model.setFilter("empid = -1")


class Tc_relational_model(QSqlRelationalTableModel):
    def __init__(self, parent=None):
        super(Tc_relational_model, self).__init__(parent)
        self.setEditStrategy(QSqlRelationalTableModel.OnFieldChange)
        self.setTable("emphours")
        self.select()

    def columnCount(self, parent=QtCore.QModelIndex()):
        return super(Tc_relational_model, self).columnCount()+1

    def data(self, index, role=QtCore.Qt.DisplayRole):
        if role == QtCore.Qt.DisplayRole and index.column()==10:
            if self.data(self.index(index.row(), 9)) is not None and self.data(self.index(index.row(), 8)) is not None:
                elapsed = self.data(self.index(index.row(), 9)).toPyDateTime() - self.data(self.index(index.row(), 8)).toPyDateTime()
            else:
                elapsed = timedelta()
            if elapsed.total_seconds() < 0:
                elapsed = ''
            else:
                elapsed = str(elapsed)
            return elapsed

        if index.column() > 10:
            index = self.index(index.row(), index.column()-1)

        return super(Tc_relational_model, self).data(index, role)

    def headerData(self, section, orientation, role=QtCore.Qt.DisplayRole):
        if section==10 and orientation==QtCore.Qt.Horizontal and role==QtCore.Qt.DisplayRole:
            return 'Elapsed time'
        if section > 10 and orientation==QtCore.Qt.Horizontal:
            section -= 1
        return super(Tc_relational_model, self).headerData(section, orientation, role)

    def flags(self, index):
        if index.column()==10:
            return QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled
        return QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsEditable

    def setData(self, index, data, role):
        if index.column() > 10:
            index = self.index(index.row(), index.column()-1)
        return super(Tc_relational_model, self).setData(index, data, role)





if __name__=="__main__":
    app=QApplication(sys.argv)
    pword = 'pword'
    dbase = 'db'
    server = 'server'
    user = 'dave'
    db = QSqlDatabase.addDatabase("QPSQL");
    db.setHostName(server)
    db.setDatabaseName(dbase)
    db.setUserName(user)
    db.setPassword(pword)
    if (db.open()==False):
        QMessageBox.critical(None, "Database Error", db.lastError().text())

    myapp = Main()
    myapp.show()
    sys.exit(app.exec_())

好吧,我对这个问题采取了完全不同的方法。下面是对代码的调整:

def new_line(self):
    row =  self.hours_model.rowCount()
    record = self.hours_model.record()
    record.setGenerated('id', False)
    record.setValue('empid', self.ui.emp_id.text())
    record.setValue('weekending', self.ui.weekending.date())
    record.setValue('department', self.ui.department.currentText())
    record.setValue('pay_type', 'Regular')
    record.setValue('starttime', QDateTime.currentDateTime())
    record.setValue('endtime', QDateTime.currentDateTime())
    self.hours_model.insertRecord(row, record)
    self.hours_view.edit(QModelIndex(self.hours_model.index(row, self.hours_model.fieldIndex('department'))))

这似乎有效。然而,它引发的问题多于它回答的问题。在我看来,我的问题中的方法虽然更冗长,但更多 model/view。所以我还是被难住了。