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

Tkinter Python Discussion :

relier échelle et graphique matplotlib


Sujet :

Tkinter Python

  1. #1
    Futur Membre du Club
    Femme Profil pro
    Étudiant
    Inscrit en
    Avril 2024
    Messages
    3
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Âge : 20
    Localisation : France, Ille et Vilaine (Bretagne)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Avril 2024
    Messages : 3
    Points : 5
    Points
    5
    Par défaut relier échelle et graphique matplotlib
    Bonjour !
    J'espère que quelqu'un pourra m'éclairer, car je suis totalement bloquée.
    Mon but est de mettre un satellite en orbite autour de la Lune.
    J'ai donc réalisé un graphique matplotlib avec la trajectoire du satellite, et je souhaite pourvoir changer la valeur de l'accélération que je lui donne pour être propulsé.
    Pour cela j'ai voulu créer une échelle, qui tracerait un nouveau graph dès que l'on change la valeur de l'accélération.
    Je ne sais pas si c'est super clair ... L'échelle et le graph s'affichent bien, mais ils ne semblent pas connectés (le graph ne change pas quelque soit la valeur de l'échelle).
    J'ai eu beau chercher je ne vois pas mon erreur et pourquoi cela ne marche pas.
    Merci d'avance

    Voila mon code (je travaille sur Spyder) :

    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
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
     
    import tkinter
    from tkinter import*
     
    from matplotlib.backends.backend_tkagg import (
        FigureCanvasTkAgg, NavigationToolbar2Tk)
    # Implement the default Matplotlib key bindings.
    from matplotlib.backend_bases import key_press_handler
    from matplotlib.figure import Figure
    import matplotlib.pyplot as plt
     
    import numpy as np
     
     
     
    # constantes
    G= 6.67428*10**(-20) #km3.kg-1.s-2
    Mt= 5.972*10**24 #kg, masse Terre
    Rt= 6500 #km, rayon Terrre
    d= 20000 #km, distance orbitale basse terrestre (h sat)
    xl= 385000 #km, abscisse Lune  # MODIF 380000
    yl= 0 #km, ordonnée Lune
    Ml= 7.36*10**22 #kg, masse Lune
    Rl= 1750 #km, rayon Lune
     
     
     
    # compteur de temps
    t=0#en s
    tmax= 1800000 #en s
    dt= 1 #pas de temps en s
    lt=[]  #liste avec valeurs prises par t
     
     
    #Satellite
    v0=np.sqrt(G*Mt/d) #vitesse initiale nécessaire à une orbite initiale circulaire
    T= 2*np.pi*np.sqrt(d/(G*Mt))*d  # période avec Kepler
    print("Vitesse initiale (orbite circulaire)", v0)
    print("Periode initiale (orbite circulaire)", T)
     
     
    #coord x
    x=0 #en km
    vx = v0 #en km.s-1
    ax= 0 #en km.s-2
    lx=[] #liste pour les abscisses du sat
     
     
    #coord y
    y=d #en km
    vy = 0 #en km.s-1, vitesse orthoradiale
    ay= 0 #en km.s-2
    ly =[] #liste pour les ordonnées du sat
     
     
    dTS= np.sqrt(x**2 + y**2) #distance Terre-sat
    dLS= np.sqrt((x-xl)**2 + (y-yl)**2) #distance Lune-sat
     
    ldTS=[]
    ldLS=[]
     
    frein = True
     
    acceleration_value = 1.695
     
    # on ajoute un compteur pour vérifier la fréquence et diminuer les listes
    compteur_enregistrement = 0
     
     
    while (t<tmax) and (dTS>Rt) and (dLS>Rl) :  #le code tourne jusqu'à tmax et s'arrête si on touche la Terre ou la Lune
        t=t+dt
        compteur_enregistrement += 1
     
        #mise à jour des distances
        dTS= np.sqrt(x**2 + y**2)
        dLS= np.sqrt((x-xl)**2+(y-yl)**2)
     
        ax= -G*Mt*x/dTS**3 - G*Ml*(x-xl)/dLS**3  # accelération = force de gravitation
        vx= vx+ ax*dt
        x= x+ vx*dt  #x(t+dt) = x(t) +vx(t)*dt
     
        ay= -G*Mt*y/dTS**3 - G*Ml*(y-yl)/dLS**3  # accelération = force de gravitation
     
        if 3/4*T< t < 3/4*T +dt : #quand le sat arrive à gauche il décolle (à environ 3/4T)
            ay= ay+ acceleration_value        #là on change les valeurs de l'accélération pour sortir de l'orbite
     
     
        #aerofreinage
        if (-1<y<1) and (x>xl) and (t>T) and (frein==True):  
            xmax=x-xl
            Mt=0
            vx=0
            vy= vy+0.1
            frein=False
     
        if (-1<y<1) and (x<xl) and (t>T) and ((xl-x)>xmax):
            frein=True
     
     
     
        vy= vy+ ay*dt
        y= y+ vy*dt
     
        #remplissage des listes
     
        # Enregistrement des positions seulement tous les 10 itérations
        if compteur_enregistrement == 20:
            lx.append(x)
            ly.append(y)
            compteur_enregistrement = 0  # Réinitialiser le compteur
        ldTS.append(dTS)
        ldLS.append(dLS)
        lt.append(t)
     
     
     
    #création graphique 
     
    # bornes du graphique
    xmax = 500000
    xmin = -100000
    ymax = 300000
    ymin = -300000
     
     
    # Création de la fonction qui dessine le graphique
     
    def draw_graph():
        # Fermer les figures précédentes sinon ca plante
        plt.close()
        # Création de la figure et des axes
        fig, ax = plt.subplots(figsize=(5, 5), dpi=200)
        ax.set_xlim(xmin, xmax)
        ax.set_ylim(ymin, ymax)
        ax.set_aspect('equal')  # permet de faire que l'échelle des x et des z soient les mêmes
     
        # Ajout des lignes pointillées horizontales et verticales
        ax.plot([0, 0], [ymin, ymax], linestyle=':', linewidth=1, color='green')
        ax.plot([xmin, xmax], [0, 0], linestyle=':', linewidth=1, color='green')
     
        # Ajout de cercles pour la Terre et la Lune
        Terre = plt.Circle((0, 0), 6500, color='blue')
        Lune = plt.Circle((385000, 0), 1737, color='red')
        ax.add_artist(Terre)
        ax.add_artist(Lune)
     
        # Ajout de la trajectoire du satellite (lx et ly sont à définir)
        ax.plot(lx, ly)
     
        # Affichage du graphique
        canvas = FigureCanvasTkAgg(fig, master=cadre_graphique)
        canvas.draw()
        canvas.get_tk_widget().pack()
     
     
    def acceleration(value):
        global acceleration_value
        acceleration_value = value
        draw_graph()
     
     
        # Régle le rappel pour surveiller les changements sur l'échelle
        fenetre.after(1000, lambda: acceleration(value))
     
     
    # Création de la fenêtre principale
    fenetre = tkinter.Tk()
    fenetre.title("Trajectoire Satellite")
     
     
    # Création d'un cadre pour les widgets principaux
    cadre_echelle = Frame (fenetre, width = 520, height = 50, bg='lightyellow')
    cadre_echelle.pack()
     
    cadre_graphique = Frame(fenetre, width=520, height=230, bg='white')
    cadre_graphique.pack()
     
    # Création du sélecteur échelle
    echelle = Scale(cadre_echelle, from_=1.600, to=1.800, command=lambda value: acceleration(value), \
                    orient=HORIZONTAL, length=480, width=30, resolution=0.0005, label="valeur d'accélération : ", \
                    showvalue=1, tickinterval=0.05, sliderlength=20)
    echelle.set(acceleration_value)
    echelle.pack()
     
     
    tkinter.mainloop()

  2. #2
    Membre éprouvé

    Homme Profil pro
    Ingénieur
    Inscrit en
    Août 2010
    Messages
    661
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Août 2010
    Messages : 661
    Points : 1 159
    Points
    1 159
    Par défaut
    Bonjour,

    Il y a plusieurs choses :
    • la partie du code qui détermine la trajectoire n'est appelée qu'une fois. Ainsi, quand votre curseur change la valeur de l'accélération, la trajectoire, elle, reste inchangée. Il faudrait encapsuler la section qui commence par la boucle while dans une fonction qui serait appelée lorsque la valeur de l'accélération change.
    • si vous placez un print(acceleration_value) à la fin de votre fonction acceleration (assez mauvais choix de nommage je trouve), vous verrez que la valeur oscille constamment entre celle définie avec le curseur et la valeur par défaut. Si vous enlevez cette ligne c'est correct.


    Il faudrait aussi essayer, dans un second temps d'optimiser le calcul de la trajectoire. Je n'ai pas mesuré le temps nécessaire, mais ça me semble trop long pour rafraichir aussi vite que l'on voudrait avec le slider.

    J

  3. #3
    Futur Membre du Club
    Femme Profil pro
    Étudiant
    Inscrit en
    Avril 2024
    Messages
    3
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Âge : 20
    Localisation : France, Ille et Vilaine (Bretagne)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Avril 2024
    Messages : 3
    Points : 5
    Points
    5
    Par défaut
    Très bien merci beaucoup !
    Je vais essayer de rectifier cela en créant une nouvelle procédure.
    Par contre je n'ai pas bien compris pourquoi la valeur acceleration_value oscille et quelle ligne je devrai enlever.
    C'est mon premier travail avec python et j'avoue que j'ai du mal.
    Malheureusement je ne pourrai rien faire pour améliorer le temps de calcul car j'ai utilisé la méthode d'Euler qui impose un pas de temps faible pour rester assez précis (il faudrait que je prenne une autre méthode de calcul numérique mais je n'aurai pas le temps avant ma soutenance).

  4. #4
    Membre éprouvé

    Homme Profil pro
    Ingénieur
    Inscrit en
    Août 2010
    Messages
    661
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Août 2010
    Messages : 661
    Points : 1 159
    Points
    1 159
    Par défaut
    La façon propre de construire votre interface passerait pas une programmation orientée objet. Il s'agirait alors de créer une classe qui défini votre interface et qui garderait en mémoire via les attributs les différents éléments. Il ne serait alors plus nécessaire de passer par des variables globales et le découpage serait bien plus facile. Il faudrait alors tout réécrire. En pratique, pour quelque chose d'aussi "petit" (rien de péjoratif, on ne parle pas d'une app mais juste d'un script qui démontre quelque chose), on peut s'en sortir avec quelques fonctions sans passer par de l'objet.

    Le plus simple c'est de réécrire uniquement l'architecture. Mettre en place les mécanismes : on créé les différentes fonctions qui sortent des trucs un peu bidons mais qui sont très simples et rapides développer.

    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
    import numpy as np
    import tkinter as tk
    import matplotlib.pyplot as plt 
    from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
     
    # On garde une référence au tracé de la trajectoire via une variable globale
    line_traj = None
     
    def calc_traj(acc):
        x = np.linspace(0, 100, 100)
        y = np.sin(acc * x)
        return x, y
     
    def draw_figure(ax):
        # Seule la trajectoire est mise à jour lorsque la valeur d'accélération change. 
        # On trace l'ensemble des éléments, et on définit une trajectoire "vide" dont on
        # conserve une référence pour être mise à jour ultérieurement
        global line_traj
        line_traj, = ax.plot([], [])
     
    def update_graph(acc):
        global line_traj
        # Calcul la trajectoire
        x, y = calc_traj(float(acc))
        # Met à jour le graph
        line_traj.set_data(x, y)
        fig = ax.get_figure()
        fig.gca().relim()               # Pas nécessaire si les limites sont fixées avant
        fig.gca().autoscale_view()      # idem
        fig.canvas.draw()
        return None
     
     
    if __name__ == '__main__':
     
        # Création de l'interface
        fenetre = tk.Tk()
        fenetre.title('Trajectoire Satellite')
     
        # Création des principales frames
        cadre_echelle = tk.Frame(fenetre, width=520, height=50, bg='lightyellow')
        cadre_echelle.pack()
        cadre_graphique = tk.Frame(fenetre, width=520, height=230, bg='white')
        cadre_graphique.pack()
     
        # Création du sélecteur échelle
        echelle = tk.Scale(
            cadre_echelle, from_=1.600, to=1.800, 
            command=lambda value: update_graph(value), 
            orient=tk.HORIZONTAL, length=480, width=30, resolution=0.0005, 
            label="valeur d'accélération : ",
            showvalue=1, tickinterval=0.05, sliderlength=20,
            )
        echelle.pack()
     
        # Creation de la figure
        fig, ax = plt.subplots(figsize=(5, 5), dpi=200)
        draw_figure(ax)
        canvas = FigureCanvasTkAgg(fig, master=cadre_graphique)
        canvas.draw()
        canvas.get_tk_widget().pack()
     
        tk.mainloop()
    Dans cet exemple, on a un objet line_traj global qui sera un axis de matplotlib. Au changement de valeur du slider, on appel une fonction update() qui prend en entrée la valeur du slider, qui recalcule la trajectoire, et change les coordonnées des points de la trajectoire. Ainsi, on ne récréé pas une figure à chaque changement, on ne retrace que les éléments. Moins couteux en temps calcul.

    Ce n'est sans doute pas la meilleure approche, mais vous n'aurez pas de mal à reprendre une bonne part de votre code pour compléter le squelette du mien.

    Maintenant, il restera à gérer les variables par défaut (les constantes, les bornes du graph etc). Une idée serait éventuellement de passer par un dictionnaire :
    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
    # Constantes
    CONSTANTES = {
        'G': 6.67428 * 10**(-20), # km3.kg-1.s-2
        }
     
    ...
     
    def draw_figure(ax, **kwargs):
        # Seule la trajectoire est mise à jour lorsque la valeur d'accélération change. 
        # On trace l'ensemble des éléments, et on définit une trajectoire "vide" dont on
        # conserve une référence pour être mise à jour ultérieurement
        global line_traj
        print(kwargs.get('G', 0.00))
        line_traj, = ax.plot([], [])
     
    ...
     
    if __name__ == '__main__':
     
        draw_figure(ax, **CONSTANTES)
    J

  5. #5
    Futur Membre du Club
    Femme Profil pro
    Étudiant
    Inscrit en
    Avril 2024
    Messages
    3
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Âge : 20
    Localisation : France, Ille et Vilaine (Bretagne)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Avril 2024
    Messages : 3
    Points : 5
    Points
    5
    Par défaut
    Merci !
    J'ai réussi à faire marcher le code avec votre aide précédente mais cela prend effectivement beaucoup de temps pour charger chaque graphique.
    Je n'ai jamais vu la programmation orientée objet ainsi que les classes, mais je vais essayer de m'y pencher et compléter votre trame pour que ce soit plus performant.

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

Discussions similaires

  1. [Python 3.X] Modification grille secondaire sur un graphique matplotlib
    Par nlbmoi dans le forum Général Python
    Réponses: 2
    Dernier message: 07/04/2021, 18h18
  2. [Python 3.X] relier l'interface graphique au jeu crée séparémment
    Par SimOnTheFlux dans le forum Tkinter
    Réponses: 1
    Dernier message: 14/12/2019, 08h55
  3. Tkinter et graphique matplotlib python
    Par Audreyplrd dans le forum Tkinter
    Réponses: 0
    Dernier message: 19/04/2019, 09h14
  4. [Python 3.X] Afficher l'équation d'une droite sur un graphique Matplotlib
    Par Olivier20000 dans le forum Calcul scientifique
    Réponses: 3
    Dernier message: 16/04/2019, 12h35
  5. Réponses: 3
    Dernier message: 24/05/2018, 22h00

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