PyQt5信号绑定异常:新信号关联旧槽而非独立槽
问题根源
- 每次选中单元格时重复连接
itemChanged信号,导致多个槽函数实例被绑定,编辑时旧的槽函数仍会执行,使用的是第一次绑定的xStart值,引发KeyError。 inCounter未在__init__中初始化,可能导致未知错误。- 直接用xStart作为字典键,当xStart修改后,旧键被删除,旧槽函数调用时会找不到对应条目。
修复方案
- 避免重复连接信号:在
Canvas初始化时只连接一次itemChanged信号,不再每次选中单元格都重复绑定。 - 优化字典键设计:改用表格行号作为字典键,行号是固定唯一的标识,避免xStart修改后键失效的问题。
- 动态获取编辑信息:在槽函数中直接通过传入的
item参数获取当前编辑的行、列和新值,无需依赖选中时的旧参数。 - 增加输入校验:对帧号输入做合法性检查,避免非整数输入导致崩溃。
修复后的完整代码
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 from functools import partial 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 highlight: def __init__(self, tag, xStart, xEnd, highlightObj): self.tag = tag self.xStart = xStart self.xEnd = xEnd self.highlightObj = highlightObj 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 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() self.highlights = {} # 用行号作为键,存储highlight对象 # 创建表格 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']) # 初始化SpanSelector self.span = mwidgets.SpanSelector(self.Axes, self.onHighlight, "horizontal", interactive = True, useblit=True, props=dict(alpha=0.5, facecolor="blue"),) self.draw_idle() # 只连接一次itemChanged信号,避免重复绑定 self.taggingTable.itemChanged.connect(self.onItemChanged) self.draw_idle() # 处理SpanSelector选中的区间 def onHighlight(self, xStart, xEnd): tagName = "No Tag" # 给表格赋值 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)))) # 创建高亮对象并存储,用行号作为键 highlightObj = self.Axes.axvspan(xStart, xEnd, color = 'blue', alpha = 0.5) self.highlights[self.rowCount] = highlight(tagName, xStart, xEnd, highlightObj) self.rowCount += 1 self.draw_idle() # 处理表格单元格修改 def onItemChanged(self, item): rowVal = item.row() colVal = item.column() # 确保该行存在高亮对象 if rowVal not in self.highlights: return highlight_item = self.highlights[rowVal] # 处理起始帧修改 if colVal == 1: try: xStartNew = int(item.text()) xEnd = highlight_item.xEnd # 移除旧的高亮区间 highlight_item.highlightObj.remove() # 创建新的高亮区间 new_highlight = self.Axes.axvspan(xStartNew, xEnd, color='blue', alpha=0.5) # 更新highlight对象的属性 highlight_item.xStart = xStartNew highlight_item.highlightObj = new_highlight self.draw_idle() except ValueError: # 输入不是整数,恢复原来的值 item.setText(str(int(highlight_item.xStart))) # 处理结束帧修改 elif colVal == 2: try: xEndNew = int(item.text()) xStart = highlight_item.xStart highlight_item.highlightObj.remove() new_highlight = self.Axes.axvspan(xStart, xEndNew, color='blue', alpha=0.5) highlight_item.xEnd = xEndNew highlight_item.highlightObj = new_highlight self.draw_idle() except ValueError: item.setText(str(int(highlight_item.xEnd))) # 处理标签名修改 elif colVal == 0: highlight_item.tag = item.text() app = QApplication(sys.argv) window = Window() window.show() app.exec()
关键修改点说明
- 行号作为字典键:行号是固定唯一的标识,对应表格中的每一行,彻底解决xStart修改后键失效的问题。
- 单次信号连接:在初始化时一次性绑定
itemChanged信号,避免多次连接导致的槽函数累积执行问题。 - 动态获取编辑数据:通过槽函数传入的
item参数直接获取当前编辑的单元格信息,无需依赖选中时的旧参数。 - 输入合法性校验:对帧号输入做整数校验,非法输入自动恢复原值,提升程序稳定性。
内容的提问来源于stack exchange,提问作者ssr
相关产品推荐
相关产品推荐

