Mac OS Monterey 12.4运行PyQt5触发zsh segmentation fault报错
问题根因
触发zsh: segmentation fault段错误的核心是三个逻辑错误:
- 每次
itemChanged信号触发时都重复新建Delegate对象并重新绑定给表格。Qt替换委托时会销毁旧实例,如果旧实例正处于绘制流程中,就会触发野指针内存访问,直接在MacOS上触发段错误。委托属于表格静态配置,不需要在单元格内容变化时重复创建绑定。 itemChanged信号并非仅在用户手动修改单元格时触发:插入新行、程序修改单元格内容、委托重绘触发的数据刷新都会触发该信号,导致反复创建委托的逻辑被递归调用,进一步放大内存访问问题。- 委托实现不规范:
IntDelegate初始化时没有将parent参数传入父类,FloatDelegate混用QItemDelegate和QStyledItemDelegate类型,paint方法没有处理单元格选中、焦点等默认状态,空单元格取值时缺少兼容逻辑,也会加剧异常概率。
修复方案
- 把委托的初始化、绑定逻辑移到表格初始化阶段,全局只执行一次,禁止在
itemChanged槽函数中重复创建、绑定委托。 - 所有会修改表格结构、内容的逻辑,执行前先阻塞
itemChanged信号,避免操作过程中递归触发槽函数。 - 统一委托父类为
QStyledItemDelegate,修正paint逻辑:先绘制单元格默认的背景、选中、焦点状态,再绘制居中的文本内容,补全空值判断避免类型转换异常。 - 如果仅需要实现文本居中对齐,不需要自定义委托,直接调用
QTableWidgetItem.setTextAlignment(Qt.AlignCenter)即可,逻辑更简单也不会出现生命周期问题。
修正后核心代码
表格初始化逻辑调整
将委托绑定移到初始化函数中,仅执行一次:
def impostazione_data_mapper(self): font = QtGui.QFont() font.setBold(True) self.tW_GrossMargin.setColumnCount(7) self.tW_GrossMargin.setRowCount(10) self.tW_GrossMargin.setSizeAdjustPolicy(QtWidgets.QAbstractScrollArea.AdjustToContents) intestazioni=[] intestazioni.append("Range - quantità da") intestazioni.append("Range - quantità a") intestazioni.append("Vendita - Gross margin Italia") intestazioni.append("Vendita - Gross margin Estero") intestazioni.append("Conto lavorazione - Gross margin Italia") intestazioni.append("Conto lavorazione - Gross margin Estero") intestazioni.append(" ") self.tW_GrossMargin.setHorizontalHeaderLabels(intestazioni) self.tW_GrossMargin.resizeColumnsToContents() header = self.tW_GrossMargin.horizontalHeader() header.setDefaultAlignment(Qt.AlignCenter|Qt.AlignVCenter) self.tW_GrossMargin.resizeColumnsToContents() for c in range(self.tW_GrossMargin.columnCount() - 1): h_item = self.tW_GrossMargin.horizontalHeaderItem(c) h_item.setFont(font) self.tW_GrossMargin.resizeColumnsToContents() # 委托仅初始化、绑定一次 self.int_delegate = IntDelegate(self.tW_GrossMargin) self.float_delegate = FloatDelegate(3, self.tW_GrossMargin) self.tW_GrossMargin.setItemDelegateForColumn(0, self.int_delegate) self.tW_GrossMargin.setItemDelegateForColumn(1, self.int_delegate) for col in range(2, 7): self.tW_GrossMargin.setItemDelegateForColumn(col, self.float_delegate)
槽函数逻辑调整
去掉槽函数里重复设置委托的代码,增加信号阻塞避免递归触发:
def allinea_campi(self, item): self.tW_GrossMargin.blockSignals(True) try: # 此处仅保留数据校验、内容调整类逻辑,不要重复绑定委托 row = item.row() col = item.column() # 如果仅需要居中对齐,直接用下面这行即可,不需要自定义委托 # item.setTextAlignment(Qt.AlignCenter) finally: self.tW_GrossMargin.blockSignals(False) def nuovo_margine(self): self.tW_GrossMargin.blockSignals(True) try: numRows = self.tW_GrossMargin.rowCount() self.tW_GrossMargin.insertRow(numRows) finally: self.tW_GrossMargin.blockSignals(False)
委托类实现修正
class IntDelegate(QStyledItemDelegate): def __init__(self, parent=None): # 必须传入parent,由Qt管理对象生命周期 super().__init__(parent) def paint(self, painter, option, index): # 先绘制单元格默认的背景、选中、焦点状态 self.initStyleOption(option, index) option.text = "" style = option.widget.style() if option.widget else QApplication.style() style.drawControl(QtWidgets.QStyle.CE_ItemViewItem, option, painter, option.widget) value = index.model().data(index, Qt.EditRole) if value in (None, ""): return painter.drawText(option.rect, Qt.AlignCenter, str(value)) class FloatDelegate(QStyledItemDelegate): def __init__(self, decimals, parent=None): # 统一使用QStyledItemDelegate作为父类 super().__init__(parent) self.nDecimals = decimals def paint(self, painter, option, index): # 先绘制默认单元格状态 self.initStyleOption(option, index) option.text = "" style = option.widget.style() if option.widget else QApplication.style() style.drawControl(QtWidgets.QStyle.CE_ItemViewItem, option, painter, option.widget) value = index.model().data(index, Qt.EditRole) try: if value in (None, ""): return number = float(value) painter.drawText(option.rect, Qt.AlignCenter | Qt.AlignVCenter, f"{number:.{self.nDecimals}f}") except: super().paint(painter, option, index)
内容的提问来源于stack exchange,提问作者MDR
相关产品推荐
相关产品推荐

