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

PyQt5 QTableView单元格高亮与数据修改后无法即时刷新问题求解

问题背景

我在开发校准小程序,需要逐单元格填充尺寸为100×100×4的数组,目前QTableView组件无法正确更新。
硬件移动到1、2、3等点位时,我会触发电压测量,通过i2c读取函数采集对应数值。
QTableView不刷新是非常常见的开发问题,但我查到的公开示例无法适配到自己的代码中,达不到预期运行效果。
参考程序运行截图,具体问题表现为:

  • 点击行或列的+/-按钮时,选中单元格的黄色高亮无法即时同步移动
  • 点击「Store i²c」按钮(功能为向选中单元格写入测试值0.0)时,单元格内容无法即时刷新显示

我已经尝试过多种通知模型数据变更的方法,均未正确生效,希望通过添加少量代码,实现视图的正确强制刷新。


附:项目代码与环境信息

fillCSV_forum.py

### 依赖库
import sys # 用于调用exit等系统函数
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import Qt
import pandas as pd # 用于表格数据处理
import numpy as np # 用于数组运算

### 自定义模块
from uLib_coloredWidget import Color    # 导入自定义功能模块                        
from rndGen import i2c_read             # 模块存放于项目目录下


### 初始设置
np.random.seed(4) # 固定numpy随机数种子,方便测试结果复现


### 全局变量
nRow = 5; nCol = 5; nSht = 4 # 表格维度参数
rowIdx = colIdx = shtIdx = 0 # 数组索引辅助变量
rndArray = np.random.rand(nSht, nRow, nCol) * 4.3 # 接入真实i2c功能前的测试数组
tabNames = ["A4", "A5","A6","A7"] # 标签页名称列表
rowIdx = 1; colIdx = 1 # 选中单元格初始索引

### 类与方法定义
class TableModel(QtCore.QAbstractTableModel):

    def __init__(self, data):
        super(TableModel, self).__init__()
        self._data = data

    def data(self, index, role):

        if role == Qt.BackgroundRole and index.column() == colIdx and index.row() == rowIdx:
            # 选中单元格返回黄色背景
            return QtGui.QColor('yellow')

        if role == Qt.DisplayRole:
            value = self._data.iloc[index.row(), index.column()]
            if isinstance(value, float): # 浮点数固定显示4位小数
                return "%.4f" % value
            return str(value)

    def rowCount(self, index):
        return self._data.shape[0]

    def columnCount(self, index):
        return self._data.shape[1]

    def headerData(self, section, orientation, role):
        # section为列/行的索引值
        if role == Qt.DisplayRole:
            if orientation == Qt.Horizontal:
                return str(self._data.columns[section])

            if orientation == Qt.Vertical:
                return str(self._data.index[section])

class App(QtWidgets.QMainWindow):

    # 初始化方法
    def __init__(self):
        super().__init__()

        self.setup_main_window() # 调用辅助方法初始化主窗口
        self.createLayout() # 创建布局与控件
        self.post_main_window() # 将布局挂载到主窗口

    # 辅助方法定义
    def setup_main_window(self): # 设置窗口基础属性
        self.centralwidget = QtWidgets.QWidget()
        self.setCentralWidget(self.centralwidget)        
        self.resize( 800, 400  )
        self.setWindowTitle( "# disposition calibration #" )

    def post_main_window(self): # 将编辑完成的布局挂载到窗口
        self.centralwidget.setLayout(self.lyoOut)
        
    def createLayout(self): # 创建布局层级
        self.lyoOut = QtWidgets.QVBoxLayout()
        self.lyoIn1 = QtWidgets.QHBoxLayout()
        self.lyoIn2 = QtWidgets.QGridLayout()

        self.createWidgets() # 向子布局添加控件

        self.lyoOut.addLayout(self.lyoIn1)
        self.lyoOut.addLayout(self.lyoIn2)

    def createWidgets(self): # 调用方法创建各布局下的控件
        # 外层布局第一行
        self.lyoIn1 = self.createNestedTabs(self.lyoIn1) # 创建主标签页
        # 外层布局第二行
        self.lyoIn2 = self.createButtons(self.lyoIn2) # 创建功能按钮

    def createNestedTabs(self, layout2modify): # 创建第一层标签页
        self.MstTabs = QtWidgets.QTabWidget()
        self.MstTabs.setTabPosition(QtWidgets.QTabWidget.North)
        self.MstTabs.addTab(self.createChildTabs(), "data")
        self.MstTabs.addTab(Color("orange"), "plot")

        stylesheet = """ 
            QTabBar::tab:selected {background: lightgreen;}
            QTabBar::tab:!selected {background: lightyellow;}
            """ 
        self.MstTabs.setStyleSheet(stylesheet)

        layout2modify.addWidget(self.MstTabs)
        return layout2modify

    def createChildTabs(self): # 创建第二层标签页
        self.ChdTabs = QtWidgets.QTabWidget()
        self.ChdTabs.setTabPosition(QtWidgets.QTabWidget.West)
        self.ChdTabs.addTab(self.createPandasTables(0), "A4") 
        self.ChdTabs.addTab(self.createPandasTables(1), "A5") 
        self.ChdTabs.addTab(self.createPandasTables(2), "A6") 
        self.ChdTabs.addTab(self.createPandasTables(3), "A7")

        return self.ChdTabs

    def createPandasTables(self, shtIdx): # 创建基于pandas数据的表格控件
        # 读取对应sheet索引的pandas数据
        Lbl = ["a","b","c","d","e"]
        self.df = pd.DataFrame(rndArray[shtIdx], columns = Lbl, index = Lbl)
        # 创建控件实例
        self.table_widget = QtWidgets.QTableView()
        self.model = TableModel(self.df)
        self.table_widget.setModel(self.model)

        # 表格格式设置
        self.table_widget.resizeColumnsToContents()
        self.table_widget.horizontalHeader().setStretchLastSection(True)
        self.table_widget.verticalHeader().setStretchLastSection(True)
        self.table_widget.setAlternatingRowColors(True)

        return self.table_widget

    def createButtons(self, layout2modify): # 创建功能按钮
        bStoreI2C = QtWidgets.QPushButton("Store i²c")
        bStoreI2C.clicked.connect(lambda:self.storeVal())        
        bStoreI2C.setStyleSheet("QPushButton::hover" 
                             "{"
                             "background-color : yellow;"
                             "}")
        layout2modify.addWidget(bStoreI2C, 1, 3, 2, 1)

        self.lbl_1 = QtWidgets.QLabel()
        self.lbl_1.setText(str(rowIdx))
        self.lbl_1.setAlignment(QtCore.Qt.AlignCenter)
        layout2modify.addWidget(self.lbl_1, 1, 5, 2, 1)

        bRowAdd = QtWidgets.QPushButton("row +")
        bRowAdd.clicked.connect(lambda:self.rowAdd())
        layout2modify.addWidget(bRowAdd, 2, 6)


        bRowSub = QtWidgets.QPushButton("row -")
        bRowSub.clicked.connect(lambda:self.rowSub())
        layout2modify.addWidget(bRowSub, 1, 6)

        return layout2modify

    def storeVal(self):

        #i2c_vals = get_i2c_values(i2c_addrs)
        for i in range (0,4):
            #self.tbData[i, rowIdx, colIdx] = i2c_vals[i] # 写入i2c读取的真实数值
            rndArray[i, rowIdx, colIdx] = 0
        #self.tbData[sht, row, col] = 99 # 测试用写入逻辑

        # 已尝试的刷新方法1
        #self.table_widget.update()
        #self.table_widget.repaint()
         #self.model.select()
        #self.table_widget.select()
        # 已尝试的刷新方法2
        
        # self.refreshModel() # 暂未生效
        #self.model = TableModel(self.df)
        #self.table_widget.setModel(self.model) 

        # print(rndArray)
        print('i²c-value(s) stored')

    def rowAdd(self):
        global rowIdx

        rowIdx = (rowIdx + 1) % nRow # 自增后取模实现索引循环
        self.lbl_1.setText(str(rowIdx)) # 更新标签显示

        print('row is ', rowIdx)

    def rowSub(self):
        global rowIdx

        rowIdx = (rowIdx - 1) % nRow # 自减后取模实现索引循环
        self.lbl_1.setText(str(rowIdx)) # 更新标签显示

        print('row is ', rowIdx)

    
### 主函数
def main():
    app = QtWidgets.QApplication(sys.argv) # 实例化应用
    window = App() # 实例化窗口

    window.show() # 显示窗口
    
    app.exec_() # 启动事件循环,窗口关闭后退出
    print('# window will be terminated.. #')
    time.sleep(2)
    print('# ..app execution closed #')


# 程序入口
if __name__ == '__main__':
    main()

rndGen.py(fillCSV_forum.py依赖模块)

import numpy as np

def i2c_read():
    floats = np.random.rand(4,1,1) * 4.3

    return floats

uLib_coloredWidget.py(fillCSV_forum.py依赖模块)

from PyQt5.QtGui import QColor, QPalette
from PyQt5.QtWidgets import QWidget


class Color(QWidget):
  def __init__(self, color):
    super().__init__()
    self.setAutoFillBackground(True)
    palette = self.palette()
    palette.setColor(QPalette.Window, QColor(color))
    self.setPalette(palette)

虚拟环境pip freeze依赖清单

numpy==1.23.0
pandas==1.4.3
PyQt5==5.15.7
PyQt5-Qt5==5.15.2
PyQt5-sip==12.11.0
python-dateutil==2.8.2
pytz==2022.1
six==1.16.0

内容的提问来源于stack exchange,提问作者El_Mo

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 13:33:23