Equation standard de la droite

Soyez le premier à donner votre avis sur cette source.

Vue 7 466 fois - Téléchargée 328 fois

Description

Ce petit programme illustre l'affichage d'une droite via sa forme standard. L'intérêt principal est d'avoir fait un Canvas modifiable, et une fonction qui retrace l'ensemble des axes et la droite en fonction de la taille (initiale ou modifiée) du Canvas, et en fonction de la variation des 3 paramètres suivants : le nombre de pixels d'une unité des axes et les coefficients a et b de l'équation de la droite sous sa forme y = a x + b. L'origine des axes est toujours bien centré dans le canvas, quel que soit sa forme et sa position. Le programme illustre aussi le fait QU'ON PEUT UTILISER pack et grid au sein d'une même application, pourvu qu'on respecte la condition de rester dans un GridManager au sein d'un conteneur donné (exemple de conteneur par excellence : Frame).

Source / Exemple :


# -*- coding:utf-8 -*-
from Tkinter import *

class Application:
    def __init__(self):
        self.root=Tk()
        self.root.title('Equation standard de la droite')

        self.root.geometry("600x400")

        self.barreEtat = Label(self.root, text="", bd=1, relief=SUNKEN, anchor=W)
        self.barreEtat.pack(side=BOTTOM, fill=X)

        # initialisation des trois variables principales :
        self.unite = 14.0 # pixels
        self.a = 1.0
        self.b = 1.0

        # Frame contenant les scales et utilisant Grid pour le positionnement de ses élements internes 
        self.c2 = Frame(self.root)
        self.c2.pack(side=RIGHT, fill=BOTH)

        label_unite = Label(self.c2, text="Unité")
        label_unite.grid(row=0, column=0)
        label_a = Label(self.c2, text="a")
        label_a.grid(row=0, column=1)
        label_b = Label(self.c2, text="b")
        label_b.grid(row=0, column=2)

        self.control_unite = Scale(self.c2, from_ = 10, to = 100, resolution = 1.0, orient = VERTICAL, length = 300, command = self.on_size_window)
        self.control_unite.set(self.unite)
        self.control_unite.grid(row=1, column=0)

        self.control_a = Scale(self.c2, from_ = -10, to = 10, resolution = 0.1, orient = VERTICAL, length = 300, command = self.on_size_window)
        self.control_a.set(self.a)
        self.control_a.grid(row=1, column=1)

        self.control_b = Scale(self.c2, from_ = -10, to = 10, resolution = 0.1, orient = VERTICAL, length = 300, command = self.on_size_window)
        self.control_b.set(self.b)
        self.control_b.grid(row=1, column=2)

        self.label_equat = Label(self.c2, text="y = ax + b")
        self.label_equat.grid(row=2, column=0, columnspan=3)
        equation_reelle = "y = "+str(self.a)+" x + "+str(self.b)
        self.label_equat2 = Label(self.c2, text=equation_reelle)
        self.label_equat2.grid(row=3, column=0, columnspan=3)

        la = 600
        ha = 400
        # nouvelles coordonnées de l'origine du graphe :
        self.x0 = la/2
        self.y0 = ha/2

        self.c = Canvas(self.root, bg="white", width=la, height=ha)
        self.c.pack(side=TOP, fill=BOTH, expand=YES)

        self.c.create_line(0, (ha/2), la, (ha/2), arrow=LAST, tag="axex")
        self.c.create_line((la/2), ha, (la/2), 0, arrow=LAST, tag="axey")

        # les deux points extrêmes pour tracer la droite :
        droite_x0 = -(la/2)/self.unite
        droite_y0 = self.equation(droite_x0)
        droite_x1 = (la/2)/self.unite
        droite_y1 = self.equation(droite_x1)
        self.c.create_line( self.crx(droite_x0), self.cry(droite_y0), self.crx(droite_x1), self.cry(droite_y1), fill="red", tag="droite")

        # les graduations sont gardées en mémoire !
        self.graduation_x_pos = []
        for i in range(1,21):
            z = self.c.create_line( self.x0 + i*self.unite, self.y0-4, self.x0 + i*self.unite, self.y0+4)
            self.graduation_x_pos.append(z)
             
        self.graduation_x_neg = []
        for i in range(1,21):
            z = self.c.create_line( self.x0 - i*self.unite, self.y0-4, self.x0 - i*self.unite, self.y0+4)
            self.graduation_x_neg.append(z)

        self.graduation_y_pos = []
        for i in range(1,21):
            z = self.c.create_line( self.x0-4, self.y0 + i*self.unite, self.x0+4, self.y0 + i*self.unite)
            self.graduation_y_pos.append(z)

        self.graduation_y_neg = []
        for i in range(1,21):
            z = self.c.create_line( self.x0-4, self.y0 - i*self.unite, self.x0+4, self.y0 - i*self.unite)
            self.graduation_y_neg.append(z)

        self.root.bind("<Configure>", self.on_size_window)

        self.root.mainloop()

    def on_size_window(self, event=None):
        # on récupère les nouvelles tailles du canvas :
        la = self.c.winfo_width()
        ha = self.c.winfo_height()
        # on récupère la nouvelle origine :
        self.x0 = la/2
        self.y0 = ha/2
        # on indique ces dimensions dans la barre d'état :
        taille = " Canvas :  larg. = "+str(la)+"     haut. = "+str(ha)
        self.barreEtat.config(text=taille)

        self.unite = float(self.control_unite.get())
        self.a = float(self.control_a.get())
        self.b = float(self.control_b.get())

        # on replace les deux axes :
        self.c.coords("axex", 0, (ha/2), la, (ha/2))
        self.c.coords("axey", (la/2), ha, (la/2), 0)

        # on replace les graduations depuis la nouvelle origine :
        j = 1
        for z in self.graduation_x_pos:
            self.c.coords( z, self.x0 + j*self.unite, self.y0-4, self.x0 + j*self.unite, self.y0+4)
            j = j + 1
        j = 1
        for z in self.graduation_x_neg:
            self.c.coords( z, self.x0 - j*self.unite, self.y0-4, self.x0 - j*self.unite, self.y0+4)
            j = j + 1
        j = 1
        for z in self.graduation_y_pos:
            self.c.coords( z, self.x0-4, self.y0 + j*self.unite, self.x0+4, self.y0 + j*self.unite)
            j = j + 1
        j = 1
        for z in self.graduation_y_neg:
            self.c.coords( z, self.x0-4, self.y0 - j*self.unite, self.x0+4, self.y0 - j*self.unite)
            j = j + 1

        # les deux points extrêmes pour tracer la droite :
        droite_x0 = -(la/2)/self.unite
        droite_y0 = self.equation(droite_x0)
        droite_x1 = (la/2)/self.unite
        droite_y1 = self.equation(droite_x1)
        self.c.coords("droite", self.crx(droite_x0), self.cry(droite_y0), self.crx(droite_x1), self.cry(droite_y1))

        if (self.a != 0.0):
            premiere_partie = str(self.a)+" x "
        else:
            premiere_partie = ""

        if (self.b < 0):
            seconde_partie = "- "+str(abs(self.b))
        elif (self.b == 0.0):
            seconde_partie = ""
        else:
            seconde_partie = "+ "+str(self.b)

        if (self.a==0.0) and (self.b==0.0):
            premiere_partie = "0"

        equation_reelle = "y = "+premiere_partie+seconde_partie
        self.label_equat2.config(text=equation_reelle)

    def equation(self, coord_x):
        # donne le résultat y de la fonction de la droite affichée
        return ((coord_x * self.a) + self.b)

    def crx(self, coord_x):
        # cette fonction transforme une coordonnée unitaire en un réel positionnement
        # en pixels sur l'écran
        return self.x0 + coord_x * self.unite

    def cry(self, coord_y):
        # cette fonction transforme une coordonnée unitaire en un réel positionnement
        # en pixels sur l'écran
        # les Y d'un canvas étant positif en allant vers le bas, il a fallu faire un -
        # pour faire fonctionner l'axe des Y avec les positifs vers le haut !
        return self.y0 - coord_y * self.unite
        

# départ du programme principal :
if __name__ == '__main__':
    app = Application()

Conclusion :


J'espère que ce petit bout de code servira à d'autres débutants, et à appréhender notamment les GridManager Pack et Grid...

Codes Sources

A voir également

Ajouter un commentaire

Commentaires

Messages postés
29
Date d'inscription
jeudi 10 juillet 2003
Statut
Membre
Dernière intervention
25 octobre 2009

J'ai fini mon petit didacticiel sur le sujet :

http://www.gigante.be/python/didact_002.php

;o)
Messages postés
336
Date d'inscription
samedi 26 novembre 2005
Statut
Membre
Dernière intervention
8 novembre 2011
1
Ok compris !
Messages postés
29
Date d'inscription
jeudi 10 juillet 2003
Statut
Membre
Dernière intervention
25 octobre 2009

En fait, les GridManager sont des outils qui permettent de créer, en les combinant, n'importe quelle interface même ultra-complexe en Tkinter. La règle n'est pas celle-ci :
"Vous ne pouvez utiliser qu'un seul GridManager dans une application."
Mais elle est la suivante :
"Vous ne pouvez utiliser qu'un seul GridManager dans un conteneur donné pour y placer ses enfants directs."

Tu peux donc très bien avoir un frame principal f1, qui contient deux frames f2 et f3 placées avec pack par exemple. Et dans les frames enfants f2 et f3, tu peux de nouveau choisir d'autres GridManager pour chacun d'eux, pourvu que tu restes dans le gridManager choisi. Tu peux donc choisir pour les enfants directs de f2 de les placer avec pack et de placer les enfants directs de f3 avec Grid. Voici un exemple sommaire pour illustrer ça :

# -*- coding:utf-8 -*-
from Tkinter import *

application = Tk()
application.title("Pack testeur")

frame1 = Frame(application, bg="yellow", width=300, height=300, padx=10, pady=10)
frame1.pack(side=RIGHT,fill=Y, expand=0)

# il reste la cavité sur la gauche...

frame2 = Frame(application, bg="blue", width=300, height=150)
frame2.pack(side=TOP, fill=BOTH)

# il reste une cavité en bas à gauche...

frame3 = Frame(application, bg="red", width=300, height=150)
frame3.pack(side=BOTTOM, fill=BOTH, expand=1)

# le frame suivant sera placé au-dessus du frame 3 !

frame4 = Frame(application, bg="green", width=200, height=100)
frame4.pack(side=TOP, fill=NONE, expand=1)

# A l'intérieur du frame 1, si on décide d'utiliser grid, on peut le faire, mais on n'utilise
# alors que grid exclusivement pour les enfants placés dans frame1.

lab1 = Label(frame1, text="label un" ).grid(row=0,column=0)
ent1 = Entry(frame1).grid(row=0,column=1)
lab2 = Label(frame1, text="label deux" ).grid(row=1,column=0)
ent2 = Entry(frame1).grid(row=1,column=1)
lab3 = Label(frame1, text="label trois").grid(row=2,column=0)
ent3 = Entry(frame1).grid(row=2,column=1)

application.mainloop()
Messages postés
336
Date d'inscription
samedi 26 novembre 2005
Statut
Membre
Dernière intervention
8 novembre 2011
1
donc en ajoutant les arguments side et fill dans pack on peut l'utiliser avec du grid ? Ou j'ai sauté quelque chose ?

Sinon quel est l'intérêt de mixer les deux ? Sinon c'est bon a savoir ^^

Sinon pourquoi adopter un algorithme si complexe alors que tu pourrais simplement faire un traceur de fonction, ce qui permettrai la compatibilité avec des équation de degré plus élevés , simplement en ajoutant une glissière... Et même avoir une glissière qui indique le degré souhaité...

Enfin, petite remarque, mais c'est très personnel, normalement __init__ sert juste pour l'initialisation, pas pour le programme lui même.. Je pense que la convention veut qu'on introduise une autre fonction... Mais bon c'est _vraiment_ un détail.

Sinon l'idée est bonne ^^, bravo !
Messages postés
29
Date d'inscription
jeudi 10 juillet 2003
Statut
Membre
Dernière intervention
25 octobre 2009

Disons que les curseurs a et b ne sont que des moyens de manipuler la valeur de a et de b... En soi, il n'y a pas de raison particulière de mettre le négatif en bas et le positif en haut... Mais je crois changer le sens est faisable quand même : voir dans les propriétés des widgets scale.

Si tu as des questions concernant une partie du code, n'hésite pas à la poser...

Merci pour ta cotation plus que généreuse...
;-)
Afficher les 11 commentaires

Vous n'êtes pas encore membre ?

inscrivez-vous, c'est gratuit et ça prend moins d'une minute !

Les membres obtiennent plus de réponses que les utilisateurs anonymes.

Le fait d'être membre vous permet d'avoir un suivi détaillé de vos demandes et codes sources.

Le fait d'être membre vous permet d'avoir des options supplémentaires.