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

PyQt主窗口打开CSV显示子窗口时发生冻结问题求助

嘿,我一眼就看出问题出在哪了——你违反了PyQt的核心规则:所有UI操作必须在主线程中执行!

问题原因

你在子线程的add方法里直接创建了TextViewer窗口并调用show(),这会彻底打乱Qt的事件循环。Qt的UI组件和事件处理都是绑定在主线程的,子线程碰UI元素必然会导致主窗口卡死,甚至崩溃。另外,你在子线程里更新listView的model也是违规操作,只是这个问题可能没立刻显现出来而已。

解决方案:用信号槽实现线程安全的UI交互

Qt的信号槽机制是线程安全的,我们可以让子线程只负责繁重的文件读取工作,然后通过信号通知主线程处理UI操作。下面是修改后的完整代码:

main.py 修改版

from PyQt5 import uic
from PyQt5.QtWidgets import QApplication, QMainWindow, QFileDialog
from PyQt5.QtGui import QStandardItemModel, QStandardItem
from PyQt5.QtCore import pyqtSignal
from text_viewer import TextViewer
import sys
import pandas as pnd
from threading import Thread

class MainUI(QMainWindow):
    # 定义两个信号:分别用于更新文件列表和显示CSV查看窗口
    update_list_signal = pyqtSignal(str)
    show_viewer_signal = pyqtSignal(object)

    def __init__(self):
        super(MainUI, self).__init__()
        self.datas = []
        uic.loadUi('main.ui', self)
        self.model = QStandardItemModel()
        self.listView.setModel(self.model)
        self.load_btn.clicked.connect(self.add_btn)
        # 把信号绑定到主线程的槽函数
        self.update_list_signal.connect(self.update_file_list)
        self.show_viewer_signal.connect(self.show_text_viewer)
        self.show()

    def add_btn(self):
        options = QFileDialog.Options()
        options |= QFileDialog.DontUseNativeDialog
        allowed_extensions = "CSV Files (*.csv)"
        files = QFileDialog.getOpenFileNames(self, "Choose Data File To Load", "", allowed_extensions, options=options)
        # 这里要判断选中的文件列表是否为空,原代码的判断逻辑有问题
        if files[0]:
            thread = Thread(target=self.process_files, args=(files[0],))
            thread.start()

    def process_files(self, file_list):
        # 子线程只做文件读取这类非UI操作
        for file in file_list:
            try:
                if file.endswith(".csv"):
                    df = pnd.read_csv(file)
                    if df is not None:
                        self.datas.append(df)
                        # 发送信号,让主线程处理UI
                        self.update_list_signal.emit(file)
                        self.show_viewer_signal.emit(df)
            except Exception as e:
                print(f"处理文件 {file} 出错: {str(e)}")

    def update_file_list(self, file_path):
        # 主线程更新列表
        item = QStandardItem(file_path)
        self.model.appendRow(item)

    def show_text_viewer(self, df):
        # 主线程创建并显示窗口
        viewer = TextViewer(df)
        viewer.show()

if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = MainUI()
    sys.exit(app.exec_())

text_viewer.py 保持不变(如果需要展示CSV内容可以后续扩展)

from PyQt5 import uic
from PyQt5.QtWidgets import QMainWindow

class TextViewer(QMainWindow):
    def __init__(self, df):
        super(TextViewer, self).__init__()
        self.df = df
        uic.loadUi('text_viewer.ui', self)
        # 这里可以加个QTableView来展示df的内容,比如:
        # from PyQt5.QtWidgets import QTableView, QAbstractItemModel
        # 实现一个DataFrame转QTableModel的逻辑,不过这是额外功能了
        self.show()

为什么这样改就没问题?

  • 子线程只负责读取CSV文件,完全不碰任何UI组件,避免了线程冲突
  • 通过pyqtSignal发送消息,Qt会自动把信号传递到主线程的槽函数中执行,保证所有UI操作都在主线程完成
  • 修复了原代码中判断文件是否选中的逻辑错误

这样修改后,主窗口就不会再冻结了,你可以正常选择并查看CSV文件啦!

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.12 04:16:29