qthread updated fentserinhalt erst nach beenden

Python und das Qt-Toolkit, erstellen von GUIs mittels des Qt-Designers.
Antworten
init-0
User
Beiträge: 38
Registriert: Samstag 22. Januar 2011, 18:46

Hi,

Ich will einen cmd.exe ersatz schreiben, weil mir das nicht so ganz gefällt. Ich habe jetzt Ein fenster mit einem textedit und will dort eingaben parsen. Ich habe einen QThread der die eingabe parst. Der Code sieht so aus:

Code: Alles auswählen

class SThread(QtCore.QThread):
    updateText = QtCore.pyqtSignal(QtCore.QString,  int,  name="updateText")
    def __init__(self):
        QtCore.QThread.__init__(self)
        self.input = None
        self.command = None
        self.index = None
        self.prefix = None
        
    def __del__(self):
        self.wait()
        
    def run(self):
        self.input.userEditable = False
        self.input.setEnabled(False)
        p = Popen(self.command,  shell=True, stdout=PIPE)
        while True:
            line = p.stdout.readline()
            if not line:
                break
            self.updateText.emit(line,  self.index)
            self.input.update()
            #self.terminal.write(line,  self.index)
        
        self.input.setEnabled(True)
        self.updateText.emit(self.prefix,  self.index)
Das problem ist wenn ich bei meinem fenster den ping signal ausführe wird der fensterinhalt erst nach dem ausführe geadded, obwohl die einzelnen zeilen ja schon vorher emitiert werden.

Code: Alles auswählen

    def execute(self,  command, index=None):
        if index is None:
            index = self.tabWidget.currentIndex()
        t = SThread()
        t.command = command
        t.index = index
        t.input = self.textEdits[index]
        t.updateText.connect(self.write)
        t.prefix = self.getPrefix()
        t.start()
        print "asdf"
Woran liegt das, dass der fensterinhalt nicht sofort geupdated wird?
lunar

@init-0: Vorneweg: Mit "QProcess" kannst Du Dir den Thread für die Ausführung des Programms sparen. "QProcess" führt das Programm automatisch im Hintergrund aus, und sendet Signale, wenn das Programm Ausgaben auf der Standardausgabe tätigt (Beispiel).

Das eigentliche Problem ist aber der Puffer der Pipe zwischen Deinem Programm und dem Unterprozess. Im Terminal wird zeilenweise gepuffert, so dass neue Ausgaben auf der Standardausgabe sofort erscheinen, bei Unterprozessen dagegen blockweise. ".readline()" kehrt also nicht unmittelbar zurück, sobald der Prozess eine Zeile geschrieben hat, sondern erst nachdem eine bestimmte Anzahl Bytes geschrieben wurde. Bei Beenden des Unterprozesses wird der Puffer der Pipe vollständig geleert, so dass alle verbleibenden Daten auf einmal an Dein Programm übergeben werden.

Ob Du das auf einfache Weise umgehen kannst, weiß ich nicht, da ich mich mit Windows in dieser Hinsicht nicht auskenne. Im Allgemeinen kannst Du "subprocess" und "QProcess" zwar die Größe des Puffers mitgeben, doch das betrifft nur Deine Seite der Pipe. Darauf, wie das aufgerufene Programm puffert, hast Du keinen Einfluss.
init-0
User
Beiträge: 38
Registriert: Samstag 22. Januar 2011, 18:46

Danke, ich hab versucht das jetzt weitestgehend so neu zu gestalten, aber bei der ausführung haperts immernoch:

Code: Alles auswählen

    def appendStdout(self, input,  process):
        print "BLAH BLUBB"
        stdout = str(process.readAllStandardOutput()).decode(
            sys.getfilesystemencoding())
        input.append(stdout)
        cursor = input.textCursor()
        cursor.movePosition(QtGui.QTextCursor.End,  QtGui.QTextCursor.MoveAnchor)
        
    def execute(self,  command, index=None):
        if index is None:
            index = self.tabWidget.currentIndex()
        
        self.proc = QtCore.QProcess()
        self.proc.readyReadStandardOutput.connect(lambda: self.appendStdout(self.textEdits[index],  self.proc))
        print "executing",  command
        self.proc.start("cmd /c \"%s\"" %(command.replace("\"",  "\\\"")))
        print "asdf"
Gibt mir immer

Code: Alles auswählen

StdErr: QProcess: Destroyed while process is still running.
zurück
Das erscheint mir nicht so ganz logisch da der prozess ja in self.proc gespeichert wird und eigentlich nicht von gc gelöscht werden sollte.
Was könnte sonst die ursache sein?
lunar

Nun, die Meldung ist doch recht eindeutig: Du zerstörst das Prozess-Objekt, während der Prozess noch läuft. Wodurch das ausgelöst wird, kann ich Dir ohne lauffähigen, vollständigen Quelltext, mit dem sich das Problem reproduzieren lässt, nicht sagen. Mag sein, dass Dir nur ein "self.proc.waitForFinished()" im "closeEvent()" des Hauptfensters fehlt.

Den "lambda"-Ausdruck im Argument zu ".readyReadStandardOutput.connect()" würde ich durch "functools.partial()" ersetzen. Lambdas als Slots sind fehleranfällig für Fehler, da man damit schnell versehentlich Closures erzeugt.
init-0
User
Beiträge: 38
Registriert: Samstag 22. Januar 2011, 18:46

Es scheint sogar an dem lambda gelegen zu haben, jetzt wo ich das lambda durch functools ersetzt habe funktioniert alles.
Antworten