Outils pour utilisateurs

Outils du site


calculext

Ceci est une ancienne révision du document !


La CALCULEXT: Calculatrice facilement extensible par les utilisateurs

[Tyrtamos déc 2007 version 1.30]

cette version 1.30 doit être préférée à l'ancienne version 1.22!

NB: Passage de la version 1.22 à 1.30 (complément dans le fichier lisezmoi.txt de l'archive v 1.30 à télécharger):

  • reprise du code de toutes les fonctions pour améliorer la vérification des paramètres, et l'efficacité du code:
  • Petites modifications de tous les modules pour les rendre compatibles avec la Calculext en ligne (voir ci-dessous).
  • ajout de plusieurs fonctions, en particulier du module probabilité: par exemple, on peut maintenant calculer les fractiles de la loi normale (=trouver x connaissant p) et générer une liste de valeurs au hasard, distribuées selon la loi normale.
  • réécriture de l'algorithme qui permet l'arrêt du calcul sur demande s'il est trop long.
  • amélioration du menu flottant.

Création d'une Calculext en ligne, accessible sans installation par un simple navigateur web (http://calculext.jpvweb.com). Son code n'est pas diffusé pour des raisons de sécurité, mais elle fait pratiquement tout ce que fait la calculext installée sur PC, et elle utilise tous ses modules sans modification. Ses limites sont mentionnées dans son fichier d'aide.

Problématique

S'il y a bien quelque chose d'énervant, c'est d'avoir un ordinateur puissant devant soi, et d'être obligé de sortir une calculatrice de poche pour faire des petits calculs.

Oui, je sais que tous les systèmes d'exploitation ont des calculatrices de poche simulées, mais pourquoi cliquer plein de boutons alors qu'on a devant soit un clavier de 104 touches?

Et puis devant une calculatrice de poche, réelle ou simulée, il y a toujours une limite de précision et des fonctions qui manquent qu'on ne peut pas ajouter.

Et j'aimerais bien avoir la même calculatrice sous Windows et sous Linux!

Objectif

Voilà, mon objectif correspond bien à ces problèmes:

  • une petite calculatrice graphique toujours à l'écran et toujours prête à être utilisée,
  • permettant de recevoir une formule algébrique tapée au clavier et aussi complexe qu'on veut,
  • capable de recevoir des fonctions supplémentaires au fur et à mesure qu'on en a besoin,
  • fonctionnant de façon identique sous linux et sous Windows.

Mais si, c'est possible!

Présentation générale

Utilisation

La calculette, a cet aspect:

Sous Windows XP:

Et sous Linux:

L'utilisation est très simple:

  • On tape la formule algébrique sur la ligne blanche (qui a toujours le focus). On a bien sûr droit à toutes les fonctions d'édition, y compris le copier-coller.
  • Un clic droit de souris sur la zone de saisie fait apparaitre un menu flottant à 2 niveaux. Si vous sélectionnez une fonction dans le menu 2ème niveau, celle-ci s'insère à l'endroit du curseur de la zone de saisie. Si la fonction a des parenthèses, le curseur se positionne entre elles.

  • Pour lancer le calcul, c'est la touche “retour” du clavier, ou le clic sur le bouton “calculer”.
  • Le résultat s'affiche sur la ligne jaune, y compris quand c'est un message d'erreur.
  • la durée du calcul s'affiche à la fin de chaque calcul (en dessous du mot “Résultat”).
  • lorsqu'un résultat est trop long pour apparaitre complètement, l'ascenseur horizontal permet de naviguer dans la ligne.

Quand on a fait plusieurs calculs, on peut afficher sur la 1ère ligne de saisie (ce qui permet de ne pas les retaper):

  • le résultat du calcul précédent en tapant sur la touche “flèche en bas”.
  • les formules précédentes en tapant sur la touche “flèche en haut”.

Le calcul s'exécute en tâche de fond, ce qui a les avantages suivants:

  • l'utilisateur a tout de suite la main après le lancement d'un calcul long et peut, par exemple, lancer l'aide pendant l'exécution de ce calcul
  • on peut stopper un calcul trop long sans avoir à “tuer” la calculatrice (bouton “stopper” ou touche F2).
  • on peut arrêter la calculatrice normalement (croix en haut à droite de la fenêtre) même si un calcul long s'effectue.

Pour les nombres réels (dits “à virgule flottante”), les calculs sont faits avec la “double précision” de la bibliothèque “C”, c'est à dire 16 ou 17 chiffres significatifs. L'affichage est cependant réduit à 15 chiffres significatifs, pour éviter des résultats irritants comme 0.1 ⇒ 0.10000000000000001. Vous noterez qu'une erreur portant sur le 17ème chiffre après la virgule n'a aucune importance pratique. Et en fait, ce n'est pas une erreur de calcul, c'est un écart qui dépend de la façon dont les nombres réels sont stockés en mémoire dans l'ordinateur, et cet écart n'est pas spécifique à python qui utilise la bibliothèque C.

A noter que cette présentation limitées à 15 chiffres ne concerne que l'affichage: les calculs sont toujours faits avec la pleine précision.

On peut changer le mode d'affichage des nombres réels avec la fonction precision(n=-15) en modifiant la valeur de n (-15<=n<=+15):

  • avec n<0, on obtient un affichage “intelligent” avec abs(n) chiffres significatifs. C'est le mode d'affichage courant, et il convient bien aussi aux applications scientifiques.
  • avec n>=0, on obtient un affichage avec n chiffres après la virgule. Par exemple avec “precision(2)”, on aura des résultats financiers plus satisfaisants. Par contre, ce n'est pas un bon affichage pour les nombres avec exposants.
  • avec “precision()” sans paramètre, on revient à l'affichage par défaut avec n=-15.
  • le dernier chiffre à l'affichage est arrondi à la valeur la plus proche. Ex avec le mode par défaut: 1.111111111111114 s'affiche 1.11111111111111 et 1.111111111111116 s'affiche 1.11111111111112.

Cet affichage fonctionne aussi:

  • si ces nombres réels se trouvent à l'intérieur d'une liste ou d'un tuple, et ce, quelque soit sa profondeur (liste de liste de liste…).
  • dans les nombres complexes. Par exemple: complex(0.1,2) s'affiche (0.1+2j) et non (0.10000000000000001+2j)

On peut obtenir le même effet ponctuellement, c'est à dire sans changer le mode courant d'affichage, avec la fonction arrond(x,n). Par exemple, arrond(0.1,4) ⇒ “0.1000” ne change pas “nbchiffres” qui peut rester à -15.

La séquence de touches Ctle-R (“R” pour Résultat) sauvegarde le dernier résultat calculé sur disque, à la fin du fichier resultat.txt. Pour que l'action soit exécutée, il faut que le focus (=le curseur) soit sur l'une des 2 lignes (saisie ou résultat).

La séquence de touches Ctle-E (“E” pour Expressions) sauvegarde la liste de toutes les expressions calculées depuis le lancement de l'exécution de la calculatrice, complétée par le dernier résultat calculé. Cette liste est ajoutée au fichier resultat.txt. Pour que l'action soit exécutée, il faut que le focus (=le curseur) soit sur l'une des 2 lignes (saisie ou résultat).

Attention 1: pour l'introduction et l'affichage des nombres décimaux, la virgule est toujours remplacée par un point décimal: c'est 2.5 et pas 2,5!

Attention 2: la calculette a été configurée pour qu'une division entre 2 nombres entiers donne un résultat décimal et pas un entier tronqué. Par exemple, “10/3” donnent bien “3.33333333333” et pas “3”. C'est une configuration inhabituelle pour python, mais habituelle pour une calculatrice!. La division entière est toujours possible avec "//": l'expression "10//3" donne bien “3”.

Le fait que les calculs en entier et en décimal soit différents a un gros avantage: en calcul “entier”, le nombre de chiffres n'a pas d'autre limite que la mémoire!.

Prenons un exemple:

  • calcul en nombre réel de 2.0 à la puissance 200:
2.0**200 => 1.60693804425899e+060
  • et en calcul en entier:
2**200 => 1606938044258990275541962092341162602522202993782792835301376

Intéressant, non?

Le bouton “effacer” de même que la touche “esc” efface les 3 lignes (saisie, résultat et durée) et met le focus sur la zone de saisie.

Le bouton “aide” ou la touche F1 fait apparaitre la notice sous forme d'une page html dans le navigateur web par défaut. Si vous ajoutez des fonctions à votre calculette, je vous suggère de mettre à jour ce manuel.

Nouveauté de la version 1.2, vous pouvez demander l'aide sur une fonction donnée en tapant directement dans la zone de saisie, par exemple:

aide(bissextile)

ce qui affichera après exécution dans la zone des résultats:

"bissextile(a): dit si l'année donnée est bissextile ou non (True=oui, False=non)"

Pour arrêter la calculatrice, il suffit de “tuer la fenêtre”: cliquez sur la croix dans la case rouge en haut et à droite de la fenêtre, ou avec le menu en haut à gauche (Fermer) ou la touche “Alt-F4”.

Exemples de calculs pour la calculatrice de base

Les fonctions intégrées dans la calculette de base sont déjà très nombreuses, car ce sont toutes celles de python et des modules python importés.

fonctions et opérateurs de base

  • opérateurs classiques (+, -, *, /, //, **, %, abs()) ,
12+65-987+(78/6)**2 => -741.0

10 % 3 => 1  (fonction modulo = reste de la division entière de 10 par 3)


  • fonctions logiques (==, <>, >, >=, <, <=, and, or, not)
(2==2) and (5>4) => True

(2<>2) and (5>4) => False


  • opérateurs sur les bits (|, &, ^, ~, <<, >>)
2 << 1 => 4


  • fonctions de conversion (voir aussi le module “bibgene” pour conversion en binaire)
hex(12345) => "0x3039"

0x3039 => 12345

oct(12345) => "030071"

030071 => 12345


  • fonction “lambda”: si vous avez une expression complexe à calculer plusieurs fois avec des valeurs différentes, ça peut être assez pénible de changer les valeurs directement dans le texte de l'expression. Par exemple, la fonction:
sin(0.25698)*1/sqrt(2*pi)*log(1.25698)**(1.6987) => 0.00827261825741359

Vous voulez calculer cette fonction avec d'autres valeurs que 0.25698 (qui intervient à 2 endroits) et 1.6987. Vous faites:

sto("toto",lambda x,y: sin(x)*1/sqrt(2*pi)*log(1.25698)**(y)) 

A la place de “toto”, vous pouvez utiliser un nom de variable plus explicite :-D

Désormais, quand vous faites:

toto(0.25698,1.6987) => 0.00827261825741359

Vous obtenez bien la même valeur que précédemment. Mais bien sûr, vous pouvez recalculer avec toutes les valeurs que vous voulez, pour autant que le calcul soit possible:

toto(0.65423,1.7298) => 0.0189192228012913
toto(1.9875,1.0749) => 0.0747066661174212
toto(2.86278,0.0749) => 0.0983086841243615
etc...

Dans le contexte de la calculette, vous pouvez utiliser un ou plusieurs paramètres avec “lambda”, mais vous n'avez droit qu'à une seule expression après le “:”.


  • Fonction “map()”: appliquer une fonction quelconque à une liste de valeur et restituer la liste des résultats

Exemple avec la fonction sinus, qui va donc calculer sin(0.1), puis sin(0.2), etc…:

map(sin,[0.1, 0.2, 0.3, 0.4, 0.5]) => [0.0998334166468282,0.198669330795061,0.29552020666134,0.389418342308651,0.479425538604203]

Autre exemple: on créé une fonction lambda quelconque avec un paramètre:

sto("double",lambda x: 2*x)

Et on applique cette fonction à toutes les valeurs suivantes: [1,2,3,4,5,6], ce qui donne:

map(double,[1,2,3,4,5,6]) => [2, 4, 6, 8, 10, 12]

Et vous pouvez même faire ça en une seule fois:

map(lambda x:2*x,[1,2,3,4,5,6]) => [2, 4, 6, 8, 10, 12]

Dernier point, la fonction lambda permet de passer plusieurs paramètres, mais cette fois-ci sous forme de liste:

sto("y", lambda L: L[0]*L[1])

Pour calculer avec une telle fonction:

y([3,5]) => 15

Et avec la fonction map:

map(y,[[3,5],[4,6],[7,9]]) => [15, 24, 63]

On se servivira de cela plus loin avec les jeux d'essais.


  • fonction “range()”: permet de créer une liste composée de valeurs itératives entières. Par exemple:
range(20,30,2) => [20,22,24,26,28]  donc; 1er paramètre=valeur initiale (défaut=0), 2ème paramètre=dernière valeur exclue, 3ème paramètre=incrément (défaut=1)

Autre exemple: la date de Pâques des 10 prochaines années.

map(paques,range(2008,2018)) => ["23/03/2008","12/04/2009","04/04/2010","24/04/2011","08/04/2012","31/03/2013","20/04/2014","05/04/2015","27/03/2016","16/04/2017"]


  • fonction “filter(f,L)”: permet de ne retenir d'une liste L que les valeurs qui rendent la fonction f vraie.

Par exemple, on fabrique la fonction lambda qui donne vrai quand son paramètre y est >=100:

sto("f",lambda y: y>=100)  

Et on cherche la liste des nombres premiers compris entre 100 et 200:

filter(f,premiers(200))  =>  [101,103,107,109,113,127,131,137,139,149,151,157,163,167,173,179,181,191,193,197,199]  

On peut aussi chercher, par exemple, des variables aléatoires distribuées au hasard selon la loi normale réduite (module probabilite), mais en ne retenant que les valeurs positives. Dans cet exemple, nous avons enchaîné les expressions dans une liste:

[sto(“f”,lambda y: y>=0),filter(f,hgaussred(10))] → [0.70683994795139,0.139106211249837,0.517782930504369,0.222072398717544,0.578393938896522]


  • Compréhension de liste

C'est une appellation étrange, parce que ce n'est pas forcément facile à comprendre. Il s'agit d'une boucle conditionnelle dans une liste qui est considérée par Python comme une expression, et donc qui est calculable par la calculext. Par exemple:

  • [3*x for x in range(0,10) if x%2==0] ⇒ [0,6,12,18,24]

Dans cette expression:

  • “3*x” est l'expression à calculer
  • “for x in range(0,10)” est la boucle qui permettra de calculer la fonction précédente avec x=0 jusqu'à x=9 (10 est exclu)
  • “if x%2==0” est la condition qui fera que de toutes les valeurs calculées par la boucle, seules les valeurs paires seront retenues (% est la fonction modulo).
  • le résultat est renvoyé sous forme d'une liste

La condition avec “if” est optionnelle. Par exemple: les 5 prochaines dates de Pâques:

[paques(x) for x in range(2008,2013)]  =>  ["23/03/2008","12/04/2009","04/04/2010","24/04/2011","08/04/2012"]  

On peut, bien sûr, combiner toutes ces fonctions: sto, lambda, map, filter, compréhension de liste, etc… pour obtenir ce qu'on veut.


module math:

  • fonctions transcendantes: logarithme, exponentielle, racine carrée, fonctions trigonométriques et hyperboliques (tous les angles sont en radians):
  • ceil(x), fabs(x), floor(x), fmod(x,y), frexp(x), ldexp(x,i), modf(x), exp(x), log(x[,base]), log10(x), pow(x, y), sqrt(x), acos(x), asin(x), atan(x), atan2(y,x), cos(x), hypot(x,y), sin(x), tan(x), degrees(x), radians(x), cosh(x), sinh(x), tanh(x), pi, e
sin(0.365)/log(2)*pi => 1.61782275494049

acos(0.5) => 1.0471975511966 (radians)

degrees(acos(0.5)) => 60 (degrés décimaux)

sqrt(2) => 1.4142135623731 

2**0.5 => 1.4142135623731

pow(2,0.5) => 1.4142135623731

2*pi => 6.28318530717959

e => 2.71828182845905

log(e) => 1  (évidemment!)

log10(10) => 1 (idem!)


  • fonctions de conversion “degrés décimaux → radians” et “radians → degrés décimaux”
radians(180) => 3.14159265358979  (conversion de degrés en radians)

degrees(3.14159265358979) => 180 (heureusement!)


module decimal:

  • permet des opérations sur des nombres décimaux sans erreurs d'arrondis sur le dernier chiffre et avec un nombre de chiffres significatifs arbitraires (28 par défaut et on peut le modifier).
  • Utilisation courante: comptabilité
Decimal("10")/Decimal("3") => Decimal("3.333333333333333333333333333")

Decimal("10.54796823145698563256")+Decimal("0.00000000000000000001") => Decimal("10.54796823145698563257")


module random

  • permet, entre autres, de générer des nombres au hasard entre 0 et 1:
random() => 0.399824531593547

random() => 0.753101408653799 


module cmath

  • permet de faire des calculs sur des nombres complexes
complex(1,2)+complex(2,3) => (3+5j)

complex(-2,5)*complex(1,-3) => (13+11j)

complex(3,1)/complex(-2,-5) => (-0.379310344827586+0.448275862068966j)   (en fait, avec le calcul à la main, on trouve (-11/29 + 13/29j))

cmath.sin(complex(3,1)) => (0.217759551622152-1.16344036370325j)  (pour éviter les confusions avec les fonctions de "math", toutes les fonctions de "cmath" doivent être préfixées par "cmath.")


fonctions traitant des chaînes de caractères

  • Dans la calculette, les chaînes de caractères sont utilisées à chaque fois qu'une valeur n'est pas dans un type numérique reconnu par python (ex: un nombre binaire '001101011010' ou une date '27/10/2007').
  • exemple de chaine: '27/10/2007' ou “27/10/2007” (on peut utiliser les guillemets ou les apostrophes, et on peut les imbriquer: “n'importe quoi” est une chaine valide malgré l'apostrophe interne)
'27/10' + '/' + '2007' => '27/10/2007' (la concaténation de plusieurs chaînes donne une nouvelle chaîne)

len('27/10/2007') => 10  (=nombre de caractères de la chaîne)

'27/10/2007'[3:5] => '10'  (extraction des caractères numéro 3 à 5 (5 exclu), le 1er caractère de la chaîne étant numéroté zéro)

avec int('27/10/2007'[3:5]), on obtient 10 sous forme numérique (ici, numéro du mois dans l'année) qu'on pourrait réutiliser dans d'autres calculs.


fonctions traitant des listes

  • Dans la calculette, les listes sont utilisées à chaque fois qu'une fonction renvoie plusieurs valeurs (puisqu'on n'a qu'une seule ligne pour les résultats).
  • exemple de liste: [12, 43, 9.765, 2] attention: les crochets font partie de la liste!
[12, 43, 9.765, 2][1] => 43 (extraction de la 2ème valeur car l'indice commence à zéro)

[12, 43, 9.765, 2][0:2] => [12, 43] (extraction de la liste des valeurs allant de 0 à 2, 2 étant exclu)

[12, 43, 9.765, 2][-2] => 765 (extraction de l'avant dernière valeur) 
  • il peut être plus pratique d'affecter une liste à une variable:
sto("x",premiers(1000))
  • on peut ainsi calculer le nombre de nombres premiers <= 1000:
len(x) -> 168
  • et extraire, par exemple, les 5 dernières valeurs:
x[-6:-1] => [967, 971, 977, 983, 991]

Fonctions de base de la calculette (calculext.py)

aide(nomdefonction)

Appel de l'aide de chaque fonction. Exemple:

aide(fact) => 'fact(n): calcule la factorielle de n (entier >= 0)'

sto(nomdevariable,expression)

Création de variables, affectation et réaffectation de valeurs de type quelconque

sto("x",2*sin(0.145)) => 0.288984859421053  (si la variable x n'existait pas, elle est crée. N'oubliez pas les guillemets ou les apostrophes autour du nom de variable)

Comme cette fonction “sto” renvoie le résultat de l'expression (2ème paramètre), on peut l'utiliser en plein milieu d'une expression.

55/sto("x",2*sin(0.145))+45.3687 => 235.690089536414  (l'expression "2*sin(0.145" a été utilisée dans le calcul et a été affectée à x en plus)

Après cette instruction, la variable x peut être utilisée dans n'importe quelle expression. Sa valeur sera utilisée dans l'expression.

(5/x-6.521873) => 10.7800715033104

On peut affecter ou réaffecter à cette variable x n'importe quel type de valeur.

sto("x",premiers(10000)) => affecte à x la liste de tous les nombres premiers <= 10000

len(x) => donne alors le nombre de nombres premiers ainsi calculés, c'est à dire 1229. 

sto("x",dec2bin(48325)) => affecte à x la conversion binaire de 48325, soit '1011110011000101'

sto("x",paques(2008)) => affecte à la variable x la date de pâques de 2008 = '23/03/2008' (voir plus loin)

arrond(x,n)

La fonction arrond(x,n) renvoie le résultat de l'expression x sous forme de chaine, avec les nombres à virgule flottante correctement affichés, conformément au mode d'affichage défini par “n”. Cela correspond à l'affichage normal du résultat, à part qu'on ne modifie par le mode d'affichage courant. Par exemple:

sin(0.123) => 0.122690090024315
arrond(sin(0.123),4) => "0.1227"

On aurait pu obtenir la même chose avec la séquence suivante:

precision(4)
sin(0.123) => 0.1227
precision()     pour revenir au mode d'affichage par défaut (affichage "intelligent" avec 15 chiffres significatifs)

Exemples de calcul avec les modules additionnels

Module "bibgene"

Bibliothèque générale - fonctions diverses


  • Conversion décimal → binaire
dec2bin(759135841) => "101101001111110111111001100001"
  • Conversion décimal ⇒ binaire, mais en binaire signé (complément à 2), avec un mot de longueur quelconque (8 bits par défaut, 12 bits pour l'exemple):
dec2bins(-91,12) => "111110100101"


  • Conversion binaire → décimal
bin2dec("1011000101011010110101010") => 23246250
  • Conversion binaire → décimal, mais en binaire signé (complément à 2), avec un mot de longueur quelconque (8 bits par défaut, 12 bits pour l'exemple):
bin2decs("111110100101",12) => -91


  • jeuxdessais(n,L1,L2): jeu d'essais de valeurs pour calculer une fonction

Si L1 et L2 sont des valeurs numériques, la fonction renvoie une liste de n valeurs dont la 1ère est L1 et la dernière L2, les valeurs intermédiaires étant calculées:

jeuxdessais(10,0,9) => [0,1,2,3,4,5,6,7,8,9]

Autre exemple plus inhabituel: la date de Pâques des 10 prochaines années:

map(paques,jeuxdessais(10,2008,2017)) => ["23/03/2008","12/04/2009","04/04/2010","24/04/2011","08/04/2012","31/03/2013","20/04/2014","05/04/2015","27/03/2016","16/04/2017"]

Si L1 et L2 sont des listes de paramètres, renvoie une liste de n listes de paramètres, le 1er jeu de paramètre étant L1 et le dernier L2, les jeux intermédiaires étant calculés:

jeuxdessais(3,[1,2,3],[3,4,5]) => [[1,2,3],[2,3,4],[3,4,5]]

Ça marche aussi si les paramètres vont dans un sens différent (l'un grandit, l'autre diminue et même devient négatif):

jeuxdessais(3,[1,2,3],[3,-2,5]) => [[1,2,3],[2,0,4],[3,-2,5]]

Si, à un moment donné on ne veut plus faire progresser le 2ème paramètre (par exemple), il suffit qu'il ait la même valeur dans L1 et L2:

jeuxdessais(3,[1,2,3],[3,2,5]) => [[1,2,3],[2,2,4],[3,2,5]]

Avec lambda et map(), on peut obtenir une liste de valeurs d'une fonction testée avec n valeurs de plusieurs paramètres passés à la fonction.

Exemple:

Créer une fonction avec lambda. On prend l'exemple ici d'une fonction simple avec 3 paramètres passés dans une liste L. Cette fonction se contente de renvoyer la somme des 3 paramètres:

sto("X",lambda L: L[0]+L[1]+L[2])

On va ensuite solliciter cette fonction avec n=5 jeux de paramètres, dont le 1er jeu est: L1=[1,2,3], le dernier: L2=[3,4,5]. La fonction jeuxdessais va calculer les paramètres intermédiaires:

jeuxdessais(5,[1,2,3],[3,4,5]) => [[1,2,3],[1.5,2.5,3.5],[2,3,4],[2.5,3.5,4.5],[3,4,5]]

Et on va utiliser la fonction python “map()” pour appliquer ces jeux de paramètres à la fonction X:

map(X, jeuxdessais(5,[1,2,3],[3,4,5])) => [6,7.5,9,10.5,12]

Vérification: vous voyez que le 2ème résultat (7.5) est bien le résultat de la fonction X sollicité par le 2ème jeu de paramètres calculé [1.5, 2.5, 3.5] puisque (1.5+2.5+3.5) ⇒ 7.5

Comme la fonction créée par lambda peut être aussi complexe qu'on veut, vous voyez avec quelle facilité vous pouvez, par exemple, tracer une courbe f(x):

sto("Y",lambda L: (2*cos(L[0])/(1+tan(L[0]))*1/sqrt(2*pi))**2)

Et calculer 10 valeurs de la fonction Y pour x=L[0] allant de 0 à 18:

Le jeu d'essais sera:

jeuxdessais(10,[0],[18]) => [[0],[2],[4],[6],[8],[10],[12],[14],[16],[18]]

Et la fonction Y=f() deviendra pour toutes ces valeurs:

map(Y,jeuxdessais(10,[0],[18])) => [0.636619772367581,0.0785068222629216,0.0584159216106966,1.16759375121064,0.000400676191347714,0.164958053317647,3.41883200806231,0.000175111120347522,0.345138427520504,14.7216632319214]

Si vous êtes gêné par le nombre de chiffres significatifs, il suffit d'utiliser la fonction arrond(x,n) comme suit (ici avec 4 chiffres après la virgule):

arrond(map(Y,jeuxdessais(10,[0],[18])),4) => "[0.6366,0.0785,0.0584,1.1676,0.0004,0.1650,3.4188,0.0002,0.3451,14.7217]"

Module "arithmetique"

  • fact(n): factorielle n
fact(20) => 2432902008176640000

Votre calculatrice python est rapide: fact(5000) est calculée immédiatement sur un PC moderne… mais sur certains PC, il y a trop de chiffres pour que le résultat soit affiché (ça dépend de l'OS et des limites du module graphique Tkinter)! En fait, il en a exactement 16325, et vous pouvez le calculer facilement avec len(repr(fact(5000))) (qui vous comptera en plus le “L” final puisque c'est un entier “long”). Pour récupérer ce résultat, utilisez la séquence de touche Ctle-R qui sauvegarde la donnée calculée sur disque en l'ajoutant au fichier “resultat.txt”, situé dans le même répertoire que la calculette (donc, il faut avoir le droit d'écrire dans ce fichier).


  • pgcd(a,b): PGCD = Plus Grand Commun Diviseur de a et b“ (utilisation de l'algorithme d'Euclide)
pgcd(20,64) => 4

On aurait pu le faire d'une autre façon moins automatique en considérant la décomposition en facteurs premiers:

facteurs(20) => [2,2,5]
facteurs(64) => [2,2,2,2,2,2]

On voit alors le pgcd: 2×2=4

Lorsque le pgcd est “1”, les 2 nombres donnés sont dits “premiers entre eux” (=ils n'ont aucun facteur commun):

pgcd(6,35) => 1


  • ppcm(a,b): PPCM = Plus Petit Commun Multiple de a et b (utilisation du PGCD)
ppcm(20,64) => 320

On aurait pu le faire d'une autre façon moins automatique en considérant la décomposition en facteurs premiers:

facteurs(20) => [2,2,5]
facteurs(64) => [2,2,2,2,2,2]

On voit alors le ppcm: (2**6)x5=320

Tout naturellement, lorsque les 2 nombres donnés sont “premiers entre eux”, leur ppcm est égal à leur produit:

ppcm(6,35) => 210 = 6*35


  • facteurs(n): décomposition de n en facteurs premiers:
facteurs(48925327594) => [2, 11, 13, 31, 1103, 5003L]   résultat obtenu en un centième de seconde (le post-fixage par "L" du dernier chiffre signifie simplement que c'est un entier long)

Quand le résultat est seulement le nombre donné, c'est que ce nombre est “premier” (=il n'est divisible que par lui-même):

facteurs(99989) => [99989] 


  • estpremier(n): teste si un nombre n est premier (renvoie True = vrai ou False = faux)
estpremier(9973) => True

estpremier(9841) => False

estpremier(1111111111111111111) => True  résultat trouvé en 9mn et 27 sec


  • premiers(n): trouve et renvoie la liste de tous les nombres premiers inférieurs ou égaux à un nombre donné n
premiers(100) => [2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97] 

NB: au delà de 10000 environ, le résultat est calculé, mais certains ordinateurs ne peuvent pas l'afficher parce que la ligne est trop longue (ça dépend de l'OS et des limitations du module graphique Tkinter). Par exemple, sur Windows XP, “premiers(100000)” calcule bien les 9592 nombres premiers (en 5 secondes!), mais refuse de les afficher. La solution la plus facile est d'enregistrer ce résultat sur disque avec Ctle-R et de le récupérer dans le fichier resultat.txt avec un simple éditeur de texte.

Au delà il faudra être patient: pour 10000000 (10 millions), la calculette trouvera bien les 664.579 nombres premiers (je l'ai fait!), mais mettra environ 24h.


Module "combinatoire"

  • permut(n): nombre de permutations de n objets
permut(3) => 6  (ex: les 6 permutations possibles des 3 objets "1", "2" et "3": 123 231 312 213 132 321)

permut(10) => 3628800

Même remarque que pour la fonction “fact(n)” du module arithmetique concernant l'affichage des résultats très longs sur certains PC (En fait, c'est le même code).


  • arrang(n,p): Arrangement de n objets pris p à p
arrang(10,3) => 720


  • combin(n,p): combinaison de n objets pris p à p (sans tenir compte de l'ordre dans lequel on place les 3 objets)
combin(10,3) => 120

On retrouve bien:

combin(10,3) = arrang(10,3) / permut(3)


Module "probabilite"

  • binom(k,n,p): loi binomiale = probabilité d'avoir k réussite(s) dans n évènements indépendants, chaque évènement ayant une probabilité p% de cas favorables.
binom(8,100,0.12) => 0.0624559656927432


  • binomcum(k,n,p): loi binomiale cumulée = probabilité d'avoir 0,1,..,k réussite(s) dans n évènements indépendants, chaque évènement ayant une probabilité p% de cas favorables.
binomcum(8,100,0.12) => 0.138592064127197


  • hypgeo(k,n,g,t): loi hypergéométrique = probabilité d'avoir k réussite(s) dans n évènements indépendants, sachant qu'il y a g cas favorables dans la population de taille t.
hypgeo(8,100,0.12,500) => 0.0560626494472544


  • hypgeocum(k,n,g,t): loi hypergéométrique cumulée = probabilité d'avoir 0,1,..,k réussite(s) dans n évènements indépendants, sachant qu'il y a g cas favorables dans la population de taille t.
hypgeocum(8,100,0.12,500) => 0.111411520509563


  • pgaussred(x): probabilité pour qu'une variable aléatoire distribuée selon la loi normale réduite soit inférieure à x
pgaussred(2) => 0.977249865802215


  • pgauss(x,m,s): probabilité pour qu'une variable aléatoire distribuée selon la loi normale de moyenne m et d'écart-type, s soit inférieure à x
pgauss(5, 3.58, 0.8547) => 0.951683594346861

Cette fonction a été calculée par “intégration numérique”, c'est à dire en décomposant la surface à trouver en très nombreux petits rectangles (2000 par écart-type). Ce qui fait que le calcul pour 3 écarts-types nécessite le calcul de 6000 petits rectangles, mais ne demande qu'un centième de seconde…

Vous pouvez, bien sûr, retrouver les valeurs habituelles des tables calculées avec la “loi normale réduite” (m=0, s=1).

Ainsi, avant +3s, il y a:

pgaussred(3) ou pgauss(3,0,1) => 0.998650104184  ou 99.865% (valeur courante des tables "papier" : 0.99865)

Et entre -2s et +2s, il y a:

2*(pgauss(2,0,1)-0.5) => 0.954499772098 ou 95.45%


  • xgaussred(p): trouve la variable aléatoire x distribuée selon la loi normale réduite, et correspondant à une probabilité p qu'elle soit inférieure
xgaussred(0.977249865802215)  => 1.99999999999984  (ça devrait être "2.0": on n'est pas tombé loin :-D) 


  • xgauss(p,m,s): trouve la variable aléatoire x, distribuée selon une loi normale (moyenne=m, écart-type=s), et correspondant à une probabilité de p qu'elle soit inférieure
xgauss(0.95,3,1.456)  =>  5.39490693071295


  • hgaussred(n=1): donne une valeur ou une liste de n valeurs aléatoires au hasard, distribuée(s) selon la loi normale réduite
hgaussred()  =>  -0.506305559604254
hgaussred()  =>  1.43100857605685
hgaussred()  =>  0.0557568797713102
hgaussred(5)  => [0.653567562762523,0.907479359323617,1.37670670103468,-0.608230227262229,-1.70607264166885]


  • hgauss(m,s,n=1): donne une valeur ou une liste de n valeurs aléatoires au hasard, distribuée(s) selon une loi normale (moyenne=m, écart-type=s)
hgauss(3,5)  =>  4.18456627105497 
hgauss(3,5)  =>  9.89972166717921
hgauss(3,5)  =>  3.79305505449398
hgauss(3,5,5)  =>  [-2.92544650845046,4.74059647970691,1.87221150326321,5.18916574661666,-2.42821512357165]


Module "credit"

Pour faire plusieurs calculs de suite avec ce module, je vous suggère de passer en précision “2” pour éviter les 10 ou 15 chiffres après la virgule:

precision(2)  affichage avec 2 chiffres après la virgule

Et à la fin, n'oubliez pas de revenir au mode par défaut (15 chiffres significatifs) avec:

precision()
  • Mensualités de remboursement d'un emprunt de 500 Euros prêtés à 6% d'intérêt par an, à rembourser en 12 mois
menscredit(500,6,12) => 43.03 Euros

Ponctuellement, vous pourriez aussi utiliser la fonction arrond(x,2) pour n'obtenir que des résultats avec 2 chiffres après la virgule:

arrond(menscredit(500,6,12),2) => "43.03"


  • Nombre de mois de remboursement d'un prêt de 1000 Euros ayant un intérêt de 8%/an et une mensualité 50
moiscredit(1000,8,50) => 21.54 (soit 21 mois + une 22ème mensualité plus faible)


  • Capital emprunté à 4,5%/an d'intérêt, remboursé en 240 mois avec une mensualité de 1500 Euros
capcredit(4.5,1500,240) => 237098.16 Euros (ex de calcul de capacité d'emprunt pour une maison, en fonction des possibilités de remboursement)


  • Intérêt annuel, connaissant le capital emprunté = 3000 Euros, la mensualité 93 Euros et le nombre de mois de remboursement 36
intcredit(3000,93,36) => 7.27%


  • Cout d'un crédit connaissant le capital 300 Euros, la mensualité 26 Euros et le nb de mois de remboursement 12 (sup = coût supplémentaire optionnel = 200 Euros frais de dossier)
coutcredit(300,26,12,200) => 212 Euros 


Module "temps"

Pour l'instant, il y a surtout des fonctions “date”. Ce sera complété par des fonctions “heures” bientôt!

  • Donne la date et l'heure de l'ordinateur:
tempsordi() => "vendredi 30/11/2007 08:43:51"


  • Donne la date et l'heure exacte (à la seconde près) par consultation d'un serveur NTP (il faut donc une connexion internet!):
tempsntp() => "vendredi 30/11/2007 8:44:20"

Le serveur NTP par defaut est: “ntp.univ-lyon1.fr”. Vous pouvez choisir un autre serveur de temps NTP en consultant, par exemple, http://www.cru.fr/services/ntp/serveurs_francais:

tempsntp("ntp.imag.fr") => "vendredi 30/11/2007 8:45:3"


  • Donne la date du jour (=de l'ordinateur!) sous le format 'jj/mm/aaaa'
cejour() => "30/11/2007"


  • Dit si l'année donnée est bissextile ou non (True=oui, False=non)
bissextile(2007) => False

bissextile(2008) => True

bissextile(2010) => False

bissextile(2000) => True

bissextile(3000) => False


  • Donne le numéro du jour dans l'année de la date D 'j/m/a' (1er janvier = 1, …)
numjouran("27/10/2007") => 300


  • Donne la date qui est le nième jour de l'année a
datenumjouran(300,2007) => "27/10/2007"


  • Nombre de jours entre 2 dates D1 et D2 (format “j/m/a”), >=0 si D2>=D1, <0 sinon
diffdate("27/10/2007","31/12/2007") => 65

diffdate("27/10/2007","1/7/2007") => -118

diffdate("14/7/1789","27/10/2007") => 79727 jours depuis la prise de la bastille :-D


  • Donne la date qui est postérieure de n jours (n>=0) ou antérieure de n jours (n<0) à la date D “j/m/a”
adddate("27/10/2007",45) => "11/12/2007" (date dans 45 jours)

adddate("27/10/2007",-90) => "29/07/2007" (date il y a 90 jours)

adddate("27/10/2007",-79727) => "14/07/1789" (Eh oui, ça marche aussi comme ça)


  • Donne le numéro du jour de la semaine d'une date D “j/m/a” (lundi=1, …)
numjoursem("27/10/2007") => 6  (c'est à dire un samedi puisque lundi=1)


  • Donne le jour de la semaine d'une date D “j/m/a” ('lundi' pour lundi, …)
joursem("27/10/2007") => "samedi" (je l'avais bien dit...)

joursem("1/1/2008") => "mardi" (le jour de l'an tombe un mardi: c'est bien pour le pont :-D)

joursem("14/7/1789") => "mardi" (oui, ils ont fait ça un mardi!)


  • Trouve la date du lundi d'une semaine qui contient la date D “j/m/a”
lundi("27/10/2007") => "22/10/2007"


  • Trouve la date de Pâques d'une année donnée

NB: j'ai utilisé la méthode Hodges. J'ai bien sûr vérifié avec les dates officielles que toutes les dates calculées étaient exactes sur toute la période de validité dans le calendrier grégorien (1583 à 4100 soit 2518 dates de Pâques).

paques(2007) => "08/04/2007"

paques(2008) => "23/03/2008"

paques(2009) => "12/04/2009"

paques(2010) => "04/04/2010"

paques(3000) => "13/04/3000" (ça, ce sera difficile à vérifier sur votre carnet de rendez-vous: il faudra attendre un peu ou me faire confiance ;-))


  • Trouve la date du jeudi de l'Ascension (Pâques + 39j)
ascension(2008) => "01/05/2008"


  • Trouve la date du dimanche de la Pentecôte (Pâques + 49j)
pentecote(2008) => "11/05/2008"


  • Trouve la date du mardi gras (Pâques - 47j)
mardigras(2008) => "05/02/2008"


  • Trouve la date du dimanche des Rameaux (Pâques - 7j)
rameaux(2008) => "16/03/2008"


  • Jours fériés France

Tant que j'y étais, j'ai fait une fonction qui renvoie sous forme de liste tous les jours fériées (France) de l'année.

- Sans le second paramètre optionnel (comme: “feries(2008)”), vous n'avez que ceux qui tombent un jour de semaine (ni samedi, ni dimanche).

- Avec un 1 comme second paramètre (comme: “feries(2008,1)”), vous les avez tous, y compris les samedis-dimanches.

- Avec un 2 comme second paramètre(comme: “feries(2008,2)”), vous les avez tous + les 2 jours fériés supplémentaires d'Alsace-Moselle: le vendredi saint et la St Etienne (lendemain de Noël).

feries(2008,1) => voir liste suivante: pour faciliter la lecture, je présente ici la liste en plusieurs lignes:
["Jour de l'an = mardi 01/01/2008",
"Jour de Pâques = dimanche 23/03/2008",
"lundi de Pâques= lundi 24/03/2008",
"Fête du Travail = jeudi 01/05/2008",
"Fête de la Victoire 1945 = jeudi 08/05/2008",
"Ascension = jeudi 01/05/2008",
"Pentecôte = dimanche 11/05/2008",
"Lundi de Pentecôte = lundi 12/05/2008",
"Fête Nationale = lundi 14/07/2008",
"Assomption = vendredi 15/08/2008",
"Toussaint = samedi 01/11/2008",
"Armistice 1918 = mardi 11/11/2008",
"Noël = jeudi 25/12/2008"]

NB: pour le lundi de la Pentecôte, vous adaptez en fonction de ce que vous avez compris (férié/pas ferié ?)

Cette fonction est à adapter à votre situation, région, pays et besoins (ainsi qu'à l'évolution de la législation!)!

Téléchargement et installation

Exigences pour que ça fonctionne

Il faut peu de choses pour que cette calculatrice fonctionne:

  • un interpréteur python,
  • le module python “Tkinter” qui permet le graphique.

Pour Linux, on trouve tout ça sous forme de paquets déjà prévus (rpm pour la suse et deb pour debian) et souvent déjà pré installés.

Pour Windows, on trouve le programme à installer ici: http://www.python.org/download/windows/

Pour Mac, je ne sais pas ce qu'il faut faire, ni si ça marche: quelqu'un peut-il me renseigner ? (page contact du présent site)

Pour les versions de python:

  • j'ai développé avec la version 2.5,
  • j'ai vérifié (partiellement) que ça fonctionnait aussi avec la 2.4.

Téléchargement

Vous pouvez consulter toutes les versions ici (y compris les anciennes versions, mais celle-ci ne bénéficient d'aucun suivi de ma part!):

NB: c'est le même programme pour Windows et pour Linux!

Composition de l'archive zip

La calculette comporte les fichiers suivants:

La calculette de base:

  • lisezmoi.txt ⇒ l'inévitable fichier texte à consulter en 1er
  • calculext.py ⇒ programme principal (c'est lui qu'on lance)
  • calculext.html ⇒ page d'aide (notice) affichable dans n'importe quel navigateur web
  • license.html ⇒ la licence d'utilisation, modification et redistribution GNU/GPL version 3
  • fondcorps.jpg ⇒ motif de fond de page de la notice et de la license
  • resultat.txt ⇒ fichier texte vide pour recevoir les expressions et les résultats sauvegardés à la demande

Les modules additionnels optionnels:

  • bibgene.py ⇒ module de fonctions générales
  • arithmetique ⇒ modules de fonctions arithmétiques
  • combinatoire ⇒ modules de fonctions d'analyse combinatoire
  • probabilite ⇒ modules de fonctions pour les calculs de probabilité
  • credit.py ⇒ module de fonctions pour calculer les crédits bancaires
  • temps.py ⇒ calculs relatifs aux dates et aux heures
  • modele.py ⇒ un modèle tout fait (non importé) pour aider les utilisateurs à créer un nouveau module

Installation et lancement sous linux

Vous placez tous les fichiers de la calculette dans un même répertoire (je l'appelle ”/chemin“ pour l'exemple).

Si vous voulez adapter cette calculette à vos besoins (c'est à dire modifier son code), vous devriez la mettre dans votre home. Si vous la mettez ailleurs, il faudrait au minimum que le fichier “resultat.txt” ait un chmod de 666 pour recevoir des données sauvegardées.

Vous pouvez lancer la calculette en console sous votre login par:

$ python /chemin/calculette.py

Mais comme c'est un programme graphique, je vous suggère plutôt de construire un raccourcis sur votre bureau KDE ou gnome pour faire ça (clic droit → etc…).

Si vous voulez ajouter des fonctions, installez aussi “idle” de python, ou un autre programme de développement python. N'oubliez pas que l'édition du code doit respecter l'encodage unicode “UTF-8”.

Et si vous avez ajouté des nouvelles fonctions, vous pouvez mettre à jour le fichier html pour l'aide avec n'importe quel outil d'édition de page web sur Windows ou sur linux. Sur linux: quanta plus ou nvu par exemple (ou même avec vim si vous êtes très très courageux (limite maso :-D))

Installation et lancement sous Windows

Vous placez tous les fichiers de la calculette dans un même répertoire.

Pour lancer la calculette, le meilleur moyen est de créer un raccourcis sur le bureau (clic-droit → etc…) avec une commande de lancement du genre:

C:\Python25\pythonw.exe "C:\Documents and Settings\Tyrtamos\Mes documents\calculette\calculette.py".

Vous noterez l'utilisation de “pythonw.exe” (et pas python.exe) pour éviter le lancement de la console dos.

Vous pouvez aussi double-cliquer sur le nom calculette.py dans l'explorateur Windows, auquel cas, c'est la terminaison ”.py“ qui appellera “python.exe”. Mais là, vous avez la console DOS en plus. Pour l'éviter, c'est très simple: vous renommez le fichier:

  • calculext.py ⇒ calculext.pyw

Avec cette modif, le double-clic sur le nom calculette.pyw appellera le programme pythonw.exe.

Si vous voulez ajouter des fonctions, il faudra lancer “idle” de python, en principe livré aussi avec le python de base, ou un autre programme de développement python. N'oubliez pas que l'édition du code doit respecter l'encodage unicode “UTF-8”. Sous Windows,c'est le cas pour “idle” de Python (c'est ce que j'utilise pour le développement), mais aussi du notepad/bloc-notes. J'ai aussi essayé avec succès “easyeclipse pour Python” (http://www.easyeclipse.org/site/distributions/index.html)

Si vous avez ajouté des nouvelles fonctions, vous pouvez mettre à jour le fichier html pour l'aide avec n'importe quel outil d'édition de page web (ou avec un simple éditeur de texte si vous êtes très courageux).

Conception commentée de la calculette de base

Structure générale de la page principale calculext.py

La partie graphique de la calculette est entièrement définie dans la classe “Calculext” qui complète et surcharge la classe Frame du module Tkinter.

Une fois lancée, la calculette s'affiche et attend les évènements à prendre en compte: saisie d'une expression, clic de souris, touche F1 pour demande d'aide, etc…

Toutes les fonctions appelées directement par ces évènements sont définies à l'intérieur de cette classe.

Quand le calcul d'une expression est lancé, il est confié à un thread (thread = processus léger qui s'exécute à l'intérieur d'un processus normal) qui fait le calcul en tâche de fond, ce qui fait que la partie graphique reprend tout de suite la main et n'est pas bloquée par un calcul long.

Ce thread de calcul est défini dans la classe “Calcul”. Il reçoit l'expression à calculer, se charge du calcul par la fonction Python “eval()”, traite les erreurs par “try: .. except:” et place le résultat dans la variable globale “resultcalcul”. C'est l'état de la variable globale “encourscalcul” qui dit si un calcul est en cours ou pas. C'est la fonction “calculer()” de la classe “Calculext()” qui teste si le calcul est terminé ou si un arrêt de calcul a été demandé. Dans ce dernier cas, il est demandé au thread de s'arrêter (avec arretcalcul=True, et il est abandonné (del calc). Dans tous les cas, cette fonction calculer() se termine par l'affichage du résultat ou de l'erreur. A noter que cette boucle d'attente doit passer la main à la fenêtre par self.update() à chaque cycle afin que cette fenêtre ne soit pas bloquée pendant tout le calcul.

Le traitement d'erreur traite:

  • les erreurs de calcul intégrées dans python (division par zéro, racine d'un nombre négatif, fonction inexistante, erreur de syntaxe, …),
  • les erreurs des fonctions des modules additionnels (ex: factorielle d'un nombre négatif) qui sont détectées dans les codes.
  • la détection d'une demande d'arrêt d'un calcul jugé trop long par l'utilisateur.

Remarque concernant l'arrêt demandé par l'utilisateur: ça ne marche pas toujours, et cela semble tenir au fonctionnement de Python. D'après ce que j'ai compris, le thread de calcul ne rend pas la main au bout d'un temps donné, mais au bout d'un certain nombre d'instructions virtuelles (100 bytecodes par défaut), ce qui fait que le thread ne rend pas toujours la main dans les boucles très courtes. Par exemple, j'ai eu beaucoup de problèmes pour arrêter la fonction factorielle (version non récursive). Heureusement, le problème ne se pose que pour des calculs rares (factorielle 20000 !) et on a toujours la possibilité d'arrêter sauvagement la calculatrice (non! ne retirez pas la prise! ;-) )…

L'affichage passe par une fonction “affiche()” qui permet de formater correctement les nombres réels (à virgule flottante), selon la valeur de la variable globale “nbchiffres”. La fonction “precision(n) avec -15<=n<=+15 permet à l'utilisateur de modifier ce formatage des nombres réels.

On peut sauvegarder le dernier résultat calculé par Ctle-R. Celui-ci est simplement ajouté au fichier texte “resultat.txt” et son contenu peut être consulté et récupéré avec un simple éditeur de texte.

Après chaque calcul, est aussi affiché la durée de ce calcul, formatée par la fonction “afficheduree(sd)”, ce qui permet de faciliter l'optimisation des fonctions qu'on ajoute.

Après chaque calcul réussi, l'expression ainsi que le dernier résultat sont empilés dans une pile (pile[]). On peut ainsi rappeler ce dernier résultat ainsi que l'une des expressions précédentes avec la flèche en haut et flèche en bas. Ce qui est rappelé vient dans la ligne de saisie, ce qui permet de ne pas avoir à les retaper. La pile n'est cependant pas conservée lors de l'extinction de la calculatrice, mais vous pouvez cependant la sauvegarder dans le fichier resultat.txt par Ctls-E.

Avec un clic droit dans la zone de saisie, un menu flottant apparait. Les items de ce menu en cascade (2 niveaux) sont définies dans la variable globale “chmenu” sous forme d'une liste de liste. Sa structure est très simple:

chmenu=(
        (titre1, item1, iten2, item3),
        (titre2, item1, item2),
        (titre3, item1, item2, item3, item4)
       )

Chaque “titre” est un texte du menu 1er niveau, qui affiche un menu 2ème niveau composé des “items” qui suivent.

Le menu lui-même est créé par le constructeur de la classe Calculext (c'est à dire dans __init__). Son codage est tel qu'il s'adaptera automatiquement aux modifications de chmenu, pour autant qu'on en respecte sa structure. Cela veut dire qu'il sera facile de mettre à jour le menu au fur et à mesure des fonctions ajoutés ultérieurement par les utilisateurs!

Le positionnement du menu à l'emplacement de la souris est défini par la fonction menupopup(self, evt). En cas de sélection d'un item du menu 2ème niveau, il y a insertion de l'item à l'emplacement du curseur (celui de la zone de saisie) par la fonction execmenu(self,ch). L'insertion est empêchée si l'item commence par “$”, ce qui permet de mettre des textes d'information dans le menu, comme par exemple un rappel des règles de priorités des opérateurs ou l'inévitable “à propos”. Si l'item commence par ”'”, seule la partie entre deux “'” est insérée. Si l'item est vide, cela déclenche l'affichage d'une ligne de séparation non sélectionnable dans le menu.

Nouveauté de la version 1.30: si on avait sélectionné du texte avant d'appeler l'insertion d'une fonction avec parenthèses au menu, le texte sélectionné se retrouve à l'intérieur des parenthèses de la fonction insérée.


J'ai finalement renoncé à afficher ici l'ensemble du code de la calculext, parce qu'il devient trop long: presque 700 lignes pour le seul fichier calculext.py. Il est cependant auto-documenté et je vous invite à le consulter directement.

Commenter ajouter des fonctions et des modules

Les “modules” python sont des fichiers “texte” séparés, importés dans le programme principal (ici “calculette.py”) par une instruction “import”. Ci-dessous les importations actuelles:

# importation des modules additionnels
#    ajoutez les modules que vous avez créés
#    et mettez en commentaire ceux que vous n'utilisez pas
from bibgene import *
from arithmetique import *
from combinatoire import *
from probabilite import *
from credit import *
from temps import *

Ajouter une fonction supplémentaire dans un module existant

C'est super simple! Prenons un exemple: une fonction qui teste si un nombre entier est pair.

def estpair(n):
    """estpair(n): dit si un nombre entier est pair (renvoie True ou False)"""
    return (n % 2)==0

Ajoutez ce code au fichier bibgene.py, enregistrez-le (n'oubliez pas l'encodage UTF-8!), relancez la calculatrice, et essayez:

estpair(42) => True
estpair(99) => False

Vous pouvez voir aussi à quoi sert la ligne entourée de triples guillemets. Faites:

aide(estpair) => "estpair(n): dit si un nombre entier est pair (renvoie True ou False)"

Bien entendu, si la fonction nécessite l'importation de modules supplémentaires, il faut ajouter l'importation de ces modules au début du module en question.

Enfin, il est évident que votre fonction doit renvoyer quelque chose (instruction “return”), sinon, aucun résultat ne sera affiché. Par contre, ce résultat peut être n'importe quoi qui s'écrit sur une seule ligne, y compris une chaine de caractère ou une liste!

Vous pouvez aussi vérifier que le “n” fourni est bien un nombre entier. L'exemple devient:

def estpair(n):
    """estpair(n): dit si un nombre entier est pair (renvoie True ou False)"""
    if not ((type(n)==int) or (type(n)==long)):
        raise ValueError ("erreur estpair(n): n doit être un nombre entier")
    return (n % 2)==0

Dans ce cas:

estpair(8.123) => 'erreur estpair(n): n doit être un nombre entier'

C'est aussi simple que ça!

Imaginons maintenant que vous vouliez ajouter une fonction dont le calcul pourrait être très long: il faut que l'utilisateur puisse l'arrêter avant la fin!

Exemple extrême: une fonction qui ne fait rien mais ne s'arrête jamais toute seule:

def bidon():
    """bidon(): fonction qui ne fait rien mais ne s'arrete jamais toute seule"""
    while True:
        pass
    return None

Vous l'ajoutez au module bibgene.py (mais vous ne lancez pas la calculatrice).

Vous remarquez que dans ce module, il y a au début:

import sys
prog=sys.modules['__main__']

C'est important, parce que cela permettra de tester si l'arrêt du calcul a été demandé.

Il suffit de modifier le code comme suit:

def bidon():
    """bidon(): fonction qui ne fait rien mais ne s'arrete jamais toute seule"""
    while True:
        prog.calc.arretcalcul()
    return None

Lorsque l'utilisateur demandera l'arrêt du calcul (bouton “stopper” ou touche F2), la variable globale arretcalcul passera à True, et à la boucle “while” suivante, il y aura une exception raise qui arrêtera le calcul en-cours dans le thread de calcul. En fait, ce test permet surtout d'arrêter le thread afin qu'il ne reste pas actif en mémoire, parce qu'il est libéré de toute façon par “del calc”.

Un détail bizarre cependant. Quelquefois, la boucle est trop rapide et ne rend pas la main à la fenêtre, ce qui fait que le calcul ne s'arrête pas sur demande. Il suffit alors de ralentir un peu la boucle avec time.sleep(0.001) pour que l'arrêt sur demande fonctionne. C'est ce que j'ai été obligé de faire avec fact(n) du module arithmetique et permut(n)du module combinatoire qui ne s'arrêtait pas avant l'arrêt normal du calcul pour n=20000.

Conseils supplémentaires:

  • n'utilisez la récursion que si vous pouvez en limiter la profondeur. Python a une pile limitée (environ 1000). Il est possible de l'augmenter, mais elle aura toujours une limite…
  • méfiez-vous des boucles “for” avec “range()” qui peuvent refuser les plages de valeurs trop grandes et générer une erreur. Préférez “for” avec “xrange()”, ou carrément des boucles while.


Si vous voulez que vos nouvelles fonctions apparaissent dans le menu flottant, il faut l'ajouter dans la variable globale chmenu du fichier calculext.py.

Si vous voulez ajouter simplement une fonction “machin(x,y)” à un menu existant:

Situation initiale:

chmenu=(
        .....
        ("titre_n", "item1", "item2", ..., "itemk"),
        .....
       )

Situation finale:

chmenu=(
        .....
        ("titre_n", "item1", "item2", ..., "itemk", "machin(x,y)"),
        .....
       )

Simple, non?

Si vous voulez créer un nouveau titre “hi-fi” au menu 1er niveau, et une fonction “calcul_hp(x,y,z)” au 2ème niveau:

Nouvelle situation finale:

chmenu=(
        .....
        ("titre_n", "item1", "item2", ..., "itemk"),
        ("hi-fi", "calcul_hp(x,y,z)"),
        .....
       )

Voilà, c'est tout ce qu'il faut connaitre pour ajouter les fonctions dont vous avez besoin.

Ajouter un module supplémentaire

Pourquoi ajouter un module supplémentaire? En général parce qu'on veut regrouper plusieurs fonctions qui appartiennent à une même famille d'application. Par exemple:

  • vous voulez construire des enceintes acoustiques hi-fi, et vous avez plusieurs fonctions de calcul (il y en a sur le web!): vous regroupez ces fonctions dans un même module “acoustique”.
  • vous faites de la photo, et vous voulez avoir quelques fonctions pour calculer la profondeur de champ des macro photos, ou la définition minimal d'impression (en nombre de pixels au pouce) d'un tirage 30×40.

Prenons le 2ème exemple pour la suite.

Pour construire un module supplémentaire, il suffit:

1- de créer le nouveau module par recopie du fichier fourni “modele.py”. Vous l'appelez comme vous voulez. Pour l'exemple, c'est “photos.py”.


2- Editez ce nouveau fichier photos.py avec un éditeur de texte (avec encodage UTF-8), et ajustez les zones à remplir par vous: votre nom, le nom du module, …


3- Vous devez ensuite ajouter dans ce module “photos.py”, l'importation des modules dont vos nouvelles fonctions auront besoin. Par exemple:

from math import *


4- Et vous ajoutez dans calculext.py l'importation de ce module par:

from photos import *

En variante, l'importation pourrait être plus “propre” en important seulement les fonctions que vous voulez rendre disponible:

from photos import fonction1, fonction2, ..., fonctionn

Mais si vous faites ça:

import photos

vos fonctions devront alors être toutes préfixées dans les expressions à calculer, avec le nom du module, par exemple: “photos.fonction1()”


5- Il ne reste plus qu'à ajouter vos nouvelles fonctions dans votre nouveau module photos.py: pour cela, voir le chapitre précédent “Ajouter une fonction supplémentaire dans un module existant”.

Conclusion

Voilà, c'est fait: vous avez votre calculette miniature toujours disponible, et faisant tout ce que vous voulez (et encore plus)!

Vous aurez du mal à trouver une autre calculatrice qui vous fait tout ça!

Les fonctions supplémentaires que je propose représentent un tout petit début de ce que je souhaite faire: surveillez l'arrivée des prochaines versions!

Si vous ajoutez de nouvelles fonctions ou de nouveaux modules, donnez moi l'info (page contact du site)!

J'espère que vous vous amuserez autant que moi!

calculext.1207981963.txt.gz · Dernière modification: 2008/04/12 08:32 de tyrtamos

Outils de la page