2009-01-30 3 views
8

in meinem Python-Programm nicht aktualisiert, um eine Datei ins Internet zu laden, verwende ich eine GTK Fortschrittsbalken, um den Upload-Fortschritt anzuzeigen. Aber die Probleme, denen man gegenübersteht, sind, dass der Fortschrittsbalken keine Aktivität zeigt, bis der Upload abgeschlossen ist, und dann zeigt er abrupt an, dass der Upload abgeschlossen ist. Ich benutze pycurl, um die HTTP-Anfragen zu machen ... meine Frage ist - Muss ich eine Multithread-Anwendung haben, um die Datei hochzuladen und gleichzeitig die GUI zu aktualisieren? Oder gibt es einen anderen Fehler, den ich mache?Fortschrittsbalken, der während des Betriebs

Vielen Dank im Voraus!

Antwort

12

Ich werde die PyGTK FAQ zitieren:

Sie haben einen Fortschrittsbalken in einem Fenster erstellt, dann loslaufen Sie eine Schleife, die etwas Arbeit tut:

Sie werden feststellen, dass das Fenster nicht einmal angezeigt wird, oder wenn der Fortschrittsbalken bleibt bis zum Ende der eingefroren Aufgabe. Die Erklärung ist einfach: gtk ist ereignisgesteuert, und Sie stehlen die Kontrolle von der gtk-Hauptschleife weg und verhindern damit, dass normale GUI-Update-Ereignisse verarbeitet werden.

Die einfachste Lösung besteht darin, auf vorübergehend geben die Kontrolle zurück jedes Mal gtk der Fortschritt geändert wird:

while work_left: 
    ...do something... 
    progressbar.set_fraction(...) 
    while gtk.events_pending(): 
     gtk.main_iteration() 

Beachten Sie, dass mit dieser Lösung kann der Benutzer die Anwendung nicht beenden können (gtk.main_quit würde funktioniert nicht wegen der neuen Schleife [gtk.main_iteration()]) bis Ihre heavy_work erledigt ist.

Eine andere Lösung besteht darin, gtk-Leerlauffunktionen zu verwenden, die von der gtk-Hauptschleife aufgerufen werden, wenn sie nichts zu tun hat. Daher hat gtk die Kontrolle und die Idle-Funktion muss etwas arbeiten. Es sollte True zurückgeben, wenn mehr Arbeit zu erledigen ist, andernfalls False.

Die beste Lösung (es hat keine Nachteile) wurde von James Henstridge hingewiesen. Es nutzt die Generatoren von Python als Leerlauffunktionen, um Python automatisch den Zustand für uns zu erhalten. Es geht so:

def my_task(data): 
    ...some work... 
    while heavy_work_needed: 
     ...do heavy work here... 
     progress_label.set_text(data) # here we update parts of UI 
     # there's more work, return True 
     yield True 
    # no more work, return False 
    yield False 

def on_start_my_task_button_click(data): 
    task = my_task(data) 
    gobject.idle_add(task.next) 

Die 'while' oben ist nur ein Beispiel. Die einzigen Regeln sind, dass es nach etwas Arbeit True ergeben sollte, und es gibt mehr Arbeit zu tun, und es muss False ergeben, wenn die Aufgabe erledigt ist.

1

Mehr als wahrscheinlich ist das Problem, dass in Ihrem Fortschritt Rückruf, wo ich vermutet, dass Sie den Fortschrittsbalken aktualisieren, Sie nicht einen Aufruf zur manuellen Aktualisierung der Anzeige, d. H. Durchlaufen die Ereignisschleife der GUI. Dies ist jedoch nur Spekulation, wenn Sie mehr Code bereitstellen können, könnte es einfacher sein, es weiter einzugrenzen.

Der Grund, warum Sie die Anzeige manuell aktualisieren müssen, liegt daran, dass Ihr Hauptthread auch den Upload durchführt, wo er blockiert.

0

In Python 2.x Integer-Operanden ergeben Integer-Division. Versuchen Sie folgendes:

#Callback function invoked when download/upload has progress 
def progress(download_t, download_d, upload_t, upload_d): 
    print 'in fileupload progress' 
    mainwin.mainw.prog_bar.set_fraction(float(upload_d)/upload_t) 
0

Ja, müssen Sie wahrscheinlich Gleichzeitigkeit, und ja Threads sind ein Ansatz, aber wenn Sie Threads verwenden, laden Sie bitte eine Methode wie diese verwenden: http://unpythonic.blogspot.com/2007/08/using-threads-in-pygtk.html das wird abstrahieren die Schmerzen, und ermöglicht es Ihnen, auf die konzentrieren wichtige Aspekte.

(Ich habe nicht alles in diesem Blogbeitrag durch Faulheit, folglich Gemeinschaftswiki wiederholt).

0

Eine Option, wenn Sie nicht mit pycurl verheiratet sind, ist die Verwendung von GObject IO Watchers.

http://pygtk.org/pygtk2reference/gobject-functions.html#function-gobject--io-add-watch

diese verwenden können Sie den Datei-Upload mit der normalen PyGTK Ereignisschleife verschachteln, und auch in Ihrem IO das set_progress Telefonieren Rückruf beobachten. Wenn Sie die gesamte Arbeit für das Hochladen auf pycurl auslagern, ist das nicht wirklich machbar, aber wenn Sie nur eine Datei über HTTP hochladen, wird io_add_watch dafür sorgen, dass ein Socket für diese auch weniger schmerzhaft ist.