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
相关产品推荐
相关产品推荐

