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实例,有两种实现方式可选:
- (推荐)表格初始化渲染行时,给第4列的每个单元格单独创建独立的QComboBox实例,提前挂载好,避免点击时临时创建带来的选中异常;
- 如果要做懒加载(点击单元格才创建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
相关产品推荐
相关产品推荐

