Partage
  • Partager sur Facebook
  • Partager sur Twitter

Intégrer un graphe matplotlib dans une IHM tkinter

prise de tête depuis trois jours ...

3 mai 2016 à 15:31:42

Bonjour à tous, 

pour mon premier post sur ce forum, je fais appel à votre aide concernant l'intégration de graphes (matplotlib, si y'a un autre moyen plus simple je suis preneur) dans une interface faite avec tkinter (si y'a mieux/plus simple je prends aussi).

Le but du logiciel que je code est tout simple, j'ai d'ailleurs quasiment fini, il s'agit de récupérer des données par requête http (par urllib), de les enregistrer dans un fichier et d'effectuer un tracé un temps réel. J'arrive à faire tout cela mais c'est pas très beau, alors j'ai décidé de passer par tkinter pour faire une interface "propre". Mais outre le placement des widgets qui me paraît limité (top, bottom, right et left... un peu trop léger à mon goût), je ne parviens pas a mettre un/plusieurs graphe(s) dans un cadre (frame / labelframe) ou un canvas. 

Voilà, si je n'ai pas été assez clair, n'hésitez pas à demander des infos supplémentaires.

Donc si une âme charitable pouvait me secourir, je lui en serai très reconnaissant.

  • Partager sur Facebook
  • Partager sur Twitter
3 mai 2016 à 19:19:06

le placement est illimité avec la fonction grid(). Au lieu d'un .pack(), tu mets un .grid(row=1, column=1) pour le placer à la ligne 1 et colonne 1 par exemple...

Avec cette maniere, tu peux mettre tes canvas les uns à coté des autres et donc les mettre ensemble devient plus trop utile ;)

Cependant, si tu tiens à les mettre ensemble, tu peux assigner une position d'origine de l'image en 1er argument de la fonction create_image du canvas. La position est un couple de coordonnée prennant pour origine le coin supérieur gauche du canvas (et indiquant l'origine du coin supérieur gauche l'image, de la meme maniere).

j'espere t'avoir aidé :)

  • Partager sur Facebook
  • Partager sur Twitter
Si je suis tête en l'air, c'est par habitude de viser le sommet
3 mai 2016 à 20:16:48

Merci pour ta réponse, voila des infos utiles :)

Cependant le cœur du problème est: comment intégrer un tracé/graphe (évoluant en temps réel) dans un canvas ou une frame, au lieu d'avoir une fenêtre matplotlib qui s'ouvre.

  • Partager sur Facebook
  • Partager sur Twitter
3 mai 2016 à 21:34:44

seulement ce tuto apprend à envoyer une image de matplotlib dans tkinter, rien de plus. Jean-EudeMorqueu cherche à ce que le graph se déssine en temps réel.

à savoir qu'à 16'30, il dit lui même qu'il ne peut pas l'animer avec tkinter, et ne sait pas comment faire MAIS qu'il existe des fonctions d'animations dans le module matplotlib! Donc chose à approfondir (personnellement j'en connaissais pas l'existence..).

  • Partager sur Facebook
  • Partager sur Twitter
Si je suis tête en l'air, c'est par habitude de viser le sommet
4 mai 2016 à 9:37:29

J'ai regardé le tuto, Dan737, il me semble bien compliqué a mettre en oeuvre.

Pour info, ça fait que deux semaine que je code en python.

Ensuite, je vais ajouter encore quelques petites infos à mon problème, afin d'éclaircir certaines zones d'ombre :

    - je parviens à tracé en temps réel

    - je parviens à faire une fenêtre tkinter, avec des boutons et des frames

    - je suis bloqué parce que matplotlib semble inapproprié à l'intégration dans une autre fenêtre que la sienne.

Voilà voilà :D

Merci encore pour vos réponses :)

  • Partager sur Facebook
  • Partager sur Twitter
5 mai 2016 à 15:31:47

Jean-EudeMornequeu a écrit:

ça fait que deux semaine que je code en python

Le problème vient peut-être de là...

J'ai pas beaucoup de temps pour élaborer. Pour intégrer ton graphe dans Tkinter, il te faut utiliser le backend backend_tkagg qui contient la classe FigureCanvasTkAgg. Celle-ci prend en argument une Figure de matplotlib. Elle a aussi une méthode get_tk_widget() qui te permet de récupérer le Canvas de Tkinter dans lequel est dessiné ton graphique.

Avec un graphique tout bête voici comment faire

import tkinter as tk
from matplotlib.figure import Figure
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg

app = tk.Tk()
app.wm_title("Graphe Matplotlib dans Tkinter")

fig = Figure(figsize=(6, 4), dpi=96)
ax = fig.add_subplot(111)
ax.plot(range(10), [5, 4, 2, 6, 9, 8, 7, 1, 2, 3])

graph = FigureCanvasTkAgg(fig, master=app)
canvas = graph.get_tk_widget()
canvas.grid(row=0, column=0)

app.mainloop()

Tu noteras que je n'utilise pas pyplot. C'est parce que pyplot est déjà un wrapper qui permet de créer la fenêtre pour afficher un graphe matplotlib. Il peut utiliser Tkinter, mais utilise généralement par défaut Qt. Bref dans notre cas, on veut prendre nous même en charge la création d'une fenêtre avec Tkinter, et donc on doit créer notre Figure nous-même.

-
Edité par Dan737 24 décembre 2016 à 15:29:34

  • Partager sur Facebook
  • Partager sur Twitter
9 mai 2016 à 9:14:04

Merci beaucoup pour ta réponse Dan737, je teste ça et te tiens au courant.

Alors oui, je code en python depuis peu, mais on m'a demandé d'apprendre tout seul et de faire un logiciel avec telles exigences... mais rassure toi je sais coder, du moins j'ai des bases même si programmer des logiciels c'est pas ma formation principale.

Au passage désolé, mais ce week-end c'était relax :D

  • Partager sur Facebook
  • Partager sur Twitter
9 mai 2016 à 13:55:50

app = tk.Tk()
app.wm_title("Graphe Matplotlib dans Tkinter")

fig = Figure(figsize=(6, 4), dpi=112)
ax1 = fig.add_subplot(111)
ax2 = fig.add_subplot(111)
while (1):
	x,y1, y2 = get_back_values()
	ax1.plot(x, y1, 'g-')
	ax2.plot(x, y2, 'r-')
	ax1.set_xlabel('Temps')
	ax1.set_ylabel('tension', color='g')
	ax2.set_ylabel('courant', color='r')
	time.sleep(2)

graph = FigureCanvasTkAgg(fig, master=app)
canvas = graph.get_tk_widget()
canvas.grid(row=0, column=0)

app.mainloop()
Voila le code que j'ai adapté du tien, mais il ne se passe rien lorsque je lance... Et puis le fait de pas utiliser Pyplot m'empêche de faire du graphe dynamique j'ai l'impression :/
  • Partager sur Facebook
  • Partager sur Twitter
9 mai 2016 à 17:02:48

Evidemment qu'il ne se passe rien. Tu as une boucle infinie. Tu n'atteindra donc jamais la ligne 16 de cet exemple !

De manière générale, dans une application avec une boucle événementielle, tu ne peux jamais utiliser de sleep ni entrer dans une boucle infinie, sinon tu ne donneras jamais la main au GUI qui ne pourra donc pas traiter les événements.

Il faut utiliser les outils du GUI pour appeler de manière répétée une fonction. Dans matplotlib, il existe la fonction FuncAnimation qui permet d'appeler une fonction à intervalle régulier.

#!/usr/bin/python3
# -*- coding: utf-8 -*-

import tkinter as tk
from matplotlib.figure import Figure
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.animation import FuncAnimation
from matplotlib import style

from random import randint

def get_back_values():
    "Dummy function"
    x = range(10)
    y1 = [randint(0, 10) for i in range(10)]
    y2 = [randint(0, 10) for i in range(10)]
    return x, y1, y2

def update_graph(dt):
    x, y1, y2 = get_back_values()
    ax1.clear()
    ax2.clear()
    ax1.set_ylim(0, 10, auto=False)
    ax2.set_ylim(0, 10, auto=False)
    ax2.set_xlabel('Temps')
    ax1.set_ylabel('tension', color='g')
    ax2.set_ylabel('courant', color='r')
    ax1.plot(x, y1, 'g-o')
    ax2.plot(x, y2, 'r-o')

app = tk.Tk()
app.wm_title("Graphe Matplotlib dans Tkinter")

style.use("ggplot")
fig = Figure(figsize=(8, 5), dpi=112)
ax1 = fig.add_subplot(211)
ax2 = fig.add_subplot(212, sharex=ax1)
ax2.set_xlabel('Temps')
ax1.set_ylabel('tension', color='g')
ax2.set_ylabel('courant', color='r')
fig.tight_layout()

graph = FigureCanvasTkAgg(fig, master=app)
canvas = graph.get_tk_widget()
canvas.grid(row=0, column=0)
 
FuncAnimation(fig, update_graph, interval=2000)
app.mainloop()
  • Partager sur Facebook
  • Partager sur Twitter
10 mai 2016 à 9:43:40

Tout d'abord, merci beaucoup de m'accorder un peu de ton temps et de me proposer de super solutions ! Cependant j'ai un petit problème avec la ligne :
fig.tight_layout()

La console me sort une erreur :

UserWarning : tight_layout : falling back to Agg renderer

warning.warn("tight_layout : falling back to Agg renderer")

J'ai remplacé la ligne fig.tight_layout() par fig.set_tight_layout(True), et l'erreur disparaît. J'aimerai savoir à quoi sert cette ligne, je n'ai pas trouvé d'info explicite la concernant.

Autre point, lorsque je lance le code tu as mis, un beau graphe se montre, mais rien n'est tracé malgré get_back_values() .

  • Partager sur Facebook
  • Partager sur Twitter
10 mai 2016 à 9:59:30

Finalement le problème venait des parenthèses absentes dans FuncAnimation(fig, update_graph(), interval=2000). Donc encore merci, je te tiens au courant ( ;) ).
  • Partager sur Facebook
  • Partager sur Twitter
10 mai 2016 à 10:22:15

Alors non, là c'est tout faux !!! Il ne faut pas mettre de parenthèses. FuncAnimation attend en deuxième argument un callable et non le retour de update_graph qui est None puisqu'on ne retourne rien.

Chez moi le code tel que posté montre 2 graphes dont les courbes changent toutes les 2 secondes.

Jean-EudeMornequeu a écrit:

La console me sort une erreur

Non la console t'indique un avertissement et non une erreur. C'est juste un problème connu : https://github.com/matplotlib/matplotlib/issues/1852 En effet en passant True comme argument certaines personnes évite le warning. Mais même avec le warning, tout fonctionne.

Pour ce que fait tight_layout, tu dois consulter la doc de Matplotlib: http://matplotlib.org/users/tight_layout_guide.html

  • Partager sur Facebook
  • Partager sur Twitter
10 mai 2016 à 13:46:51

Et bien ! Faut pas s'énerver  ^^

Moi je te dis ce qu'il se passe, et les solutions que je trouve; sans les parenthèses le graphe s'affichait mais pas de points ou de trace et encore moins d'actualisation toutes les deux secondes.

J'utilise Python 3.5 et j'ai toutes les libs nécessaires. Pour le warning du tight_layout, je comprends, merci (bien qu’avoir un code qui sort pas d'avertissement c'est plus propre). Pour ce qui est de consulter la doc de matplotlib, je l'ai fait évidemment avant de poster, sauf que c'est pas toujours très clair pour moi (d'où le post). 

Sache que je demande de l'aide ici en dernier recours, que ça fait quand même 3 semaines que je me débrouillais, sauf qu'à un certain point j'ai bloqué et il n'y avait pas de sujet ressemblant de près ou de loin à mon problème. Jusque là tu m'as bien aidé, et je t'en suis reconnaissant.

  • Partager sur Facebook
  • Partager sur Twitter
10 mai 2016 à 14:16:10

C'est bon, j'ai trouvé d'où vient le problème. Désormais ça fonctionne comme tu l'as décrit. Attention, je remets pas en cause tes propos, je parle de mon cas. Je me suis servi de ça. Je mets juste les lignes que j'ai modifiées :

import matplotlib.animation as animation
...
ani = animation.FuncAnimation(fig, update_graph, interval=500)

C'est peut être pas grand chose, je sais pas, en tout cas là c'est bon.

(oui l'intervalle plus court c'est juste pour le "fun")

  • Partager sur Facebook
  • Partager sur Twitter
11 mai 2016 à 10:43:26

J'ai un autre petit problème :  seuls les markers apparaissent sur le graphe, aucune ligne malgré le paramètre '-' ou linestyle='-' .
  • Partager sur Facebook
  • Partager sur Twitter
12 mai 2016 à 9:24:10

Bonjour, aujourd'hui j'aborde deux points : le premier, le problème pour lequel j'ai ouvert le topic est réglé, j'ai fini en me débrouillant, comme j'avais commencé d'ailleurs. Donc merci Dan737, c'est en partie grâce à toi. J'ai donc quasiment fini mon logiciel. Deuxièmement, pour ce qui est du "...code minimal qui reproduise ton problème", il s'agit tout simplement de ton code, qui adapté au mien semble ne plus afficher les lignes mais uniquement les markers. Lorsque je teste avec des séquences de nombres aléatoires, les lignes sont bien tracées, cependant quand je trace les points que je récupère sur le réseau par requête http, ce n'est plus le cas.

-
Edité par Jean-EudeMornequeu 12 mai 2016 à 9:24:54

  • Partager sur Facebook
  • Partager sur Twitter
12 mai 2016 à 10:14:22

D'où l'importance d'arriver à reproduire ce problème avec un code minimal. Il y a de forte chance qu'en faisant ce travail, tu vas découvrir d'où vient le problème.

Dans l'ordre:

  • Mon script fonctionne.
  • Mon script dans lequel tu remplaces la fonction get_back_values() par quelque chose de ton crû ne fonctionne plus.

cependant quand je trace les points que je récupère sur le réseau par requête http, ce n'est plus le cas

Alors pour aller étape par étape, je commencerais par ne faire qu'une seule requête (et non une toutes les 2 secondes). J'imagine que le problème sera toujours là. Alors ensuite j'inspecterais les valeurs retournée par la fonction get_back_values(). Dans ma version, elle retourne 3 itérables de même longueurs contenant toutes des floats. Toi qu'as-tu ?

C'est en procédant ainsi que tu vas arriver à trouver d'où vient le problème. Sans nous montrer ce que retourne get_back_values() on ne peut faire que des hypothèses.

-
Edité par Dan737 12 mai 2016 à 10:14:37

  • Partager sur Facebook
  • Partager sur Twitter
16 septembre 2021 à 15:47:45

Bonjour à tous !

J'ai une préoccupation concernant le langage python. 

Je souhaite afficher mon graphique matplotlib issu de mon datagramme Pandas, sur mon scripts tkinter du langage python.

Voici mon script est j'aimerais l'afficher sur une fenêtre tkinter.

Merci pour votre aide 

 conn = bd.connect(

            host = "localhost",
            user = "root",
            password = "VivreSonReve@23",
            database = "login"
        )
df = pd.read_sql_query("SELECT modele, SUM(Quantite) AS Qty FROM eMarque INNER JOIN eModele ON eMarque.marqueID = eModele.marqueID INNER JOIN PrixEntry ON eModele.modeleID = PrixEntry.modeleID GROUP BY modele"conn)

#print(df['Qty'])

modeles = df['modele']
quantite = df['Qty']
plt.xticks(rotation = '90')
plt.bar(modelesquantitecolor = cm.rainbow(np.linspace(01len(modeles))))
plt.show()
  • Partager sur Facebook
  • Partager sur Twitter
16 septembre 2021 à 16:11:32

joli déterrage. tu as vu (et testé !!) les réponses données au moins puisqu'elles ont répondu à la question initiale qui est la même que la tienne ?
  • Partager sur Facebook
  • Partager sur Twitter
16 septembre 2021 à 16:21:27

Bonjour,

Déterrage

Citation des règles générales du forum :

Avant de poster un message, vérifiez la date du sujet dans lequel vous comptiez intervenir.

Si le dernier message sur le sujet date de plus de deux mois, mieux vaut ne pas répondre.
En effet, le déterrage d'un sujet nuit au bon fonctionnement du forum, et l'informatique pouvant grandement changer en quelques mois il n'est donc que rarement pertinent de déterrer un vieux sujet.

Au lieu de déterrer un sujet il est préférable :

  • soit de contacter directement le membre voulu par messagerie privée en cliquant sur son pseudonyme pour accéder à sa page profil, puis sur le lien "Ecrire un message"
  • soit de créer un nouveau sujet décrivant votre propre contexte
  • ne pas répondre à un déterrage et le signaler à la modération

Je ferme ce sujet. En cas de désaccord, me contacter par MP.

  • Partager sur Facebook
  • Partager sur Twitter

Moderateur forum || FAQ 3D || discord 3D francophone || OC Tweak script