Python在使用Qt运行日志拖缆时崩溃 目标
我有一个进程,它在运行时记录一个文件(realtime.log),我想实时打印应用程序中该文件的每一行。换句话说,我想将输出从进程重定向到GUI。这意味着我有两个不同的进程运行:“引擎”和GUI 我已经通过使用Tkinter实现了这一点,但由于我必须制作更复杂、更专业、更美观的GUI,我决定切换到Qt for Python(PySide2) 问题 当我启动GUI时,Python经常会崩溃,并显示错误消息:Python已停止工作。窗口开始打印行,在某个点停止工作 在多次尝试和搜索之后,我发现只有在点击GUI窗口时程序才会崩溃。此外,程序不会突然崩溃,而是在引擎执行结束时崩溃 环境Python在使用Qt运行日志拖缆时崩溃 目标,python,multithreading,logging,pyside2,qrunnable,Python,Multithreading,Logging,Pyside2,Qrunnable,我有一个进程,它在运行时记录一个文件(realtime.log),我想实时打印应用程序中该文件的每一行。换句话说,我想将输出从进程重定向到GUI。这意味着我有两个不同的进程运行:“引擎”和GUI 我已经通过使用Tkinter实现了这一点,但由于我必须制作更复杂、更专业、更美观的GUI,我决定切换到Qt for Python(PySide2) 问题 当我启动GUI时,Python经常会崩溃,并显示错误消息:Python已停止工作。窗口开始打印行,在某个点停止工作 在多次尝试和搜索之后,我发现只有在
- 视窗10
- Python 3.6.5
- PySide2 5.12.6
datalog_path = "realtime.log"
def get_array_from_file(file_path):
try:
with open(file_path, 'r') as file:
lines = file.readlines()
return lines
except:
print('error in file access')
class Streamer(QRunnable):
def __init__(self, stream, old_array, edit):
super().__init__()
self.stream = stream
self.old_array = old_array
self.edit = edit
def run(self):
try:
while self.stream:
array_file = get_array_from_file(datalog_path)
if len(array_file) != len(self.old_array):
for line in array_file[len(self.old_array) - 1:len(array_file)]:
self.edit.append(line)
# print(line)
self.old_array.append(line)
except:
print('problem in streaming funct')
class Window(QMainWindow):
def __init__(self):
super().__init__()
layout = QVBoxLayout()
self.setWindowTitle("DATALOG")
self.thread_pool = QThreadPool()
self.edit = QTextEdit()
self.stream = True
self.old_array = get_array_from_file(datalog_path)
self.streamer = Streamer(self.stream, self.old_array, self.edit)
self.thread_pool.start(self.streamer)
window = QWidget()
layout.addWidget(self.edit)
window.setLayout(layout)
self.setCentralWidget(window)
def closeEvent(self, event):
self.stream = False
event.accept()
if __name__ == '__main__':
app = QApplication(sys.argv)
win = Window()
win.show()
app.exec_()
虽然我对Python Qt不太熟悉,但问题可能是,您使用的GUI对象
edit
来自不同的线程。这是不允许的,GUI部分必须全部在同一(主)线程中运行
要解决这个问题,您需要有一些其他的方式让线程来传达UI更改。由于您的QRunnable不是QObject,所以您不能仅仅发出一个信号,而是可以使用它的可调用方法。请让我知道这是否直接起作用:
# self.edit.append(line) # can't do this from a thread!
# instead, invoke append through GUI thread event loop
QtCore.QMetaObject.invokeMethod(self.edit,
'append',
QtCore.Qt.QueuedConnection,
QtCore.QGenericArgument('QString', line)
虽然我对Python Qt不太熟悉,但问题可能是,您使用的GUI对象
edit
来自不同的线程。这是不允许的,GUI部分必须全部在同一(主)线程中运行
要解决这个问题,您需要有一些其他的方式让线程来传达UI更改。由于您的QRunnable不是QObject,所以您不能仅仅发出一个信号,而是可以使用它的可调用方法。请让我知道这是否直接起作用:
# self.edit.append(line) # can't do this from a thread!
# instead, invoke append through GUI thread event loop
QtCore.QMetaObject.invokeMethod(self.edit,
'append',
QtCore.Qt.QueuedConnection,
QtCore.QGenericArgument('QString', line)
指出部分解释了问题的原因,但其解决方案不适用于PySide2(在PyQt5中,必须进行小的修改,请参见),另一种方法是创建具有以下信号的QObject:
class Signaller(QtCore.QObject):
textChanged = Signal(str)
class拖缆(QRunnable):
def_uuuinit_uuu(自、流、旧数组):
super()。\uuuu init\uuuuu()
self.stream=流
self.old\u数组=old\u数组
self.signaller=signaller()
def运行(自):
尝试:
而self.stream:
数组\u文件=从\u文件(数据日志\u路径)获取\u数组\u
如果len(数组_文件)!=len(self.old_数组):
对于数组_文件中的行[len(self.old_数组)-1:len(数组_文件)]:
self.signaller.textChanged.emit(行)
#打印(行)
self.old_数组.append(行)
除:
打印(“流式处理功能中存在问题”)
self.stream=True
self.old\u array=从\u文件(数据日志\u路径)获取\u array\u
self.streamer=拖缆(self.stream、self.old_阵列)
self.streamer.signaller.textChanged.connect(self.edit.append)
self.thread_pool.start(self.streamer)
指出的问题解释了问题的原因,但其解决方案不适用于PySide2(在PyQt5中,必须进行一个小的修改,请参阅),另一种方法是创建一个具有以下信号的QObject:
class Signaller(QtCore.QObject):
textChanged = Signal(str)
class拖缆(QRunnable):
def_uuuinit_uuu(自、流、旧数组):
super()。\uuuu init\uuuuu()
self.stream=流
self.old\u数组=old\u数组
self.signaller=signaller()
def运行(自):
尝试:
而self.stream:
数组\u文件=从\u文件(数据日志\u路径)获取\u数组\u
如果len(数组_文件)!=len(self.old_数组):
对于数组_文件中的行[len(self.old_数组)-1:len(数组_文件)]:
self.signaller.textChanged.emit(行)
#打印(行)
self.old_数组.append(行)
除:
打印(“流式处理功能中存在问题”)
self.stream=True
self.old\u array=从\u文件(数据日志\u路径)获取\u array\u
self.streamer=拖缆(self.stream、self.old_阵列)
self.streamer.signaller.textChanged.connect(self.edit.append)
self.thread\u pool.start(self.streamer)
非常感谢您的回答。正如@eyllanesc所述,您的解决方案不适用于PySide2,但概念绝对正确。非常感谢您的回答。正如@eyllanesc所述,您的解决方案不适用于PySide2,但概念绝对正确。