Outils pour utilisateurs

Outils du site


calculext

La CALCULEXT: une calculatrice scientifique facilement extensible par les utilisateurs

[Tyrtamos juin 2008 version 1.40]

Nombreuses améliorations par rapport à la version précédente: voir le fichier lisezmoi.txt de l'archive à télécharger.

Pour Windows, ajout d'une version “exe” permettant l'utilisation de la Calculext sans installer l'interpréteur Python.

Modernisation simultanée de la version en ligne accessible sans installation, par un simple navigateur web (http://calculext.jpvweb.com). Son code n'est pas diffusé, mais elle utilise les mêmes modules sans modification et fait pratiquement tout ce que fait la Calculext installée sur PC, avec quelques limites mentionnées dans le manuel d'utilisation (commun avec la Calculext installable).

Objectif

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 soi 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!

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

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 (shift-clic gauche sur le mac) 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. Si vous aviez sélectionné une valeur avant, cette valeur se retrouve entre les parenthèses après insertion.



  • 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.

Pour la suite: accédez au manuel complet ci-dessous.

Manuel d'utilisation

Ce manuel d'utilisation accompagne la Calculext téléchargée et pourra être appelé à tout moment pendant son exécution.

Il est aussi le manuel de la version dérivée en ligne sur le web et pourra être appelé par le lien hypertexte sur sa page web.

Manuel d'utilisation

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, ça semble aussi fonctionner avec l'interpréteur Python téléchargé ici: http://www.python.org/download/mac/

Pour les versions de Python:

  • la Calculext a été développée avec la version 2.5,
  • vérification (partielle) que ça fonctionne aussi avec la version 2.4.

Téléchargement

Version pour Windows-Linux-Mac

Version exe pour Windows

Composition de l'archive zip

Voir le fichier “lisezmoi” contenu dans l'archive calculext_v1.40.zip.

Installation et lancement sous linux

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

Ce répertoire peut être n'importe où, et, une fois les fichiers copiés, vous n'avez pas besoin d'avoir le droit d'écrire, sauf, bien sûr, si vous voulez modifier la Calculext.

A partir de cette version 1.40, la Calculext saisit comme répertoire de travail le répertoire d'où elle a été lancée, et c'est dans ce répertoire que vous devez avoir le droit d'écrire pour sauvegarder l'historique et le dernier résultat. Vous pouvez aussi désigner un répertoire de travail dans votre /home (par exemple) si vous créez une icône de lancement sur le bureau. Le grand avantage est que plusieurs utilisateurs peuvent utiliser la Calculext en conservant chacun ses données.

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

$ python /chemin/calculext.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”.

Installation et lancement sous Windows

Il n'y a pas de programme d'installation, et vous ne risquez pas de “saloper” votre registre… Vous placez simplement tous les fichiers de la Calculext dans un même répertoire. Le chemin complet de ce répertoire ne doit avoir ni espace, ni caractère accentué. Par exemple: c:\calculext. Ceci est valable pour la Calculext modifiable, et aussi pour la version “exe”.

Pour lancer la calculette modifiable:

  • 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:\calculext\calculext.py"

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

  • Vous pouvez aussi double-cliquer sur le nom calculext.py dans l'explorateur Windows, auquel cas, c'est la terminaison “.py” qui appellera “python.exe”, et la console DOS sera affichée. Pour l'éviter, c'est très simple: vous renommez simplement le fichier:
calculext.py  =>  calculext.pyw

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

  • Vous pouvez aussi lancer la Calculext dans une console DOS (cmd). Vous vous placez dans le répertoire de travail (commande cd), et vous lancez par une commande du genre:
C:\Python25\pythonw.exe "C:\calculext\calculext.py"

Pour lancer la calculette dans sa version “exe” (non modifiable):

  • Vous faites comme pour lancer n'importe quel programme “exe”. Je vous conseille de créer une icône sur le bureau.

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)

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, appel du menu, 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. Par contre, pendant un calcul long, la saisie d'une nouvelle expression est neutralisée.

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 ou le message d'erreur dans la ligne graphique de résultat et affiche aussi le temps de calcul.

L'utilisateur qui a lancé un calcul trop long peut demander l'arrêt du calcul en cliquant sur le menu “stopper” ou en tapant F2. Dans ce cas, il est demandé au thread de s'arrêter (appel de la méthode stopcalcul() du thread). Grâce à “settrace”, les fonctions potentiellement longue n'ont plus besoin d'être codées spécialement pour cela. Cependant, quand une fonction longue a un résultat très très long (50000 caractères par exemple), la fonction d'affichage, qui - elle - ne peut pas être interrompue, peut prendre plus de temps que la fonction de 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.

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.

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 et la recharger plus tard avec les fonctions du menu.

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. Si l'item est vide, cela déclenche l'affichage d'une ligne de séparation non sélectionnable dans le menu.

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.


Le code est trop long pour être affiché ici, mais il est largement auto-documenté et je vous invite à le consulter directement.

Commenter ajouter des fonctions et des modules

Les “modules” additionnels python utilisés par la Calculext sont des fichiers “texte” séparés, importés dans le programme principal (ici “calculext.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 conversions import *
from arithmetique import *
from combinatoire import *
from probabilite import *
from credit import *
from temps import *

Je vous suggère d'éditer l'un de ces modules pour voir comment il est présenté.

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é à part “None” (= “rien”). Par contre, ce résultat peut être n'importe quoi qui peut s'écrire 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!

La vérification des données est importante au moins pour 2 raisons:

  • la calculatrice ne doit pas renvoyer un mauvais résultat “silencieusement”.
  • le message d'erreur doit renseigner le mieux possible l'utilisateur sur l'erreur commise afin qu'il puisse la corriger.

Pour faciliter la vérification des données, vous pouvez utiliser les fonctions prédéfinies suivantes:

estentier(n)
estnombre(n)
estchaine(t)
estliste(L)

L'appel de ces fonctions situées au début du programme principal calculext.py, à partir d'un module additionnel, nécessite le code suivant à mettre au début de ce module additionnel:

from sys import modules
pmain = modules['__main__']

Et les fonctions citées devront être préfixées par “pmain”. Par exemple:

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

Vous pouvez aussi recopier les fonctions dont vous avez besoin au début du module additionnel.

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 à un module additionnel existant.

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.txt · Dernière modification: 2008/07/08 19:14 de tyrtamos

Outils de la page