Python 当 运行 使用 Qt 的日志流送器时崩溃
Python crashes when running a log streamer using Qt
目标
我有一个在文件 (realtime.log) 上登录的进程,而 运行 我想在我的应用程序中实时打印该文件的每一行。换句话说,我想将进程的输出重定向到 GUI。这意味着我有 两个不同的进程 运行:“引擎”和 GUI。
我已经通过使用 Tkinter 实现了这一点,但由于我必须制作更复杂、更专业、更美观的 GUI,我决定切换到 Qt for Python (PySide2).
问题
Python 经常 在我启动 GUI 时崩溃并显示错误消息:Python 已停止工作 . window 开始打印行,并在某个时候停止工作。
经过多次尝试和搜索,我发现只有在单击 GUI window 时程序才会崩溃。而且,程序不会突然崩溃,而是在引擎执行结束时崩溃。
环境
- Windows10
- 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,你不能只发出一个信号,但你可以在它的可调用方法上使用 QMetaObject::invokeMethod
.如果这直接有效,请告诉我:
# 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)
points out explains the reason for the problem but its solution is not applicable in PySide2 (in PyQt5 a small modification would have to be made, see ),另一种方法是创建一个具有信号的 QObject:
class Signaller(QtCore.QObject):
textChanged = Signal(str)
class Streamer(QRunnable):
def __init__(self, stream, old_array):
super().__init__()
self.stream = stream
self.old_array = old_array
<b>self.signaller = Signaller()</b>
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)]:
<b>self.signaller.textChanged.emit(line)</b>
# print(line)
self.old_array.append(line)
except:
print('problem in streaming funct')
self.stream = True
self.old_array = get_array_from_file(datalog_path)
<b>self.streamer = Streamer(self.stream, self.old_array)
self.streamer.signaller.textChanged.connect(self.edit.append)</b>
self.thread_pool.start(self.streamer)
目标
我有一个在文件 (realtime.log) 上登录的进程,而 运行 我想在我的应用程序中实时打印该文件的每一行。换句话说,我想将进程的输出重定向到 GUI。这意味着我有 两个不同的进程 运行:“引擎”和 GUI。
我已经通过使用 Tkinter 实现了这一点,但由于我必须制作更复杂、更专业、更美观的 GUI,我决定切换到 Qt for Python (PySide2).
问题
Python 经常 在我启动 GUI 时崩溃并显示错误消息:Python 已停止工作 . window 开始打印行,并在某个时候停止工作。
经过多次尝试和搜索,我发现只有在单击 GUI window 时程序才会崩溃。而且,程序不会突然崩溃,而是在引擎执行结束时崩溃。
环境
- Windows10
- 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,你不能只发出一个信号,但你可以在它的可调用方法上使用 QMetaObject::invokeMethod
.如果这直接有效,请告诉我:
# 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)
class Signaller(QtCore.QObject):
textChanged = Signal(str)
class Streamer(QRunnable):
def __init__(self, stream, old_array):
super().__init__()
self.stream = stream
self.old_array = old_array
<b>self.signaller = Signaller()</b>
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)]:
<b>self.signaller.textChanged.emit(line)</b>
# print(line)
self.old_array.append(line)
except:
print('problem in streaming funct')
self.stream = True
self.old_array = get_array_from_file(datalog_path)
<b>self.streamer = Streamer(self.stream, self.old_array)
self.streamer.signaller.textChanged.connect(self.edit.append)</b>
self.thread_pool.start(self.streamer)