点击按钮后QTableView无法自动更新及程序崩溃问题求助
问题解决:Qt考勤表的表格刷新与崩溃问题
问题1:打卡/退卡后QTableView不自动刷新
直接修改self.data列表元素不会触发视图刷新,因为Qt MVC架构要求模型通过信号通知视图数据变化。此外你的setData方法存在语法错误,导致无法正确更新数据并发送信号。
修复步骤:
- 修正
TableModel的setData方法:
原代码中self._data.index[index.row()][index.column()]是错误写法,列表访问应直接用下标,改为self._data[index.row()][index.column()]。 - 在打卡/退卡方法中,找到对应行索引后调用模型的
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
相关产品推荐
相关产品推荐

