Threading für Anfänger

Python und das Qt-Toolkit, erstellen von GUIs mittels des Qt-Designers.
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

@BlackJack: Ein Blick in meinem Beitrag, und du siehst, dass alles behoben wurde. Das mit dem i habe ich bereits selbst gefunden. Und ich habe die Funktion on_progress hinzugefügt. Also on_start startet den Thread und in on_progress erwarte ich, dass der Prozessbalken sich entsprechend aktualisiert. Tut er aber nicht.
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

Ich weiß zwar nicht ob ich die Lösung korrekt gefunden habe, aber der Prozessbalken bewegt sich jetzt:

Code: Alles auswählen

import os
import requests
import sys

from PyQt4.QtCore import QThread, pyqtSignal, Qt
from PyQt4.QtGui import QVBoxLayout, QPushButton, QDialog, QProgressBar, QApplication

class MyCustomDialog(QDialog):

    def __init__(self, parent=None):
        super(MyCustomDialog, self).__init__(parent)
        layout = QVBoxLayout(self)

        # Create a progress bar and a button and add them to the main layout
        self.progressBar = QProgressBar(self)
        self.progressBar.setAlignment(Qt.AlignCenter)
        #self.progressBar.setValue(0)
        #self.progressBar.setRange(0, 1)
        layout.addWidget(self.progressBar)

        button = QPushButton("Start", self)
        layout.addWidget(button)

        button.clicked.connect(self.check_folder_exists)

        # Set data for download and saving in path
        self.location = os.path.abspath(os.path.join('temp', 'example-app-0.3.win32.zip'))
        self.url = 'http://sophus.bplaced.net/download/example-app-0.3.win32.zip'

        self.download_task = Download_Thread(self.location, self.url)
        self.download_task.notify_progress.connect(self.on_progress)

    def on_progress(self, i):
        self.progressBar.setValue(i)

    def on_start(self):
        self.download_task.start()

    def check_folder_exists(self):
        location = os.path.abspath(os.path.join('temp'))
        if not os.path.exists(location):
            os.makedirs(location)
            print "Folder was created"
            self.on_start()
        else:
            print "Folder already exists"
            self.on_start()

    def onFinished(self):
        # Stop the pulsation
        self.progressBar.setRange(0, 1)


class Download_Thread(QThread):
    finished_thread = pyqtSignal()
    notify_progress = pyqtSignal(int)

    def __init__(self, loc, link):
        QThread.__init__(self)

        self.url = link
        self.location = loc

    def run(self):
        print self.url
        print self.location
        file = requests.get(self.url, stream=True)
        file_size = int(requests.head(self.url).headers.get('content-length', [0]))


        chunk_size = (10000)
        downloaded_bytes = 0
        block_size = 1024*8

        with open(self.location, 'wb') as fd:
            for chunk in file.iter_content(chunk_size):
                fd.write(chunk)
                downloaded_bytes += block_size
                print (float(downloaded_bytes)/file_size*100)
                self.notify_progress.emit(float(downloaded_bytes)/file_size*100)

        print "Finish"
        self.finished_thread.emit()

if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = MyCustomDialog()
    window.resize(600, 400)
    window.show()
    sys.exit(app.exec_())
Ich habe hier Zeile 17 und 18 auskommentiert, und schon macht der Balken was er soll. Aber wie gesagt, ob es nun korrekt ist? Ich bin mir deswegen nicht sicher, weil BlackJack und Sirius3 die ganze Zeit von anderen Problemen sprachen.
BlackJack

@Sophus: Was soll sich denn bei den Werten 0 und 1 auch grossartig bewegen‽ Gibt ja nur zwei mögliche Werte, 0 (der Balken ist ”leer”) und 1 (der Balken ist 100% ”voll”).
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

@BlackJack: Ach, das meintest du vorhin mit Werte? Aber ich habe ein weiteres Problem. Eher ein mathematisches Problem. Wie kriege ich das korrekt auf dem Balken angezeigt, sprich, die herunterzuladende Datei ist sagen wir mal 65 MB groß. Bei meinem Beispiel hält mein Programm immer bei 81 Prozent an. Das heißt, die Datei ist komplett heruntergeladen, und der Prozessbalken müssten dann auch 100 Prozent anzeigen und nicht 81 Prozent?Hättest du da eine Idee?
BlackJack

@Sophus: Ich würde ”mathematisch” gar nichts (selber) machen. Man kann den Wertebereich für den Balken ja beliebig setzen. Zum Beispiel auch auf 0 und die Dateigrösse in Bytes. Und dann als aktuellen Wert einfach die aktuelle Anzahl von Bytes setzen.
Sirius3
User
Beiträge: 17749
Registriert: Sonntag 21. Oktober 2012, 17:20

BlackJack hat geschrieben:Und dann als aktuellen Wert einfach die aktuelle Anzahl von Bytes setzen.
Da ist ja das Problem ;-)
BlackJack

@Sirius3: Wir brauchen dringend ein Smiley für: m) :twisted: OMG…
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

Inwiefern ist das ein Problem? Allerdings bin ich noch nicht weitergekommen mit dem Balken, also dass dies korrekt angezeigt wird :-)
BlackJack

@Sophus: Es ist schlicht falsch was Du da als aktuelle Byteanzahl ermittelst. Wenn die Balkenanzeige nicht stimmt wäre dass doch das erste was mal mal *selber* überprüft: Ob die Werte die man da setzt überhaupt stimmen.
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

@BlackJack: Inwiefern sollten die Bytezahlen nicht stimmen?
BlackJack

@Sophus: Na die stimmen halt nicht. Wie zwei Zahlen halt nicht übereinstimmen können. Die sind nicht gleich. Also sind sie ungleich. Weil der Code nicht das aufaddiert was empfangen/gespeichert wurde. Aber so wirklich *gar nicht*.
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

@BlackJack: Redest du gern in Rätseln? :-) Viel geschrieben, wenig gesagt.

Nun, ich habe mal geschaut, und zwar:

Code: Alles auswählen

        file_size = int(requests.head(self.url).headers.get('content-length', [0]))
        print "%s Byte" %file_size
Ausgabe
65250090 Byte
Die Datei ist also 62,2 MB groß. Und nun nochmal, was soll daran nicht stimmen?
BlackJack

@Sophus: Daran ist nichts falsch. Da wird auch nichts falsches aufaddiert. Da wird nämlich gar nichts addiert. Du hast da aber Code bei dem wird aufaddiert. Und zwar was völlig falsches. Und *das* ist auch der *Wert* der beim Fortschrittsbalken *gesetzt* wird. `file_size` dagegen wird *nicht* als *Wert* beim Fortschrittsbalken *gesetzt*. Ich spreche nicht in Rätseln sondern in Deutsch und IMHO ziemlich eindeutig. Das liegt hier am Empfänger und nicht am Sender.
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

@BlackJack: Wie mal mein alter Latein-Lehrer so schön sagte, wenn die Schüler nichts verstehen, dann hat der Lehrer es nicht richtig erklärt.Weise Worte :-) Es wäre also sehr kurzsichtig alles auf den Empfänger zu schieben. Aber egal. Kommen wir zurück zum Code. Ich habe die ganze Zeit die chunk-Größe missachtet, weil ich davon ausging, dass ich diese Größe nur brauche, damit Python weiß, in welchen Häppchen er die Bytes laden soll, damit der Arbeitsspeicher nicht in einem Rutsch vollgestopft wird. Also habe ich mir gedacht, nimmst du die Chunks dafür:

Code: Alles auswählen

        chunk_size = (8127)
        downloaded_bytes = 0
            
        with open(self.location, 'wb') as fd:
            for chunk in file.iter_content(chunk_size):
                fd.write(chunk)
                downloaded_bytes += chunk_size
                print (float(downloaded_bytes)/file_size*100)
                self.notify_progress.emit(float(downloaded_bytes)/file_size*100)
Ich denke mal, mit deinem Rätsel meintest du es so? Für mein Verständnis habe ich deshalb die Chunks-Größen genommen, weil die Bytes-Häppchen solange geladen werden, bis die Datei vollständig ist. Wenn du das nicht meinst, dann bin ich etwas überfragt.
BlackJack

@Sophus: Ja das liegt wahrscheinlich an mir das Du ziemlich gezielt nichts oder etwas falsch verstehst…

Ich verstehe immer noch nicht wie man auf die Idee kommen kann das wenn man bei einer Methode `chunk_size` angibt, erwarten kann das in jedem Schleifendurchlauf `block_size` Bytes gelesen wurden, wobei `block_size` ansonsten nirgends benutzt oder bekannt gemacht wird. Das ist wohl einfach so ein supermagischer Name. Oder Python ist eine Sprache die nicht das macht was man schreibt sondern das was man meint. Also eine die Gedanken lesen kann… :roll:

Fehlerfrei ist das immer noch nicht. Was Dir eigentlich bei Deiner Testdatei hätte auffallen müssen. Oder mit ein bisschen Nachdenken…
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

@BlackJack: Die Testdatei ist in Ordnung. Beim Entpacken tauchen keinerlei Fehler auf. Oder was genau soll mir da auffallen?
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Sophus hat geschrieben:Hallo Hyperion und BlackJack, danke für die Hinweise, und für das beste Beispiel der Welt, Hyperion. Aber ich habe zu spät bemerkt, dass du mir ein Beispiel präsentierst, ...
Wieso "zu spät"? Das Beispiel rennt ja nicht weg :mrgreen:
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
Sirius3
User
Beiträge: 17749
Registriert: Sonntag 21. Oktober 2012, 17:20

@Sophus: wenn Du Dir diese beiden Zeilen anschaust:

Code: Alles auswählen

               fd.write(chunk)
                downloaded_bytes += chunk_size
was ist da das naheliegendste, die Bytes, die gerade heruntergeladen wurden, zu ermitteln?
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

@Sirius3: Meinst du etwa 'chunk'? Sprichst du hier das Problem an, was BlackJack meinte, von wegen nicht "Fehlerfrei"? Aber was spricht dagegen die chunk_size zu verwenden? Ich meine, die Häppchen werden ja solange heruntergeladen, bis die Datei komplett ist, also 100%. Und der Prozessbalken verhält sich auch "korrekt".

EDIT:

Ich habe mich mit dem 'chunk' auseinandergesetzt, udn wie zu vermuten war, hatte es auch nicht geklappt. Hier die beiden Versionen:

Version 1

Code: Alles auswählen

        with open(self.location, 'wb') as fd:
            for chunk in file.iter_content(chunk_size):
                fd.write(chunk)
                downloaded_bytes += chunk
                print (float(downloaded_bytes)/file_size*100)
                self.notify_progress.emit(float(downloaded_bytes)/file_size*100)
Hier dachte ich, könnte man die heruntergeladenen Bytes (chunk) zu den downloaded_bytes hinzuaddieren. Klappt aber nicht.

Version 2

Code: Alles auswählen

        with open(self.location, 'wb') as fd:
            for chunk in file.iter_content(chunk_size):
                fd.write(chunk)
                self.notify_progress_emit(chunk)
Hier wollte ich nicht addieren, sondern die 'chunks' direkt benutzen.

Beide Versionen funktionieren nicht.
Sirius3
User
Beiträge: 17749
Registriert: Sonntag 21. Oktober 2012, 17:20

@Sophus: Du hast dich nicht auseinandergesetzt, sondern wild herumgeraten.

Ja requests liefert immer exakt chunk_size Bytes (und es ist unglaublich schwierig, ihm das abzugewöhnen), bis auf den letzten Chunk.
Antworten