PyQt QTableWidget插入QSpinBox/QComboBox后取值返回None的问题
问题核心根源
QTableWidget 的 model 数据接口仅能读取单元格原生 QTableWidgetItem 存储的属性值,完全感知不到通过setCellWidget方法插入到单元格位置的独立控件,这是你所有取值返回None的本质原因。
结合代码逻辑,共有3处错误共同导致输出结果完全失效:
- 取值路径错配:
setCellWidget插入的QSpinBox、QComboBox是独立渲染在单元格上层的控件,和表格model完全隔离,model层不会感知这些控件的存在,调用model.data()时永远返回None。你之前能正常读取QLineEdit类型单元格的值,通常是因为那类QLineEdit是作为单元格的临时编辑代理创建,编辑完成后会自动把值同步回model,和当前直接插入常驻控件的场景逻辑不通用。 - 列索引越界:表格总列数设置为4,列索引范围为0-3,但原有取值逻辑中访问了
model.index(row,4),对应不存在的第5列,哪怕全是原生单元格也无法正常取值。 - 字典key逻辑错位:原有代码把第1列(插入QSpinBox的Danger列)的model值作为字典的key,本该把第0列的Name列值作为字典key,最终导致字典的key直接变成None,和实际输出的
{'None': {...}}结果完全匹配。
正确实现方案
对于通过setCellWidget插入的常驻控件,必须使用QTableWidget自带的cellWidget(行号, 列号)方法获取对应位置的控件实例,再调用控件自身的取值接口读取内容,完全绕过model层的data接口即可。
修正后的取值方法示例:
def load_script_button_clicked(self): targeting_table_content = {} row_count = self.targeting_table.rowCount() for row in range(row_count): # 读取第0列的原生Name单元格(用item插入的原生表格项) name_item = self.targeting_table.item(row, 0) name = name_item.text() if name_item else "未配置名称" # 读取第1列插入的QSpinBox(Danger列) danger_spin = self.targeting_table.cellWidget(row, 1) danger_value = danger_spin.value() if danger_spin else 0 # 读取第2列插入的Loot下拉选择框 loot_combo = self.targeting_table.cellWidget(row, 2) loot_value = loot_combo.currentText() if loot_combo else "No" # 读取第3列插入的Only if trapped下拉选择框(修正原有越界取第4列的错误) trapped_combo = self.targeting_table.cellWidget(row, 3) trapped_value = trapped_combo.currentText() if trapped_combo else "No" targeting_table_content[name] = { 'danger': str(danger_value), 'loot': str(loot_value), 'only_if_trapped': str(trapped_value) } print(targeting_table_content)
补充注意事项
- 无需依赖
self.targeting_table.model()做取值操作,setCellWidget插入的控件和表格model是完全独立的渲染层,model不会自动同步这些控件的实时值。 - 如果后续需要让控件值自动同步回model,需要额外连接控件的
valueChanged、currentTextChanged信号,手动把值写入对应位置的QTableWidgetItem中。 - 删除行操作时,Qt会自动清理被删除行位置插入的控件实例,无需额外手动释放内存。
内容的提问来源于stack exchange,提问作者emceef
相关产品推荐
相关产品推荐

