glib.io_add_watch Problem

Programmierung für GNOME und GTK+, GUI-Erstellung mit Glade.
Antworten
Gremlin
User
Beiträge: 166
Registriert: Freitag 28. Mai 2010, 23:49

Hallo zusammen,

Hab hier ein "kleines" Problem dass meine Fähigkeiten etwas zu sehr ausreizt, oder vielleicht ist es auch die Geduld, wer weiß...
Mein Vorhaben:

Ich habe ein GUI mit wxPython erstellt. Das startet einen anderen Prozess (7zip). Während dieser Prozess läuft, soll das GUI eine Progressbar anzeigen. Die Progressbar soll anhand der Zeilen die der Prozess an stdout sendet gesteuert werden.

So weit so gut. GUI läuft, ProgressBar ist da.. naja, und dann ist gar nichts mehr gut.
Den Prozess starte ich mit subprocess und stdout sowie stderr landen in einer "pipe". Da aber dadurch erst alles in ein Fileobject geschrieben wird, und dann erst wieder verarbeitet werden kann, war das halt leider nichts mit der Progressbar. Wär ja auch zu schön gewesen.

Also hab ich mich versucht mit unserem allerbesten Freund Google "schlau" zu machen und bin beim gtk+ gelandet. Um genau zu sein, beim glib Modul. Oder um noch genauer zu sein, bei der Funktion glib.io_add_watch. Nachdem ich mich (letztendlich) erfolgreich mit der Installation von diesem "Zeug" rumgeschlagen hab, bin ich nun soweit:

Code: Alles auswählen

def callback(fd, con):
	if con == glib.IO_IN:
		print fd.read()
		return True
	else:
		print 'ende'
		return False

p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, startupinfo=info)
glib.io_add_watch(p.stdout, glib.IO_IN, callback)
test = p.wait()
Aber das Ergebnis lautet leider wie folgt:
(python.exe:2196): GLib-WARNING **: g_io_channel_unix_new: 3 is neither a file d
escriptor or a socket.

(python.exe:2196): GLib-CRITICAL **: g_io_add_watch_full: assertion `channel !=
NULL' failed

(python.exe:2196): GLib-CRITICAL **: g_io_channel_unref: assertion `channel != N
ULL' failed
Was darf ich darunter verstehen?

Ich habe mich inzwischen tot gesucht, hab aber nur lauter Zeug gefunden von dem ich nur die Hälfte verstehe (Ist mein erstes pythonprog mit gui, und mit Prozessen hatte ich bisher auch nur bedingt zu tun) oder es einfach nichts mit python zu tun hat.

Falls es wichtig ist, und ich glaube das ist es..: Mein System ist Win 7 Prof x64, Python x86 2.6.5, wxPython x86 2.8, pycairo 1.8.6, pygobject 2.14.2-2/2.20.0, pygtk 2.12.1-3, gtk+ bundle 2.16.6

Ich hoffe mir kann in diesem Fall geholfen werden, ansonsten werden sich meine Benutzer mit einer "Sanduhr" begnügen müssen :(


PS: Und bevor jemand fragt, zuerst wollte ich mich ins gtk+ einarbeiten, habs dann aber aufgrund der Kra.. äh, Installation sein gelassen und mich für wxPython entschieden.
ms4py
User
Beiträge: 1178
Registriert: Montag 19. Januar 2009, 09:37

Also, zuerst mal eins: Die Installation von PyGTK unter Windows ist wirklich null Problem und ausführlich dokumentiert.

Dann zu deinem Problem: AFAIK funktioniert `io_add_watch` unter Windows nicht, steht allerdings nicht in der Doku... Bin jedenfalls auch schon mal daran gescheitert.

Die Frage ist nur, warum es ein Problem für dich ist mit der Pipe. IMHO müsstest du problemlos den stdout (byte-weise) lesen können. (Also z.B. proc.stdout.read(1) für 1 Byte, nur read() wird vermutlich blockierend sein).
„Lieber von den Richtigen kritisiert als von den Falschen gelobt werden.“
Gerhard Kocher

http://ms4py.org/
Benutzeravatar
Trundle
User
Beiträge: 591
Registriert: Dienstag 3. Juli 2007, 16:45

@ms4py: Äh nein, das stimmt so nicht. `io_add_watch()` funktioniert unter Windows nur nicht mit jedem beliebigem FD, was im Wesentlichen daran liegt, dass `select()` unter Windows nur mit Sockets funktioniert. Wobei `io_add_watch()` auch unter Windows mehr als nur Sockets kann (es wird einfach ein Thread gestartet, in dem ein blockierendes `read()` aufgerufen wird), nur wird das eben nur für Handles funktionieren, die auch mit PyGtk erstellt wurden.

Ich würde es einfach mal mit `glib.spawn_async()` probieren, das liefert dann einen FD zurück, den man dann hoffentlich mit `io_add_watch()` verwenden kann. Zum Lesen muss man dann einen `glib.IOChannel()` daraus erstellen (einfach den FD übergeben), damit man dann die `read()`-Methode davon verwenden kann. Das ist wichtig, denn `os.read()` funktioniert nicht, da `io_add_watch()` wie bereits gesagt unter Windows selbst durch ein `read()` implementiert wurde.
"Der Dumme erwartet viel. Der Denkende sagt wenig." ("Herr Keuner" -- Bertolt Brecht)
Gremlin
User
Beiträge: 166
Registriert: Freitag 28. Mai 2010, 23:49

ms4py hat geschrieben:Die Frage ist nur, warum es ein Problem für dich ist mit der Pipe. IMHO müsstest du problemlos den stdout (byte-weise) lesen können. (Also z.B. proc.stdout.read(1) für 1 Byte, nur read() wird vermutlich blockierend sein).
Tja, wäre für mich auch kein Problem gewesen, wenn ich das mit dem "byte-weise" vorher mal gehört hätte. :oops:
Und was bedeutet eigentlich "blockierend"? Was wird blockiert?
Trundle hat geschrieben:Ich würde es einfach mal mit `glib.spawn_async()` probieren, ...
Danke für den Hinweis, aber der Vorschlag von ms4py entspricht (im Moment) eher meiner (simplen) Vorstellung.
Mal abgesehen von der Sache mit "blockierend". Hänge nämlich grade daran, was ich in der while schleife machen soll wenns über ne längere zeit nix neues zu lesen gibt.

Code: Alles auswählen

bla = ''
while True:
	wx.MilliSleep(5)
	c = p.stdout.read(1)

	if not line:
		break

	if c == '\n':
		print bla
		bla = ''
	else:
		bla = bla + c
Wenn 7zip eine größere Datei entpackt, wird ja über eine längere Zeit nichts neues gesendet. Und an solchen Stellen tut sich dann nichts mehr, erst einige Zeit später (da ist diese Datei aber schon längst fertig) gehts weiter.

Was passiert da? Bzw. wenn da gar nichts passiert, wie sag ich das der Schleife? ('' und None kanns ja nicht sein, sonst wär die schleife ja zu ende)
Oder ist das jetzt der Zeitpunkt den Vorschlag von Trundle anzugehen? :roll:
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Gremlin hat geschrieben:Mal abgesehen von der Sache mit "blockierend". Hänge nämlich grade daran, was ich in der while schleife machen soll wenns über ne längere zeit nix neues zu lesen gibt.
Spätenstens hier hier hättest du aber darüber fallen müssen, was blockierend bedeutet ;-)
Das Leben ist wie ein Tennisball.
Dav1d
User
Beiträge: 1437
Registriert: Donnerstag 30. Juli 2009, 12:03
Kontaktdaten:

Es gibt auch noch .readline(), mehr hier: http://blog.dav1d.de/code/subprocess-un ... us-stdout/
the more they change the more they stay the same
Gremlin
User
Beiträge: 166
Registriert: Freitag 28. Mai 2010, 23:49

Dav1d hat geschrieben:Es gibt auch noch .readline()
Das war mein allererster Versuch, selbes Problem. Jedenfalls mit 7zip.
EyDu hat geschrieben:Spätenstens hier hier hättest du aber darüber fallen müssen, was blockierend bedeutet
Ääääh, ja, naja, so im Nachhinein... :lol:


Ich versuchs jetzt mal wieder mit glib.io_add_watch. Mal sehen obs diesmal was wird, mit den Hinweisen von Trundle..


Edit:
Hm.. Fehlermeldung krieg ich jetzt keine mehr, aber ein Prozess wird auch nicht gestartet.

Code: Alles auswählen

import glib, os

def callback(fd, con):
	print 'bla'
	print fd
	print con

p = os.getcwd() + '\\tools\\gslist.exe'
a = '-I 78.46.75.230 2312'

id, stdin, stdout, stderr = glib.spawn_async([ p, a ], standard_output=True, standard_error=True)

print id
print stdin
print stdout
print stderr

watch = glib.io_add_watch(stdout, glib.IO_IN|glib.IO_PRI, callback)

print watch
Ergebnis:
0 (id)
None (stdin)
3 (stdout)
5 (stderr)
1 (watch)

Habs auch mal gegen geprüft, die exe findet er.
Ohne Parameter das gleiche.

Was mach ich falsch?
Benutzeravatar
Trundle
User
Beiträge: 591
Registriert: Dienstag 3. Juli 2007, 16:45

Du übergibst alle Parameter für "gslist.exe" als einen anstatt als verschiedene, das mag "gslist.exe" nicht und beschwert sich, allerdings auf `stderr`, was du aber nicht beobachtest.
"Der Dumme erwartet viel. Der Denkende sagt wenig." ("Herr Keuner" -- Bertolt Brecht)
Gremlin
User
Beiträge: 166
Registriert: Freitag 28. Mai 2010, 23:49

Aber selbst dann müsste doch etwas anderes bei ProcessId rauskommen als 0? Oder bin ich da aufm falschen Dampfer?

Mal abgesehen davon, selbst wenn ich die Parameter einzeln angebe, ([ p, a1, a2, a3 ]) tut sich leider nichts. :K

Habs auch mal mit nem anderen Programm versucht, keine Änderung.
Benutzeravatar
Trundle
User
Beiträge: 591
Registriert: Dienstag 3. Juli 2007, 16:45

Die Dokumentation zu `glib.spawn_async()` meint dazu: "On Windows, child pid will be returned only if you specified the glib.SPAWN_DO_NOT_REAP_CHILD flag" und zumindest unter Linux wird auch eine Ausnahme geworfen, wenn der Kindprozess nicht gestartet werden konnte (weil beispielsweise das Programm nicht gefunden wurde).

Wobei es allerdings auch nicht weiter verwunderlich ist, dass einfach nichts passiert: Es läuft keine glib-Ereignisschleife (`glib.MainLoop()` zum Beispiel oder eben die von Gtk+).
"Der Dumme erwartet viel. Der Denkende sagt wenig." ("Herr Keuner" -- Bertolt Brecht)
Gremlin
User
Beiträge: 166
Registriert: Freitag 28. Mai 2010, 23:49

Gut, nachdem ich bereits 70% von meinem Gui mit wxPython erstellt habe, hab ich mir jetzt nicht die Mühe gemacht und das ganze umgeschrieben damit ich die glib-Funktionen nutzen kann... Hab einfach mit py2exe nen programm erstellt das mir einen Prozess ausführt und die Ergebnisse in eine Textdatei schreibt. Die wiederum les ich dann zur gleichen Zeit mit dem Gui aus. Ergebnis: Nix wird blockiert und ich hab meine Progressbar, juhu!

Ja, nicht unbedingt eine perfekte Lösung, aber alle anderen Alternativen gingen entweder gar nicht oder nur bedingt mit Windows.

Trotzdem vielen Dank für eure Hilfe :)
lunar

wxWidgets hat doch eine Process-Klasse (iirc wx.Process). Bietet die nicht asynchronen, ereignisgesteuerten Zugriff auf die Pipes?
Dav1d
User
Beiträge: 1437
Registriert: Donnerstag 30. Juli 2009, 12:03
Kontaktdaten:

This allows the program to be (asynchronously) notified about the process termination and also retrieve its exit status which is unavailable from wxExecute() in the case of asynchronous execution.
=> http://docs.wxwidgets.org/stable/wx_wxp ... #wxprocess
the more they change the more they stay the same
Antworten