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:
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 dune 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 lobjet 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 linstance 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 lexception.
:param exc_tb: Trace dexecution au moment de lexception.
"""
pass
if __name__ == '__main__':
# Instance de lapplication.
app = QApplication(sys.argv)
# Fenêtre principale de lapplication.
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.