Outils pour utilisateurs

Outils du site


issue96:python

Différences

Ci-dessous, les différences entre deux révisions de la page.

Lien vers cette vue comparative

Les deux révisions précédentesRévision précédente
Prochaine révision
Révision précédente
issue96:python [2015/05/23 19:52] fredphil91issue96:python [2015/05/24 11:39] (Version actuelle) fredphil91
Ligne 96: Ligne 96:
 Pour ce qui est de la méthode DrawLine dont nous venons de parler, il n'y a rien d'extraordinaire. Nous dessinons une ligne de 580 pixels qui descend le long du Frame, commençant à X=60 et terminant à X=700. Ensuite nous traçons une ligne qui part de X=60, Y=580 et va jusqu'à X=60, Y=80. Cette ligne est tirée de bas en haut, mais vous pourriez la tracer de haut en bas. Pour ce qui est de la méthode DrawLine dont nous venons de parler, il n'y a rien d'extraordinaire. Nous dessinons une ligne de 580 pixels qui descend le long du Frame, commençant à X=60 et terminant à X=700. Ensuite nous traçons une ligne qui part de X=60, Y=580 et va jusqu'à X=60, Y=80. Cette ligne est tirée de bas en haut, mais vous pourriez la tracer de haut en bas.
  
-Ensuite, nous nous occuperons de la routine DessinerTitre. Une fois encore, nous passons le dc de la fenêtre ainsi que le texte que nous voulons dessiner. Durant le processus, pensez que l'on dessine du texte plutôt que l'afficher. Ce n'est pas grand chose, mais ça aide.+Ensuite, nous nous occuperons de la routine DessineTitre. Une fois encore, nous passons le dc de la fenêtre ainsi que le texte que nous voulons dessiner. Durant le processus, pensez que l'on dessine du texte plutôt que l'afficher. Ce n'est pas grand chose, mais ça aide.
  
 Cette routine est plus longue que la plupart des autres, mais c'est dû en partie aux commentaires que j'ai mis. Les deux premières lignes initialisent la police et le style d'écriture que nous utiliserons. Dans la première ligne (SetFont), nous définissons la police qui sera celle par défaut, 20 points, pas italique et grasse. Ensuite, nous déclarons noire la couleur du crayon et la largeur à 20. Maintenant nous devons estimer la largeur du texte pour le centrer dans la boîte. Nous obtenons cette information en appelant GetFullTextExtent avec le texte que nous voulons dessiner, en donnant la police et sa taille, la largeur du trait et tout ce que nous venons de définir. Le tuple qui est retourné contient Width, Height, Decent (largeur, hauteur, décalage - jusqu'à quel point des lettres comme « g » ou « y » passeront sous la ligne de base) et toute espace initiale. Pour nos besoins, seule la largeur nous importe. Si vous vous souvenez, nous avons défini une largeur de boîte de 790 dans la fonction <nowiki>__init__</nowiki>. Pour trouver le centre de notre texte dans la boîte, nous prenons la largeur de la boîte moins la largeur du texte et nous divisons par 2. Ce sera la valeur X à utiliser pour tracer le texte. Enfin, nous réinitialisons la taille du crayon et la couleur. Plutôt que d'utiliser des valeurs par défaut prises on ne sait où, nous aurions pu appeler la fonction dc.GetPen avant de commencer, mais quand j'ai commencé le projet, je n'y ai pas pensé. Cette routine est plus longue que la plupart des autres, mais c'est dû en partie aux commentaires que j'ai mis. Les deux premières lignes initialisent la police et le style d'écriture que nous utiliserons. Dans la première ligne (SetFont), nous définissons la police qui sera celle par défaut, 20 points, pas italique et grasse. Ensuite, nous déclarons noire la couleur du crayon et la largeur à 20. Maintenant nous devons estimer la largeur du texte pour le centrer dans la boîte. Nous obtenons cette information en appelant GetFullTextExtent avec le texte que nous voulons dessiner, en donnant la police et sa taille, la largeur du trait et tout ce que nous venons de définir. Le tuple qui est retourné contient Width, Height, Decent (largeur, hauteur, décalage - jusqu'à quel point des lettres comme « g » ou « y » passeront sous la ligne de base) et toute espace initiale. Pour nos besoins, seule la largeur nous importe. Si vous vous souvenez, nous avons défini une largeur de boîte de 790 dans la fonction <nowiki>__init__</nowiki>. Pour trouver le centre de notre texte dans la boîte, nous prenons la largeur de la boîte moins la largeur du texte et nous divisons par 2. Ce sera la valeur X à utiliser pour tracer le texte. Enfin, nous réinitialisons la taille du crayon et la couleur. Plutôt que d'utiliser des valeurs par défaut prises on ne sait où, nous aurions pu appeler la fonction dc.GetPen avant de commencer, mais quand j'ai commencé le projet, je n'y ai pas pensé.
Ligne 124: Ligne 124:
 If it is a tuple, we create two lists, one for the dates and one for the values. We then walk the list splitting the data between the two lists. Once we have that done, we then find the highest value (max(self.ValList)) and send it the roundup function (shown above) so we can get our scaling value. If the data isn’t in tuples, then we clear BOTH lists and do the same steps as above.** If it is a tuple, we create two lists, one for the dates and one for the values. We then walk the list splitting the data between the two lists. Once we have that done, we then find the highest value (max(self.ValList)) and send it the roundup function (shown above) so we can get our scaling value. If the data isn’t in tuples, then we clear BOTH lists and do the same steps as above.**
  
-Disons que pour un calcul donné notre valeur maximum sera 395. Nous pourrions simplement tracer une barre de 395 pixels de haut pour représenter la valeur. Au calcul suivant, ce maximum est de 2 345. Si nous essayons de tracer la barre à sa pleine hauteur, ça dépassera le haut du graphe. De façon à montrer cette valeur, je dois l'arrondir au 500 le plus près au-dessus, c'est-à-dire 2 500, à prendre comme valeur la plus haute de l'axe. Nous pouvons alors mettre à l'échelle en divisant 2 500 par 500 soit un facteur d'échelle de 4. Maintenant, si nous prenons nos données et que nous divisons chacune par le facteur d'échelle, nous pouvons tracer les valeurs, qui tiendront dans le graphe.+Disons que pour un calcul donné notre valeur maximum sera 395. Nous pourrions simplement tracer une barre de 395 pixels de haut pour représenter la valeur. Au calcul suivant, ce maximum est de 2 345. Si nous essayons de tracer la barre à sa pleine hauteur, ça dépassera le haut du graphe. De façon à montrer cette valeur, je dois l'arrondir au 500 le plus près au-dessus, c'est-à-dire 2 500, à prendre comme valeur la plus haute de l'axe. Nous pouvons alors mettre à l'échelle en divisant 2 500 par 500 soit un facteur d'échelle de 5. Maintenant, si nous prenons nos données et que nous divisons chacune par le facteur d'échelle, nous pouvons tracer les valeurs, qui tiendront dans le graphe.
  
-Aussi (montré en haut à droite), nous avons besoin de trouver la valeur la plus haute dans nos données et de l'arrondir au multiple de 500 supérieur le plus proche. Ainsi, pour 375, ce sera 500 ; pour 3 750, ce sera 4 000 et ainsi de suite.+Aussi (voir en haut à droite), nous avons besoin de trouver la valeur la plus haute dans nos données et de l'arrondir au multiple de 500 supérieur le plus proche. Ainsi, pour 375, ce sera 500 ; pour 3 750, ce sera 4 000 et ainsi de suite.
  
-Ensuite, nous devons décider quel type de données nous allons utiliser. Nous verrons plus loin dans le programme que je fournis deux types différents de données dans les listes. L'un assure que les plages de dates que nous utiliserons, le long de l'axe des X, sont les données pour octobre, mais vous pouvez facilement suivre le code (montré dans un petit instant) et changer pour le mois que vous voulez. La seconde liste de données est plus générique et fournit à la fois une date et une valeur comme une liste de tuples. Ceci permet de passer des données de n'importe quelle période. La date est une chaîne et la valeur est soit un entier, soit en virgule flottante. La fonction SetData regarde la première valeur de la liste de données et détermine si c'est un tuple. Si c'est le cas, nous supposons que la structure de la liste correspond à la seconde option, sinon, c'est la première.+Ensuite, nous devons décider quel type de données nous allons utiliser. Nous verrons plus loin dans le programme que je fournis deux types différents de données dans les listes. L'un assure que les plages de dates que nous utiliserons, le long de l'axe des X, sont les données pour octobre, mais vous pouvez facilement suivre le code (montré dans un petit instant) et changer pour le mois que vous voulez. La seconde liste de données est plus générique et fournit à la fois une date et une valeur comme une liste de tuples. Ceci permet de passer des données de n'importe quelle période. La date est une chaîne et la valeur est soit un entier, soit en virgule flottante. La fonction ReglerDonnees regarde la première valeur de la liste de données et détermine si c'est un tuple. Si c'est le cas, nous supposons que la structure de la liste correspond à la seconde option, sinon, c'est la première.
  
-Si c'est un tuple, nous créons deux listes, une pour les dates et une pour les valeurs. Ensuite, nous parcourons la liste en la séparant en deux listes. Une fois cela fait, nous trouvons la plus haute valeur (max(Self.ValList)) et nous lançons la fonction d'arrondi (montrée ci-dessus) pour déterminer notre facteur d'échelle. Si les données ne sont pas en tuples, nous effaçons les DEUX listes et faisons les mêmes étapes qu'au-dessus.+Si c'est un tuple, nous créons deux listes, une pour les dates et une pour les valeurs. Ensuite, nous parcourons la liste en la séparant en deux listes. Une fois cela fait, nous trouvons la plus haute valeur (max(Self.ListeValeurs)) et nous lançons la fonction d'arrondi (voir ci-dessus) pour déterminer notre facteur d'échelle. Si les données ne sont pas en tuples, nous effaçons les DEUX listes et faisons les mêmes étapes qu'au-dessus.
  
 **Now that we have our scale value we can draw our tics and the values that will represent our vertical axis. We again use a for loop, this time from 580 to 30 with a step of -50 to work our way up the line and draw a 10 pixel line.  Next we set the font (just in case it gets changed somehow) and draw the value of each of our values.  **Now that we have our scale value we can draw our tics and the values that will represent our vertical axis. We again use a for loop, this time from 580 to 30 with a step of -50 to work our way up the line and draw a 10 pixel line.  Next we set the font (just in case it gets changed somehow) and draw the value of each of our values. 
Ligne 142: Ligne 142:
 Maintenant que nous avons notre facteur d'échelle, nous pouvons tracer les traits d'échelle et les valeurs qui vont représenter l'axe vertical. Nous utilisons à nouveau une boucle for, cette fois-ci de 580 à 30 par pas de -50 le long de la ligne, en traçant des traits de 10 pixels. Après, nous configurons la police (juste au cas où elle aurait changé) et nous dessinons chaque valeur. Maintenant que nous avons notre facteur d'échelle, nous pouvons tracer les traits d'échelle et les valeurs qui vont représenter l'axe vertical. Nous utilisons à nouveau une boucle for, cette fois-ci de 580 à 30 par pas de -50 le long de la ligne, en traçant des traits de 10 pixels. Après, nous configurons la police (juste au cas où elle aurait changé) et nous dessinons chaque valeur.
  
-Maintenant, nous regardons les routines qui créeront les traits d'échelle pour les dates le long de l'axe des X si nous choisissons d'avoir une simple liste de données sans inclure les dates. Nous avons deux routines de renfort, une appelée DateToStamp et l'autre Timestamp2Date (Oui, j'étais un peu fainéant quand j'ai écrit celle-ci.) Plutôt que de passer par un paquet de routines DateTime compliquées pour déterminer le nombre de jours d'un mois donné, je vais utiliser une date de début et une date de fin et convertir les deux en horodatage Unix pour obtenir le bon jour du mois dans la séquence. Je vous ai montré la routine DateToStamp précédemment et la routine Timestamp2Date exécute le processus inverse.+Maintenant, regardons les routines qui créeront les traits d'échelle pour les dates le long de l'axe des X si nous choisissons d'avoir une simple liste de données sans inclure les dates. Nous avons deux routines de renfort, une appelée DateToStamp et l'autre Timestamp2Date (Oui, j'étais un peu fainéant quand j'ai écrit celle-ci.) Plutôt que de passer par un paquet de routines DateTime compliquées pour déterminer le nombre de jours d'un mois donné, je vais utiliser une date de début et une date de fin et convertir les deux en horodatage Unix pour obtenir le bon jour du mois dans la séquence. Je vous ai montré la routine DateToStamp précédemment et la routine Timestamp2Date exécute le processus inverse.
  
 La routine suivante prend les dates de début et de fin, comme présenté auparavant, les convertit en horodatage Unix, puis ajoute 86 400 (le nombre de traits dans une période de 24 heures) pour être sûr d'avoir la dernière valeur de la séquence, puis utilise une autre boucle for pour dessiner le texte en biais où nous le voulons. La routine suivante prend les dates de début et de fin, comme présenté auparavant, les convertit en horodatage Unix, puis ajoute 86 400 (le nombre de traits dans une période de 24 heures) pour être sûr d'avoir la dernière valeur de la séquence, puis utilise une autre boucle for pour dessiner le texte en biais où nous le voulons.
  
-Nous arrivons maintenant au gestionnaire d'événements OnPaint qui appelle toute les routines d'aide que nous devons gérer. Souvenez-vous, quand nous avons utilisé la routine PaintDC, chaque fois que la frame est bougée, redimensionnée, couverte ou découverte, le gestionnaire d'événement OnPaint est appelé, assurant de ce fait que notre graphe sera persistant.+Nous arrivons maintenant au gestionnaire d'événements OnPaint qui appelle toute les routines utilitaires que nous devons gérer. Souvenez-vous, en utilisant la routine PaintDC, à chaque fois que la fenêtre est bougée, redimensionnée, couverte ou découverte, le gestionnaire d'événement OnPaint est appelé, assurant de ce fait que notre graphe sera persistant.
  
 **First (shown on the next page, top left) we get an instance of our dc, and then we call the DrawBox, DrawAxis, DrawTitle, and the DrawDateTicks routines. We then determine if the DateList list (created in the SetData routine called from __init__ routine) is empty or if it has dates for us to draw. If so, we call the DrawDates routine with the proper values. We then call the DrawValues routine and finally the DrawBars routine. Now you should understand why I broke everything down into little bitty chunks. **First (shown on the next page, top left) we get an instance of our dc, and then we call the DrawBox, DrawAxis, DrawTitle, and the DrawDateTicks routines. We then determine if the DateList list (created in the SetData routine called from __init__ routine) is empty or if it has dates for us to draw. If so, we call the DrawDates routine with the proper values. We then call the DrawValues routine and finally the DrawBars routine. Now you should understand why I broke everything down into little bitty chunks.
Ligne 156: Ligne 156:
 Until next time, have fun coding.** Until next time, have fun coding.**
  
-D'abord (présenté en haut à gauche de la page suivante), nous obtenons une instance de notre dc, puis nous appelons les routines DrawBoxDrawAxisDrawTitle et DrawDateTicks. Ensuite, nous déterminons si la liste Datelist (créée dans la routine SetData appelée par la routine <nowiki>__init__</nowiki>) est vide ou si des dates peuvent en être extraites. Si c'est le cas, nous appelons la routine DrawDates avec les bonnes valeurs. Puis nous appelons la routine DrawValues et, enfin, la routine DrawBars. Maintenant, vous devriez comprendre pourquoi j'ai découpé le sujet en tout petits bouts.+D'abord (voir en haut à gauche de la page suivante), nous obtenons une instance de notre dc, puis nous appelons les routines DessineBoiteDessineAxeDessineTitre et DessineBarresDates. Ensuite, nous déterminons si la ListeDates (créée dans la routine ReglerDonnees appelée par la routine <nowiki>__init__</nowiki>) est vide ou si des dates peuvent en être extraites. Si c'est le cas, nous appelons la routine DessineDates avec les bonnes valeurs. Puis nous appelons la routine DessineValeurs et, enfin, la routine DessineBarres. Maintenant, vous devriez comprendre pourquoi j'ai découpé le sujet en tout petits bouts.
  
-La dernière chose que nous avons à regarder est la routine d'exécution. Vous vous souvenez probablement que le « if <noxiki>__name__</nowiki> == “<nowiki>__main__</nowiki>” » fonctionne si nous appelons le programme seul plutôt que comme une bibliothèque. Les deux lignes suivantes sont les données fictives que j'ai utilisées pour tester le programme. Vous pouvez commenter la première et lancer le programme avec la seconde ligne qui est celle qui utilise le tuple. Les trois dernières lignes instanceront les routines wxPython, puis la classe Line et enfin appellera la routine wxPython app.MainLoop pour lancer la frame.+La dernière chose que nous avons à regarder est la routine d'exécution. Vous vous souvenez probablement que le « if <nowiki>__name__</nowiki> == “<nowiki>__main__</nowiki>” » fonctionne si nous appelons le programme seul plutôt que comme une bibliothèque. Les deux lignes suivantes sont les données fictives que j'ai utilisées pour tester le programme. Vous pouvez commenter la première et lancer le programme avec la seconde ligne qui est celle qui utilise le tuple. Les trois dernières lignes instancieront les routines wxPython, puis la classe Ligne et enfin appellera la routine wxPython app.MainLoop pour lancer la fenêtre.
  
-Voila ! Nos programme et bibliothèque personnels de graphe/tableau. J'ai mis le code complet sur pastebin à : http://pastebin.com/m2feeh5P.+Et voilà notre programme et notre bibliothèque personnalisés de graphe/tableau. J'ai mis le code complet sur pastebin à : http://pastebin.com/fJ00bhud.
  
 Jusqu'à la prochaine fois, amusez-vous bien à coder. Jusqu'à la prochaine fois, amusez-vous bien à coder.
 +
 +====== code encadré orangé page 17 haut ======
 +
 +class Ligne(wx.Frame):
 +    def __init__(self, parent, id, TitreFenetre, DonneesEntrantes, TitreGraphe):
 +        wx.Frame.__init__(self, parent, id, TitreFenetre, size=(1024, 768))
 +        self.Bind(wx.EVT_PAINT, self.OnPaint)
 +        self.LargeurBoite = 790
 +        self.HauteurBoite = 690
 +        self.TitreGraphe = TitreGraphe
 +        self.donnees = []
 +        self.ReglerDonnees(DonneesEntrantes)
 +        self.Centre()
 +        self.Show(True)
 +
 +====== page 17 milieu ======
 +
 +modifier uniquement la 1ère ligne du code :
 +    def DessineBoite(self,dc):
 +
 +puis traduire le texte en noir (et laisser la dernière ligne en orange) :
 +
 +C'est plutôt simple. On passe le dc de la fenêtre puis on dessine 4 lignes.
 +Les paramètres de la fonction DrawLine sont :
 +
 +====== page 17 bas ======
 +
 +    def DessineAxe(self,dc):
 +        # Horizontal
 +        dc.DrawLine(60,580,700,580)
 +        # Vertical
 +        dc.DrawLine(60,580,60,80)
 +
 +====== page 18 haut ======
 +
 +    def DessineTitre(self,dc,txt):
 +        dc.SetFont(wx.Font(20,wx.DEFAULT,wx.NORMAL,wx.BOLD))
 +        dc.SetPen(wx.Pen(wx.NamedColour('black'),20))
 +        # Recupere la longueur du texte a dessiner
 +        vals = dc.GetFullTextExtent(txt)
 +        # Retourne (Largeur,hauteur,Decalage,espacementInitial)
 +        # Recupere la position gauche (x) pour centrer le texte
 +        txtleft = (self.LargeurBoite-vals[0])/2
 +        dc.DrawText(txt,txtleft,30)
 +        # Raz taille et couleur du stylo
 +        dc.SetPen(wx.Pen(wx.NamedColour('black'),2))
 +
 +====== page 18 milieu ======
 +
 +modifier juste la 1ère ligne :
 +    def DessineBarresDates(self,dc,dcount):
 +
 +====== page 18 bas ======
 +
 +modifier juste la 1ère ligne :
 +    def DessineTexteRot(self,dc,txt,x,y):
 +
 +====== page 19 haut ======
 +
 +    #==================================
 +    # Arrondi au 500 le plus proche
 +    #==================================
 +    def arrondi(self,x):
 +        return int(math.ceil(x/500.0))*500
 +
 +====== page 19 milieu ======
 +
 +    def ReglerDonnees(self,DonneesAUtiliser):
 +        if type(DonneesAUtiliser[1]) is tuple:
 +            self.ListeDates=[]
 +            self.ListeValeurs=[]
 +            for l in DonneesAUtiliser:
 +                self.ListeDates.append(l[0])
 +                self.ListeValeurs.append(l[1])
 +            self.ValeurMax = self.arrondi(max(self.ListeValeurs))
 +            self.ValeurEchelle = self.ValeurMax/500
 +        else:
 +            self.ListeValeurs=[]
 +            self.ListeDates=[]
 +            for l in DonneesAUtiliser:
 +                self.ListeValeurs.append(l)
 +            self.ValeurMax = self.arrondi(max(self.ListeValeurs))
 +            self.ValeurEchelle = self.ValeurMax/500
 +
 +====== page 19 bas ======
 +
 +modifier juste la 1ère ligne :
 +    def DessineValeurs(self,dc):
 +
 +et la dernière :
 +            c2 = c2 + (50 * self.ValeurEchelle)
 +
 +====== page 20 haut ======
 +
 +modifier juste la 1ère ligne :
 +    def DessineBarres(self,dc):
 +
 +====== page 20 milieu ======
 +
 +    #==================================
 +    #  Convertit dd/mm/yy en timestamp unix
 +    #==================================
 +    def DateToStamp(self,x):
 +        x = x+" 00:00:00"
 +        return(time.mktime(time.strptime(x, "%d/%m/%Y %H:%M:%S")))
 +    #==================================
 +    #  Convertit un horodatage unix en dd/mm/yy
 +    #==================================
 +    def Timestamp2Date(self,tstmp):
 +        return datetime.fromtimestamp(int(tstmp)).strftime('%d/%m/%Y')
 +
 +====== page 20 bas ======
 +
 +    #==================================
 +    #  Dessine les dates en biais
 +    #==================================
 +    def DessineDates(self,dc,startdate,enddate):
 +        sd = int(self.DateToStamp(startdate))
 +        ed = int(self.DateToStamp(enddate))
 +        ed = ed + 86400
 +        stp = 1
 +        for cntr in range(sd,ed,86400):
 +            dt = self.Timestamp2Date(cntr)
 +            self.DessineTexteRot(dc,dt,65+(stp*20),600)
 +            stp = stp + 1
 +
 +====== page 21 milieu ======
 +
 +    #==================================
 +    #  Routine principale
 +    #==================================
 +    def OnPaint(self,event):
 +        dc = wx.PaintDC(self)
 +        self.DessineBoite(dc)
 +        self.DessineAxe(dc)
 +        self.DessineTitre(dc,self.TitreGraphe)
 +        # Barres de dates et dates
 +        self.DessineBarresDates(dc,31)
 +        leng = len(self.ListeDates)
 +        if leng > 0:
 +            sd = self.ListeDates[0]
 +            ed = self.ListeDates[4]
 +            self.DessineDates(dc,sd,ed)
 +        else:
 +            self.DessineDates(dc,"02/01/2015","03/01/2015")
 +        # Barres de valeurs - Dessine 10 barres 
 +        self.DessineValeurs(dc)
 +        # Enfin on dessine les barres de donnees
 +        self.DessineBarres(dc)
 +
 +====== page 21 bas ======
 +
 +modifier « data » en « donnees » sur les 2 premières lignes, et remplacer la ligne :
 +
 +    Ligne(None, -1, 'Bar Chart',donnees,"Ventes mensuelles - Colorado Springs")
 +
issue96/python.1432403527.txt.gz · Dernière modification : 2015/05/23 19:52 de fredphil91