You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

点击按钮后QTableView无法自动更新及程序崩溃问题求助

问题解决:Qt考勤表的表格刷新与崩溃问题

问题1:打卡/退卡后QTableView不自动刷新

直接修改self.data列表元素不会触发视图刷新,因为Qt MVC架构要求模型通过信号通知视图数据变化。此外你的setData方法存在语法错误,导致无法正确更新数据并发送信号。

修复步骤:

  1. 修正TableModel的setData方法:
    原代码中self._data.index[index.row()][index.column()]是错误写法,列表访问应直接用下标,改为self._data[index.row()][index.column()]。
  2. 在打卡/退卡方法中,找到对应行索引后调用模型的setData方法,自动触发视图刷新。

修改后的setData:

def setData(self, index, value, role=Qt.EditRole):
    if index.isValid() and role == Qt.EditRole:
        self._data[index.row()][index.column()] = value
        self.dataChanged.emit(index, index, [Qt.DisplayRole])
        return True
    return False

修改punch_in方法:

def punch_in(self):
    today = datetime.date.today()
    for row_idx, item in enumerate(self.data):
        if item[1] == today:
            if item[2] != '':
                dlg = QtWidgets.QMessageBox(self)
                dlg.setWindowTitle("Dobbia timbratura")
                dlg.setText("Hai già timbrato l'ingresso!")
                dlg.exec_()
                return
            x = datetime.datetime.now()
            if x.minute > 45:
                punchIn = str(x.hour + 1)
            elif 15 < x.minute < 30:
                punchIn = str(x.hour + 0.3)
            else:
                punchIn = str(x.hour)
            # 调用模型方法更新数据并触发刷新
            index = self.model.index(row_idx, 2)
            self.model.setData(index, punchIn)
            return

同理修改punch_out方法:

def punch_out(self):
    today = datetime.date.today()
    for row_idx, item in enumerate(self.data):
        if item[1] == today:
            if item[2] == '':
                dlg = QtWidgets.QMessageBox(self)
                dlg.setWindowTitle("Mancata timbratura")
                dlg.setText("Non hai ancora timbrato l'entrata!")
                dlg.exec_()
                return
            elif item[3] != '':
                dlg = QtWidgets.QMessageBox(self)
                dlg.setWindowTitle("Dobbia timbratura")
                dlg.setText("Hai già timbrato l'uscita!")
                dlg.exec_()
                return
            x = datetime.datetime.now()
            if x.minute > 45:
                punchOut = str(x.hour + 1)
            elif 15 < x.minute < 30:
                punchOut = str(x.hour + 0.3)
            else:
                punchOut = str(x.hour)
            index = self.model.index(row_idx, 3)
            self.model.setData(index, punchOut)
            return

问题2:新建月份按钮崩溃及随机崩溃

崩溃原因是new_month方法直接替换self.data后,模型仍引用旧数据列表,且未正确通知模型数据已完全替换。原setData的错误也会导致随机崩溃。

修复new_month方法:

给TableModel添加数据更新方法,用beginResetModel和endResetModel包裹数据替换,通知视图重置模型:

在TableModel中添加方法:

def update_data(self, new_data):
    self.beginResetModel()
    self._data = new_data
    self.endResetModel()

修改new_month方法:

def new_month(self):
    self.month, self.data = monthList()
    self.ui.monthLabel.setText(self.month)
    self.model.update_data(self.data)
    self.ui.attendanceTable.resizeColumnsToContents()

完整修正后的核心代码片段

TableModel类

class TableModel(QtCore.QAbstractTableModel):
    def __init__(self, data):
        super().__init__()
        self._data = data
        self.hheaders = ['Giorno', 'Data', 'In', 'Out', 'St. In', 'Str. Out',
                         'Ord.', 'Nott.', 'Str.', 'Str.nt.', 'Tot.', 'Note']

    def data(self, index, role):
        if role == Qt.DisplayRole:
            value = self._data[index.row()][index.column()]
            if isinstance(value, datetime.date):
                return value.strftime("%d/%m/%Y")
            return value

        if role == Qt.TextAlignmentRole:
            value = self._data[index.row()][index.column()]
            if isinstance(value, int) or isinstance(value, float):
                return Qt.AlignVCenter + Qt.AlignHCenter

        if role == Qt.ForegroundRole:
            value = self._data[index.row()][index.column()]
            if ((isinstance(value, int) or isinstance(value, float)) and
                value < 0):
                return QtGui.QColor('#ff0000')

    def setData(self, index, value, role=Qt.EditRole):
        if index.isValid() and role == Qt.EditRole:
            self._data[index.row()][index.column()] = value
            self.dataChanged.emit(index, index, [Qt.DisplayRole])
            return True
        return False

    def rowCount(self, index):
        return len(self._data)

    def columnCount(self, index):
        return len(self._data[0]) if self._data else 0  # 避免空列表索引错误

    def headerData(self, section, orientation, role):
        if role == Qt.DisplayRole and orientation == Qt.Horizontal:
            return self.hheaders[section]
        if role == Qt.FontRole:
            font = QtGui.QFont()
            font.setBold(True)
            return font
        return super().headerData(section, orientation, role)
    
    def update_data(self, new_data):
        self.beginResetModel()
        self._data = new_data
        self.endResetModel()

额外优化点

  • 在columnCount中添加空列表判断,避免启动时数据为空导致错误
  • 简化headerData返回值,PyQt会自动处理Python类型到QVariant的转换
  • 打卡逻辑中去掉多余的return None,直接return即可

内容的提问来源于stack exchange,提问作者Fabio Cricelli

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 14:15:56