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

PyQt5信号绑定异常:新信号关联旧槽而非独立槽

问题根源
  • 每次选中单元格时重复连接itemChanged信号,导致多个槽函数实例被绑定,编辑时旧的槽函数仍会执行,使用的是第一次绑定的xStart值,引发KeyError。
  • inCounter未在__init__中初始化,可能导致未知错误。
  • 直接用xStart作为字典键,当xStart修改后,旧键被删除,旧槽函数调用时会找不到对应条目。
修复方案
  1. 避免重复连接信号:在Canvas初始化时只连接一次itemChanged信号,不再每次选中单元格都重复绑定。
  2. 优化字典键设计:改用表格行号作为字典键,行号是固定唯一的标识,避免xStart修改后键失效的问题。
  3. 动态获取编辑信息:在槽函数中直接通过传入的item参数获取当前编辑的行、列和新值,无需依赖选中时的旧参数。
  4. 增加输入校验:对帧号输入做合法性检查,避免非整数输入导致崩溃。
修复后的完整代码
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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.24 23:36:20