【问题标题】:Tail file graphically with PyQt使用 PyQt 以图形方式显示尾部文件
【发布时间】:2013-06-21 10:58:48
【问题描述】:

我正在尝试做一些听起来相当简单的事情,但我总是遇到各种各样的问题。 我正在尝试创建一个可以使用 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_())

【问题讨论】:

    标签: python multithreading qt pyqt


    【解决方案1】:

    问题是主线程没有在后台线程上等待。

    你告诉他们到此为止:

    def closeEvent(self, event):
        for fname, t in self.threads.items():
            t.foo()
    

    因此,这会杀死所有子进程,这将使所有后台线程最终退出。但这不会让他们立即停下来。直到下一次每个人都到达其readline 时才会发生这种情况。

    杀死子进程后,您返回,让 Qt 立即关闭您的窗口并销毁您的小部件。任何试图向该小部件发送信号的后台线程都会失败。

    想象一下,线程 1 已经完成了一个readline,并且在线程 0 尝试关闭时处于其rstrip 的中间。因此,线程 0 杀死线程 1 的子进程,然后删除主小部件。线程 1 完成其 rstrip 并调用 emit,现在它正在发送到已删除的小部件。

    【讨论】:

    • 您推荐什么作为解决方案?
    • 一个快速而肮脏的解决方案是阻塞直到线程在closeEvent 中消失,但这很丑陋(理论上,阻塞 UI 线程的时间可能足够长,导致抢滩球)。或者,使用deleteLater 对删除进行排序。或者你可以吞下close 并添加另一个信号(或者类似地让close 发生,但不要直接去quit 来阻止破坏对象)。或者间接地发出newline 信号(例如,通过一个信号到另一个对象,或weakref),这样即使在主线程已经死亡之后它也是安全的。或者使用moveToThread 而不是覆盖QThread.run
    • 但是再看一遍你的代码……它实际上可能是更基本的东西。您能否添加__del__ 方法,这些方法可以同时打印TailerTailWidget,并在崩溃和不崩溃时显示序列?因为它可能只是因为Tailer 被删除而不是TailWidget 而崩溃,在这种情况下,只要让Tailers 存活更长时间就可以解决问题。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-07-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-03-24
    • 1970-01-01
    相关资源
    最近更新 更多