PyQt5槽函数首次调用后参数不更新问题排查
问题原因
- 核心错误是重复绑定信号且未断开旧连接:每次触发单元格选中逻辑
onCellSelect时,你都会给taggingTable.itemChanged信号新增一个lambda槽连接,但从未断开之前的旧连接。Qt信号支持绑定多个槽,信号触发时所有历史绑定的槽会按绑定顺序依次执行,所以第一次绑定的、持有旧xStart和旧rowVal的槽永远会被调用,后续新选中单元格绑定的新槽也会同时执行,直接导致始终操作第一次编辑的区间,旧key被删除后再访问就触发KeyError崩溃。 - 冗余的
inCounter防重入逻辑不可靠,本质是为了掩盖重复绑定槽导致的多次触发问题,没有解决根本原因。 - 设计逻辑不合理:信号只需要在初始化时绑定一次,不需要每次选中单元格都重复绑定。
正确实现方案
- 初始化阶段只绑定一次
itemChanged信号,不要在选中单元格的回调里重复绑定。 - 新增类成员变量
self.currentEditRow记录当前选中的行号,选中单元格时仅更新这个变量的值即可。 - 编辑单元格触发槽函数时,必要时先阻塞表格的信号避免递归触发,再根据当前选中行读取新旧区间值,更新绘图上的高亮区域,最后恢复信号、刷新画布。
- 操作
highlights字典时,先匹配取出旧区间对象再删除key,避免key不存在的报错。
修正后的可运行代码
import sys from PyQt5.QtWidgets import * from PyQt5 import QtCore from PyQt5.QtCore import * from PyQt5.QtGui import * from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas from matplotlib.figure import Figure import matplotlib.widgets as mwidgets class Window(QMainWindow): def __init__(self, parent = None): super(Window, self).__init__(parent) self.resize(1600, 800) self.MyUI() def MyUI(self): canvas = Canvas(self, width=14, height=12, dpi=100) canvas.move(0,0) class Canvas(FigureCanvas): def __init__(self, parent, width = 14, height = 12, dpi = 100): Plot = Figure(figsize=(width, height), dpi=dpi) self.Axes = Plot.add_subplot(111) self.Axes.set_position([0.05, 0.58, 0.66, 0.55]) self.rowCount = 0 self.currentEditRow = -1 # 记录当前选中编辑的行 super().__init__(Plot) self.setParent(parent) # 绘制基础曲线 self.Axes.plot([0,1,2,3,4], [3, 4, 5, 6, 7]) self.Axes.set_xlabel('Frame', fontsize = 10) self.Axes.grid() self.Axes.set_aspect(1) Plot.canvas.draw() # 高亮区间存储结构 class highlight: def __init__(self, tag, xStart, xEnd, highlightObj): self.tag = tag self.xStart = xStart self.xEnd = xEnd self.highlightObj = highlightObj self.highlights = {} # 初始化编辑表格 self.taggingTable = QTableWidget(self) self.taggingTable.setColumnCount(3) self.taggingTable.setRowCount(100) self.taggingTable.setGeometry(QRect(1005,85, 330, 310)) self.taggingTable.setHorizontalHeaderLabels(['Behavior','Start Frame', 'End Frame']) # 区间选择回调 def onHighlight(xStart, xEnd): tagName = "No Tag" self.taggingTable.blockSignals(True) # 阻塞信号避免触发编辑回调 self.taggingTable.setItem(self.rowCount, 0, QTableWidgetItem(tagName)) self.taggingTable.setItem(self.rowCount, 1, QTableWidgetItem(str(int(xStart)))) self.taggingTable.setItem(self.rowCount, 2, QTableWidgetItem(str(int(xEnd)))) self.taggingTable.blockSignals(False) self.rowCount = self.rowCount + 1 highlightObj = self.Axes.axvspan(xStart, xEnd, color = 'blue', alpha = 0.5) self.highlights[int(xStart)] = highlight(tagName, xStart, xEnd, highlightObj) self.draw_idle() self.span = mwidgets.SpanSelector(self.Axes, onHighlight, "horizontal", interactive = True, useblit=True, props=dict(alpha=0.5, facecolor="blue"),) # 单元格内容修改回调:初始化时只绑定一次 def onItemChanged(item): # 只处理起始帧列的修改 if item.column() != 1 or self.currentEditRow < 0: return rowVal = self.currentEditRow # 匹配读取旧起始值 oldXStart = None for k in list(self.highlights.keys()): if abs(self.highlights[k].xEnd - int(self.taggingTable.item(rowVal,2).text())) < 1e-6: oldXStart = k break if oldXStart is None: return xStartNew = int(item.text()) xEnd = self.highlights[oldXStart].xEnd tagName = self.highlights[oldXStart].tag # 删除旧高亮 self.highlights[oldXStart].highlightObj.remove() del self.highlights[oldXStart] # 添加新高亮 highlightObj = self.Axes.axvspan(xStartNew, xEnd, color = 'blue', alpha = 0.5) self.highlights[xStartNew] = highlight(tagName, xStartNew, xEnd, highlightObj) self.taggingTable.clearSelection() self.currentEditRow = -1 self.draw_idle() # 单元格选中回调:只更新当前编辑行号,不重复绑定信号 def onCellSelect(): index = self.taggingTable.selectedIndexes() if len(index) != 0: rowVal = index[0].row() if self.taggingTable.item(rowVal, 1) is not None: self.currentEditRow = rowVal # 统一绑定所有信号,仅绑定一次 self.taggingTable.selectionModel().selectionChanged.connect(onCellSelect) self.taggingTable.itemChanged.connect(onItemChanged) self.draw_idle() app = QApplication(sys.argv) window = Window() window.show() app.exec()
补充说明:如果需要同时支持结束帧xEnd的编辑,只需要在
onItemChanged槽里增加列判断逻辑,对应更新xEnd值和高亮区间即可,不需要新增重复的信号绑定。
内容的提问来源于stack exchange,提问作者ssr
相关产品推荐
相关产品推荐

