1.前言
本文主要是介绍如何将控制台的内容实时的输出到GUI中。
主要实现方式就是通过多线程,一个GUI的线程,还有一个线程提供给想要输出的程序。
需要两个线程的原因是,GUI主线程一旦启动,直到下一次调用刷新页面功能的操作出现之前,都无法改变当前界面,因此需要另外一个线程来改变GUI的内容。
2.实现
实现功能为:将训练模型的信息,输出到GUI中的文本框中,假设文本框为 W 。
思想如下:
- 在GUI中,定义一个如何更新
W 中内容的函数 onUpdateText() - 自定义一个输出流
Stream ,将某个地方的输出重定向到另一个地方 - 在GUI中,将
onUpdateText() 函数 绑定到 输出流 Stream 中的 newText 中,表示新的输出点为 onUpdateText() 函数所操作的对象 - 将sys的stdout设置为自定义的输出流
Stream ,即将控制台的输出重定向到 Stream 中定义的新位置 - 编写另一个线程,将功能函数(即训练函数)写在线程的run中
- 在GUI中调用线程,执行功能函数,功能函数原本输出在控制台的信息,就被重定向到
W 中 - 在关闭GUI的时候,将sys的stdout重置为 sys.__stdout__
3.代码
本次实验中使用的GUI库为PySide6 ,训练代码基于TensorFlow 编写
import ctypes
import sys
from PySide6.QtCore import QObject, Signal, QThread
from PySide6.QtWidgets import QMainWindow, QPushButton, QApplication, QTextEdit
from PySide6.QtGui import QTextCursor
from test_code.tmp_train_saxs import train_saxs
import win32con
class Stream(QObject):
"""Redirects console output to text widget."""
newText = Signal(str)
def write(self, text):
self.newText.emit(str(text))
def flush(self):
""" flush(self) """
pass
class MyThread(QThread):
def __init__(self):
super(MyThread, self).__init__()
self.is_on = True
def run(self):
self.handle = ctypes.windll.kernel32.OpenThread(
win32con.PROCESS_ALL_ACCESS, False, str(QThread.currentThread()))
while self.is_on:
train_saxs(data_dir="./data_split_test/saxs/train", epochs=5)
self.is_on = False
class GenMast(QMainWindow):
def __init__(self):
super().__init__()
self.initUI()
self.sm = Stream()
self.sm.newText.connect(self.onUpdateText)
sys.stdout = self.sm
sys.stderr = self.sm
self.my_thread = MyThread()
self.my_thread.finished.connect(self.thread_finish_process)
def onUpdateText(self, text):
"""Write console output to text widget."""
cursor = self.process.textCursor()
cursor.movePosition(QTextCursor.End)
cursor.insertText(text)
self.process.setTextCursor(cursor)
self.process.ensureCursorVisible()
def closeEvent(self, event):
sys.stdout = sys.__stdout__
super().closeEvent(event)
def initUI(self):
"""Creates UI window on launch."""
btnGenMast = QPushButton('Run', self)
btnGenMast.move(450, 50)
btnGenMast.resize(100, 200)
btnGenMast.clicked.connect(self.genMastClicked)
self.process = QTextEdit(self, readOnly=True)
self.process.ensureCursorVisible()
self.process.setLineWrapColumnOrWidth(500)
self.process.setLineWrapMode(QTextEdit.FixedPixelWidth)
self.process.setFixedWidth(400)
self.process.setFixedHeight(200)
self.process.move(30, 50)
self.setGeometry(300, 300, 600, 300)
self.setWindowTitle('Generate Master')
self.show()
def train_and_echo_to_gui(self):
self.my_thread.start()
def thread_finish_process(self):
print("thread is end...")
def genMastClicked(self):
"""Runs the main function."""
print('Begin.')
self.train_and_echo_to_gui()
print('Done.')
if __name__ == '__main__':
app = QApplication(sys.argv)
app.aboutToQuit.connect(app.deleteLater)
gui = GenMast()
sys.exit(app.exec_())
参考
https://blog.csdn.net/weixin_42532587/article/details/105689637 https://blog.csdn.net/ccj15010192778/article/details/102704301 https://blog.csdn.net/qq_47203885/article/details/110676076 https://zhuanlan.zhihu.com/p/373032781
|