I'm trying to do what sounds fairly simple but I keep running in to all sorts of problems. I'm trying to create a GUI that can tail several files at the same time using PyQt. I saw this answer on how to tail a file in pure Python
How can I tail a log file in Python?
I have tried using this code inside of a QThread. The issues I'm having here are that the tail process never stops by itself; it needs to be killed. It should be killed when the GUI is closed. The other issues I'm getting with this specific solution below is
QThread: Destroyed while thread is still running
and
QWaitCondition::wakeAll(): mutex lock failure: 
and
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
Other implementations I've tried have had the tail process complaining about a broken pipe but those stopped appearing once I did stderr=PIPE as well. I'm worried now that I could be missing errors since I never read from stderr (since it would block and there shouldn't be any output).
To get the errors fire this up trying to tail 3 different file. I wrote another script that loops and writes to those 3 files doing a sleep of 0.1 seconds. I close the GUI and start it up over and over again. Sometimes I get errors sometimes I don't.
Please tell me what I'm doing wrong here.
#!/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_())
 
     
    