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

PyQt导入CSV填充QTableWidget实现及报错问题排查

问题描述

作为Python及程序开发初学者,在搭建PyQt GUI应用时,需要将多个CSV文件的数据展示在界面中,最初尝试使用Python内置csv模块实现加载逻辑,初始代码片段如下:

def loadCsvFile(self, test.csv):
        with open(test.csv, "r") as stream:
            for row in csv.reader(stream):

目标是将CSV内容填充到名为Xerox_work_centre_db的表格控件中,更新代码后运行触发如下报错:

File "C:\Temp\GitHub\CSV_to_UI\main.py", line 176, in loadCsv
self.model.appendRow(items)
AttributeError: 'MainWindow' object has no attribute 'model'

相关UI代码(ui_interface.py)片段:

self.verticalLayout_13.addWidget(self.Monitor_header, 0, Qt.AlignTop)

self.Monitor_main_content = QFrame(self.Monitor)
self.Monitor_main_content.setObjectName(u"Monitor_main_content")
sizePolicy.setHeightForWidth(self.Monitor_main_content.sizePolicy().hasHeightForWidth())
self.Monitor_main_content.setSizePolicy(sizePolicy)
self.Monitor_main_content.setFrameShape(QFrame.StyledPanel)
self.Monitor_main_content.setFrameShadow(QFrame.Raised)
self.verticalLayout_18 = QVBoxLayout(self.Monitor_main_content)
self.verticalLayout_18.setSpacing(0)
self.verticalLayout_18.setObjectName(u"verticalLayout_18")
self.verticalLayout_18.setContentsMargins(0, 0, 0, 0)
#######################################
### CSV CONTENT
self.model = QStandardItemModel(MainWindow)
self.Xerox_workcentre_db = QTableView(self.Monitor_main_content)
self.Xerox_workcentre_db.setObjectName(u"Xerox_workcentre_db")
self.Xerox_workcentre_db.setModel(self.model)
self.Xerox_workcentre_db.horizontalHeader().setStretchLastSection(True)

self.verticalLayout_18.addWidget(self.Xerox_workcentre_db)
#######################################

相关主逻辑代码(main.py)片段:

## MAIN WINDOW CLASS
########################################################################
class MainWindow(QMainWindow):
    def __init__(self, parent=None):
        QMainWindow.__init__(self)
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)

        self.setWindowFlags(Qt.FramelessWindowHint)
        self.setAttribute(Qt.WA_TranslucentBackground)
        self.shadow = QGraphicsDropShadowEffect(self)
        self.shadow.setBlurRadius(50)
        self.shadow.setXOffset(0)
        self.shadow.setYOffset(0)
        self.shadow.setColor(QColor(0, 92, 157, 550))
        self.ui.centralwidget.setGraphicsEffect(self.shadow)
        self.setWindowIcon(QIcon(""))
        # Set window tittle
        self.setWindowTitle("Printer Monitoring v1.0")
        QSizeGrip(self.ui.size_grip)

        #######################################################################
        ## Button for loadCsv
        self.ui.Xerox_workcentre_button.clicked.connect(self.loadCsv)
        
#######################################################################

        self.ui.minimize_window_button.clicked.connect(lambda: self.showMinimized())
        
        self.ui.close_window_button.clicked.connect(lambda: self.close())
        self.ui.exit_button.clicked.connect(lambda: self.close())

        
        self.ui.restore_window_button.clicked.connect(lambda: self.restore_or_maximize_window())
        

        def moveWindow(e):
            if self.isMaximized() == False:  # Not maximized
                if e.buttons() == Qt.LeftButton:             
                    self.move(self.pos() + e.globalPos() - self.clickPosition)
                    self.clickPosition = e.globalPos()
                    e.accept()

        
        self.ui.header_frame.mouseMoveEvent = moveWindow     
        self.ui.open_close_side_bar_btn.clicked.connect(lambda: self.slideLeftMenu())

        self.show()

    
    def slideLeftMenu(self):
        # Get current left menu width
        width = self.ui.slide_menu_container.width()

        # If minimized
        if width == 0:
            # Expand menu
            newWidth = 200
            self.ui.open_close_side_bar_btn.setIcon(QIcon(u":/icons/icons/chevron-left-solid-white.svg"))
        # If maximized
        else:
            # Restore menu
            newWidth = 0
            self.ui.open_close_side_bar_btn.setIcon(QIcon(u":/icons/icons/bars-solid-white.svg"))

        # Animate the transition
        self.animation = QPropertyAnimation(self.ui.slide_menu_container, b"minimumWidth")  # Animate minimumWidth
        self.animation.setDuration(350)
        self.animation.setStartValue(width)  # Start value is the current menu width
        self.animation.setEndValue(newWidth)  # end value is the new menu width
        self.animation.setEasingCurve(QEasingCurve.InOutQuart)
        self.animation.start()

    def mousePressEvent(self, event):
        self.clickPosition = event.globalPos()

    def restore_or_maximize_window(self):
        # If window is maximized
        if self.isMaximized():
            self.showNormal()
            # Change Icon
            self.ui.restore_window_button.setIcon(QIcon(u":/icons/icons/window-maximize-regular-white.svg"))
        else:
            self.showMaximized()
            self.ui.restore_window_button.setIcon(QIcon(u":/icons/icons/window-restore-solid-white.svg"))
    #######################################################################
    # CALL XEROX CSV FUNCTION

    def loadCsv(self):
        fileName= 'Xerox_workcentre_test.csv'
        with open(fileName, "r") as fileInput:
            for row in csv.reader(fileInput):
                items = [
                    QtGui.QStandardItem(field)
                    for field in row
                ]
                self.model.appendRow(items)
报错原因
  • model属性定义在Ui_MainWindow类实例下,挂载在self.ui对象上,并非MainWindow类自身的直接属性,直接调用self.model无法找到对应属性,因此抛出AttributeError。
  • 初始csv加载函数的参数写法test.csv不符合Python语法,函数参数名不能包含.特殊字符。
  • 加载CSV前没有清空原有模型数据,重复点击加载按钮会导致数据重复追加。
  • 打开文件时未指定编码,遇到中文等非ASCII字符时可能触发编码报错。
修复方案

1. 修正属性调用路径

将loadCsv方法中所有对self.model的调用改为self.ui.model,指向UI实例下的模型对象。

2. 优化CSV加载逻辑

加载前先清空模型原有数据,避免重复加载导致数据冗余;如果CSV第一行是表头,可单独提取设置为表格表头;打开文件时指定通用编码避免乱码。

3. 修正后的loadCsv代码

import csv
from PyQt5 import QtGui

def loadCsv(self):
    fileName = 'Xerox_workcentre_test.csv'
    # 加载前清空原有模型数据
    self.ui.model.clear()
    # 指定utf-8编码打开文件,适配多语言字符
    with open(fileName, "r", encoding='utf-8') as fileInput:
        csv_reader = csv.reader(fileInput)
        # 读取第一行作为表头,若CSV无表头可删除以下两行
        headers = next(csv_reader)
        self.ui.model.setHorizontalHeaderLabels(headers)
        # 逐行加载数据
        for row in csv_reader:
            items = [
                QtGui.QStandardItem(field)
                for field in row
            ]
            self.ui.model.appendRow(items)
    # 可选:加载完成后自动调整列宽适配内容
    self.ui.Xerox_workcentre_db.resizeColumnsToContents()

补充:QTableWidget原生加载方案

如果不需要使用Model/View架构,直接用最初的QTableWidget控件加载CSV,可参考以下逻辑:

from PyQt5.QtWidgets import QTableWidgetItem
import csv

def loadCsvToTableWidget(self):
    fileName = 'Xerox_workcentre_test.csv'
    table = self.ui.Xerox_workcentre_db
    table.clear()
    with open(fileName, "r", encoding='utf-8') as fileInput:
        csv_reader = list(csv.reader(fileInput))
        row_count = len(csv_reader)
        col_count = len(csv_reader[0]) if row_count > 0 else 0
        table.setRowCount(row_count)
        table.setColumnCount(col_count)
        for row_idx, row in enumerate(csv_reader):
            for col_idx, cell_value in enumerate(row):
                table.setItem(row_idx, col_idx, QTableWidgetItem(cell_value))
    table.resizeColumnsToContents()

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.02 01:01:25