如何在PyQt5 GUI程序中公开TCP Socket的accept()创建的conn对象?
解决方案与修复后的代码
核心问题分析
conn作用域问题:conn是OpenConn方法内的局部变量,外部Stream函数无法访问,需将其保存为MainWindow的实例属性。- 文件读取逻辑错误:
f.readlines()会将文件指针移至末尾,后续readline无法读取内容,需先缓存所有行再处理。 - 未保存选中的文件名:原代码仅更新了UI输入框,未将文件名存入
self.filename,导致传输判断逻辑失效。 - 线程选择不当:
threading.Timer仅执行一次,不适合循环流数据场景,应使用普通Thread。 - PyQt与Tkinter混用:两套GUI框架混用易引发冲突,统一使用PyQt的对话框和消息框。
- Socket重复定义:全局与主函数内重复创建Socket实例,需统一管理。
修复后的完整代码
import sys import socket import threading as th from time import sleep from PyQt5.QtWidgets import ( QApplication, QMainWindow, QFileDialog, QMessageBox ) from GUI import Ui_MainWindow TCP_IP = '127.0.0.1' TCP_PORT = 5005 BUFFER_SIZE = 1024 def Stream(): while True: while main_win.Transmit: try: with open(main_win.filename, "r", encoding='utf-8') as f: lines = f.readlines() total_lines = len(lines) # 发送未传输的行 while main_win.Lastline < total_lines and main_win.Transmit: data = lines[main_win.Lastline] main_win.Lastline += 1 # Socket发送需字节类型,编码文本 main_win.conn.send(data.encode('utf-8')) except FileNotFoundError: QMessageBox.critical(main_win.main_win, 'Error', 'Error: Archivo no encontrado') main_win.Transmit = False except Exception as e: QMessageBox.critical(main_win.main_win, 'Error', f'Error de transmisión: {str(e)}') main_win.Transmit = False main_win.Connected = False break sleep(main_win.ui.doubleSpinBox.value()) class MainWindow: Transmit = False Lastline = 0 filename = "" Started = False Connected = False def __init__(self): self.main_win = QMainWindow() self.ui = Ui_MainWindow() self.ui.setupUi(self.main_win) # 绑定按钮事件 self.ui.pushButton.clicked.connect(self.on_pushButton_clicked) self.ui.pushButton_2.clicked.connect(self.transmit_clicked) self.ui.pushButton_4.clicked.connect(self.OpenConn) # 初始化Socket self.s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.s.bind((TCP_IP, TCP_PORT)) self.s.listen(1) self.conn = None # 初始化连接对象 def show(self): self.main_win.show() def on_pushButton_clicked(self): # 使用PyQt文件对话框 filename, _ = QFileDialog.getOpenFileName( self.main_win, "Seleccionar archivo", "/", "Archivos de texto (*.txt);;Todos los archivos (*.*)" ) if filename: self.filename = filename self.ui.lineEdit.setText(filename) def transmit_clicked(self): if not self.Connected: QMessageBox.critical(self.main_win, 'Error', 'Error: No hay una conexión establecida') return if self.filename == "": QMessageBox.critical(self.main_win, 'Error', 'Error: No ha seleccionado el archivo') return if not self.Transmit: self.Transmit = True self.ui.pushButton_2.setText("Detener Streaming de datos") if not self.Started: # 创建守护线程,程序退出时自动终止 self.stream_thread = th.Thread(target=Stream, daemon=True) self.stream_thread.start() self.Started = True else: self.Transmit = False self.ui.pushButton_2.setText("Iniciar Streaming de datos") def OpenConn(self): # 保存连接对象为实例属性 self.conn, addr = self.s.accept() self.Connected = True print(f"Conectado a {addr}") if __name__ == '__main__': app = QApplication(sys.argv) main_win = MainWindow() main_win.show() sys.exit(app.exec_())
关键修改点说明
conn作用域修复:在MainWindow类中添加self.conn属性,OpenConn方法中赋值,Stream函数通过main_win.conn访问连接对象。- 文件读取逻辑优化:使用
with语句安全操作文件,先读取所有行缓存,再按行发送,避免指针位置问题。 - 文件名保存:选中文件后将路径存入
self.filename,确保传输判断逻辑正常。 - 线程替换:用
threading.Thread替代Timer,设置daemon=True保证程序退出时线程自动结束。 - GUI框架统一:移除所有Tkinter相关代码,使用PyQt原生的
QFileDialog和QMessageBox。 - Socket管理:将Socket创建移至
MainWindow初始化方法,避免重复定义。
内容的提问来源于stack exchange,提问作者jorgeneo560
相关产品推荐
相关产品推荐

