2013-06-24 1 views
0

나는 아주 간단하게 들리려고 노력하고 있지만 나는 모든 종류의 문제를 계속 실행 중이다. PyQt를 사용하여 여러 파일을 동시에 꼬리 수있는 GUI를 만들려고합니다. 나는 순수 파이썬에서 꼬리에 파일이꼬리 파일을 그래픽으로 PyQt로

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 가지 파일로 시도하십시오. 나는 0.1 초의 잠자기를하는 3 개의 파일에 루프와 쓴 다른 스크립트를 썼다. 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

문제는 기본 스레드가 백그라운드 스레드에서 대기하고 있지 않습니다.

현재 중단하라고 :

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

그래서,이 배경 스레드의 모든 결국을 종료 할 것입니다 하위 프로세스를 모두 죽인다. 그러나 그들은 즉시 을 멈추게하지 않을 것입니다. 다음에 각자가 readline에 도착할 때까지 그런 일은 일어나지 않을 것입니다.

서브 프로세스를 종료 한 후 돌아 오면 Qt가 즉시 창을 닫고 위젯을 삭제합니다. 그 위젯에 신호를 보내려는 모든 백그라운드 스레드는 실패합니다.

스레드 1이 readline을 수행했으며 스레드 0이 닫힐 때 rstrip 중간에 있다고 상상해보십시오. 따라서 스레드 0은 스레드 1의 하위 프로세스를 종료 한 다음 기본 위젯을 삭제합니다. 스레드 1은 rstrip을 완료하고 emit을 호출하고 이제 삭제 된 위젯으로 보냅니다.

+0

해결책으로 무엇을 권하고 싶습니까? –

+0

'closeEvent'에서 스레드가 사라질 때까지 차단하는 것이 신속하고 더러운 솔루션이지만, 이는 추한 것입니다 (이론적으로 볼 수 있듯이 UI 스레드가 비치볼을 막을 수있을만큼 충분히 차단할 수 있음). 또는'deleteLater'를 사용하여 삭제 순서를 지정하십시오. 아니면'close'를 삼켜서 다른 시그널을 추가 할 수 있습니다. (비슷하게'close'가 일어나도록합니다. 그러나'quit'을 바로 가지지 않음으로써 객체를 파괴하는 것을 멈출 수 있습니다.) 또는'newline' 신호를 간접적으로 (예를 들어., 신호를 통해 다른 객체 또는'weakref')를 호출 할 수 있으므로 주 스레드가 죽은 후에도 안전합니다. 또는'QThread.run' 대신에'moveToThread'를 사용하십시오. – abarnert

+0

그러나 코드를 다시 보면 ... 실제로는 더 기본적인 것일 수 있습니다. 'Tailer'와'TailWidget' 모두에 무언가를 출력하는'__del__' 메쏘드를 추가 할 수 있습니까? 'TailWayget '이 아니라'Tailer'가 삭제되기 때문에 충돌 만있을 가능성이 있기 때문에,'Tailer's를 오래 유지하면 문제가 해결됩니다. – abarnert