image gauche logo Clairinfo image droite

Version du : 28/01/2007

TUTORIAL PARADOX POUR WINDOWS

Leçon 4 - ObjectPal, bibliothèque de code

Introduction :

Les leçons précédentes nous ont permis de présenter l'objectif de ce tutorial : construire une application de gestion commerciale en Paradox pour windows. Nous sommes arrivés à un schéma des données à gérer, nous avons construit les tables dans Paradox puis nous avons constuit notre première fiche Paradox pour constituer le menu de notre application. Nous allons maintenant coder les événements intéressants de cette fiche et tenter de rendre réutilisable le code qui peut l'être en l'intégrant dans une bibliothèque.

Les événements intéressants pour notre fiche menu :

Nous avons déjà rencontré pour nos boutons "poussoir" les méthodes PushButton(), MouseEnter(), MouseExit() mais plus globalement pour notre fiche Menu.fsl nous allons programmer d'autre événements :

- l'Open de la fiche , pour paramétrer le cadre de notre application (taille, position, titre, menu principal)
- Le Close de la fiche pour demander une confirmation à l'utilisateur avant de quitter l'application
- La réaction aux touches de fonctions F1, F2 ... afin de bloquer toute fonctionnalité non souhaitée

Pour l'open il est prudent de prévoir un code centralisé permettant d'effectuer les mêmes opérations pour toutes les fiches à ouvrir, comme la constitution d'un menu commun etc... Quitte à centraliser du code nous allons également intégrer la routine d'incrémentation qui nous servira à fournir à la demande des numéros uniques pour indexer nos tables.

Créer une bibliothèque pour centraliser du code :

- Dans la fenêtre projet, cliquez avec le bouton droit sur Bibliothèque et choisissez Nouveau
- Dans la fenêtre vierge qui s'affiche, cliquez avec le bouton droit et demandez Explorateur d'objets
- Vérifiez que vous êtes sur l'onglet Evénement et double-cliquez sur Nouvelle méthode

La méthode OpenForm : (Centralisera le code en ouverture de fiche)

- Choisissez OpenForm comme nom de méthode et validez

- Tapez alors le code suivant :

method OpenForm()
Var
 M Menu
 sTItre String
EndVar

sTitre="Mon Appli"
HideToolBar()
Maximize()
M.addText(sTitre)
M.show()
FormatSetDateDefault("Windows court")

endmethod

En gros cette méthode déclare 2 variables M et sTitre qui vont nous servir à créer notre menu d'application, elle définit le titre de l'application, ici "Mon Appli" puis elle demande de masquer la barre d'outils de Paradox avec HideToolBar() afin d'empêcher l'utilisateur d'accéder directement aux fonctionnalités de Paradox, elle maximise la taille de la fenêtre puis elle construit un menu qu'elle affiche et enfin elle demande de gérer les dates sur le style "Windows court" c'est à dire jj/mm/aaaa dans le cas le plus courant.

En 9 lignes de code nous réalisons là toute une série de manipulations qui nécessiteraient bien plus d'instructions dans un langage moins performant. Cette simplicité et cette lisibilité du code constitue l'avantage principal de Paradox sur les autres outils. Même Access, réputé facile, est très loin de cette simplicité quand il s'agit de programmer une application.

- Vérifiez la syntaxe avec <F9>, un message en bas d'écran doit vous annoncer "Auncune erreur de syntaxe".
- Fermer alors la fenêtre de la méthode OpenForm

La méthode GetNextID : (Centralisera le code d'incrémentation pour nos identifiants numériques)

- Double-cliquez sur Nouvelle méthode
- Choisissez comme nom de méthode et validez
- Tapez (ou récupérez) alors le code suivant :

method GetNextID(Const vID SmallInt) LongInt
Var
  P,PControle TCursor
  sRep,sTable String
  vCle LongInt
EndVar

if not P.open(":App:NoPiece") then
  MsgStop("Erreur !","Impossible d'ouvrir la table NoPiece.")
  return 0
endif

if not P.locate("ID",vID) then
  MsgStop("Erreur !","Impossible de trouver le paramètre N°Pièce demandé.")
  P.close()
  return 0
endif

if P.TableControle="" then
  MsgStop("Erreur !","Impossible de trouver la table de contrôle.")
  P.close()
  return 0
endif

sTable=":App:"+P.TableControle

if not PControle.open(stable) then
  MsgStop("Erreur !","Impossible d'ouvrir la table "+sTable)
  return 0
endif

P.edit()
sRep=""

while true

if not P.LockRecord() then
  sRep=msgQuestion("Un autre utilisateur demande un "+P.NomPiece+" :","On réessaye ?")
  if sRep="No" then
    quitloop
  else 
    loop
  endif
else
  quitloop
endif

endwhile

if sRep="No" then
  P.endedit()
  P.Close()
  return 0
endif

vCle=P.NoPiece + 1

;// On controle que la clé n'existe pas déjà dans la table de contrôle associée

while true

if PControle.locate(1,vCle) then
  vCle=vCle+1
else
  quitloop
endif

endwhile

P.NoPiece=vCle

P.endedit()
P.close()
PControle.close()

return vCle

endmethod

Bon pas de panique, nous allons détailler précisément ce qu'elle fait.

On commence par déclarer quelques variables dont 2 d'un type particulier : le TCursor.

Qu'est-ce qu'un TCursor ?

C'est la baguette magique du développeur Paradox ! En effet un TCursor (un curseur en mémoire sur une table) permet une fois ouvert sur une table donnée de parcourir celle-ci mais également de restraindre la vue aux enregistrements vérifiant des conditions et surtout d'appliquer aux données contenues dans les enregistrements tout une série de manipulations :

- insertion d'enregistrement
- suppression d'enregistrement
- modification des données d'un champ
- calcul sur les données...

Lors du développement de cette application nous aurons très souvent recours aux TCursors. Ils permettent également de manipuler les tables sans les afficher à l'écran et ,notamment grâce à la boucle Scan / EndScan, d'appliquer une série d'instructions OPAL aux enregistrements sélectionnés.

Les noms de variables :

C'est à vous de nommer vos variables comme bon vous semble. Il existe des nomenclatures plus ou moins officielles sur le sujet mais le bon sens impose surtout d'être consistant et simple. Pour notre part nous avons pris quelques habitudes mais elles ne sont pas forcément exemplaires :

P : Pointeur de type TCursor
T : Pointeur de type Table
(Paradox offre un autre objet pour manipuler physiquement les tables : création, suppression, copie etc...)

s : Variable de type String (chaîne de caractères)
v : Variable de type Number (nombre avec décimales) ou SmallInt (petit nombre entier) ou LongInt (grand nombre entier)
d : Variable de type Date ou DateTime (Heure + Date)

L'explication du code :

Après donc avoir déclaré les variables nécessaires on essaie d'ouvrir un TCursor "P" sur la table NoPiece.db qui contient les derniers numéros utilisés. Notez l'utlisation très importante de l'alias avant le nom de table ce qui permettra par la suite de déplacer les tables ou même de se constituer très facilement une application de test par un simple changement dans la définition de l'alias. Souvenez-vous également pour une application réelle de la nécessité de spécifier pour les tables un alias différent des fiches et des états ( par exemple :Data: ) comme expliqué dans la partie 2 du tutorial.

En cas d'insuccès lors de l'open on envoie un message d'erreur à l'utilisateur (admirez la simplicité de la syntaxe !) puis on arrête la fonction en renvoyant 0.

En cas de succès, on cherche à localiser dans la table (donc NoPiece.db) l'enregistrement souhaité. Cet enregistrement est passé en paramètre à la fonction sous la forme d'une constante numérique appelée ici vID. La fonction locate utilisée ici est l'une des plus importantes à connaître pour le TCursor car elle permet de tenter de se positionner sur l'enregistrement répondant aux critères transmis. Cette première syntaxe demande de localiser le premier enregistrement de la table dont le champs nommé "NoID" contient la valeur vID. En cas de succès le TCursor se déplace sur l'enregistrement trouvé et la fonction locate renvoie True, sinon le TCursor ne bouge pas et la fonction locate renvoie False.

Puis on vérifie qu'une table de contrôle a bien été spécifiée dans la table NoPiece pour la clé désirée. L'intérêt de la chose et de s'assurer contre tout problème possible sur ce dernier numéro stocké. Que faire si l'utilisateur se trompe en revenant en arrière pour sa séquence de N°Facture alors qu'il existe des N° de factures déjà attribués ? Que faire si un problème informatique (oui cela arrive parfois, micro coupure etc...) décale la valeur du dernier numéro utilisé ? Un moyen simple est de protéger l'application en vérifiant que le numéro que l'on s'apprête à attribuer n'est pas déjà utilisé. Si par harsard il l'était alors on demande à l'application de trouver le prochain numéro libre en incrémentant et on reprend ce premier numéro disponible comme valeur de départ. En somme le programme s'auto-corrige en cas de souci !

La syntaxe if P.TableControle="" permet de tester le champ TableControle de la table NoPiece pour l'enregistrement sélectionné auparavant.

Une fois le nom de la table de contrôle obtenu, on ouvre un deuxième TCursor sur celle-ci. On passe ensuite le TCursor pointant sur NoPiece.db en mode édition afin de pouvoir changer la valeur du dernier numéro. On initialise une variable string sRep à blanc pour pouvoir la tester plus loin, puis on boucle en attendant qu'une instruction renvoie une valeur False stoppant la boucle while true.

L'objectif est de verrouiller l'enregistrement car cela garantirait que personne d'autre sur le réseau ne peut, en même temps que nous, incrémenter une clé. Paradox gère pour nous automatiquement ces verrous d'enregistrement. Dès qu'un utilisateur modifie un enregistrement, les autres ne peuvent que consulter celui-ci. Cela étant nous voulons que si le verrouillage échoue notre utilisateur puisse recommencer la tentative autant de fois qu'il le désire en espérant que le verrou aura été libéré entre-temps. Le risque est grand avec une telle boucle de lancer une tâche sans fin donc la question posée à l'utilisateur assure qu'en cas de problème il pourra sortir de la boucle.

Si tout est bon à ce stade (verrouillage réussi) on récupère dans la variable vCle le prochain numéro à utiliser pour le champ correspondant. Mais avant de l'attribuer on vérifie dans la table de contrôle que cette valeur ne pose pas de problème. La syntaxe if PControle.locate(1,vCle) then utilise une autre possiblité de la méthode locate de l'objet TCursor, la possibilité de spécifier en premier paramètre non pas le nom du champ entre guillemets mais le numéro de colonne du champ. C'est ce qu'il nous faut car nous ne connaissons pas à ce stade le nom du champ clé de la table de contrôle qui dépend du champ à incrémenter. Nous savons par contre que ce sera toujours la première colonne.

Ce contrôle effectué , nous affectons cette nouvelle valeur à notre compteur dans la table NoPiece.db, nous validons l'enregistrement en arrêtant le mode édition puis nous fermons nos TCursor pour être sûr que la mémoire soit bien libérée. Nous retournons enfin vCle comme résultat de la fonction.

Pas trop mal au crâne ? Bien, vous avez vu le plus compliqué ! Le reste est un jeu d'enfant...

Mais continuons nos routines de bibliothèque.

La méthode ConstMenu : (centralisera la création d'un menu pour les éditions à l'écran)

method ConstMenu(Const NomEtat String)
Var
  PmFichier,PmEtat PopUpMenu
  M Menu
  Etat Report
EndVar

Etat.open(NomEtat)
Etat.maximize()

if not M.isAssigned() then

PmFichier.AddText("&Imprimer...",MenuEnabled,MenufilePrint)
PmFichier.AddText("&Configurer l'imprimante...",MenuEnabled,MenufilePrinterSetUp)
PmFichier.AddText("&Fermer",MenuEnabled,MenuControlClose)
PmEtat.AddText("Première page",MenuEnabled,MenuReportPageFirst)
PmEtat.AddText("Page suivante",MenuEnabled,MenuReportPageNext)
PmEtat.AddText("Page précédente",MenuEnabled,MenuReportPagePrevious)
PmEtat.AddText("Dernière page",MenuEnabled,MenuReportPageLast)
PmEtat.AddText("Aller à la page...",MenuEnabled,MenuReportPageGoto)

M.AddPopUp("&Fichier",PmFichier)
M.AddPopUp("&Etat",PmEtat)

endif

Etat.setmenu(M)

endmethod

Bon là rien de bien sorcier si ce n'est que ce code vous rendra de grands services ! On construit un menu avec deux rubriques, l'une Fichier offira les options classiques : Imprimer, Configurer l'imprimante, Fermer tandis que l'autre, Etat offrira la navigation dans les pages : Première page, page suivante, page précédente, dernière page, aller à la page...

Attention ce code ne fonctionne qu'à partir de la version 5.0 de Paradox. Il est important car il vous assure que lors de la visualisation d'un état l'utilisateur ne verra pas apparaître le menu complet de Paradox qui lui permettrait par exemple d'accéder directement aux tables sans passer par votre application ou de lancer des commandes sans contrôle.

Sauvegarder la bibliothèque :

Une fois ces méthodes utiles créées et leur syntaxe vérifiée il convient de sauvegarder cette bibliothèque:

- Menu Fichier / Sauvegarder
- Choisissez Lib.lsl comme nom de fichier

Remarquez qu'encore une fois Paradox crée un fichier à part pour chaque bibliothèque ce qui s'avère à l'usage très pratique pour dupliquer du code entre différents projets.

L'utilité de centraliser le code en bibliothèque :

- Faciliter la maintenance de l'application : On change à un endroit le code qui est appelé par les différentes fiches
- Facilité pour réutiliser le code : On duplique sa ou ses bibliothèques pour les nouveaux projets.

Dès que l'on rencontre un code qui semble revenir souvent il faut essayer de le rendre général avec, si nécessaire, l'introduction de paramètres et le centraliser en bibliothèque.

 

Haut de page    Précédent    Suivant

© Copyright 2000-2007 , Clairinfo ® , http://www.clairinfo.fr