Seite 1 von 1
subprocess statt os
Verfasst: Montag 18. Juni 2007, 17:49
von midan23
Hallo zusammen ...
Ich hätte da ein kleines Problem ... wieder mal ...
In folgendem Code:
Code: Alles auswählen
class MyPanel(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent, wx.ID_ANY)
self.output = wx.TextCtrl(self, wx.ID_ANY, style=wx.TE_MULTILINE)
self.button = wx.Button(self, wx.ID_ANY, "Start")
self.button.Bind(wx.EVT_BUTTON, self.OnClick)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(self.output, 1, wx.EXPAND)
sizer.Add(self.button)
self.SetSizer(sizer)
def OnClick(self, event):
self.output.Value = ""
self.button.Disable()
out = os.popen(COMMAND, "r")
while True:
line = out.readline()
if line:
self.appendText(line)
else:
break
self.button.Enable()
def add_text(self, txt):
self.output.AppendText(txt)
möchte ich
subprocess statt
os verwenden ... ich weiss nur nicht, wie ich das anstellen soll.
Laut
Doku soll man zwar statt
Code: Alles auswählen
pipe = Popen(cmd, shell=True, bufsize=bufsize, stdout=PIPE).stdout
verwenden, aber irgendwie haut das nicht so hin ... es wird nähmlich gar nichts angezeigt ...
Wäre für Anregungen dankbar ...
Verfasst: Montag 18. Juni 2007, 21:46
von midan23
Und selber was gefunden ... aber mit
wx.Process und
wx.Execute:
Code: Alles auswählen
COMMAND = r"/bin/bash test.sh"
class MyPanel(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent, wx.ID_ANY)
self.process = None
self.output = wx.TextCtrl(self, wx.ID_ANY, style = wx.TE_MULTILINE)
self.button = wx.Button(self, wx.ID_ANY, "Start")
self.button.Bind(wx.EVT_BUTTON, self.OnClick)
self.Bind(wx.EVT_END_PROCESS, self.OnProcessEnded)
self.Bind(wx.EVT_IDLE, self.OnIdle)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(self.output, 1, wx.EXPAND)
sizer.Add(self.button)
self.SetSizer(sizer)
def __del__(self):
if self.process is not None:
self.process.Detach()
self.process.CloseOutput()
self.process = None
def OnClick(self, event):
self.output.Value = "Starting '%s'\n" % COMMAND
self.button.Disable()
self.process = wx.Process(self)
self.process.Redirect()
wx.Execute(COMMAND, wx.EXEC_ASYNC, self.process)
def OnProcessEnded(self, event):
self.append_text()
self.process.Destroy()
self.process = None
self.button.Enable()
def OnIdle(self, event):
if self.process is not None:
self.append_text()
def append_text(self):
stream = self.process.GetInputStream()
if stream.CanRead():
self.output.AppendText(stream.read())
Hat etwas länger gedauert ... die Doku schweigt sich über
wx.Process und
wx.Execute aus ... Zum Glück gibt es die Demo, das Beispiel hat sehr geholfen ...
Verfasst: Montag 18. Juni 2007, 23:33
von BlackJack
Vielleicht schweigt sich die Doku darüber aus, weil man lieber die Möglichkeiten der Standardbibliothek benutzen sollte.
Verfasst: Dienstag 19. Juni 2007, 07:07
von midan23
BlackJack hat geschrieben:Vielleicht schweigt sich die Doku darüber aus, weil man lieber die Möglichkeiten der Standardbibliothek benutzen sollte.
Das kann sein ... wenigstens funktioniert es und sieht etwas besser aus als mit
os ...
Mein Ziel bleibt allerdings die Verwendung von
subprocess.
Das heisst: Vorschläge sind willkommen ...
Verfasst: Dienstag 19. Juni 2007, 10:12
von midan23
Kann mir mal jemand erklären, warum folgender Code
Code: Alles auswählen
cmd = r"d:\fg\t.cmd"
process = subprocess.Popen(cmd, stdout=subprocess.PIPE)
out = process.stdout
for line in out.readlines():
self.output.AppendText(line)
interaktiv eingegeben funktioniert, aber als Methode einer von
wx.Panel abgeleiteten Klasse zu diesem Fehler führt:
Code: Alles auswählen
Traceback (most recent call last):
File "C:\Dokumente und Einstellungen\gobin\Desktop\test.pyw", line 79, in <module>
main()
File "C:\Dokumente und Einstellungen\gobin\Desktop\test.pyw", line 75, in main
app = MyApp(filename="error.txt")
File "D:\fg\python\Lib\site-packages\wx-2.8-msw-unicode\wx\_core.py", line 7757, in __init__
self._BootstrapApp()
File "D:\fg\python\Lib\site-packages\wx-2.8-msw-unicode\wx\_core.py", line 7354, in _BootstrapApp
return _core_.PyApp__BootstrapApp(*args, **kwargs)
File "C:\Dokumente und Einstellungen\gobin\Desktop\test.pyw", line 70, in OnInit
frame = MyFrame()
File "C:\Dokumente und Einstellungen\gobin\Desktop\test.pyw", line 65, in __init__
e.execute(r"d:\fg\t.cmd")
File "C:\Dokumente und Einstellungen\gobin\Desktop\test.pyw", line 35, in execute
process = subprocess.Popen(cmd, stdout=subprocess.PIPE)
File "D:\fg\python\lib\subprocess.py", line 586, in __init__
errread, errwrite) = self._get_handles(stdin, stdout, stderr)
File "D:\fg\python\lib\subprocess.py", line 699, in _get_handles
p2cread = self._make_inheritable(p2cread)
File "D:\fg\python\lib\subprocess.py", line 744, in _make_inheritable
DUPLICATE_SAME_ACCESS)
WindowsError: [Error 6] Das Handle ist ungültig
Ich verstehe es zumindest nicht ...
Verfasst: Dienstag 19. Juni 2007, 11:49
von Y0Gi
Da gibt es vermutlich Rangelei bzgl. stdin/-out/-err.
Eine Email scheint das zu bestätigen:
>> >> I get an OSError exception when I try:
>> >>
>> >> prog = subprocess.Popen(command, stdout=subprocess.PIPE)
>> >>
>> >> with the message: '[Error 9] The handle is invalid'
[...]
This is probably the usual subprocess bug when the script runs
in a GUI process (no console) on Windows. The OP should try
to run the unfrozen script with pythonw.exe instead of python.exe;
I assume the same problem occurs then. The problem is that subprocess
tries to duplicate the handles for stdin, stdout, and stderr, and this
fails because the handles are already closed in a gui process.
IIRC the workaround is to connect stdin and stderr also to PIPE in the
subprocess call.
Verfasst: Dienstag 19. Juni 2007, 13:12
von midan23
Kann sein ... aber ich bin auch aus einem anderen Grund mit meiner subprocess-Lösung unzufrieden:
Das Fenster mit der Ausgabe des Befehls erscheint erst, nachdem der Befehl fertig ist ...
Ich möchte aber, das die AUsgaben des Befehls im Fenster erscheinen während der Befehl läuft ...
(Sozusagen als Fortschrittsanzeige ...)
Ist nicht ganz das wahre, ein Programm zu starten und ohne weitere Hinweise erscheint das Fenster erst ein paar Minuten später ...
Verfasst: Donnerstag 21. Juni 2007, 07:10
von midan23
So ... hier mal eine Lösung mit
subprocess:
Code: Alles auswählen
class ExecPanel(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent, wx.ID_ANY)
self.process = None
self.output = wx.TextCtrl(self, wx.ID_ANY, style=wx.TE_MULTILINE|wx.TE_READONLY)
self.Bind(wx.EVT_IDLE, self.OnIdle)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(self.output, 1, wx.EXPAND)
self.SetSizer(sizer)
def execute(self, cmd):
self.output.Value = "Starting '%s'\n" % cmd
self.process = subprocess.Popen(cmd, stdout=subprocess.PIPE)
def OnIdle(self, event):
out = self.process.stdout
line = out.readline()
if line:
self.output.AppendText(line)
Aber wirklich zufrieden bin ich damit auch nicht ...
- Wenn das Fenster inaktiv ist, tut sich gar nichts im Textfeld
- Wenn das Fenster aktiv ist, werden die Zeilen ungefähr im Sekunden-Rythmus ausgegeben
- Wenn ich den Mauszeiger im Fenster bewege (egal ob aktiv oder inaktiv) tut sich richtig was ... aber nur, so lange der Mauszeiger bewegt wird ...
Mir scheint
wx.EVT_IDLE ist der falsche Platz, um die Anzeige zu aktualisieren ...
Was gäbe es denn für Alternativen ?
Verfasst: Donnerstag 21. Juni 2007, 07:45
von veers
In +/- jedem mir bekannten GUI System ist es möglich Timer in die EventQueue des Mainloop einzufügen. Mit wx kenne ich mich leider nicht wirklich aus. Das dürfte aber sein was du suchst:
http://wiki.wxpython.org/Timer
Gruss,
Jonas
Verfasst: Donnerstag 21. Juni 2007, 08:03
von midan23
wx.Timer ... hab ich auch schon drüber nachgedacht ...
Im Vergleich zur wx.EVT_IDLE würde das zwar dafür sorgen, das die Ausgabe regelmässiger und schneller aktualisiert wird, aber so aktuell wie ein separates DOS-Fenster ist es wahrscheinlich nicht ...
Ich versuchs mal ...
Verfasst: Donnerstag 21. Juni 2007, 08:16
von veers
midan23 hat geschrieben:wx.Timer ... hab ich auch schon drüber nachgedacht ...
Im Vergleich zur wx.EVT_IDLE würde das zwar dafür sorgen, das die Ausgabe regelmässiger und schneller aktualisiert wird, aber so aktuell wie ein separates DOS-Fenster ist es wahrscheinlich nicht ...
Ich versuchs mal ...
Hängt vermutlich stark davon ab wie häufig du pollst. Schade? das Wintendo kein select() auf Pipes unterstützt

Ist es mit subprocess möglich mit Synchronem IO zu arbeiten? Wenn ja könntest du das ganze auch mit Threads lösen. Der Thread kann dann wenn eine neue Zeile gelesen wurde diese per wx.PostEvent an den Hauptthread weiterleiten.
Gruss,
Jonas
Verfasst: Donnerstag 21. Juni 2007, 09:21
von midan23
Also, mit
wx.Timer geht es halbwegs:
Ich starte den Timer mit
und alles läuft wie gewünscht.
Noch eine Frage: Reicht es, wenn der Timer so
Code: Alles auswählen
def OnTimer(self, event):
out = self.process.stdout
line = out.readline()
if line:
self.output.AppendText(line)
else:
self.timer.Stop()
gestoppt wird ?