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

PyQt5 QTableWidget点击触发QComboBox后无法选中旧单元格问题

问题根因

这个交互bug完全是代码逻辑错误导致的,核心问题有3个:

  • 全局只实例化了1个QComboBox对象。Qt的setCellWidget本质是把控件挂载到指定单元格上,同一个控件实例同一时间只能属于一个位置,每次点击新单元格调用setCellWidget时,系统会自动把这个combobox从之前绑定的旧单元格上移除,挪到新单元格位置,旧单元格失去可交互控件,自然无法选中。
  • combobox创建逻辑写在showdata()的行遍历循环中,每次循环都会覆盖全局的self.combo_customers引用,且全程没有调用customers_combo()加载客户选项,combobox本身是空的。
  • 存库逻辑中读取的self.combo_customers.currentText()永远是全局唯一combobox的当前值,和正在编辑的行完全不对应,会导致存储数据错误。
修复方案

不要全局复用单个QComboBox实例,有两种实现方式可选:

  1. (推荐)表格初始化渲染行时,给第4列的每个单元格单独创建独立的QComboBox实例,提前挂载好,避免点击时临时创建带来的选中异常;
  2. 如果要做懒加载(点击单元格才创建combobox),点击时先判断当前单元格是否已经挂载过combobox,没有的话新建独立实例再挂载,绝对不能复用全局唯一的控件实例。
修正后的核心代码
def showdata(self):
    # 提前加载客户列表
    customers = [] 
    self.customers_combo(customers)
    delegate = MyDelegate(self)
    tablerow = 0 
    conteggio = c.execute('SELECT COUNT(ID) FROM Viaggi').fetchone()[0]
    self.tabella_registrazioni.blockSignals(True)
    self.tabella_registrazioni.setRowCount(conteggio)
    for row in c.execute("SELECT * FROM Viaggi "):
        rigavalore = 0
        for r in range(0, 19):
            self.tabella_registrazioni.setItem(tablerow, rigavalore, QtWidgets.QTableWidgetItem(str(row[rigavalore])))
            rigavalore+= 1
        # 为当前行第4列创建独立的combobox实例
        combo_customers = QComboBox()
        combo_customers.addItems(customers)
        # 初始化时直接挂载到对应单元格,不需要等点击再创建
        self.tabella_registrazioni.setCellWidget(tablerow, 4, combo_customers)
        # 绑定行号,选值变化时精准更新对应行数据
        combo_customers.currentTextChanged.connect(lambda text, current_row=tablerow: self.update_row_customer(current_row, text))
        tablerow+= 1
    self.tabella_registrazioni.blockSignals(False) 
    self.tabella_registrazioni.itemChanged.connect(self.changeIcon) 
    self.tabella_registrazioni.setItemDelegateForColumn(0, delegate)
    # 移除原来点击临时创建combobox的绑定,初始化已经完成所有combobox渲染
    # self.tabella_registrazioni.clicked.connect(self.pressed) 

# 新增单行客户字段更新方法,避免读错全局combobox的值
def update_row_customer(self, row, customer_text):
    row_id = self.tabella_registrazioni.item(row, 0).text()
    c.execute('UPDATE Viaggi SET Codice_cliente = ? WHERE ID = ?', (customer_text, row_id))
    c.commit()

# 新增行时记得同步给新行第4列创建combobox
def generariga(self):       
    nulla = None
    c.execute('INSERT INTO Viaggi (Codice_cliente) VALUES (?)', nulla)
    c.commit()
    self.datitabella()
    # 给新插入的最后一行创建独立combobox
    new_row = self.tabella_registrazioni.rowCount() - 1
    customers = []
    self.customers_combo(customers)
    combo_customers = QComboBox()
    combo_customers.addItems(customers)
    self.tabella_registrazioni.setCellWidget(new_row, 4, combo_customers)
    combo_customers.currentTextChanged.connect(lambda text, current_row=new_row: self.update_row_customer(current_row, text))
注意事项
  • 原来的changeIcon方法中quattro = str(self.combo_customers.currentText())逻辑必须修改,如果保留全字段更新的逻辑,这里要读取当前行单元格内挂载的combobox值,写法为quattro = str(self.tabella_registrazioni.cellWidget(row, 4).currentText()),否则存库的客户值永远和当前编辑行不匹配。
  • 如果坚持使用点击时懒加载combobox的逻辑,pressed方法修改为如下写法,每次点击未挂载combobox的单元格时新建独立实例:
def pressed(self, item):
    row = item.row()
    column = item.column()
    if column == 4 and not self.tabella_registrazioni.cellWidget(row, column):
        # 仅当当前单元格没有combobox时,新建独立实例挂载
        customers = []
        self.customers_combo(customers)
        combo = QComboBox()
        combo.addItems(customers)
        self.tabella_registrazioni.setCellWidget(row, column, combo)

内容的提问来源于stack exchange,提问作者IT-SRL

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.01 23:36:17