我正在尝试做听起来相当简单的事情,但我一直在遇到各种各样的问题。 我正在尝试使用PyQt创建一个可以同时拖动多个文件的GUI。 我看到了关于如何在纯Python中拖尾文件的答案
How can I tail a log file in Python?
我尝试在QThread中使用此代码。 我在这里遇到的问题是尾部过程永远不会停止;它需要被杀死。 关闭GUI时应该将其终止。 我在下面的具体解决方案中遇到的其他问题是
QThread: Destroyed while thread is still running
和
QWaitCondition::wakeAll(): mutex lock failure:
和
QThread: Destroyed while thread is still running
Traceback (most recent call last):
File "./tailer.py", line 27, in run
self.emit(SIGNAL('newline'), line.rstrip())
RuntimeError: underlying C/C++ object has been deleted
我尝试过的其他实现让尾部进程抱怨管道损坏但是一旦我做了stderr = PIPE就停止出现了。 我现在担心我可能会错过错误,因为我从未读过stderr(因为它会阻塞并且不应该有任何输出)。
要获取错误,请尝试将3个不同的文件拖尾。 我写了另一个脚本,循环写入这些3个文件,休眠时间为0.1秒。 我关闭GUI并一遍又一遍地启动它。有时我会收到错误,有时我不会。
请告诉我这里我做错了什么。
#!/usr/bin/env python
from PyQt4.QtCore import *
from PyQt4.QtGui import *
import os
from subprocess import Popen, PIPE
class Tailer(QThread):
def __init__(self, fname, parent=None):
super(Tailer, self).__init__(parent)
self.fname = fname
self.connect(self, SIGNAL('finished()'), self.cleanup)
def cleanup(self):
print 'CLEANING UP'
self.p.kill()
print 'killed'
def run(self):
command = ["tail", "-f", self.fname]
print command
self.p = Popen(command, stdout=PIPE, stderr=PIPE)
while True:
line = self.p.stdout.readline()
self.emit(SIGNAL('newline'), line.rstrip())
if not line:
print 'BREAKING'
break
def foo(self):
self.p.kill()
class TailWidget(QWidget):
def __init__(self, fnames, parent=None):
super(TailWidget, self).__init__(parent)
layout = QGridLayout()
self.threads = {}
self.browsers = {}
for i, fname in enumerate(fnames):
if not os.path.exists(fname):
print fname, "doesn't exist; creating"
p = Popen(['touch', fname], stdout=PIPE, stderr=PIPE)
out, err = p.communicate()
ret = p.wait()
assert ret == 0
t = Tailer(fname, self)
self.threads[fname] = t
b = QTextBrowser()
self.browsers[fname] = b
layout.addWidget(QLabel('Tail on %s' % fname), 0, i)
layout.addWidget(b, 1, i)
self.connect(t, SIGNAL("newline"), b.append)
t.start()
self.setLayout(layout)
def closeEvent(self, event):
for fname, t in self.threads.items():
t.foo()
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
tw = TailWidget(sys.argv[1:])
tw.show()
sys.exit(app.exec_())
答案 0 :(得分:0)
问题是主线程没有在后台线程上等待。
你告诉他们停在这里:
def closeEvent(self, event):
for fname, t in self.threads.items():
t.foo()
因此,这会杀死所有子进程,这将使所有后台线程最终退出 。但它不会让他们立即停止。直到下一次每个人到达readline
时才会发生这种情况。
在杀死子进程后,你返回,让Qt立即关闭你的窗口并销毁你的小部件。尝试向该窗口小部件发送信号的任何后台线程都将失败。
想象一下,线程1已经完成readline
,并且当线程0尝试关闭时,它位于rstrip
的中间位置。因此,线程0杀死线程1的子进程,然后删除主要小部件。线程1完成其rstrip
并调用emit
,现在它正在发送到已删除的小部件。