IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

PyQt Python Discussion :

Threads et urlretrieve


Sujet :

PyQt Python

  1. #1
    Membre habitué
    Profil pro
    Inscrit en
    Août 2009
    Messages
    195
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Août 2009
    Messages : 195
    Points : 156
    Points
    156
    Par défaut Threads et urlretrieve
    Bonjour,

    je cherche à réaliser un module permettant de télécharger un fichier.

    Pour le moment, j'ai réalisé une simple fenêtre avec une progressbar et un bouton qui lance un téléchargement.
    Bien entendu, je désire que la progressBar soit mis à jour en fonction de la progression du téléchargement.
    Je suppose que les threads vont servir mais je ne sais pas comment y parvenir.
    Avez vous déjà mis en place ce genre de solution? Merci

  2. #2
    Expert éminent

    Homme Profil pro
    Inscrit en
    Octobre 2008
    Messages
    4 300
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations forums :
    Inscription : Octobre 2008
    Messages : 4 300
    Points : 6 780
    Points
    6 780
    Par défaut
    La solution dépendra du framework graphique.

    Pour ne citer qu'un exemple, avec Qt, il faudra 'sortir' du thread au moyen d'un signal parce que la progressBar est un pixmap et doit être mis à jour dans la main loop.

    Mais je te rassure, ça n'a rien de bien compliqué.

  3. #3
    Membre habitué
    Profil pro
    Inscrit en
    Août 2009
    Messages
    195
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Août 2009
    Messages : 195
    Points : 156
    Points
    156
    Par défaut
    ok, j'utilise beaucoup qt.

    Y a t il un avantage a utiliser Qthread? comment met on en place?

    Pour obtenir la taille en cours du fichier téléchargé, dois je utiliser la classe urllib ou alors dois je faire un getinfo du fichier en téléchargement?

    merci

  4. #4
    Expert éminent

    Homme Profil pro
    Inscrit en
    Octobre 2008
    Messages
    4 300
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations forums :
    Inscription : Octobre 2008
    Messages : 4 300
    Points : 6 780
    Points
    6 780
    Par défaut
    En fait je ne pensais pas spécialement aux QThread, je préfère utiliser python pour tout ce qu'il fait tout seul sans passer par une librairie, mais pour y avoir jeté un oeil, les QThread me semblent aussi simple à mettre en oeuvre.

    Pour le téléchargement, je ne sais pas trop quel est le meilleur moyen d'en connaître l'avancement.

    J'ai fais une interface pour une appli qui download les vidéos de Arte+7 mais ce n'est pas moi qui ait écrit la partie téléchargement, je n'ai fais que l'adapter à Qt avec entre autre un signal dans le thread de téléchargement pour mettre à jour la QProgressBar.

    Si ça peut t'inspirer, le code est ici:
    http://bazaar.launchpad.net/~arte+7r...rder-qt-0.2.1/

    avec:
    arte7recorder.py le main
    arte7_ui.py l'interface
    Catalog.py qui sert à dresser la liste des contenu

  5. #5
    Expert éminent
    Avatar de tyrtamos
    Homme Profil pro
    Retraité
    Inscrit en
    Décembre 2007
    Messages
    4 465
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Var (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Retraité

    Informations forums :
    Inscription : Décembre 2007
    Messages : 4 465
    Points : 9 257
    Points
    9 257
    Billets dans le blog
    6
    Par défaut
    Bonjour,

    Je confirme que la mise en oeuvre des QThread est aussi simple (et quasi identique) aux threads "normaux" de Python. En ce qui me concerne, j'utilise plutôt Qthread avec PyQt4.

    Pour résoudre le pb posé, ce que je sais pas, c'est s'il y a un indicateur de progression du téléchargement qu'on peut interroger périodiquement. Et il faudrait qu'on ait dès le début du téléchargement la valeur maxi.

    Dans ce cas, en plus du QThread qui se charge du téléchargement, on peut utiliser la classe QTimer, en donnant un intervalle de, par exemple, 100 millisecondes, ce timer lancera donc un signal périodique qui déclenchera une méthode de la fenêtre graphique, qui lira la progression, la comparera par rapport au maxi, et mettra à jour la progressBar. J'ai déjà utilisé cela: ça marche très bien.

    En tout cas, il ne faut pas que les threads touchent au graphique: il faut donc systématiquement utiliser les échanges de messages (QtCore.SIGNAL).

    Si tu veux creuser cette solution, dis-le!

    Tyrtamos

    Edit: je viens de regarder urlretrieve de urllib: il peut envoyer, en déclarant une fonction avec reporthook, les infos de progression du téléchargement: on peut donc utiliser cette fonction au lieu du QTimer pour envoyer un message à la fenêtre graphique afin de mettre à jour la progressBar.
    Un expert est une personne qui a fait toutes les erreurs qui peuvent être faites, dans un domaine étroit... (Niels Bohr)
    Mes recettes python: http://www.jpvweb.com

  6. #6
    Expert éminent sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 287
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Manche (Basse Normandie)

    Informations professionnelles :
    Activité : Architecte technique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2008
    Messages : 21 287
    Points : 36 776
    Points
    36 776
    Par défaut
    Salut,

    example utilisant le reporthook d'urlretrieve pour afficher la progression.
    Comme çà ne fait copier que le script courant en "bidon.txt", c'est assez limité.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    from urllib import urlretrieve
     
    def fetch(url, toFileName):
        def reporthook(count, blockSize, totalSize):
            print count, blockSize, totalSize
     
        urlretrieve(url, toFileName, reporthook)
     
    if __name__ == '__main__':
        import os
        url = 'file:///%s' % os.path.abspath(__file__)
        print 'url: ', url
        fetch(url, 'bidon.txt')
    - W
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

  7. #7
    Membre habitué
    Profil pro
    Inscrit en
    Août 2009
    Messages
    195
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Août 2009
    Messages : 195
    Points : 156
    Points
    156
    Par défaut
    En cherchant par ci par là, j'ai pondu un script en recopiant/adaptant sur le web:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
     
    from PyQt4.QtGui  import *
    from PyQt4.QtCore import *
    import sys
    import os
    import urllib2
     
    url=  'http://www.xsolutions.nl/' #'http://www.sedis-logistics.com/eurocompta/'
    filename = '10mb.bin' #'eurocomp.txt'
    path ='D:/temp/'
     
     
    class MainWindow(QMainWindow):
            def __init__(self, parent = None):
                    QMainWindow.__init__(self, parent)
                    self.pbDownload=QPushButton(self.tr("Download"))
                    self.connect(self.pbDownload, SIGNAL("clicked()"), self.download)
                    self.pbCancel=QPushButton(self.tr("Cancel"))
                    self.connect(self.pbCancel, SIGNAL("clicked()"), self.cancel)
                    self.centralwidget = QWidget(self)
                    self.setCentralWidget(self.centralwidget)
                    self.gridLayout = QGridLayout(self.centralwidget)
                    self.gridLayout.addWidget(self.pbDownload,0,0)
                    self.gridLayout.addWidget(self.pbCancel,1,0)
                    self.pBar=QProgressBar()
                    self.gridLayout.addWidget(self.pBar,2,0)
     
     
            def download(self):
                    self.threadDownload = Download(url, path, filename,self)
                    self.threadDownload.start()
     
            def cancel(self):
                    self.threadDownload.interrupt() # Not terminate() !
                    self.threadDownload.wait()
     
    class Download(QThread):
            def __init__(self, url, path, filename, parent = None):
                    QThread.__init__(self, parent)
                    self.parent = parent
                    self.path=path
                    self.url=url
                    self.filename=filename
                    self.interrupted = False # We will have to change this to terminate thread!
     
            def interrupt(self):
                    self.interrupted = True
     
            def run(self):
                    os.chdir(self.path)
                    block_size = 4096
                    i = 0
                    counter = 0
                    temp = urllib2.urlopen(self.url+self.filename)
                    headers = temp.info()
                    size = int(headers['Content-Length'])
                    self.parent.pBar.setMaximum(size)
                    data = open(self.filename, 'wb')
                    print 'SIZE,',size
                    while i < size:
                       if not self.interrupted:
                         data.write(temp.read(block_size))
                         i += block_size
                         counter += 1
                         print i
                         #self.parent.pBar.setValue(i)
                         #self.myreporthook(counter, block_size, size) # you can use reporthook!
                       else:
                         break
                    data.close()
                    temp.close()
                    self.quit() # terminate() too dangerous, now we can use quit() or exit(int).
     
     
    app = QApplication(sys.argv)
    qb = MainWindow()
    qb.show()
     
    sys.exit(app.exec_())
    D'après Tyrtamos, je ne dois pas toucher à la fenêtre dans le thread, ce qui explique peut être mon plantage si j'enleve mon commentaire dans le run: self.parent.pBar.setValue(i)

    comment peut on y remédier? La proposition avec un timer est interessante, mais est ce facile?

    Pour l'exemple de Wiztricks, je n'avais encore jamais vu deux 'def' imbriqués de cette façon! J'ai encore de la marge ..

  8. #8
    Expert éminent

    Homme Profil pro
    Inscrit en
    Octobre 2008
    Messages
    4 300
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations forums :
    Inscription : Octobre 2008
    Messages : 4 300
    Points : 6 780
    Points
    6 780
    Par défaut
    Il faudra que tu enlèves 'self.parent.pBar' du thread

    Je te propose ceci:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     
    class Download(QThread):
            def __init__(self, url, path, filename, parent = None):
                    QThread.__init__(self, parent)
                    self.parent = parent
                    self.path=path
                    self.url=url
                    self.filename=filename
                    self.interrupted = False 
                    self.p_signal = ProgressSignal() # ajoutes ces deux lignes
                    self.p_signal.bind(parent)
    ensuite remplace self.parent.pBar.setValue(i) par ceci:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
                        ...
                         counter += 1
                         print i
                         self.p_signal.value = i
                         self.p_signal.emit_signal()
    Maintenant ta classe signal:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
     
    class ProgressSignal(QtCore.QObject):
        loadProgress = QtCore.pyqtSignal()
     
        def bind(self, ui):
            self.ui = ui
            self.value = None
            self.loadProgress.connect(self.next)
     
        def emit_signal(self):
            self.loadProgress.emit() 
     
        def next(self):
            self.ui.pBar(self.value)
    Si je n'ai pas mélangé des noms à toi avec des noms à moi, ce devrait être bon

  9. #9
    Expert éminent
    Avatar de tyrtamos
    Homme Profil pro
    Retraité
    Inscrit en
    Décembre 2007
    Messages
    4 465
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Var (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Retraité

    Informations forums :
    Inscription : Décembre 2007
    Messages : 4 465
    Points : 9 257
    Points
    9 257
    Billets dans le blog
    6
    Par défaut
    Bonjour,

    Comme le pb m'intéressait, j'ai creusé un peu.

    Voilà un code PyQt4 simplifié, et probablement imparfait, mais qui marche: il télécharge un fichier avec barre de progression et possibilité d'arrêter le téléchargement avant la fin.

    Le thread est nécessaire pour éviter que l'interface graphique ne se fige pendant tout le téléchargement. Par contre, QTimer n'est pas nécessaire, puisque urlretrieve renseigne une méthode de la progression du téléchargement: il suffit que cette méthode envoie l'info à la fenêtre graphique sous forme de message.

    Il est enfin nécessaire à la fin du téléchargement que le thread informe par un message la fenêtre graphique qu'il a terminé.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
     
    #!/usr/bin/python
    # -*- coding: utf-8 -*-
    from __future__ import division
    # Python 2.7
     
    import sys, os
    import time
    import urllib
     
    from PyQt4 import QtCore, QtGui
     
    #############################################################################
    class Monthread(QtCore.QThread):
     
        #========================================================================
        def __init__(self, source, destination, parent=None):
            QtCore.QThread.__init__(self, parent)
            self.source = source
            self.destination = destination
            self.stop = False
     
        #========================================================================
        def run(self):
            # lancement du téléchargement
            try:
                filename, msg = urllib.urlretrieve(self.source, self.destination, 
                                                       reporthook=self.infotelech)
                messagefin = u"Téléchargement terminé"
            except Exception:
                messagefin = u"Téléchargement avorté"
     
            # fin du thread: émission du message de fin
            self.emit(QtCore.SIGNAL("finmonthread(PyQt_PyObject)"), messagefin)
     
        #========================================================================
        def infotelech(self, telechbloc, taillebloc, totalblocs):
            """reçoit les infos de progression du téléchargement"""
            if self.stop:
                raise Exception
     
            # envoie les infos à la fenêtre graphique
            self.emit(QtCore.SIGNAL("infotelech(PyQt_PyObject)"), [telechbloc, taillebloc, totalblocs])
     
        #========================================================================
        def stoptelech(self):
            # permet l'arrêt du téléchargement avant la fin
            self.stop = True
     
    #############################################################################
    class Fenetre(QtGui.QWidget):
     
        #========================================================================
        def __init__(self, parent=None):
            super(Fenetre,self).__init__(parent)
     
            self.depart = QtGui.QPushButton(u"Départ", self)
            self.depart.clicked.connect(self.depart_m)
     
            self.stop = QtGui.QPushButton(u"Stop", self)
            self.stop.clicked.connect(self.stop_m)
     
            self.barre = QtGui.QProgressBar(self)
            self.barre.setMinimum = 0
            self.barre.setMaximum = 100
     
            self.barre.setValue(0)
     
            posit = QtGui.QGridLayout()
            posit.addWidget(self.depart, 0, 0)
            posit.addWidget(self.stop, 1, 0)
            posit.addWidget(self.barre, 2, 0)
            self.setLayout(posit)        
     
            self.monthread = None
     
            self.source = "http://docs.python.org/archives/python-2.7.1-docs-pdf-a4.zip"
            self.destination = "python-2.7.1-docs-pdf-a4.zip"
     
        #========================================================================
        def depart_m(self):
            if self.monthread==None or not self.monthread.isRunning():
                self.barre.setValue(0)
                # démarre le thread
                self.monthread = Monthread(self.source, self.destination)
                self.connect(self.monthread, QtCore.SIGNAL("infotelech(PyQt_PyObject)"), 
                                                                self.majbarre)
                self.connect(self.monthread, QtCore.SIGNAL("finmonthread(PyQt_PyObject)"), 
                                                                self.finmonthread)
                self.monthread.start()
     
        #========================================================================
        def majbarre(self, x):
            """lancé à chaque réception d'info sur la progression du téléchargement"""
            if x[2]>0:
                p = int(x[0]*x[1]/x[2]*100)
                if p>100:
                    p = 100
                self.barre.setValue(p)
                QtCore.QCoreApplication.processEvents() # force le rafraichissement
            else:
                self.barre.setMaximum = 0
     
        #========================================================================
        def finmonthread(self, msg):
            """est lancé quand le thread se termine (normalement ou pas)"""
            QtGui.QMessageBox.information(self,
                u"Téléchargement",
                msg)
     
        #========================================================================
        def stop_m(self):
            """demande l'arrêt du téléchargement avant la fin"""
            if self.monthread!=None and self.monthread.isRunning():
                self.monthread.stoptelech()
     
        #========================================================================
        def closeEvent(self, event):
            self.stop_m()
            event.accept()
     
    #############################################################################
    if __name__ == "__main__":
        app = QtGui.QApplication(sys.argv)
        fen = Fenetre()
        fen.show()
        sys.exit(app.exec_())
    Tyrtamos
    Un expert est une personne qui a fait toutes les erreurs qui peuvent être faites, dans un domaine étroit... (Niels Bohr)
    Mes recettes python: http://www.jpvweb.com

  10. #10
    Membre habitué
    Profil pro
    Inscrit en
    Août 2009
    Messages
    195
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Août 2009
    Messages : 195
    Points : 156
    Points
    156
    Par défaut
    Je vais regarder un peu tout ça et prendre le temps d'analyser/comprendre

    Merci à tous d'avoir pris le temps de m'aider

  11. #11
    Expert éminent
    Avatar de tyrtamos
    Homme Profil pro
    Retraité
    Inscrit en
    Décembre 2007
    Messages
    4 465
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Var (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Retraité

    Informations forums :
    Inscription : Décembre 2007
    Messages : 4 465
    Points : 9 257
    Points
    9 257
    Billets dans le blog
    6
    Par défaut
    Bonjour,

    Comme c'était un truc intéressant, je viens d'en faire un tuto:

    http://python.jpvweb.com/mesrecettes..._fichier_pyqt4

    Le code est un peu plus complet que sur mon dernier message.

    Tyrtamos
    Un expert est une personne qui a fait toutes les erreurs qui peuvent être faites, dans un domaine étroit... (Niels Bohr)
    Mes recettes python: http://www.jpvweb.com

  12. #12
    Membre habitué
    Profil pro
    Inscrit en
    Août 2009
    Messages
    195
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Août 2009
    Messages : 195
    Points : 156
    Points
    156
    Par défaut
    Bonjour,

    Je viens de parcourir le tuto et ça me semble très clair.

    Maintenant, mon fichier que je télécharge est un fichiez zip que je vais extraire via python (2.5 pour moi) et le module zipfile (que je parcours pour le moment) .
    Je suppose que lorsque je vais faire l'extract de chacun des fichiers, mon programme sera figé.
    Je souhaite faire apparaitre une messageBox avec le nom du fichier extrait en cours + son %d'extraction (et peut être le % général)
    Il va falloir travailler encore avec un (des?) thread j'imagine.

  13. #13
    Expert éminent
    Avatar de tyrtamos
    Homme Profil pro
    Retraité
    Inscrit en
    Décembre 2007
    Messages
    4 465
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Var (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Retraité

    Informations forums :
    Inscription : Décembre 2007
    Messages : 4 465
    Points : 9 257
    Points
    9 257
    Billets dans le blog
    6
    Par défaut
    Pour zipfile, il faut utiliser un thread si le temps d'attente devient une gène pour l'utilisateur: au delà de quelques secondes. Mais maintenant, tu connais la méthode!

    Je ne connais pas assez zipfile pour savoir si l'extraction renvoie un indicateur de progression. En l'absence de cet indicateur, tu peux toujours avoir la barre avec la chenille.

    Cependant, je ne sais pas si les messagebox standards supportent une barre de progression. Il faudra probablement créer une petite fenêtre spécifique. Et dans ce cas, c'est cette petite fenêtre qui devra lancer le thread d'extraction et recevoir les infos d'extraction s'il y en a: tu as encore du boulot!

    Tyrtamos
    Un expert est une personne qui a fait toutes les erreurs qui peuvent être faites, dans un domaine étroit... (Niels Bohr)
    Mes recettes python: http://www.jpvweb.com

Discussions similaires

  1. Tri multi-threadé
    Par Tifauv' dans le forum C
    Réponses: 8
    Dernier message: 28/06/2007, 09h00
  2. récupérer la valeur de sortie d'un thread
    Par jakouz dans le forum Langage
    Réponses: 3
    Dernier message: 31/07/2002, 11h28
  3. Programmer des threads
    Par haypo dans le forum C
    Réponses: 6
    Dernier message: 02/07/2002, 13h53
  4. Réponses: 5
    Dernier message: 12/06/2002, 15h12
  5. [Kylix] Pb de Thread !!
    Par Anonymous dans le forum EDI
    Réponses: 1
    Dernier message: 25/04/2002, 13h53

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo