Problem mit Fenster Update

Fragen zu Tkinter.
Antworten
Krauzi
User
Beiträge: 77
Registriert: Montag 22. Oktober 2007, 18:06
Kontaktdaten:

hihohallo,

Ich habe ein Problem mit diesem Code:

Code: Alles auswählen

WARCRAFT_INSTALLPATH = "D:\Spiele\WarCraft III"
import urllib, os
from Tkinter import *

dlurl = urllib.urlopen( "http://www.getdota.com/download/map/6.61c" ).read( )


linkStart = "<script>document.location.href='"
linkEnd = "';</script>"
link = dlurl[dlurl.find( linkStart ) + len( linkStart ) : dlurl.find( linkEnd, dlurl.find( linkStart ) )]
os.chdir( WARCRAFT_INSTALLPATH + "\Maps\\Download" )
root = Tk( )
text = ''
window = Label( root, text="0%", height = 2, width = 15 )
window.pack(fill=BOTH, expand=1)

def dlProgress( count, blockSize, totalSize ):
    global test
    percent = int( count*blockSize*100/totalSize )
    #sys.stdout.write("%d%%" % percent)
    #sys.stdout.flush( )
    window.config( text = "%d%%" % percent )

def updatewindow( text2 ):
    global text
    if text != text2:
        text = text2
        window.config( text = text2 )

urllib.urlretrieve( link.replace( " ", "%20" ), "DotA Allstars v6.61c.w3x", reporthook=dlProgress )
root.mainloop()
So bekomme ich nur ein Fenster, wenn der Download bereits abgeschlossen ist. Wenn ich allerdings die oberen sys befehle un kommentiere, und das ganze mit idle bearbeite, und dann auch gleich mit F5 (Run module) ausführen lasse, klappt alles.

Aber wenn ich die beiden zeilen kommentiert lasse, kommt auch hier nur ein 100% fenster, wenn der download bereits abgeschlossen ist.

Was habe ich vergessen?
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Hallo!

Mit "klappt alles" meinst du wahrscheinlich, dass die Ergebnisse auf der Konsole ausgegeben werden, aber kein Fenster zu sehen ist. Das liegt daran, dass die Befehle nach einander abgearbeitet werden. Erst wenn Zeile 30 fertig ist wird also Zeile 31 ausgeführt. Mit Threads und der "after"-Methode von Tkinter-Objekten kannst du das lösen. Ich habe aber vorher noch ein paar Tipps welche du beachten solltest, bevor du das Problem weiter angehst:

- Wenn du Pfade angibst, dann benutze raw-Strings. In deinem Fall (Zeile 1) hast du Glück, dass es funktioniert, da weder "\S" noch "\W" eine besondere Sequen (Escape-Sequenz) im String darstellen. Also besser:
WARCRAFT_INSTALLPATH = r"D:\Spiele\WarCraft III"

- Mache keine *-Importe. Auch wenn es in vielen Beispielen so gezeigt wird. Lieber ein "import Tkinter as tk" und den Modulinhalt dann qualifiziert über "tk.Label" ansprechen. Ein *-Import ist vielleicht auf den ersten Blick einfacher, bringt aber Unübersichtlichkeit und Namenskollisionen mit sich.

- Schreibe keinen Programmcode auf Modulebene. Das Programm solltest du in eine main-Funktion packen, damit du dein Modul später importieren kannst. Die Main Funktion solltest du am Ende der Datei so aufrufen:

Code: Alles auswählen

if __name__=="__main__":
    main()
- Wirf mal einen Blick in PEP 8 bezüglich der Namen. Auch solltest du vernünftige Namen vergeben: "text" sagt genau nichts über den Inhalt des Text aus. Der Name sollte eine Aussage darüber machen, was und mit welchem Zweck das entsprechende Objekt dort zu suchen hat. Auch gehören keine Leerzeichen an das linke und das rechte Ende von Funktionsaufrufen. Statt

Code: Alles auswählen

funktion( eins, zwei, drei)
sollte es heißen

Code: Alles auswählen

funktion(eins, zwei, drei)
Das gilt auch bei Definitionen.
Auch sollten die Leerzeichen um die Gleichheitszeichen bei Zuweisungen an die Paramter entfallen:

Code: Alles auswählen

window = Label( root, text="0%", height 2, width=15 )
Bei normalen Zuweisungen bleiben sie aber erhalten!
Die Importe gehören ganz nach oben ans Modul, alles andere kommt danach.

- Benutze mehr Konstanten.

- Geöffnete Verbindungen, wie in Zeile 5, müssen auch wieder geschlossen werden.

- "os.chdir" zu benutzen ist eine schlechte Idee, da du damit globale Variablen veränderst und unter Umständen sehr unübersichtliche Strukturen verwendest. Setze Pfade besser mit "os.path.join" zusammen.

- Deine Suche nach dem Link im Inhalt sollte man noch besser gestalten. Z.B. mit einem der vielen in Python verfügbaren HTML-Parser.

- Ganz wichtig: Benutze keine globalen Variablen. Streiche das Statement "global" aus deinem Python Wortschatz. Die richtigen Lösungen sind Parameter, Rückgabewerte und, wenn das Beides nicht hilft, Klassen.

- Das ersetzen der Leerzeichen in Strings geht viel besser und allgemeiner mit "urllib.quote".

- Es fehlt noch Fehlerbehandlung in deinem Code.

- "print" ist auch eine tolle Erfindung ;-)

Das ist alles, was ich so auf die schnelle gesehen habe.

Sebastian
Das Leben ist wie ein Tennisball.
Krauzi
User
Beiträge: 77
Registriert: Montag 22. Oktober 2007, 18:06
Kontaktdaten:

ähm erst mal danke für die vielen tips.

Mit funktioniert meinte ich, es funktioniert wirklich (also fenster wird geupdatet). Nur das problem ist: Ich brauche die sys befehle ( oder ein print von dem aktuellen fortschritt), sonst klappts irgendwie nicht. Und es geht auch nur wenn ich es im bzw über den python idle ausführe
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Das es über Idle funktioniert ist vermutlich nur ein Zufall, da Idle selbst auf Tkinter-Basis läuft. Da kommen öfter solche Probleme auf. Zum Entwickeln solltest du einen vernünftigen Editor verwenden und deine Programme über die Konsole starten. Auf die Ergebnisse kannst du dich dann auch verlassen.
Das Leben ist wie ein Tennisball.
Krauzi
User
Beiträge: 77
Registriert: Montag 22. Oktober 2007, 18:06
Kontaktdaten:

hm dann stellt sich die frage wie ich mein problem (download fortschritts balken) am besten löse.

Eine andere idee wie oben habe ich nicht :-(.
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Die Lösung habe ich dir in meinem ersten Absatz verraten.
Das Leben ist wie ein Tennisball.
Krauzi
User
Beiträge: 77
Registriert: Montag 22. Oktober 2007, 18:06
Kontaktdaten:

hm trotzdem möchte ich wissen was an meinem code falsch ist.
Ich habe nochmal etwas gekürzt und übersichtlicher gestaltet:

Code: Alles auswählen

import urllib
import Tkinter as TK

url = "http://ineedwahwah.com/getdota/eng/DotA%20Allstars%20v6.61.w3x"

w = TK.Tk( )
w.title('Progress')
w.window = TK.Label( master=w, text="0%", height="3", width="30" )
w.window.pack( )

def dlProgress( count, blockSize, totalSize ):
    percent = int( count*blockSize*100/totalSize )
    w.window.config( text = "%d%%" % percent )

urllib.urlretrieve( url, "DotA Allstars v6.61.w3x", dlProgress )
#TK.mainloop( )
yipyip
User
Beiträge: 418
Registriert: Samstag 12. Juli 2008, 01:18

So funktioniert es jedenfalls:

Code: Alles auswählen

import urllib
import Tkinter as tk

url = 'http://ineedwahwah.com/getdota/eng/DotA%20Allstars%20v6.61.w3x'

root = tk.Tk()
root.title('Progress')
svar = tk.StringVar()
svar.set('-')
root.label = tk.Label(root, height=3, width=30, textvariable=svar)
root.label.pack()

def progress(blocks, blocksize, size):
    percent = int(blocks * blocksize * 100 / size)
    info = "%d %d %d -> %d%%" % (blocks, blocksize, size, percent)
    svar.set(info)
    root.update()
    print info

    
u = urllib.urlretrieve(url, 'unique_name', progress)
tk.mainloop()
Da 'urlretrieve()' dafuer sorgt, dass die uebergebene Funktion mehrmals aufgerufen wird, reicht ein einfaches 'update()' fuer 'root'.

:wink:
yipyip
Benutzeravatar
wuf
User
Beiträge: 1529
Registriert: Sonntag 8. Juni 2003, 09:50

Hallo Krauzi

Hier noch deine ergänzte Lösung:

Code: Alles auswählen

import urllib
import Tkinter as tk

url = "http://ineedwahwah.com/getdota/eng/DotA%20Allstars%20v6.61.w3x"

w = tk.Tk( )
w.title('Progress')
w.window = tk.Label(master=w, text="0%", height="3", width="30")
w.window.pack()

def dlProgress(count, blockSize, totalSize):
    percent = int( count*blockSize*100/totalSize)
    w.window.config(text = "%d%%" % percent)
    w.window.update()

urllib.urlretrieve(url, "DotA Allstars v6.61.w3x", dlProgress)

tk.mainloop()
Die Lösungszeile heisst:

Code: Alles auswählen

w.window.update()
Wie schon yipyip herausgefunden hatte.

Ich persönlich würde:
a) TK durch tk ersetzen (Grossschreibung für Konstanten)
b) Nach und vor Klammern keine Leerzeichen einfügen. (Nicht PEP-8 Standard)

Gruss wuf :wink:
Take it easy Mates!
Krauzi
User
Beiträge: 77
Registriert: Montag 22. Oktober 2007, 18:06
Kontaktdaten:

oh man viiiiiiiiiiielen dank. jetzt klappts :D.

Aber wieso ist es so schlimm, wenn ich vor und nach der klammer leerzeichen habe?
Früher habe ich das auch nicht gemacht, aber als ich mich dann mit c++ beschäftigt habe, bin ich auf ein projekt gestoßen (ghost++) in dem das so gemacht wurde (leerzeichen vor/nach klammern). Dann habe ich am projekt was verändert und das beibehalten. Jetzt gefällts mir recht gut und deshalb mach ich das jetzt immer so. Aber wenns ein gutes argument dagegen gibt, lasse ich mich gerne überzeugen.
BlackJack

Es ist ungewöhnlich. Übrigens ja auch in normalen Texten, wo etwas in Klammern gesetzt wird, als auch in der Mathematik. Und damit liest es sich komisch, hält also unnötig auf und lenkt ab.
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Weil sich der Hinweis auf PEP 8 in EyDus langem Text ziemlich verliert, hol ich das mal nach: http://www.python.org/dev/peps/pep-0008/
Antworten