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 :

QThread ou Threading?


Sujet :

PyQt Python

  1. #1
    Membre régulier
    Homme Profil pro
    Ingénieur développement de composants
    Inscrit en
    Décembre 2019
    Messages
    113
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 35
    Localisation : France, Haute Savoie (Rhône Alpes)

    Informations professionnelles :
    Activité : Ingénieur développement de composants

    Informations forums :
    Inscription : Décembre 2019
    Messages : 113
    Points : 72
    Points
    72
    Par défaut QThread ou Threading?
    Bonjour,
    je cherche à utiliser les thread dans le but de ne pas figer l'interface lorsqu'une opération lourde est demandée (par exemple un traçage de 1000000 de points sur matplotlib) ou pour laisser la main à l'utilisateur lorsque l'interface ouvre outlook pour proposer un envoie de message.

    en parcourant la doc, j'ai compris qu'il y a deux moyens de gérer les thread: soit utiliser QThread (issu de PyQt5) soit utiliser threading (issu de python lui même).
    du coup j'ai deux interrogations:
    1- lequel choisir de préférence et pourquoi?
    2- dans les deux codes ci dessous, les deux marchent... mais sont ils correctement "écrit"?

    cas utilisant le threading python:

    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
    from matplotlib.backends.backend_qt5agg import (FigureCanvas, NavigationToolbar2QT as NavigationToolbar)
    from matplotlib.figure import Figure
    from matplotlib.pyplot import * 
    from scipy import stats
    from pathlib import Path
    from PyQt5.QtCore import *
    from datetime import date
    import csv
    import time
    import threading
     
    from mise_en_forme_user_interface import *
    from creation_widgets import *
     
    import pandas as pd
    import numpy as np
    import sys, os
     
    def update(self):
        print("mon premier thread")
        time_values= [i for i in range(1000000)]
        voltage_values=np.random.randn(1000000)
        self.graphique.plot(time_values, voltage_values, label="e")
        self.graphique.set_xlabel("Temps (s)", fontsize = 14)
        self.graphique.set_ylabel("Tension (V)", fontsize = 14)
        self.graphique.set_title("Mesures Brutes", fontsize = 14)
        self.canvas.draw()     
     
    class Fenetre_Principale(QMainWindow):
        def __init__(self):
            QMainWindow.__init__(self)
            self.fenetre_widget = QWidget()
     
            self.liste_x=["1","2","3"]
            self.liste_y=["a","b","c"]
     
            #Boutons
            self.bouton_charger_data = Bouton_simple(self, longueur=None, hauteur=None, texte="Charger Data")
            self.tracer_data = Bouton_simple(self, longueur=None, hauteur=None, texte="Tracer Data")
     
            #Labels (self, texte, style, nomDeLObjet, longueur, hauteur, icone, icone_name, back_color, ui):
            self.label_x = creation_Label(texte="Axe x:", taille_texte=12, couleur_texte="black", alignement=None, style="normal", locked=False, longueur=None, hauteur=None, border_color="rgb(87,117,131)", border_radius=None, icone=False, icone_name=None, back_color="transparent", ui=self)
            self.label_y = creation_Label(texte="Axe y:", taille_texte=12, couleur_texte="black", alignement=None, style="normal", locked=False, longueur=None, hauteur=None, icone=False, border_color=None, border_radius=None, icone_name=None, back_color="transparent", ui=self)
            self.label_separateur = creation_Label(texte="Séparateur:", taille_texte=14, couleur_texte="black", alignement=None, style="normal", locked=False, longueur=None, hauteur=None, border_color=None, border_radius=None, icone=False, icone_name=None, back_color="transparent", ui=self)
            self.label_decimal = creation_Label(texte="Decimale:", taille_texte=14, couleur_texte="black", alignement=None, style="normal",locked=False, longueur=None, hauteur=None,border_color=None, border_radius=None, icone=False, icone_name=None, back_color="transparent", ui=self)
     
            self.edit_separateur = creation_ligne_texte(self, texte=None, taille_texte=16, coul_texte="rgb(87,117,131)", style_texte="normal", nom=None, longueur=None, hauteur=None, read_only=False)
            self.edit_decimal = creation_ligne_texte(self, texte=None, taille_texte=16, coul_texte="rgb(87,117,131)", style_texte="normal", nom=None, longueur=None, hauteur=None, read_only=False)
     
            #Combobox
            self.combobox_axe_x=creation_combobox(self, self.liste_x, taille_police=None, background_color=None, longueur=None, hauteur=None)
            self.combobox_axe_y=creation_combobox(self, self.liste_y, taille_police=None, background_color=None, longueur=None, hauteur=None)
     
            self.combobox_axe_x.setCurrentIndex(-1)
            self.combobox_axe_y.setCurrentIndex(-1)
     
            self.table_donnees=creation_table_b(self, 4, 4)
     
            self.graphique=QWidget()
            self.graph=QWidget()
            self.canvas=FigureCanvas(Figure())        
            self.graphique=self.canvas.figure.subplots(nrows=1, ncols=1)
            self.toolbar=NavigationToolbar(self.canvas, self)
     
            #Layout
            self.layout=construction_fenetre(self)
     
            self.tracer_data.clicked.connect(self.tracer)
            self.showMaximized()
     
        def tracer(self):
            x=threading.Thread(target=update(self))
            x.start()
     
    def main(args):
        appli=QApplication(args)
        fenetre=Fenetre_Principale()
        fenetre.show()
        r=appli.exec_()
        return r
     
    if __name__=="__main__":
        main(sys.argv)
    cas utilisant QThread de PyQt5:

    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
    from PyQt5.QtWidgets import QComboBox, QProgressBar, QFileDialog, QTabWidget, QTextEdit, QMessageBox, QGroupBox, QMainWindow, QWidget, QPushButton, QLabel, QVBoxLayout, QHBoxLayout, QApplication, QGraphicsScene, QGraphicsView, QCheckBox
    from matplotlib.backends.backend_qt5agg import (FigureCanvas, NavigationToolbar2QT as NavigationToolbar)
    from matplotlib.figure import Figure
    from matplotlib.pyplot import * 
    from scipy import stats
    from pathlib import Path
    from PyQt5.QtCore import *
    from datetime import date
    import csv
    import time
    import threading
     
    from mise_en_forme_user_interface import *
    from creation_widgets import *
     
    import pandas as pd
    import numpy as np
    import sys, os
     
    class test(QThread):
        signal = pyqtSignal('PyQt_PyObject')
        def __init__(self, ui):
            QThread.__init__(self)
            self.ui=ui
        def run(self):
            time_values= [i for i in range(1000000)]
            voltage_values=np.random.randn(1000000)
            self.ui.graphique.plot(time_values, voltage_values, label="e")
            self.ui.graphique.set_xlabel("Temps (s)", fontsize = 14)
            self.ui.graphique.set_ylabel("Tension (V)", fontsize = 14)
            self.ui.graphique.set_title("Mesures Brutes", fontsize = 14)
            self.ui.canvas.draw()
     
     
    class Fenetre_Principale(QMainWindow):
        def __init__(self):
            QMainWindow.__init__(self)
            self.fenetre_widget = QWidget()
     
            self.liste_x=["","",""]
            self.liste_y=["","",""]
     
            #Boutons
            self.bouton_charger_data = Bouton_simple(self, longueur=None, hauteur=None, texte="Charger Data")
            self.tracer_data = Bouton_simple(self, longueur=None, hauteur=None, texte="Tracer Data")
     
            #Labels (self, texte, style, nomDeLObjet, longueur, hauteur, icone, icone_name, back_color, ui):
            self.label_x = creation_Label(texte="Axe x:", taille_texte=12, couleur_texte="black", alignement=None, style="normal", locked=False, longueur=None, hauteur=None, border_color="rgb(87,117,131)", border_radius=None, icone=False, icone_name=None, back_color="transparent", ui=self)
            self.label_y = creation_Label(texte="Axe y:", taille_texte=12, couleur_texte="black", alignement=None, style="normal", locked=False, longueur=None, hauteur=None, icone=False, border_color=None, border_radius=None, icone_name=None, back_color="transparent", ui=self)
            self.label_separateur = creation_Label(texte="Séparateur:", taille_texte=14, couleur_texte="black", alignement=None, style="normal", locked=False, longueur=None, hauteur=None, border_color=None, border_radius=None, icone=False, icone_name=None, back_color="transparent", ui=self)
            self.label_decimal = creation_Label(texte="Decimale:", taille_texte=14, couleur_texte="black", alignement=None, style="normal",locked=False, longueur=None, hauteur=None,border_color=None, border_radius=None, icone=False, icone_name=None, back_color="transparent", ui=self)
     
            self.edit_separateur = creation_ligne_texte(self, texte=None, taille_texte=16, coul_texte="rgb(87,117,131)", style_texte="normal", nom=None, longueur=None, hauteur=None, read_only=False)
            self.edit_decimal = creation_ligne_texte(self, texte=None, taille_texte=16, coul_texte="rgb(87,117,131)", style_texte="normal", nom=None, longueur=None, hauteur=None, read_only=False)
            #self.edit_service = creation_ligne_texte(self,texte=None, taille_texte=16, coul_texte="rgb(87,117,131)", style_texte="normal", nom=None, longueur=150, hauteur=30, read_only=False)
            #Combobox
            self.combobox_axe_x=creation_combobox(self, self.liste_x, taille_police=None, background_color=None, longueur=None, hauteur=None)
            self.combobox_axe_y=creation_combobox(self, self.liste_y, taille_police=None, background_color=None, longueur=None, hauteur=None)
     
            self.combobox_axe_x.setCurrentIndex(-1)
            self.combobox_axe_y.setCurrentIndex(-1)
     
            self.table_donnees=creation_table_b(self, 4, 4)
     
            self.graphique=QWidget()
            self.graph=QWidget()
            self.canvas=FigureCanvas(Figure())        
            self.graphique=self.canvas.figure.subplots(nrows=1, ncols=1)
            self.toolbar=NavigationToolbar(self.canvas, self)
     
            #Layout
            self.layout=construction_fenetre(self)
     
            #Connect_fonctions_Boutons
            self.mon_premier_thread=test(self)
            self.tracer_data.clicked.connect(self.tracer)
            self.showMaximized()
     
        def tracer(self):
            self.mon_premier_thread.start()
     
    def main(args):
        appli=QApplication(args)
        fenetre=Fenetre_Principale()
        fenetre.show()
        r=appli.exec_()
        return r
     
    if __name__=="__main__":
        main(sys.argv)
    merci :-)

  2. #2
    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,

    Avec les programmes graphiques PyQt5, j'utilise de préférence QThread, la plupart du temps en le sous-classant, pour 2 raisons:

    - cela permet au thread d'envoyer des signaux, avec si nécessaire des informations, au programme principal. Par exemple pour un téléchargement ou un calcul: mettre à jour une barre de progression et signaler la fin du thread avec le résultat. Les threads sont souvent nécessaires avec les programmes graphiques pour accomplir des tâches longues non-graphiques qui risqueraient de "geler" le graphique.

    - On peut arrêter "sauvagement" un QThread (.terminate()), ce qui n'est pas prévu avec threading. Ce n'est pas recommandé et il faut prendre quelques précautions, mais c'est quelquefois utile.

    A noter que, pour le 1er point, il est possible de continuer à utiliser threading en faisant de l'héritage multiple avec threading et QObject! C'est en effet l'héritage de QObject qui permet d'envoyer des signaux.

    A noter aussi que dans certains cas, il vaut mieux utiliser les processus que les threads afin d'exploiter les CPU multicores actuels. J'utilise alors plutôt le module Python "multiprocessing", mais il faut trouver des astuces pour remplacer la communication par signaux.

    QThread et threading fonctionnent bien tous les 2 dans toutes les applications que j'ai faites, et je n'ai pas poussé plus loin mes comparaisons.
    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

  3. #3
    Membre régulier
    Homme Profil pro
    Ingénieur développement de composants
    Inscrit en
    Décembre 2019
    Messages
    113
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 35
    Localisation : France, Haute Savoie (Rhône Alpes)

    Informations professionnelles :
    Activité : Ingénieur développement de composants

    Informations forums :
    Inscription : Décembre 2019
    Messages : 113
    Points : 72
    Points
    72
    Par défaut
    Bonjour,
    merci pour toutes ces informations!
    je vais m'orienter plutôt sur les QThread (mais sans oublier threading).

    au passage, j'ai eu un souci en essayant de faire entrer la "vraie " procédure dans le run du thread...
    je voulais ouvrir une fenêtre outlook avec un mail prérempli (pour laisser l'utilisateur choisir d'envoyer ou non).
    sans Thread, ça "marche" mais dès que outlook se lance, j'ai l'interface d'utilisateur qui se fige tant que le mail n'est pas envoyé ou que la fenêtre outlook n'est pas fermée.

    Ci dessous j'ai placé les lignes de gestion de l'ouverture d'outlook dans le run du thread...
    Et apparemment c'est le "outlook = win32.Dispatch('outlook.application')" qui ne passe pas... et je ne vois pas pourquoi? parce que ça marche quand je place ces même lignes dans une fonction de ma QmainWindow...

    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
    class thread_mail(QThread):
        def __init__(self, ui):
            QThread.__init__(self)
            self.ui=ui
        def run(self):           
            try:
                outlook = win32.Dispatch('outlook.application')
                mail = outlook.CreateItem(0)
                mail.To = self.ui.liste_destinataire_copie[0] 
                mail.Cc = self.ui.liste_destinataire_copie[1] 
                mail.Subject = 'Demande de Travaux'
                mail.Body = """Bonjour,\n\nVeuillez trouver ci-joint la demande de travaux portant le n°""" + str(self.ui.date_annee)[2:4] + "-" + str(self.ui.numero_dt) + """.\nCordialement\n\n""" + str(self.nom_demandeur)
                pj_a_envoyer=self.ui.edit_pj.toPlainText().split("\n")
                for i in range(0, len(self.liste_pj), 1):
                    attachment= self.ui.liste_pj[i]
                    mail.Attachments.Add(attachment)
                attachment  = r"**************")
                mail.Attachments.Add(attachment)                 
                mail.Display(True)
            except:
                print("pb!")

  4. #4
    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,

    Citation Envoyé par clement_74 Voir le message
    Et apparemment c'est le "outlook = win32.Dispatch('outlook.application')" qui ne passe pas... et je ne vois pas pourquoi? parce que ça marche quand je place ces même lignes dans une fonction de ma QmainWindow...
    Cette question là a à voir avec COM et le threading.
    Elle est expliquée dans la documentation microsoft.
    Et un peu de recherche sur Internet vous montrerait comment résoudre çà avec pywin32.

    - W
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

  5. #5
    Membre régulier
    Homme Profil pro
    Ingénieur développement de composants
    Inscrit en
    Décembre 2019
    Messages
    113
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 35
    Localisation : France, Haute Savoie (Rhône Alpes)

    Informations professionnelles :
    Activité : Ingénieur développement de composants

    Informations forums :
    Inscription : Décembre 2019
    Messages : 113
    Points : 72
    Points
    72
    Par défaut
    Bonjour,
    merci beaucoup pour l'orientation sur l'origine de mon pb!
    en effet, c'était bien un souci de com/Thread

    en parcourant un peu internet, la solution ci dessous semble fonctionner...

    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
    import win32com.client
    import pythoncom
     
    class thread_mail(QThread):
        def __init__(self, ui):
            QThread.__init__(self)
            self.ui=ui
        def run(self):
            pythoncom.CoInitialize()           
            try:
                outlook = win32com.client.Dispatch('outlook.application')
                mail = outlook.CreateItem(0)
                mail.To = self.ui.liste_destinataire_copie[0]
                mail.Cc = self.ui.liste_destinataire_copie[1]
                mail.Subject = 'Demande de Travaux Laboratoire'
                mail.Body = """Bonjour,\n\nVeuillez trouver ci-joint la demande de travaux """ + str(self.ui.nom_demandeur)
                pj_a_envoyer=self.ui.edit_pj.toPlainText().split("\n")
                for i in range(0, len(self.ui.liste_pj), 1):
                    attachment= self.ui.liste_pj[i]
                    print(attachment, type(attachment))
                    mail.Attachments.Add(attachment)
     
                attachment  = r"\\******************" + str("\\") + str(self.ui.ad_save)
                mail.Attachments.Add(attachment)                 
                mail.Display(True)
            except:
                print("pb")

Discussions similaires

  1. [QThread] Probleme lorsque le thread s'execute deux fois
    Par poulecaca dans le forum Multithreading
    Réponses: 15
    Dernier message: 30/12/2008, 18h01
  2. [Thread] Synchronisation d'un QThread avec l'IHM
    Par cfdev dans le forum Multithreading
    Réponses: 12
    Dernier message: 04/06/2008, 00h21
  3. [Thread] Comment faire fonctionner QThread?
    Par Elv13 dans le forum Multithreading
    Réponses: 1
    Dernier message: 07/05/2008, 15h49
  4. [Thread] class QThread
    Par tody1234 dans le forum Multithreading
    Réponses: 5
    Dernier message: 28/04/2008, 09h13
  5. [Thread] QThread et QSocket
    Par G3G3 dans le forum Multithreading
    Réponses: 7
    Dernier message: 30/10/2007, 20h25

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