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

Calcul scientifique Python Discussion :

Mise à jour d’un graphique avec Matplotlib (avec PyQt5) [Python 3.X]


Sujet :

Calcul scientifique Python

  1. #1
    Membre confirmé
    Avatar de Le Farfadet Spatial
    Homme Profil pro
    En cours de précision…
    Inscrit en
    Avril 2008
    Messages
    186
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : En cours de précision…

    Informations forums :
    Inscription : Avril 2008
    Messages : 186
    Points : 604
    Points
    604
    Par défaut Mise à jour d’un graphique avec Matplotlib (avec PyQt5)
    Salut à tous !

    Je travaille sur une application réalisant notamment de la visualisation de données. Pour cela, j’utilise Matplotlib. Parfois, je dois effacer la figure pour en afficher une autre. Dans un tel cas, j’ai un problème : la place de la précédente échelle de couleur (colorbar) reste vide, le tracé de la figure ne se faisant plus dans l’intégralité de la fenêtre. J’ai réalisé un exemple minimum complet montrant mon problème :

    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
    #!/usr/bin/env python3
    # -*- coding: utf-8 -*-
     
    """
    Exemple de mise à jour d’une figure 2D avec Matplotlib et PyQt5.
     
    :module: figureRefresh
    :author: Yoann LE BARS
    :version: 1.0
    :date: 30/08/2018
    """
     
    import sys
    from PyQt5.QtWidgets import (
            QApplication, QWidget, QVBoxLayout, QSizePolicy, QPushButton
    )
    import matplotlib.pyplot as plt
    from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
    import numpy as np
    import random
     
    class FenetrePrincipale (QWidget):
        """
        Classe décrivant la fenêtre principale.
        """
     
        def _trace (self):
            """
            Réalise la figure.
            """
     
            # Valeurs à tracer.
            Z = np.sin(8. * self._X) * np.sin(8. * self._Y) \
                * random.uniform(-1., 1.)
     
            # Valeur minimale à tracer.
            minimum = min(Z.flatten())
            # Valeur maximale à tracer.
            maximum = max(Z.flatten())
            # Pas de l’échelle des valeurs.
            pas = (maximum - minimum) / 30.
            # Échelle des valeurs.
            echelle = np.arange(minimum - pas, maximum + pas, pas)
     
            # Contours à tracer.
            cf = self._axe.contourf(self._X, self._Y, Z, levels = echelle)
            # Barre de légende.
            self._cBar = self._figure.colorbar(cf, ax = self._axe,
                                               format = '%.2f')
     
            self._figure.canvas.draw()
     
        def _retrace (self):
            """
            Action pour relancer le tracé.
            """
     
            self._cBar.remove()
            self._axe.cla()
            self._trace()
     
        def __init__ (self):
            """
            Initialiseur de la classe.
     
            :param parent: Parent de l’objet instancié.
            :param **kwargs: Arguments de la ligne de commande.
            """
     
            QWidget.__init__(self)
     
            # Espace principal de la fenêtre.
            espPrinc = QVBoxLayout(self)
     
            # Définition de la figure à tracer.
            self._figure, self._axe = plt.subplots()
            zoneTrace = FigureCanvas(self._figure)
            zoneTrace.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
            zoneTrace.updateGeometry()
            espPrinc.addWidget(zoneTrace)
     
            # Boutton pour relancer le tracé.
            retraceBoutton = QPushButton('Retracer')
            retraceBoutton.clicked.connect(self._retrace)
            espPrinc.addWidget(retraceBoutton)
     
            # Variable intermédiaire pour la définition de la grille.
            x = np.linspace(0, 1, 100)
            # Définition de la grille.
            self._X, self._Y = np.meshgrid(x, x)
     
            self._trace()
     
        def __enter__ (self):
            """
            Retourne l’instance courrante.
            """
     
            return self
     
        def __exit__ (self, exc_type, exc_val, exc_tb):
            """
            Désallocation des ressources.
     
            :param exc_type: Si une exception a été lancée, son type.
            :param exc_val: Valeur de l’exception.
            :param exc_tb: Trace d’execution au moment de l’exception.
            """
     
            pass
     
    if __name__ == '__main__':
        # Instance de l’application.
        app = QApplication(sys.argv)
        # Fenêtre principale de l’application.
        with FenetrePrincipale() as fenPrinc:
            fenPrinc.show()
            sys.exit(app.exec_())
    En lançant ce code, on voit bien qu’à chaque fois que l’on appuie sur le bouton, la zone de tracé devient plus petite, ne s’étendant pas sur l’espace de la précédente échelle de couleur.

    Ce code est en Python 3 et utilise PyQt5 (afin de réaliser un bouton de mise à jour), mais je pense vraiment que mon problème provient purement de mon utilisation de Matplotlib. En définitive, mon problème est : comment puis-je supprimer la barre de légende (colorbar) sans laisser un espace vide dans la zone de tracé ?

    Dans le cas particulier que je présente ici, dans la méthode « _retrace » de la classe « FenetrePrincipale », la ligne « self._axe.cla() » n’est pas indispensable. Cependant, dans les cas concrets d’utilisations de mon application, elle est nécessaire, aussi l’ai-je conservé dans ce code d’exemple. De toute façon, avec ou sans cette ligne, le problème persiste. Également, nous sommes d’accord que la figure tracée ici n’est pas d’un grand intérêt, c’est simplement pour réaliser un exemple qui ne nécessite aucun fichier par ailleurs.

    Donc, quelqu’un a-t-il déjà été en mesure de mettre à jour une figure avec Matplotlib sans obtenir un vide à la place de l’ancienne barre de légende ? Cela fait quelque temps que je cherche sur le net et j’ai fait plusieurs essais (je vous ai mis ici ce que j’ai de plus concluant), mais pas moyen de résoudre ce problème.

    À bientôt.

  2. #2
    Membre émérite

    Homme Profil pro
    Ingénieur calcul scientifique
    Inscrit en
    Mars 2013
    Messages
    1 229
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Alpes Maritimes (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Ingénieur calcul scientifique

    Informations forums :
    Inscription : Mars 2013
    Messages : 1 229
    Points : 2 328
    Points
    2 328
    Par défaut
    En fait le colorbar est contenu également dans un axe. Quand tu fais appel au colorbar, ca créé dynamiquement un nouvel axe sans que tu ne t'en rende compte. Donc lorsque tu lance une 1ere fois tu as self._axe + un autre axe sur la droite. Et ce colorbar n'ayant pas de place, est venu s'en faire une, en venant reduire la taille de l'axe contenant le graphique. Tu peux printer en console la liste des axes contenu dans ta figure pour t'en rendre compte (avec la méthode self._figure.get_axes(). Et sur le deuxième coup, tu ne reprends que self._axe qui correspond à la fenêtre amputé du précédent colorbar, car tu détruis le précédent colorbar, mais ca ne remet pas en place l'axe que cela avait rogné. Et tu trace une nouvelle figure plus un nouveau colorbar dans self._axe qui reste rogné. D'où l'effet produit.

    Si tu as toujours la même forme de graphique avec le colorbar à droite, plutot que de chercher à l'effacer puis à le retracer à chaque fois, simplement l'actualiser serait peut-être plus judicieux (modifier les données sous jacente, puis refaire appel à un fig.draw() ou fig.show()). Cette méthode a également le mérite de nécéssité beaucoup moins de ressources (on update plutot que de détruire/recréer).

    Autrement, il ne faut pas juste nettoyer ton axe (avec cla), mais carrément le détruire, et le recréer à partir de la figure, et là ca devrait bien se passer. Chez moi ca corrige ce qu'il faut.

    Dans le consructeur, on ne créé que la figure:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
            self._figure = plt.figure()
    L'axe est créé dans _trace :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
            self._axe = self._figure.add_subplot(1,1,1)
    et on détruit également l'axe dans la méthode _retrace car il est recréé par la méthode _trace :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
        def _retrace (self):
            """
            Action pour relancer le tracé.
            """
     
            self._cBar.remove()
            self._axe.remove()
            self._trace()

  3. #3
    Membre confirmé
    Avatar de Le Farfadet Spatial
    Homme Profil pro
    En cours de précision…
    Inscrit en
    Avril 2008
    Messages
    186
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : En cours de précision…

    Informations forums :
    Inscription : Avril 2008
    Messages : 186
    Points : 604
    Points
    604
    Par défaut
    Salut à tous !

    Citation Envoyé par lg_53 Voir le message
    Si tu as toujours la même forme de graphique avec le colorbar à droite, plutot que de chercher à l'effacer puis à le retracer à chaque fois, simplement l'actualiser serait peut-être plus judicieux (modifier les données sous jacente, puis refaire appel à un fig.draw() ou fig.show()). Cette méthode a également le mérite de nécéssité beaucoup moins de ressources (on update plutot que de détruire/recréer).
    Oui, j’avais envisagé cette possibilité, mais dans mon cas concret j’ai parfois suffisamment de différences (des zones transparentes qui ne sont pas les mêmes) pour nécessiter d’effacer entièrement la figure.

    Citation Envoyé par lg_53 Voir le message
    Autrement, il ne faut pas juste nettoyer ton axe (avec cla), mais carrément le détruire, et le recréer à partir de la figure, et là ca devrait bien se passer. Chez moi ca corrige ce qu'il faut.
    Merci, ça a totalement corrigé mon problème. Je vais donc valider cette réponse. Je poste toutefois ci-après le code complet corrigé, pour le cas où quelqu’un en aurait besoin :

    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
    #!/usr/bin/env python3
    # -*- coding: utf-8 -*-
     
    """
    Exemple de mise à jour d’une figure 2D avec Matplotlib et PyQt5.
     
    :module: figureRefresh
    :author: Yoann LE BARS
    :version: 1.0
    :date: 30/08/2018
    """
     
    import sys
    from PyQt5.QtWidgets import (
            QApplication, QWidget, QVBoxLayout, QSizePolicy, QPushButton
    )
    import matplotlib.pyplot as plt
    from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
    import numpy as np
    import random
     
    class FenetrePrincipale (QWidget):
        """
        Classe décrivant la fenêtre principale.
        """
     
        def _trace (self):
            """
            Réalise la figure.
            """
     
            # Valeurs à tracer.
            Z = np.sin(8. * self._X) * np.sin(8. * self._Y) \
                * random.uniform(-1., 1.)
     
            # Valeur minimale à tracer.
            minimum = min(Z.flatten())
            # Valeur maximale à tracer.
            maximum = max(Z.flatten())
            # Pas de l’échelle des valeurs.
            pas = (maximum - minimum) / 30.
            # Échelle des valeurs.
            echelle = np.arange(minimum - pas, maximum + pas, pas)
     
            # Axe de la figure.
            self._axe = self._figure.add_subplot(1,1,1)
            # Contours à tracer.
            cf = self._axe.contourf(self._X, self._Y, Z, levels = echelle)
            # Barre de légende.
            self._cBar = self._figure.colorbar(cf, ax = self._axe,
                                               format = '%.2f')
     
            self._figure.canvas.draw()
     
        def _retrace (self):
            """
            Action pour relancer le tracé.
            """
     
            self._cBar.remove()
            self._axe.remove()
            self._trace()
     
        def __init__ (self):
            """
            Initialiseur de la classe.
     
            :param parent: Parent de l’objet instancié.
            :param **kwargs: Arguments de la ligne de commande.
            """
     
            QWidget.__init__(self)
     
            # Espace principal de la fenêtre.
            espPrinc = QVBoxLayout(self)
     
            # Définition de la figure à tracer.
            self._figure = plt.figure()
            zoneTrace = FigureCanvas(self._figure)
            zoneTrace.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
            zoneTrace.updateGeometry()
            espPrinc.addWidget(zoneTrace)
     
            # Boutton pour relancer le tracé.
            retraceBoutton = QPushButton('Retracer')
            retraceBoutton.clicked.connect(self._retrace)
            espPrinc.addWidget(retraceBoutton)
     
            # Variable intermédiaire pour la définition de la grille.
            x = np.linspace(0, 1, 100)
            # Définition de la grille.
            self._X, self._Y = np.meshgrid(x, x)
     
            self._trace()
     
        def __enter__ (self):
            """
            Retourne l’instance courrante.
            """
     
            return self
     
        def __exit__ (self, exc_type, exc_val, exc_tb):
            """
            Désallocation des ressources.
     
            :param exc_type: Si une exception a été lancée, son type.
            :param exc_val: Valeur de l’exception.
            :param exc_tb: Trace d’execution au moment de l’exception.
            """
     
            pass
     
    if __name__ == '__main__':
        # Instance de l’application.
        app = QApplication(sys.argv)
        # Fenêtre principale de l’application.
        with FenetrePrincipale() as fenPrinc:
            fenPrinc.show()
            sys.exit(app.exec_())
    À bientôt.

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Mise à jour du graphique avec excel 2016
    Par AlfredKr dans le forum Macros et VBA Excel
    Réponses: 2
    Dernier message: 23/10/2015, 21h06
  2. Réponses: 15
    Dernier message: 12/12/2011, 17h07
  3. Mise à jour de données temps réel avec Pentaho Data Integration
    Par jonathansauret dans le forum kettle/PDI
    Réponses: 0
    Dernier message: 29/10/2008, 15h55
  4. Réponses: 2
    Dernier message: 22/06/2008, 15h06
  5. Problème de mise à jour des champs Auto incrémentés avec VB6.
    Par NHenry dans le forum VB 6 et antérieur
    Réponses: 1
    Dernier message: 28/05/2007, 16h06

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