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

