Konsole umleiten in wxFenster

Plattformunabhängige GUIs mit wxWidgets.
The Hit-Man
User
Beiträge: 407
Registriert: Montag 20. Februar 2006, 18:11
Wohnort: Menden / Sauerland
Kontaktdaten:

Mittwoch 6. August 2008, 08:37

Ich habe hier jetzt 3 Sachen im Forum gelesen, wie man die Ausgabe einer Konsole umleiten kann und bin verwirrt :( Mein Problem ist, ich möchte zum Beispiel das Programm "cdda2wav" von einem wxFenster ausführen und die Ausgaben, des Programms ( einige davon ) auf mein Fenster packen. Das Konsolen Programm gibt einen Prozentwert aus, wie weit es gerade ist, und auch diesen Wert brauche ich in meinem Fenster. Aber wie funktioniert so was genau? Die 3 anderen Threads haben mich nen bischen verwirrt :(
The Hit-Man
User
Beiträge: 407
Registriert: Montag 20. Februar 2006, 18:11
Wohnort: Menden / Sauerland
Kontaktdaten:

Mittwoch 6. August 2008, 12:03

Ich habe das mal so, wie in einem Beispiel, in dem Forum ausprobiert:

Code: Alles auswählen

    def transcode(self, event): # wxGlade: MainWin.<event_handler>
        process = subprocess.Popen('bin\\devdump', shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        process.wait()
        proc_output_stdout = process.stdout.read()
        self.list_box_1.Append (proc_output_stdout)
        event.Skip()
Das externe Programm wird auch gestartet, allerdings wartet mein Programm bis das externe sich beendet hat und ins Listfeld wird auch nicht geschrieben, außer alles Leerzeichen :(
Wieso bekomme ich denn nichts zurück? wenn ich den "dir" einsetze, also mal das externe Programm tausche, zeigt mir der Befehl "dir" auch etwas in meinem Listfeld an.
Weiß jemand rat?
midan23
User
Beiträge: 116
Registriert: Sonntag 21. Mai 2006, 21:41
Wohnort: Müchen
Kontaktdaten:

Mittwoch 6. August 2008, 13:33

Kurze Frage: Hast du auch hier mal nachgeschaut ?
The Hit-Man
User
Beiträge: 407
Registriert: Montag 20. Februar 2006, 18:11
Wohnort: Menden / Sauerland
Kontaktdaten:

Mittwoch 6. August 2008, 14:16

Danke, hatte ich noch nicht gesehen und probiere es gerade mal aus. Also bis jetzt funtzt es noch nicht. Die Ausgaben kommen nur in der Konsole und nicht in mein Textfeld, obwohl ich es genau so gemacht habe, wie in dem Beispiel. Mein komplettes Form freezt. Liegt warscheinlich daran, das ich den Timer noch nicht untergebracht habe.
The Hit-Man
User
Beiträge: 407
Registriert: Montag 20. Februar 2006, 18:11
Wohnort: Menden / Sauerland
Kontaktdaten:

Mittwoch 6. August 2008, 15:00

Also ich bin ratlos. Wenn ich es so mache ( wie auch im Beispiel ), freezt mein Fenster und ich bekomme nichts in mein Textfeld.

Code: Alles auswählen

# -*- coding: iso-8859-15 -*-
# generated by wxGlade 0.6.3 on Wed Aug 06 12:09:01 2008

import wx
import subprocess

# begin wxGlade: dependencies
# end wxGlade

# begin wxGlade: extracode

# end wxGlade

class MainWin(wx.Frame):
    def __init__(self, *args, **kwds):
        # begin wxGlade: MainWin.__init__
        kwds["style"] = wx.DEFAULT_FRAME_STYLE
        wx.Frame.__init__(self, *args, **kwds)
        self.process = None 
        self.timer = wx.Timer(self)
        self.Bind(wx.EVT_TIMER, self.OnTimer, self.timer)
        self.panel_1 = wx.Panel(self, -1)
        self.text_ctrl_1 = wx.TextCtrl(self.panel_1, -1, "")
        self.static_line_1 = wx.StaticLine(self.panel_1, -1)
        self.btn_transcode = wx.Button(self.panel_1, -1, "Umwandeln")

        self.__set_properties()
        self.__do_layout()

        self.Bind(wx.EVT_BUTTON, self.transcode, self.btn_transcode)
        # end wxGlade
        
    def __set_properties(self):
        # begin wxGlade: MainWin.__set_properties
        self.SetTitle("CD2MP3")
        self.text_ctrl_1.SetMinSize((400, 150))
        # end wxGlade

    def __do_layout(self):
        # begin wxGlade: MainWin.__do_layout
        sizer_1 = wx.BoxSizer(wx.VERTICAL)
        sizer_2 = wx.BoxSizer(wx.VERTICAL)
        sizer_3 = wx.BoxSizer(wx.VERTICAL)
        sizer_3.Add(self.text_ctrl_1, 0, wx.ALL, 5)
        sizer_3.Add(self.static_line_1, 0, wx.ALL|wx.EXPAND, 5)
        sizer_3.Add(self.btn_transcode, 0, wx.ALL, 5)
        sizer_2.Add(sizer_3, 0, wx.EXPAND, 0)
        self.panel_1.SetSizer(sizer_2)
        sizer_1.Add(self.panel_1, 1, wx.EXPAND, 0)
        self.SetSizer(sizer_1)
        sizer_1.Fit(self)
        self.Layout()
        self.Centre()
        # end wxGlade

    def transcode(self, event): # wxGlade: MainWin.<event_handler>
        self.timer.Start(10)
        self.process = subprocess.Popen('bin//cdda2wav.exe -L 0', stdout=subprocess.PIPE) 
        event.Skip()
        
    def OnTimer(self, event):
        
        out = self.process.stdout
        line = out.readline()
        if line:
          self.text_ctrl_1.AppendText(line) 
        else:
            self.timer.Stop() 
# end of class MainWin
Was mache ich denn da bloß falsch?
EDIT:
Es muß irgendwas auch am Timer liegen, den mein Event wird gar nicht erst angesprungen :(
EDIT:
Timer funtzt doch, aber line bleibt leer, daher beendet sich der Timer ja :(
mein Stream bleibt einfach leer :(
Benutzeravatar
str1442
User
Beiträge: 520
Registriert: Samstag 31. Mai 2008, 21:13

Mittwoch 6. August 2008, 21:02

wx leitet automatisch die stdout Ausgabe in Textfenster um, wenn welche zur Verfügung stehen.

Dazu musst du dein App Objekt mit True als erstem Argument instanzieren.
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5555
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Oberhofen im Inntal (Tirol)
Kontaktdaten:

Mittwoch 6. August 2008, 21:45

Hallo The Hit-Man!

Suche in der wxPython-Demo nach "Process". Schau dir dort das Beispiel an. Dort wird genau das gemacht was du machen möchtest.

Es gibt noch andere Möglichkeiten. Z.B. könntest du das aufzurufende Programm über einen zweiten Thread starten und immer wenn etwas vom zweiten Thread kommt, dann kannst du es im Hauptthread in einem Fenster anzeigen.

Oder du arbeitest mit einem Timer. Aber die Timer-Lösung ist mir persönlich am unliebsten.

Aber was ein wirklicher Stopper für dein Vorhaben sein kann -- wenn das aufgerufene Programm die Ausgabe cached. Und das ist ziemlich oft der Fall. Dann bekommst du die Ausgabe des Programmes immer nur in größeren Blöcken. -- Leider. Aber das merkst du sofort, wenn du dein Konsolenprogramm über die wxPython-Demo testest.

mfg
Gerold
:-)
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
The Hit-Man
User
Beiträge: 407
Registriert: Montag 20. Februar 2006, 18:11
Wohnort: Menden / Sauerland
Kontaktdaten:

Donnerstag 7. August 2008, 07:40

wx leitet automatisch die stdout Ausgabe in Textfenster um, wenn welche zur Verfügung stehen.

Dazu musst du dein App Objekt mit True als erstem Argument instanzieren.
Verstehe ich nicht so ganz. Woher sollte mein Textfeld denn dann wissen, welche Sachen es annehmen sollte?
Suche in der wxPython-Demo nach "Process". Schau dir dort das Beispiel an. Dort wird genau das gemacht was du machen möchtest.
Ich finde leider nur dieses hier :( http://www.wxwidgets.org/downloads/demos.htm und da finde ich nichts mit Process.
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5555
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Oberhofen im Inntal (Tirol)
Kontaktdaten:

Donnerstag 7. August 2008, 09:38

Hallo The Hit-Man!
The Hit-Man hat geschrieben:
wx leitet automatisch die stdout Ausgabe in Textfenster um, wenn welche zur Verfügung stehen.
Dazu musst du dein App Objekt mit True als erstem Argument instanzieren.
Verstehe ich nicht so ganz. Woher sollte mein Textfeld denn dann wissen, welche Sachen es annehmen sollte?
Das ist keine Lösung für dich. Vergiss es.

The Hit-Man hat geschrieben:
Suche in der wxPython-Demo nach "Process". Schau dir dort das Beispiel an. Dort wird genau das gemacht was du machen möchtest.
Ich finde leider nur dieses hier :( http://www.wxwidgets.org/downloads/demos.htm und da finde ich nichts mit Process.
Bitte nicht wxWidgets mit wxPython verwechseln. ;-)

Ich meinte diese Demo hier:
- http://downloads.sourceforge.net/wxpyth ... .8.8.1.exe (für Windows)
- http://downloads.sourceforge.net/wxpyth ... .1.tar.bz2 (Quellcode)

Zu finden in der Download-Seite von wxPython: http://wxpython.org/download.php (suche dort nach "Demo")

Die wxPython-Demo ist so ziemlich die beste Beispielsammlung für die Arbeit mit wxPython. Wenn ich mal kurz nicht weiß, mit welchem Widget ich etwas anzeigen soll, dann schaue ich mir die wxPython-Demo an. Die wxPython-Demo solltest du fast auswendig können. Denn dann weißt du welche Bausteine du für deine Programme zur Verfügung hast.

mfg
Gerold
:-)
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
The Hit-Man
User
Beiträge: 407
Registriert: Montag 20. Februar 2006, 18:11
Wohnort: Menden / Sauerland
Kontaktdaten:

Donnerstag 7. August 2008, 09:59

Suche in der wxPython-Demo nach "Process". Schau dir dort das Beispiel an. Dort wird genau das gemacht was du machen möchtest.
Habe ich gemacht ;) Da war ja auch nen Demo bei, in dem ich meinen eigenen externen Befehl testen konnte. Hatte mal "cdda2wav -L 0" eingetragen. Das lief auch an, allerdings zeigte mir das Ausgabefenster nichts an :( der Task von dem externen Programm lief aber.
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5555
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Oberhofen im Inntal (Tirol)
Kontaktdaten:

Donnerstag 7. August 2008, 10:15

The Hit-Man hat geschrieben:Hatte mal "cdda2wav -L 0" eingetragen.
Hallo Hit-Man!

Probiere es mal mit dem zusätzlichen Parameter ``--gui``.

mfg
Gerold
:-)
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
The Hit-Man
User
Beiträge: 407
Registriert: Montag 20. Februar 2006, 18:11
Wohnort: Menden / Sauerland
Kontaktdaten:

Donnerstag 7. August 2008, 10:21

Leider genau das gleiche. Das Augabefenster bleibt leider leer :(
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5555
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Oberhofen im Inntal (Tirol)
Kontaktdaten:

Donnerstag 7. August 2008, 11:02

The Hit-Man hat geschrieben:Leider genau das gleiche. Das Augabefenster bleibt leider leer :(
Hallo Hit-Man!

Ändere die Funktion "OnSendText" mal so ab:

Code: Alles auswählen

    def OnSendText(self, evt):
        text = self.inp.GetValue()
        self.inp.SetValue('')
        self.log.write('OnSendText: "%s"\n' % text)
        self.process.GetOutputStream().write(text + '\n')
        self.process.GetErrorStream().write(text + '\n')
        self.inp.SetFocus()
Und probiere es noch einmal.

mfg
Gerold
:-)
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
The Hit-Man
User
Beiträge: 407
Registriert: Montag 20. Februar 2006, 18:11
Wohnort: Menden / Sauerland
Kontaktdaten:

Donnerstag 7. August 2008, 11:47

ne, leider auch nichts :( Es soll doch so laufen. Oben in der Komandozeile gebe ich meinen Befehl ein. Diesen starte ich dann mit Execute. Dann sollten doch die Ausgaben im Ausgabenfenster erscheinen? Da kommt aber leider nichts. Wenn ich cdda2wav in der Konsole starte, kommt alles wie es sein sollte, aber nicht in dem Ausgabefenster. KAnn mir nicht vorstellen, das das nicht gehen sollte, da ja extra der --gui Parameter in dem Programm existiert.
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5555
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Oberhofen im Inntal (Tirol)
Kontaktdaten:

Donnerstag 7. August 2008, 12:03

The Hit-Man hat geschrieben:ne, leider auch nichts
Hallo Hit-Man!

Gut, dann die Thread-Methode:

Code: Alles auswählen

##!/usr/bin/env python
# -*- coding: iso-8859-15 -*-

import os
import wx
import threading
import subprocess

wx.SetDefaultPyEncoding("iso-8859-15")


class WorkerThread(threading.Thread):
    
    def __init__(self, statuslog_function):
        threading.Thread.__init__(self)
        self.statuslog_function = statuslog_function
    
    
    def run(self):
        args = ["dir", os.path.expandvars("$WINDIR")]
        process = subprocess.Popen(
            args, stdout = subprocess.PIPE, stderr = subprocess.STDOUT
        )
        for line in process.stdout:
            self.statuslog_function(line)


class MyFrame(wx.Frame):
    
    def __init__(
        self, parent = None, title = "Example", size = wx.Size(550, 420)
    ):
        wx.Frame.__init__(self, parent, -1, title, size = size)
        
        panel = wx.Panel(self)
        
        vbox_main = wx.BoxSizer(wx.VERTICAL)
        panel.SetSizer(vbox_main)
        
        self.textctrl = wx.TextCtrl(panel, style = wx.TE_MULTILINE)
        vbox_main.Add(self.textctrl, 1, wx.EXPAND | wx.ALL, 5)
        
        btn_start = wx.Button(panel, label = u"Start")
        vbox_main.Add(btn_start, 0, wx.ALL | wx.ALIGN_CENTER_HORIZONTAL, 5)
        btn_start.Bind(wx.EVT_BUTTON, self.start_thread)
    
    
    def statuslog_function(self, logentry):
        wx.CallAfter(self.textctrl.AppendText, logentry)
    
    
    def start_thread(self, event = None):
        self.workerthread = WorkerThread(self.statuslog_function)
        self.workerthread.start()


def main():
    """Testing"""
    app = wx.PySimpleApp()
    f = MyFrame()
    f.Center()
    f.Show()
    app.MainLoop()


if __name__ == "__main__":
    main()
Für dein Programm müsste die args-Zeile in etwa so aussehen:

Code: Alles auswählen

args = ["<pfad zu cdda2wav>", "-L", "0", "--gui"]
mfg
Gerold
:-)
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
Antworten