mardi 16 février 2010

Le champ Webcam

0. Introduction

Quel intérêt pédagogique peut-il y avoir à utiliser le champ Webcam ? En fait, ce support va montrer à travers un exemple ludique l'utilisation d'une Webcam pour faire de la détection d'état.

À travers cet exemple, plusieurs thèmes vont être abordés : la création de fichiers image, l'utilisation de fonctions de dessins, l'utilisation du RVB, la création de fichiers de configuration.


Cet applicatif va être utile pour savoir si une zone de couleur définie est présente. Les usages détournés peuvent être :

  • La détection de mouvement.
  • Vérifier si un bouchon est présent sur une ligne d'embouteillage.
  • Etc.

Pour réaliser cet exemple, l'utilisation de la méthodologie objet aurait pu être envisagé, j'ai préféré revenir sur le procédural. Le lecteur motivé pourra regrouper les procédures dans des classes s'il le désire à titre d''approfondissement.


Ce projet se nommera Webcam, et n'aura pas d'analyse. Il y aura 2 fenêtres :

  • Départ (la fenêtre initiale de l'application)
  • Param ( la fenêtre de paramétrage).

Le but de cette application est de faire prendre à la webcam un cliché initial qui servira d'étalon, de sélectionner sur l'étalon la zone dont on va ensuite tester la présence.





I. Les écrans :


I-A. Voici la première fenêtre nommée départ :


A droite la zone de la webcam, à gauche le cliché.


I-B. Voici le second écran nommé param :


II. La conception


II-A. Voici l'écran "Départ" en mode conception :


La fenêtre à comme dimension : 676x410.

Pour connaître ou modifier les dimensions données ici, regardez en bas à droite de l'éditeur WinDev :


On trouve comme info :

Le nom de la fenêtre.

La position relative de l'objet dans la fenêtre.

La taille de l'objet.

Le plan dans lequel il se trouve.


Pour changer les dimensions vous pouvez modifier directement les valeurs. Commencer par modifier les valeurs de la fenêtre départ

Si vous voulez réécrire la taille le signe de la multiplication est la lettre X et non l'étoile de la multiplication.

Voici les caractéristiques de tous les objets :

Type de champ Nom Libelle Position relative Taille
Libelle Par défaut Source 6,6 324X309
Libelle Par défaut Capture 336,6 324X309
Champ de saisie EtatObjet Aucun 209,336 250X26
Champ Créer une image Source Aucun 12,26 310X242
Champ Web Caméra Capture Aucun 343,26 310X242
Bouton Bparam Paramètrage 13,284 80X24
Bouton Bcapture Capture 243,284 80X24
Bouton Btest Test 448,284 80X24
Champ Créer une image Cible Aucun 691,-13 310X242

Placez les objets et normalement si vous respectez les consignes votre fenêtre ressemblera à celle représentée en haut de cette page.


Le champ webcam doit être "branché" sur votre Webcam, vous allez le faire manuellement en cliquant avec le bouton droit sur le champ. Choisissez description puis dans l'onglet général faites apparaître votre Webcam dans la combo "Webcam utilisée"


Maintenant, voyons le code de cet écran :


Lorsque l'application se lance s'il y a déjà eu une image "étalon" nous allons faire en sorte qu'elle s'affiche dans sa zone image.


Dans la zone de code d'initialisation de la fenêtre départ saisissez ceci :

source="etalon.jpeg"


Voici le code du bouton Bcapture :
dSauveImageJPEG(capture,"etalon.jpeg")

source=""
source="etalon.jpeg"

La fonction dSauveImageJpeg gele le contenu du champ Webcam "capture" et l'enregistre sous "etalon.jpeg".

L'instruction suivante enlève le contenu du champ image "source"

L'instruction d'après mets l'image "etalon.jpeg" dans le champ "source"


Voici le code du bouton Btest :
dSauveImageJPEG(capture,"test.jpeg")

cible=""
cible="test.jpeg"
Compare

Vous avez surement vu que je vous ai fait placer une zone image hors fenêtre ? Ce champ image va nous servir au test de comparaison.


Donc, le code du bouton Btest fait prendre un cliché appelé "test.jpeg", l'affecte au champ image "cible" et lance la procédure de comparaison.


Voyons de suite le contenu de cette procédure compare. Créez une procédure locale nommée compare dont voici le code :

J'en vois qui, rien qu'a la lecture de ce code, claquent des dents, il y en même qui s'évanouissent !


Je vous rappelle le principe du programme.

La fenêtre "param" va vous permettre de sélectionner une zone rectangulaire de test, nous aurons donc 4 points (X1,X2,Y1,Y2)


Comme nous ferons des recherches sur les couleurs il faut pouvoir donner un pourcentage (c'est grâce au champ potentiomètre de la fenêtre "param" ) de spectre rouge acceptable, idem pour le vert et le bleu. C'est pour cette raison que vous verrez des limites bleu-, bleu+ .etc..


Voyons les variables :

Resultante est un entier qui contiendra le code couleur d'un pixel situé à un point (x,y) de la zone image cible et qui servira pour le test de comparaison avec l'image étalon.


X1,X2,Y1,Y2 sont des entiers qui contiennent les points définissants la zone rectangulaire.

Letableau est un tableau qui contient les limites couleur RVB

Trouve est un booléen qui nous indiquera si les couleurs recherchées sont toutes trouvées.


On va placer dans les variables X1,X2,Y1,Y2 les points définissants notre zone rectangulaire de test. Ces points sont dans un fichier Ini nommé ParametresW.ini.

Pour avoir une idée du fonctionnement de INITLit ouvrez l'aide.

Le tableau va être rempli avec les valeurs couleur limites.
TANTQUE x1resultante=dPixelCouleur(cible,x1,y1)

trouve=Spectre(resultante,letableau)
x1+=1
y1+=1
FIN

Les plus rapides ont-ils compris ce que fait ce code ? Pour les autres voici une explication. En fait, on va tracer une diagonale dans notre zone rectangulaire, point par point. Pour chaque point on relève sa couleur dans la variable résultante. Cette variable résultante est passée dans la procédure globale Spectre qui contrôle si la couleur est comprise dans les limites RVB de références.


Selon le résultat du booléen trouve, on affiche un message dans la zone EtatObjet.


Voici le code de la procédure globale "Spectre" :


Explication du code :


Pour trouver la valeur RGB comprise dans l'entier renvoyé par la fonction DpixelCouleur on est obligé d'effectuer certains calculs.

Ainsi pour avoir la dominante Rouge on effectue le calcul suivant l'entier couleur modulo 256.


Dans le tableau nous avons nos limites par couleur. Le but est de voir si la couleur récupéree est comprise entre ces 2 valeurs du tableau.


Nous avons un booléen qui selon son état indiquera si la couleur est dans les limites.


Vous aurez une vue d'ensemble lorsque la fenêtre param aura été vue.


Voyons le code du bouton Bparam :

Ouvre(Param)


II-B. Maintenant que tout le code de la fenêtre départ est décrit passons à la fenêtre Param.


La fenêtre a les dimensions suivantes : 660X320


Voici le tableau de positionnement :

Type de champ Nom Libelle Position relative Taille
Champ image Mire Source 5,5 310X242
Bouton Selection Capture 336,6 324X309
Bouton Enregistre Enregistrer les variables d'étalonnages 365,161 219X24
Potentiometre Par défaut 335,26 290X38
Libelle Par défaut Potentiometre d'imprécision 328,6 304X66
Champ de saisie Nomdezone Nom de la zone sélectionnée : 328,78 297X22

Vous devez avoir maintenant une fenêtre ressemblante à celle présentée ci-dessus.


Dans la zone de déclaration globale de la fenêtre inscrivez ceci :
rescouleur,Crouge,Cvert,Cbleu sont des entiers

Lex,ley sont des entiers
letab est un tableau de 6 entiers

Le potentiomètre va nous servir pour avoir un pourcentage dans la couleur sélectionnée. Par exemple, si l'utilisateur a cliqué dans un rouge de code RVB 200 et que la valeur retournée par le potentiomètre soit 10, on appliquera 10 % de plus à 200 pour la couleur la plus haute et -10% pour avoir l'intervalle bas.

Ces 2 valeurs seront ensuite enregistrées dans un tableau.


Voici le descriptif du potentiomètre :

Faites un clic droit dessus et choisissez "Description" ensuite l'onglet "Détail ". Dans la zone valeur initiale mettez 10, dans la valeur minimale mettez 0 et 100 pour la maximale.


Voici le code du bouton sélection :

Mire..CurseurSouris = curCroix


Comme vous le voyez, ce code ne fait pas grand-chose, il fait en sorte que le curseur de survol de l'image soit une croix.


Voyons maintenant comment s'opère la sélection d'une zone couleur. Pour pouvoir capturer une zone, il faut que l'utilisateur clique dans l'image. Le code sera donc dans la partie clic de la zone image. Le voici :


Il faut donc qu'à chaque clic le système nous délimite une zone couleur sur l'image étalon, pour éviter de superposer les rectangles de sélection à chaque clic l'image de base est réécrite dans la zone image.

C'est le rôle de la première ligne.


Ensuite nous récupérons la position exacte de la souris lors du clic.

Puis nous récupérons la valeur couleur du pixel situé à la position de la souris.

Nous calculons les Valeurs RVB.

Il nous faut ensuite remplir le tableau des valeurs couleur du point cliqué. C'est la fonction Spectre_origine qui s'en charge.

Voici son code :


Ce code ne sert qu'à remplir un tableau de valeurs correspondantes à un entier sur lequel on applique un pourcentage donné par le potentiomètre.


Voyons maintenant la fonction CalculZone.
PROCEDURE calculzone(posx,posy,letableau)

Pxd,pxg,pyb,pyh sont des entiers
trouve est un booléen=Vrai

Pxd=posx
pxg=posx
pyb=posy
pyh=posy

dDébutDessin(Mire)

// on recherche une zone rectangulaire comportant cette couleur
// on commence par les x vers la droite

TANTQUE trouve
SI Spectre(dPixelCouleur(Mire, Pxd, posy),letableau) ALORS
Pxd=Pxd+1
SINON
trouve=Faux
FIN
FIN

// on continue les x vers la gauche
trouve=Vrai
TANTQUE trouve
SI Spectre(dPixelCouleur(Mire, pxg, posy),letableau) ALORS
pxg=pxg-1
SINON
trouve=Faux
FIN
FIN

// on cherche y vers le bas
trouve=Vrai
TANTQUE trouve
SI Spectre(dPixelCouleur(Mire, posx, pyb),letableau)ALORS
pyb=pyb+1
SINON
trouve=Faux
FIN
FIN

// on cherche y vers le haut
trouve=Vrai

TANTQUE trouve
SI Spectre(dPixelCouleur(Mire, posx, pyh),letableau) ALORS
pyh=pyh-1
SINON
trouve=Faux
FIN
FIN

INIEcrit("Points","X1",NumériqueVersChaîne(pxg),fRepEnCours()+"\ParametresW.ini")
INIEcrit("Points","Y1",NumériqueVersChaîne(pyh),fRepEnCours()+"\ParametresW.ini")
INIEcrit("Points","X2",NumériqueVersChaîne(Pxd),fRepEnCours()+"\ParametresW.ini")
INIEcrit("Points","Y2",NumériqueVersChaîne(pyb),fRepEnCours()+"\ParametresW.ini")

dRectangle(pxg,pyh,Pxd,pyb,iTransparent,iNoir)

Cette fonction a un rôle important. Lorsque l'on clique sur l'image elle va essayer de trouver une zone rectangulaire comprise dans un spectre, plus ou moins le pourcentage, défini par la couleur de référence du clic. Une fois la zone couleur définie, les valeurs des points ayant servis à faire la zone rectangulaire sont mémorisées dans le fichier ini.


Maintenant voici le code du bouton enregistre :


Ce code ne fait qu'enregistrer les variables dans un fichier ini qui sera utilisé dans le code de la fenêtre départ pour interpréter l'image.


Voilà le code est fini de saisir, vous pouvez essayer votre application.

Accès à des bases de données tierces (Mysql)


I. Présentation

Objectifs : Accès à des bases de données tierces (Mysql)Langage Sql, Fonctions Sql de WinDev, gestion avancée des tree-view, des tables mémoire, des chaînes de caractères.

A l'heure actuelle, les bases de données de type Clients/Serveurs ont la faveur de plus en plus d'informaticiens. Une de ces bases se détache du lot, il s'agit de Mysql. Cette base de donnée offre plusieurs avantages non négligeables.

  • Elle est gratuite.
  • Elle est rapide
  • Elle est fiable.
  • Elle est Multi-Plateforme.

Le fait qu'elle existe sous plusieurs systèmes d'exploitations est appréciable, en effet à l'heure actuelle beaucoup de Fournisseurs d'Accès Internet vous permettent de vous connecter à une base de donnée, dans la majorité des cas cette base de donnée est Mysql. Pourquoi la choisissent elles ? En fait, ils ont fait le choix du logiciel libre, donc le système d'exploitation est Linux, comme les internautes utilisent de plus en plus le couple Php/Mysql, cette base de données était le choix technologique le plus évident à faire.


Maintenant imaginez la situation suivante : Vous êtes développeur dans une société qui a sur Internet un site où les clients peuvent passer des commandes. Le site utilise Php et Mysql, comment faire pour que votre logiciel commercial puisse récupérer les commandes directement dans la base de donnée Mysql qui est sur Internet ?




Suite




Je vous sens dubitatif ! Heureusement super Jean-Luc a la solution : Nous allons, ensemble, développer un logiciel de connexion à une base de donnée Mysql, créer des requêtes, récupérer les résultats...Elle n'est pas belle la vie ? Mais cependant certaines mises en garde sont nécessaires :


Je vous rappelle qu'il est inutile de faire ce "mini lab" sans maîtriser les "mini lab" précédents. Je ne reviens pas sur les notions abordées dans les supports précédents donc voici l'adresse ou vous les trouverez : www.btsig.org.

Pour le bon déroulement de ce support téléchargez et installez easyphp 1.6 que vous trouverez à l'adresse suivante : http://www.easyphp.org/

Durant le Mini lab, prenez le temps de comprendre ce que vous faite, aucun apprentissage efficace ne peut avoir lieu si on se contente de copier ou imiter. Donc si vous vous sentez « largué », pas de souci, apprenez à faire marche arrière et relisez. Je fais toujours en sorte que les explications soient claires, mais n'ayez pas honte de ne pas comprendre immédiatement, faites l'effort et vous serez toujours récompensé. N'oubliez pas l'excellente aide de WinDev (vous savez 1 !)

Durant cet exercice, vous avez le droit de faire des pauses ;-))

j0252349 Attention : Si vous n'êtes pas administrateur de votre ordinateur une librairie de Mysql ne s'installera pas correctement, il s'agit de libmysql.dll, de plus le serveur Mysql s'arrêtera à la moindre tentative de connexion. Autre point important : Si des erreurs récurrentes se produisent alors que votre code est propre et que vous utilisiez Easyphp, téléchargez la dll libmysql.dll directement chez Mysql.De plus, allez sur www.pcsoft.fr dans la rubrique téléchargement pour télécharger l'accès natif à Mysql

Avant de commencer, vérifiez qu'Easyphp est actif (Vous devez apercevoir un E noir avec un point rouge clignotant dans la barre des taches). S'il n'est pas en fonctionnement, vous ne pourrez pas accéder au serveur MySql. Je vous laisse lire la doc fournie avec pour le faire fonctionner, c'est simple.


Nous allons maintenant entrer dans le vif du sujet. Créez un nouveau projet nommé TP6 qui ne comporte aucune analyse, normal puisque nous allons accéder à des données distantes. Ce projet comportera 2 fenêtres : Une orienté gestion de , l'autre sur l'édition de requêtes.

Voici le prototype de la première fenêtre que vous nommerez départ et qui sera la première fenêtre du projet.

tp6-1

En haut à gauche : Ici vous trouvez 4 champs de saisie nommés : Numip, Login, Mdp, Etatcnx. Un bouton nommé Cnx. Ces champs sont dans un champ libellé de style « Libellé-Acheval_DoubleB », dont le libellé est : « Connexion au serveur Mysql »

En haut à droite : Ici, créez un champ arbre que vous nommerez listebase. Vous pouvez l'encadrer par un champ libellé.

En bas : Attention, ici c'est une table mémoire que vous nommerez Table1 dont toutes les colonnes sont invisibles. Dans cette table sera affiché le contenu des tables de votre base de donnée Mysql. L'affichage est activé par un choix de table dans l'arbre listebase. Comme nous ne connaissons pas à priori le nombre de colonnes de la table a afficher nous allons employer une grosse ruse Cheyenne. On va créer une quinzaine de colonnes texte nommées c1,c2,c3...c15 et toutes invisible, capito ? Nous créerons l'affichage par programmation plus tard

Les boutons : Insérez 2 boutons un nommé Requete, l'autre Quitter. Je vous laisse trouver le code du bouton Quitter.


Maintenant que la scène est installée, voyons les comportements des objets et ce que l'on attend d'eux. Pour ce connecter à une base de donnée Mysql nous avons besoin de divers renseignements :


L'adresse Ip de l'ordinateur où est située votre base de donnée Mysql. Si vous l'avez sur votre ordinateur, plusieurs possibilités s'offrent à vous, soit vous inscrirez dans ce champ l'adresse ip de votre ordinateur, soit localhost, soit une adresse de bouclage ex : 127.0.0.1.

Le login : Si vous venez d'installer votre base de donnée le login par défaut est : root. Sinon demandez un login à votre administrateur réseau.

Le mot de passe : Si vous venez d'installer Mysql sur votre poste le mot de passe par défaut n'est pas défini, donc cette zone restera vide, sinon contactez votre administrateur si vous n'en avez pas.


Donc, lorsque ces renseignements seront saisis nous essayerons de nous connecter à la base de donnée via le bouton de connexion, si la connexion réussie nous remplirons l'arbre avec les bases Mysql et pour chaques bases, les tables constituantes. Dans le champ etatcnx nous mettrons un texte nous indiquant le succès de la connexion sinon l'échec.


On y va ?Vous avez placé tous les objets ? Votre fenêtre ressemble à la mienne ? Vous l'avez enregistrée sous le nom de départ ? Vous l'avez déclarez comme première fenêtre du projet ? Non ! ! Mais qu'est-ce que vous faites ?


Nous allons avoir besoins de 2 variables globales à la fenêtre, allez dans le code de la fenêtre et dans la zone « déclaration globale de départ » inscrivez ceci :
GLOBAL

mabase,matable sont des chaînes

Ces 2 variables doivent être connues ou vues par tous les objets de la fenêtre, c'est pour cela que l'on les déclare globales au plus haut dans le conteneur principal.

NB : Le mot clé global pourrait dans ce cas là être omis.


Intéressons nous maintenant au comportement du bouton de connexion. Son rôle est d'établir une connexion avec la base de données Mysql. Il a besoin de l'adresse ip de la base, du login et du mot de passe pour établir le contact.


Voici son code :
resultat est un entier


resultat=SQLConnecte(Numip,login,mdp,"","MySQL")
SI resultat=0 ALORS
etatcnx="Votre serveur Mysql n'a pas repondu"
SINON
etatcnx="Votre connexion est active"
lesbases()
FIN
SQLDeconnecte()// Une fois les traitements fait, on se déconnecte

importante est SQLConnecte son rôle est de connecter l'application en cours à une base de données à interroger par SQL. Pour cela elle a besoin de paramètre qui sont le contenu du champ Numip, de login, du mot de passe. Le 4eme paramètre est vide (les 2 apostrophes), il est dévolu au nom de la base de donnée souhaitée. Le 5eme paramètre est le type de base de données attaqué, dans notre cas MySQL. Appuyez sur F1 en étant positionné sur le mot SQLConnecte et regardez dans l'aide toutes les bases de données susceptibles d'être interfacées avec WinDev !


Cette procédure retourne un entier qui vaut 0 si la connexion n'a pas pu être établie (mauvaise adresse ip, mauvais login, mot de passe, serveur Mysql arrêté.etc.). Une bonne habitude à prendre est de tester la réussite ou l'échec d'une fonction. Donc, si resultat=0 on informe l'utilisateur qu'il y a un boulon dans le potage. Sinon c'est que tout va bien : on fait afficher dans le champ de saisie etatcnx que la connexion est active. Pour alléger le code nous allons créer une procédure locale à la fenêtre que nous nommerons lesbase. Le rôle de cette procédure est de nous remplir l'arbre avec les bases de données MySql et les tables les composants. Voyons son code :
PROCEDURE lesbases()

resultat est un booléen

resultat=SQLExec("show databases","requete1")

SI resultat ALORS // L'exécution de la requête est réussie
TANTQUE SQLFetch("requete1") = 0
ArbreAjoute(listebase,"Bases"+TAB+ SQLLitCol("requete1", 1),aDéfaut,aDéfaut,SQLLitCol("requete1", 1))
lestables(SQLLitCol("requete1", 1))
FIN
SQLFerme("requete1")
ArbreDéroule(listebase,"Bases")
SINON
Info("Vous avez un problème de connexion")
FIN

Résultat est un booléen qui va recevoir le résultat d'exécution de ci va exécuter la requête nommée « requete1 », dont le texte associé est « show databases » (cette commande Sql fait retourner l'ensemble des bases de donnée présentent sur votre serveur MySql).Si la requête s'exécute bien resultat vaut vrai sinon faux.

TANTQUE SQLFetch("requete1") = 0  


Cette ligne de code peut vous paraître étonnante, en fait ne lance pas la récupération de toutes les lignes du résultat de la requête : seul l'enregistrement en cours est récupéré par bizarre est que tant qu'elle retourne 0 la lecture de l'enregistrement s'est bien passée. Il y a des fois où la logique n'est plus de ce monde ! Mais ne nous laissons pas perturber par cette petite bizarrerie.. !

crée un tree-view avec les éléments que l'on va lui passer.

Dans notre cas il faut remplir avec le contenu résultant de la requête précédente. En fait le résultat de la requête est, dans ce cas là, une table d'une colonne contenant un nom de base par ligne.


SQLFetch parcourt les lignes de la table et SQLLitcol("requete1", 1) lit pour la requête passée en paramètre, le contenu de la colonne passée aussi en paramètre (ici 1).

Je vous laisse regarder l'aide d'ArbreAjoute et comparer avec le code, vous allez vite comprendre son fonctionnement. Nous allons donc créer un arbre affichant les bases de données disponibles, mais pour chaque base il nous faut aussi insérer dans l'arbre les tables qui 'est le rôle de la procédure globale lestable que nous allons créer.

Comme vous le remarquez la procédure lestables prend comme paramètre le nom de la base de donnée contenu dans SQLLitCol("requete1", 1)


Créez donc cette procédure
PROCEDURE lestables(labase)

resultat est un booléen

SQLConnecte(Numip,login,mdp,labase,"MySQL")
resultat=SQLExec("show tables","requete2")
SI resultat ALORS
TANTQUE SQLFetch("requete2") = 0
ArbreAjoute(listebase,"Bases"+TAB+ labase+TAB+SQLLitCol("requete2", 1),aDéfaut,aDéfaut,SQLLitCol("requete2", 1))
FIN
SQLFerme("requete2")
ArbreDéroule(listebase,"Bases"+TAB+labase)
FIN
SQLDeconnecte()

Comme vous pouvez le constater cette procédure récupère comme argument une chaîne de caractère (labase) contenant le nom de la base de donnée a traiter. Il faut ensuite se connecter à cette base de donnée (ligne 3) pour demander l'ensemble des tables la constituant (ligne 4).Une fois la requête exécutée, si elle a fonctionné, tant que des lignes existent dans le contenu du résultat de la requête, on les ajoute au bon endroit dans le tree-view (l'arbre). On ferme la requête avec on déroule l'arbre pour des raisons esthétiques. Pour des raisons de sécurité on utilise SQLDeconnecte() qui ferme la connexion en cours et libère l'espace mémoire utilisé par fonction SQLDéconnecte doit être appelée systématiquement pour fermer la connexion, même si cette connexion a échoué


Arrivé à ce stade, vous pouvez tester le résultat. Entrez les bons paramètres de connexion, appuyez sur le bouton connexion et sous vos yeux ébouriffés de surprise le tree-view est rempli de l'arborescence bases de données + Tables. Si sous vos yeux effrayés vous ne voyez rien paraître, vérifiez les points suivants :

  • Votre serveur est-il actif ?
  • Votre code est-il exempt de Bug ?
  • Les paramètres de connexion sont-ils free of âneries ?
  • Vos lunettes sont-elles propres ?
  • ..

Maintenant, ce serait super de pouvoir lister le contenu d'une table dont on aurait cliqué sur le nom dans l'arbre. Pour cela il faudrait récupérer le nom de la table choisie et remplir la table mémoire. C'est tout simple voici le code que vous allez inscrire dans la zone clic sur listebase de l'objet listebase (l'arbre).
resultat est une chaîne


resultat=ArbreSelect(MoiMême)
mabase=ExtraitChaîne(resultat,2)
matable=ExtraitChaîne(resultat,3)

SI matable<>EOT ET mabase <>EOT ALORS //Nous avons la base et la table
remplirtable(mabase,matable)
FIN

ArbreSelect(MoiMême) renvoie l'élément cliqué sous forme d'une chaine. Le problème c'est qu'il ne renvoi pas la terminaison (la feuille) mais l'arborescence complète. Par exemple si vous cliquez sur la table clients, contenue dans la base gestcom ayant comme racine Bases la chaîne resultat sera comme ceci : « Bases Gestcom clients ». Les éléments susceptibles de nous intéresser sont Gestcom, pour le nom de la base et clients pour fonction ExtraitChaine va nous être d'un grand secourt. On lui donne la chaîne initiale et on lui dit de nous renvoyer le Xieme mot. Dans notre cas nous allons mettre dans la variable globale mabase le deuxième terme de la chaîne et dans la variable matable le troisième terme. Si jamais vous avez cliqué sur le nom de la base au lieu de cliquer sur le nom de la table, resultat sera composé de 2 mots et non de trois, dans ce cas ExtraitChaine (resultat, 3) renverra EOT. Nous allons pouvoir remplir la table si les 2 variables (mabase et matable) sont différentes de EOT. Pour alléger la lecture et faciliter la compréhension, on va donc créer un traitant de remplissage de table, la procédure remplirtable qui va prendre 2 arguments : le nom de la base et le nom de la table.


Voici le code de remplirtable(mabase,matable)
PROCEDURE remplirtable(labase,latable)

resultat est un booléen
numconnexion est un entier
texte est une chaîne

TableSupprimeTout(Table1)

numconnexion=SQLConnecte(Numip,login,mdp,labase,"MySQL")

texte=SQLColonne(numconnexion,latable,Faux)

SQLExec("select * from "+latable,"requete3")
SQLInfoGene()
miseenforme(texte,SQL.NbCol)

SQLTable("requete3", "Table1")
SQLFerme("requete3")
SQLDeconnecte()

Voici l'explication du code. On commence par vider la table mémoire nommé table1. Ensuite on se connecte à la base de donnée dont le nom fut passé en paramètre.

Cette ligne : texte=SQLColonne(numconnexion,latable,Faux) renvoie dans la variable « texte » le nom des colonnes de la table choisie par l'utilisateur dans le tree-view. Le nom des colonnes nous sera utile pour mettre en forme la table mémoire on mettra en entête de table le nom des colonnes, ce sera plus parlant que c1,c2,c3.c15. La ligne suivante fait une requête select classique qui liste le contenu intégral d'un fichier donné (latable).

SQLInfoGene() va renseigner diverses variables sur la dernière requête lancée (requete3).


Nous ce qu'il nous intéresse, c'est de connaître le nombre de colonnes que va générer notre requête. Notre table sélectionnée contient-elle 5 colonnes, 2, 10 ? En fait, a priori nous n'en savons rien, c'est pour cela que je vous ai demandé de créer une table mémoire de 15 colonnes par défaut. C'est qui va nous dire combien la requête a de colonnes. Mais n'oubliez pas que SQL.NbCol ne contient des infos qu'après l'appel de SQLInfoGene()


Nous allons commencer à créer l'entête de la table mémoire(table1) avant d'y transférer les données. C'est le rôle de la procédure « miseenforme(texte,SQL.NbCol) »
PROCEDURE miseenforme(lescolonnes,nbcol)

i est un entier=1
nomcol,exnomcol sont des chaînes

TANTQUE i<>nbcol
nomcol=ExtraitChaîne(lescolonnes,i,RC)
exnomcol="C"+i
{exnomcol}..Titre=nomcol
{exnomcol}..Etat=Visible
i++
FIN

Le but de cette procédure est de remplacer les c1,c2, c3...nbcol par un le nom de la colonne renvoyé par allons donc affecter le nouveau nom à l'ancien. Nous avons passé a la procédure 2 paramètres : Une chaîne contenant les noms des colonnes séparés par un espace et le nombre de colonnes. Nous avons comme impératif de renommer la première colonne (C1) par le 1er terme contenu dans la chaîne ( lescolonnes ), la deuxième colonne (C2) par le 2eme terme de la chaîne et cela jusqu'à nbcol. Comme vous le voyez c'est ce que fait la boucle tantque.

On initialise une variable i à 1, puis tant qu'elle est différente de nbcol on place dans nomcol le terme contenu dans la chaîne lescolonnes à l'indice i. Ensuite on recrée le nom de colonne basé sur l'indice pour être en phase. La partie la plus surprenante est le fait de mettre entre accolades le nom de 'est ce que l'on appelle une indirection, comme le nom du champ est dans une variable on fait ainsi comprendre à WinDev de traiter le contenu de la variable comme étant le nom du champ souhaité. On modifie le nom de la colonne par la constante ..Titre et on la rend visible par ..Etat=Visible. Voilà la procédure a fait la mise en forme. Elle s'arrête et repasse la main a la procédure appelante : la procédure remplirtable. L'exécution continue sur (« requete3 », « Table1 »). Cette commande fait un transfert du contenu de la requête dans la table mémoire.


Voilà le tout est joué, sauvegardez et testez votre travail. C'est super non ?


Mais tout cela serait plus magique si vous aviez une zone de saisie de requête sql pour créer des nouvelles bases de données, créer des nouvelles tables, insérer des enregistrements...


C'est ce que l'on va réaliser maintenant, mais tout d'abord voici le code du bouton requete
SI mabase<>"" ALORS

Ouvre(frequete,Numip,login,mdp,mabase)
SINON
Info("Choisissez une base de donnée")
FIN

Si l'utilisateur a cliqué sur une base du Tree-view alors on peut lancer l'ouverture de la 2eme fenêtre nommée requête. Fenêtre à qui on passe 4 arguments de connexion : le numéro ip, le login, le mot de passe et la base sur laquelle on désire travailler.


Champ de saisie nommé larequeteVoici l'image de la nouvelle fenêtre que vous nommerez « frequete »


Bouton nommé executeChamp liste nommé resultattp6-2


Voyons les éléments :

  • Un champ de saisie nommé larequete dans lequel vous taperez le texte de votre requête
  • Un champ liste nommé résultat dans lequel le résultat de votre requête apparaîtra.
  • Un bouton execute qui contient le code chargé de se connecter à , de faire exécuter la requête et d'afficher le résultat.

Pour que les variables passées par le code du bouton requête de la fenêtre « Départ » soient prisent en compte par la fenêtre « frequete », il faut saisir dans la zone « déclaration globale de frequete » la ligne suivante :

Procedure frequete (Numip,login,mdp,mabase)


Voyons de suite le code du bouton execution
i est un entier

texte est une chaîne

ListeSupprimeTout(resultat) // on vide la liste resultat

SQLConnecte(numip,login,mdp,labase,"MySQL")// on se connecte à la base
SI larequete<>"" ALORS // si du texte a été frappé
SQLExec(larequete,"requete4") // on fait exécuter la requête par Mysql
SQLInfoGene() // on fait générer les variables concernant la requête
SQLPremier("requete4") // on se positionne sur la première ligne des données retournées par la requête

TANTQUE PAS SQL.EnDehors // Tant qu'il reste des lignes à lire
i=1
texte="" // Je vais construire dans texte la ligne en concaténant les colonnes
TANTQUE i<=SQL.NbCol // de i au nombre de colonnes
texte=texte+TAB+SQLCol("requete4", i) // Je concatene
i++
FIN
ListeAjoute(resultat,texte) // J'ajoute dans la liste résultat la ligne que je viens de créer
SQLSuivant("requete4") // je passe à la ligne suivante
FIN
SQLFerme("requete4") // je détruis ma requête
SINON
Info("Veuillez saisir une requête")
FIN
SQLDeconnecte() // Je me déconnecte

Que puis-je dire de plus ? Tout est expliqué ! Ah ! Si ! ! Le cours numéro 6 est fini, je vous remercie d'avoir eu la patience d'arriver jusqu'ici.


Je vous dis à bientôt pour un nouveau "lab".


II. Exercice applicatif

Créer un système d'administration Mysql.


Imaginez une fenêtre ou l'on puisse créer des bases de données, les supprimer, créer des tables, les supprimer, ajouter des utilisateurs, des droits, des permissions..


Vous voyez ? Essayez de faire le plus simple et le plus ergonomique, n'oubliez jamais que l'informaticien n'est jamais l'utilisateur !


Bien sur vous aurez besoin de commandes Sql simples du type


Drop database

Drop table

Create database....


A vous de jouer, bon courage !

Etude des Sockets


0. Présentation

Objectifs : Études des Sockets, communication répartieRappels de notions réseaux, Tcp/ip, Ports.

Grâce à ce nouveau Tp nous allons rentrer dans le monde merveilleux de la communication distante via réseau. Cette jolie introduction pour vous faire comprendre que ce support va vous apprendre à faire discuter 2 ( ou plusieurs ) ordinateurs entre-eux. Nous allons employer les Sockets et les threads.


Commençons par définir ces 2 termes :


Les Sockets : (Tous ceux qui pensent à une chaussette sont virés) Une Socket est définie comme une extrémité d'une communication.

Une paire de processus ( ou de Threads ) communiquant sur un réseau emploie une paire de sockets, une pour chaque processus. Une socket est constituée d'une adresse IP concaténée à un numéro de port. En général les sockets utilisent une architecture Client/Serveur. Le serveur attend des requêtes entrantes du client en écoutant un port spécifique. Dès réception d'une requête, il accepte une connexion de la socket du client. Les serveurs implémentant des services particuliers ( par exemple, telnet, ftp, mail, http ), écoutent des ports bien connus ( telnet écoute le port 23, un serveur ftp le port 21, un serveur web [ Http ] le port 80 ). Les ports inférieurs à 1024 sont considérés comme connus et sont utilisables pour les services standard. Lorsqu'un thread client commence une requête de connexion, il se voit assigner un port par la machine hôte. Ce port est un nombre supérieur à 1024.

Par exemple, lorsqu'un client de l'hôte X d'adresse Ip 192.168.5.20 souhaite établir une connexion avec un serveur Web (qui écoute le port 80) d'adresse 192.168.6.10, l'hôte X peut se voir affecter le port 1625. La connexion est constituée d'une paire de sockets : (192.168.5.20 : 1625) sur l'hôte X et (192.168.6.10 : 80) sur le serveur Web.


Un thread peut être perçu comme un flot de contrôle à l'intérieur d'un processus. Dans notre cas il joue le rôle d'interface entre les 2 ordinateurs, interceptant les données transmises.


Notre exercice consistera à créer 2 exécutables, un client et un serveur. Le serveur sera en attente de réception de message et le client essayera de se connecter au serveur et de lui envoyer des messages. Pour cela il nous faudra 2 projets un pour le client, un pour le serveur


Je vous rappelle que plus on avance au fil des supports moins je détaille les fonctionnalités que je considère comme devant être acquises. Donc si à ce stade vous éprouvez des difficultés reprenez les cours précédents. Nous montons en gamme en terme d'expertise donc il est probable que vous éprouverez certaines difficultés sur ce support. Restez motivé et recommencez le autant de fois que nécessaire. Le but étant de le réaliser sans avoir recours à ce support.


I. Partie Serveur

Vous allez commencer par créer un nouveau projet nommé « Serveur ». Nous ne travaillerons pas sur des fichiers donc faites en sorte de n'utiliser aucune analyse. Nous n'aurons besoin que d'une fenêtre que vous nommerez « Depart » et qui sera la première fenêtre du projet. Faites en sorte quelle ressemble à celle ci :


Cette zone est destinée à recevoir le numéro du port d'écoute. C'est un objet champ de saisie que vous nommerez « portip »

Ces 2 boutons seront nommés « demarre » et « arrete ». A vous de choisir qui est qui.


Ici s'afficheront les phrases envoyées par le client. C'est un objet liste que vous nommerez « liste1 »Cette zone de texte nous indiquera si le serveur est actif ou pas. C'est un objet champ de saisie que vous nommerez « etatserveur »


Analysons le fonctionnement du serveur :

  • Il faut lui donner un port a écouter.
  • Il faut lancer la boucle d'écoute ( boucle infinie ) et faire en sorte que les événements d'entrés ( demande de connexion.) soient traités par des threads.
  • Une fois la connexion acceptée le texte recu sera inscrit dans la liste déroulante.

Retroussons nos manches et en avant pour le codage. Nous allons commencer par créer une procedure d'attente ( la boucle d'écoute ). Pour cela créez une procédure globale nommée "attente"


Voici le code que nous allons analyser.
other
PROCEDURE attente()
BOUCLE // début de la boucle
SI SocketAttendConnexion("serveur") ALORS // si une demande de connexion est en attente
canal est une chaîne
canal=SocketAccepte("serveur") //Cette fonction permet de créer le canal de communication entre la socket serveur et la socket cliente.
ThreadExécute("threadcnx",threadNormal,"affichemes",canal) //Lance l'exécution d'un "thread"
Multitache(-30) //La fonction Multitache avec un negatif suspend l'application
FIN
FIN

Vous pouvez constater que nous nous trouvons devant une boucle sans fin ou en attente dite active. A l'intérieur de cette boucle une fonction WinDev ( SocketAttendConnexion ), est chargée de verifier si des demandes de connexion se produisent. Comme parametre cette fonction prend un argument qui est le nom de la socket ici "serveur". Ne vous inquietez pas pour l'instant, vous allez voir ou nous allons définir la socket nommée "serveur". Pour l'instant l'essentiel est de comprendre le principe de la boucle d'attente active. Donc si une demande de connexion se produit pour la socket "serveur" on l'accepte en créant un canal de communication. Vous pouvez considérer ce canal comme un tunel ou les données vont transiter.

La ligne : ThreadExécute("threadcnx",threadNormal,"affichemes",canal), est chargée de faire en sorte que le code de la fonction affichemes soit executée comme un Thread normal appelé "threadcnx" utilisant le parametre "canal")

Multitache(-30) : L'exécution de l'application est suspendue durant 100ème de seconde. D'autres traitements peuvent être exécutés durant cette période de temps (ré-affichage ou exécution d'un code de clic par exemple). Dans notre cas la boucle est gelée pour permettre aux threads de s'exécuter durant leurs quantums.

Donc : la socket s'appelle « serveur », le canal crée s'appelle canal, le thread gestionnaire se nomme « threadcnx ».

J'admets qu'au premier abord cela puisse vous sembler complexe, alors que c'est extrêmement logique. Relisez le paragraphe précédent et imaginez le fonctionnement : la boucle sans fin, l'attente de connexion, le traitement de la connexion..


Maintenant nous allons nous intéresser à la procédure « affichemes ».C'est elle le cour de notre serveur puisque c'est la gestionnaire d'événement. Pour ce faire créez une procédure globale « affichemes » (affichemes pour affiche messages).
PROCEDURE affichemes(canal)
texte est une chaîne
BOUCLE
texte=SocketLit(canal,Vrai)
ListeAjoute("liste1",texte)
FIN
ThreadArrête("",500)
Multitache(-30)

Comme vous pouvez le constater la procédure prend comme paramètre le nom du canal reliant les 2 sockets. Je vous rappelle que cette procédure est lancée en temps que thread. lit le contenu du canal et le mets dans la variable texte. Le paramètre vrai signifie à SocketLit que la durée d'attente sur canal est indéfinie. Une fois le message récupéré il doit être mis dans notre liste déroulante « liste1 ». Le reste de la procédure n'appelle pas de commentaires particuliers.


Continuons par le code du bouton "demarre".
SI PAS SocketCrée("serveur",PORTIP,NetAdresseIp()) ALORS
Erreur("Erreur de création " + ErreurInfo(errMessage))
ETATSERVEUR="Problème lors du démarrage du serveur"
SINON
ETATSERVEUR="Serveur démarré"
ThreadExécute("thread1",threadNormal,attente)
FIN

Ce code lance l'exécution du serveur. utilise plusieurs parametres :

Le nom de la socket que l'on va créer.

Le port sur lequel on écoute.

L'adresse ip du poste serveur.


Comme vous le voyez à la lecture de ce code si la socket "serveur" est créée on fait de notre procédure globale "attente" un thread qui appellera lui meme le thread "affichemes". C'est pas super l'informatique ?


Voici le code du bouton arreter
SocketFerme("serveur")
ETATSERVEUR="Arret du serveur"

Voilà la partie serveur est maintenant terminée. Il ne vous reste plus qu'a créer l'exécutable (Menu Projet/Créer l'executable).


NB: Si vous avez des Warnings concernant une boucle sans condition de sortie ne vous en souciez pas !


Dés a présent vous avez concu un serveur, il ne nous reste plus qu'a créer le client. Son rôle est d'essayer de se connecter au serveur et de lui envoyer des message. C'est clair ? Alors Avanti !


II. Partie Cliente

Pour le client, comme ce doit etre une application autonome, il nous faut donc créer un projet, sans analyse ayant pour nom "Client". Ce projet ne contiendra qu'une fenetre nommée "Depart". Elle ressemblera a ceci :


Objet champ de saisieNom : ADRIP

tp5-2

Objet : Champ de saisie.Nom : ETATCNXObjet : Champ de saisie.Nom : messa


Objet : champ de saisie.Nom : Portip


Comme vous pouvez le constater le client est vraiment minimaliste. On saisit l'adresse ip du serveur, le port d'écoute, le message à transmettre. Le bonton Connexion nous servira pour établir la liaison, le bouton envoyer transmettra le message.


Interressons nous au bouton connexion. Son rôle est de nous mettre en relation avec la socket du serveur. Donc il est impératif que le serveur soit actif avant le client, mais ca les plus rusés l'avaient remarqué.
SI PAS SocketConnecte("serveur", PORTIP,ADRIP) ALORS
Erreur("erreur de connexion " + ErreurInfo(errMessage))
SINON
ETATCNX="Vous etes en ligne"
FIN

Voici le code du bouton.


On essaye de ce connecter à la socket nommée "Serveur" ( vous savez c'est celle que l'on vient de créer coté serveur ?!), écoutant le port défini ( PORTIP), à telle adresse IP (ADRIP). Si tout ce passe bien on ecrit "Vous etes en ligne dans le champ de saisie Etatcnx. Sinon on envoie le message d'erreur.


Ben oui, c'est tout. Simple, non ?


Maintenant voyons le code du bouton envoyer.
SI SocketEcrit("serveur", messa) = Faux ALORS
Info("Un problème est survenu")
FIN

On envoit un message ( le texte contenu dans le champ de saisie "messa" ) à la socket "serveur". Si ca ne fonctionne pas on affiche une boite de dialogue d'avertissement.


Voilà tout est fini, vous pouvez compiler, créer l'executable et tester votre client/serveur. Pour la mise en ouvre vous avez besoin de lancer le serveur, le mettre en écoute d'un port. Ensuite vous lancez le client que vous branchez sur le port d'écoute et envoyez le message. Si vous etes en réseau utilisez la bonne adresse ip du serveur et have fun !!


III. Exercices applicatifs

Maintenant que vous etes un pro de la socket essayez de réaliser l'exercice suivant.


A partir de l'executable faites en sorte que le client oblige le serveur a executer une action ( par exemple activer la calculatrice )


Pistes de réalisation :


Coté client creez un bouton qui envoie le chiffre 1 par exemple à la socket serveur.


Coté serveur analysez les mesages recu et si message =1 alors ...


Avec cet exercice vous etes en train d'apprendre les base d'un outils de maintenance à distance. A vous de le perfectionner


Pour faire lancer une application windows par Windev voici la syntaxe : LanceAppli("CALC.EXE »). Calc.exe étant la calculatrice

Création d'un projet avec plusieurs fichiers liés

0. Présentation

L'objectif de ce cours est de vous familiariser avec l'utilisation des fichiers liés.


Nous allons créer une mini GPAO (Gestion de Production Assistée par Ordinateur). Vous travaillez pour un assembleur informatique, son processus de production est le suivant : Il reçoit les différentes pièces détachées (disque dur, mémoires, cartes mères.) et assemble ces différentes pièces pour en faire un modèle fini. Comme vous pouvez le percevoir, le modèle conceptuel travaillera avec 2 entités (Ensemble fini et composants). Voici une représentation du MCD :

image
WB01071_ Remarque : Je n'ai pas besoin de vous rappeler que les propriétés soulignées et en gras désignent les identifiants de l'entité, donc je ne le fais pas.

Vous allez commencer par créer un nouveau projet nommé TP4. Dans l'Assistant, vous sélectionnerez votre thème préféré, confirmerez le choix de création d'analyse et finirez le processus de l'assistant. Un nouvel assistant de Création d'analyse va apparaître. Vous pouvez ensuite valider les différentes fenêtres de l'Assistant jusqu'à arriver à l'Assistant de Création de fichier.


Restez sur Créer une nouvelle description de fichier, donnez Ensemble comme Nom et Ordinateurs assemblés comme Libellé. Pensez à « décocher » Le fichier possède un identifiant automatique puisque nous avons Refens qui sera notre identifiant. Gardez Hyper File Réseau ou Monoposte comme Type de base de données et ne laissez coché que l'option une fenêtre en mode table dans la dernière partie de l'assistant.


Maintenant la fenêtre suivante apparaît

image

C'est elle qui va vous permettre de saisir toutes les propriétés de votre entité. Remplissez-la pour qu'elle ressemble à ceci :


image


Une fois remplies, cliquez sur Ok et retournez sous l'éditeur de Windev. Enregistrez.

Attention un écran va vous demander si vous voulez créer une fenêtre ou allez sous l'éditeur, cliquez sur Allez directement dans l'éditeur de Windev pour rester dans l'analyse.


Si vous regardez bien votre éditeur d'analyse en haut vous devez découvrir une barre d'outils comme celle-ci :

image

Voyez les utilisations des différentes icônes en passant le curseur de la souris dessus.


Comme vous êtes dégourdis et intelligents je vous laisse finir de concevoir le modèle !


Voilà à quoi vous devez arriver :

image

Vous venez donc de créer le Modèle Logique de Données (MLD). Enregistrez.


Attention : N'oubliez pas que ce n'est pas parce que l'analyse est créée que les fichiers sont physiquement créés sur votre disque dur. Allez dans le code du projet « Projet / Code du projet » et inscrivez la séquence suivante dans la zone « Initialisation de TP4 » :

HCréationSiInexistant(Composant)
HCréationSiInexistant(Ensemble)
HCréationSiInexistant(Intègre)
HGèreIntégrité("*", "*", hCardinalité+hEnModification+hEnSuppression, Faux)
La dernière ligne indique à WinDev de ne pas se soucier de gérer l'intégrité référentielle, nous le ferons nous-même.


Maintenant que l'analyse est créée, nous allons commencer à construire notre application. Choisissez Fichier / Nouveau / Fenêtre.


Créez une fenêtre nommée départ qui sera la première fenêtre du projet. Faites en sorte qu'elle ait les caractéristiques suivantes :


I. Onglet Général :

Nom logique : départ

Description : Première fenêtre du projet

Titre : Bienvenue dans la Mini Gp


Onglet IHM :

Taille : Taille 640*480


II. Onglet Détail :

Type de fenêtre : Mère Mdi

WB01071_ Remarque : Une fenêtre mère MDI est obligatoirement la première fenêtre d'un projet WinDev. Cette fenêtre permet d'afficher toutes les fenêtres de l'application.Une fenêtre mère MDI a les caractéristiques suivantes (non modifiables) : bords modifiables, bouton d'iconisation, d'agrandissement, menu système,barre de titre, barre d'icônes, possibilité d'avoir un menu déroulant et des barres outils.Dans une fenêtre mère MDI, seuls les champs situés dans la zone "barre d'icônes" peuvent être en saisie. En dehors de cette zone, aucun champ ne peut être en saisie, aucun clic souris n'est actif.

Nous allons placer une barre d'outils dans cette fenêtre départ, pour cela cliquez sur l'objet Créer une barre d'outils image et placez le dans le bord supérieur gauche de la fenêtre. Faites un clic droit sur ce nouvel objet et allez dans Description. Nous allons placer 5 boutons dans cette barre d'outils (un pour saisir les nouveaux composants, un pour créer des ensembles, un pour imprimer la liste des composants, un pour imprimer l'ensemble et ses composants, le dernier pour quitter l'application).


Vous allez donc cliquer sur le bouton Créer puis choisissez Bouton. Le résultat obtenu devrait ressembler à ceci :

image

Cliquez sur Bouton1, puis sur le bouton Editer, dans cette fenêtre Description d'un bouton saisissez nc dans la zone Nom, supprimez le libellé par défaut, cliquez sur Catalogue. pour la zone Image.


Le catalogue apparaît, sur la gauche choisissez seulement 16*16 (nous n'aurons que des petites images), dans la zone Recherche frappez « dossier » puis appuyez sur le bouton Rechercher.


Prenez le 4ème bouton de la 4ème ligne puis validez. Dans l'onglet Aide de la fenêtre description, dans la zone Bulle d'aide inscrivez Saisie d'un nouveau composant.

Pour les boutons suivants, faites de même en suivant les instructions suivantes :

Boutons Nom Bulle d'aide Mot clé (recherche icône)
Bouton2 Créer Créer des ensembles « Créer »
Bouton3 ImprimeC Imprimer les composants « Imprimer »
Bouton4 ImprimeE Imprimer les ensembles « Imprimer »
Bouton5 Fermer Quitter l'application « Annuler »

Vous choisirez les icônes les plus en phase avec le but du bouton.

WB01071_ Remarque : Si vous avez des difficultés pour placer les boutons (chevauchement par exemple), allez dans leur onglet Détail et affectez leurs les dimensions suivantes : largeur 32, hauteur 24.

Voici un exemple de ce que devrait être la barre d'outils :

image

Nous allons créer les 4 fenêtres filles nécessaires pour faire fonctionner notre application (fenêtre filles car elles doivent s'exécuter à l'intérieur de la fenêtre mère, logique ! Non ? ). Pour le bouton Quitter, je vous laisse mettre le code correspondant, vous n'avez plus besoin de mon aide, sinon retour aux TP précédents.


Pour la première fenêtre associée au boutonimage, respectez les consignes ci dessous :


Le code d'ouverture de la fenêtre fille sera : OuvreFille(Gcompo)


III. Onglet Général de la description

Nom logique : Gcompo

Description : Gestion des composants

Titre : Gestion des composants


Onglet IHM

Largeur : 500

Hauteur : 350

Position : Relatif à la mère


IV. Onglet Détail

Type de fenêtre : Fille Mdi


Voici à quoi elle devrait ressembler :

image

Infos : Ces 3 champs sont liés avec les champs du fichier Composant (faites-les glisser depuis l'onglet à droite de l'écran). Si certains ont des difficultés : retour au TP3. Faites en sorte que RefComp ait un masque de saisie TOUT EN MAJUSCULES.


Créez 2 boutons : BAjout et BSup


Nom de la table mémoire : Table1

RefComp : type texte longueur 15

Désignation : type texte longueur 80

PrixAchat : type numérique (Titre : Prix d'Achat)

Choisir dans l'assistant Je veux remplir moi-même la table

Mettez la table en Affichage seulement (sinon la saisie ne serait pas validée)

Pour ceux qui auraient encore des difficultés : voir TP3


Maintenant analysons le comportement que devra avoir cette fenêtre à l'ouverture : Elle devra parcourir le fichier Composant et afficher les tuples dans la table mémoire s'il y en a.


Le bouton Ajouter devra :


.Inscrire la valeur des 3 champs dans le fichier correspondant en interceptant une erreur de doublonnage si l'utilisateur saisi 2 fois une référence existante,

.Il devra aussi mettre à blanc les 3 champs pour préparer une nouvelle saisie,

.Réafficher la table mémoire (supprimer le contenu existant et re-écrire avec le contenu du fichier),

.Et enfin avertir l'utilisateur que tout s'est bien passé.


Le bouton Supprimer devra :


.Supprimer physiquement la ligne pointée dans la table mémoire

.Réafficher la table mémoire

Voici les différents codes (je vous rappelle qu'ils ont déjà été étudiés à la leçon 3) :


Nous allons créer une procédure locale affichetable qui aura pour rôle de réafficher la table mémoire (supprimer le contenu existant et re-écrire avec le contenu du fichier) :
other
TableSupprimeTout(Table1)
HLitPremier(Composant,RefComp)
TANTQUE H.trouve ET PAS H.EnDehors
TableAjoute(Table1,Composant.RefComp+TAB+Composant.DésignationC+TAB+Composant.PrixAHT)
HLitSuivant(Composant,RefComp)
FIN
Cette séquence devrait vous être familière donc je ne vous la commente pas, sinon retour au cours 3.


Rappel pour faire une procédure locale cliquez sur « Procédures locales » en bas de l'écran :


image


Initialisation de Gcompo (la fenêtre) :
other
// Appel de la procédure affichetable
affichetable()

Maintenant on est sûr que dès que cette fenêtre s'ouvrira, la table mémoire sera en phase avec le contenu du fichier.

WB01071_ Remarque : Certains d'entre vous se demandent peut être pourquoi utiliser des tables mémoire pour afficher le contenu des fichiers plutôt que des tables fichiers qui semblent toutes destinées à cette utilisation. Ma réponse est simple : la table mémoire vous apportera plus de souplesse et une gestion plus fine des enregistrements et des lignes dans certains cas. Et puis qui peut le plus peut le moins ;-)))

Voici le code du bouton BAjout :
other
EcranVersFichier() // Transfère les valeurs contenues dans les champs de la fenêtre
// dans la zone de structure du fichier
HAjoute(Composant) // Passe la structure physiquement dans le fichier
RAZ(Vrai)// Efface les valeurs contenues dans les champs liés pour saisir un nouveau composant
affichetable()

Voici le code du bouton BSup :
other
HLitRecherche(Composant,RefComp,RefComp) // Recherche dans la table Composant,
// sur la rubrique RefComp, la valeur contenue dans RefComp de Table1
SI HTrouve ALORS// Si la valeur est trouvée alors
HSupprime(Composant)// On supprime physiquement la ligne dans le fichier Composant
Info("Suppression réussie")// On informe l'utilisateur de la réussite de la suppression
affichetable()
SINON// Sinon, on n'a pas trouvé ?!!
Info("Il y a un boulon dans le potage")// Bizarre, bizarre !
FIN

Enregistrez la fenêtre et testez votre projet sans oublier de définir qu'elle est la 1ère fenêtre du projet (départ), normalement les comportements doivent être cohérents. Dans le cas contraire, reprenez le support et vérifiez que vous n'avez pas oublié quelques instructions.


Nous allons maintenant faire la fenêtre la plus intéressante au niveau intellectuel, celle qui gère l'assemblage d'un ordinateur.


Créez une nouvelle fenêtre (Fichier / Nouveau / Fenêtre). Prenez une fenêtre vierge et enregistrez la sous Gensemble.


N'oubliez pas d'en faire une fenêtre fille (cf. : étapes identiques à la fenêtre Gcompo) et de mettre le code d'ouverture de cette fenêtre dans le deuxième bouton de la barre d'outils de la fenêtre départ.


Voici la fenêtre telle quelle devrait être :


23131211109876541 image

Objet N° Nom Rôle
1 RefEns Lié à Ensemble.RefEns (TOUT EN MAJUSCULES)
2 PrixVHT Lié à Ensemble.PrixVHT
3 Désignation Lié à Ensemble.Désignation
4 BPremier Bouton nous positionnant sur le premier enregistrement du fichier Ensemble
5 BPrecedent Bouton nous positionnant sur l'enregistrement précédent
6 BSuivant Bouton nous positionnant sur l'enregistrement suivant
7 BDernier Bouton nous positionnant sur le dernier enregistrement du fichier Ensemble
8 Table1 Table mémoire contenant des rubriques similaires à la structure de Composant
9 BAjout Bouton nous permettant d'ajouter un composant à l'ensemble
10 BSupprime Bouton nous permettant de supprimer un composant à l'ensemble
11 BNouveau Bouton nous permettant de créer un nouvel ensemble
12 BValide Bouton nous permettant de valider un ensemble
13 BSupp Bouton nous permettant de supprimer un ensemble

Analyse des éléments de la fenêtre Gensemble.


A l'ouverture de la fenêtre dans le code d'initialisation, nous devrons nous positionner sur le premier ensemble (s'il existe) et rechercher tous ces composants constitutifs.


Voici comment faire :


Il nous faut lire la référence de l'ensemble (RefEns), parcourir le fichier de liaison (Intègre) sur la clé RefEns, tant que l'on trouve Ensemble.RefEns=Intègre.RefEns il nous faut chercher dans la table composant la clé de liaison (Intègre.RefComp = Composant.RefComp) et récupérer Intègre.Qté. C'est clair ? Non !! Alors relisez en ayant l'analyse en tête.


Nous allons créer une fonction (en fait une procédure, WinDev ne faisant pas de différence entre fonction et procédure) qui, prenant en paramètre le code RefEns, nous rempli la table. Facile et efficace. Notez qu'une requête SQL nous aurait évité pas mal de lignes, à vous de juger.
other
PROCEDURE remplir(code)
TableSupprimeTout(Table1)
FichierVersEcran(Gensemble) // on affecte aux champs de la fenêtre les données liées
HLitRecherche(Intègre,RefEns,code) // On fait rechercher dans le fichier Intègre une valeur de RefEns égale à code
TANTQUE HTrouve ET PAS HEnDehors
HLitRecherche(Composant,RefComp,Intègre.RefComp) // A vous de trouver
TableAjoute(Table1,Composant.RefComp+TAB+Composant.DésignationC+TAB+Composant.PrixAHT+TAB+Intègre.Qté)
HLitSuivant(Intègre,RefEns)
FIN

En fait, comme vous pouvez le voir, nous venons de « jongler » avec 3 fichiers sans trop de difficultés. Regardons le code d'initialisation de la fenêtre :
other
HLitPremier(Ensemble,RefEns)
SI HNbEnr(Ensemble)>0 ALORS // si le fichier Ensemble contient au moins un enregistrement
remplir(Ensemble.RefEns)
SINON
Info("Le fichier est vide")
FIN

Voyons le code nécessaire aux boutons 4, 5, 6 et 7 (pour les icônes, faites un tour du côté des flèches en 16x16). Je vous donne le code du bouton 4, vous êtes suffisamment aguerri pour pouvoir créer le code des boutons 5, 6 et 7. Comment ça ? Réfléchissez un peu et vous y arriverez ! Non mais, il faudrait tout leur faire.


(Si vous ne savez pas. hop, au TP 3). D'ailleurs, je ne vous le donne pas, non je suis pas méchant, j'ai seulement la flemme de vous réécrire la séquence de code du dessus. Eh oui, c'est ce bout de code, analysez le et regardez à quel point il est idéal pour notre bouton 4.


Nous allons traiter le bouton 9. Ce bouton doit nous permettre de sélectionner dans le fichier Composant le composant que nous voulons insérer dans la table mémoire. Nous allons voir si WinDev peut nous générer la fenêtre qui va bien. Allez sur Fichier / Nouveau / Fenêtre. Cherchez une fenêtre nommée Vision plus et validez.


Dans l'Assistant qui arrive, décochez Avec code de rafraîchissement automatique, puis cliquez sur Suivant 2 fois, ensuite cliquez sur Composant, cliquez encore sur Suivant, vérifiez que tous les champs soient cochés, cliquez encore sur Suivant 2 fois. Prenez une taille 640*480. Puis saisissez selection comme Nom. Enfin cliquez sur Terminer.


Vous avez maintenant une fenêtre fonctionnelle dans laquelle on peut supprimer le bouton Nouveau (on peut utiliser la création de composants avec notre 1er bouton du menu). Réduisez-la pour quelle soit plus petite. Voici à quoi elle pourrait ressembler :

image

Regardons comment nous pourrions modifier le code pour qu'il nous soit utile. Allez dans le code du bouton OK. Vous pouvez voir que la fenêtre retourne vrai si on clique sur OK, faux sur Annuler. Qui dit bandeau déplacé dit structure mémoire du fichier contenant les valeurs recherchées ! Donc, nous n'aurons qu'à lancer la fenêtre et tester si elle retourne vrai.


Fermez-la et retournez sur Gensemble dans le code du bouton BAjout. Saisissez la séquence de code suivante :
other
resultat est un booléen=Faux
resultat=Ouvre(selection)
SI resultat=Vrai ALORS // la fenêtre nous a renvoyé vrai, donc le bandeau a été déplacé
TableAjouteLigne(Table1,Composant.RefComp,Composant.DésignationC,Composant.PrixAHT)
FIN

Je vous laisse méditer sur la différence entre tableajoute et tableajouteligne (n'oubliez pas d'utiliser l'aide, touche F1).


Sauvegardez et exécutez la fenêtre, normalement le clic sur le bouton BAjout lance bien la fenêtre selection qui nous retourne vrai ou faux selon le bouton cliqué.


Pour saisir la Qté, cliquez dans la colonne pour saisir le nombre souhaité. N'oubliez pas de vérifier que la Table est En saisie et d'avoir seule la rubrique Qté En saisie / actif, les autres restant en mode Affichage seulement.


Pour le bouton 10 (BSupprime), la séquence de code a déjà été étudiée lors du TP3. Pour le bouton Nouveau (BNouveau), il vous faut programmer un traitement qui efface les champs 1, 2, 3 et la table mémoire, vous savez faire, donc je passe.


Le bouton Supprime (BSupp) va nous obliger à scanner 2 tables et supprimer les références « Ensemble » recherchées. Il faut donc prendre la valeur du champ 1 (RefEns), parcourir la table Intègre sur le champ RefEns et supprimer tous ceux égaux au champ RefEns. Et ensuite le supprimer dans la table Ensemble.


Voici une séquence de code qui devrait être efficace :
other
SI OuiNon("Voulez vous vraiment supprimer cet enregistrement ?")=Oui ALORS
HLitRecherche(Intègre,RefEns,RefEns)
TANTQUE HTrouve ET PAS HEnDehors
HSupprime(Intègre)
HLitSuivant(Intègre)
FIN
HLitRecherche(Ensemble,RefEns,RefEns)
SI HTrouve ALORS
HSupprime(Ensemble)
Info("La suppression est réussie")
HLitPremier(Ensemble,RefEns)
FIN
FIN

Vous n'avez pas besoin que je vous commente ce code, il est limpide.

La séquence du bouton Valider va être un peu plus rude, mais guère plus. En fait, lorsqu'un nouvel ensemble est créé ou modifié par l'adjonction d'un nouveau composant, il va nous falloir faire plusieurs actions différentes.


Il faut supprimer l'ensemble existant dans le fichier pour le recréer avec ses nouveaux composants, donc on commence par une phase de suppression (voir code ci-dessus) et on achève par une phase d'insertion dans le fichier.


Le code est long mais n'est pas complexe, prenez le temps de l'étudier.
other
i est un entier

HLitRecherche(Ensemble,RefEns,RefEns)
SI HTrouve ALORS // l'enregistrement existait dans le fichier, on va le supprimer
HLitRecherche(Intègre, RefEns, RefEns)
TANTQUE HTrouve ET PAS HEnDehors
HSupprime(Intègre)
HLitSuivant(Intègre)
FIN
HSupprime(Ensemble)
FIN
// Maintenant inscrivons l'ensemble dans le fichier
Ensemble.RefEns=RefEns
Ensemble.DésignationE=DésignationE
Ensemble.PrixVHT=PrixVHT
HAjoute(Ensemble)

// Remplissons le fichier Intègre
POUR i=1 A TableOccurrence(Table1) // Pour i=1 aux max de la table mémoire
Intègre.RefComp=Table1.RefComp[i] // [i] indique l'indice de la ligne
Intègre.RefEns=RefEns
Intègre.Qté=Table1.Qté[i]
HAjoute(Intègre)
FIN
Info("L'ajout c'est bien passé")

Voilà, la super fenêtre de gestion des ensembles est finie. N'oubliez pas de sauvegarder votre ouvre.

image

Le troisième bouton de la barre d'outil sert à imprimer la liste des composants, vous avez déjà étudié la procédure dans le TP 3 donc je n'y reviens pas.


Nous allons nous laisser guider par l'Assistant pour faire un état avec des données provenant des 3 tables. Allez dans Fichier / Nouveau / Etat.


Choisissez ensuite un type d'état tableau, cliquez sur Suivant.


Vérifiez que les données proviennent bien d'une requête et cliquez sur Suivant.


Faites lui comprendre que les données proviennent d'une requête que vous voulez définir maintenant, cliquez sur Suivant.


Maintenant vous devez avoir cette fenêtre à l'écran :

image

Cliquez ensuite sur Suivant. A la question « Voulez-vous des ruptures dans l'état » répondez Oui et cliquez sur Suivant.


La fenêtre suivante vous demande de sélectionner la rupture, vérifiez que Référence de l'ensemble soit bien coché, cliquez sur Suivant.


Vous devriez avoir maintenant une fenêtre qui ressemble à ceci :

image

Cliquez ensuite sur Suivant. La fenêtre suivante vous demande quels sont les champs numériques sur lesquels vous voulez effectuer un calcul, essayer de faire en sorte quelle ressemble à ceci :

image

Cliquez sur Suivant. Les choix suivants concernent les formats du papier, laissez les par défaut et cliquez sur Suivant.


Choisissez un gabarit et Suivant. Nommez cet état Iensemble (attention, le i est en minuscule), comme titre « Etat des ensembles ». Ouf ! Vous pouvez enfin cliquer sur Terminer.


Comme votre état est plus large que la feuille, passez en mode paysage. Votre état apparaît maintenant dans l'éditeur d'état. Si vous voulez faire quelques retouches, c'est maintenant.


Il ne vous reste plus qu'à activer l'état dans le bouton de la barre d'outils avec la séquence de code suivante :
other
iAperçu(i100)
iImprimeEtat(Iensemble)

Voilà, ce modeste "mini-lab" est fini. Vérifiez que tous les boutons fonctionnent, que les traitements sont cohérents. Il y aurait des améliorations possibles (concevoir un compteur qui totalise les prix HT des composants entrant dans la composition d'un ensemble.). Mais je considère que vous êtes apte à le faire vous-même.


Vous avez mérité la pause syndicale.


V. Exercice Pratique

Enoncé :


La municipalité de votre lieu de résidence désire ouvrir une bibliothèque municipale. Connaissant vos nombreuses compétences informatiques, l'adjoint au maire chargé de la culture vous demande de concevoir le logiciel chargé de la gestion des prêts de livre et des achats.


Voici quelques éléments de réflexion :


Chaque livre est référencé par un numéro ISBN qui sert d'identifiant et qui est défini de façon internationale, par exemple le livre « Redhat 8 administration et maintenance » écrit par Arnaud Brugnon aux éditions Campus Press a le numéro 2744014516.


Le directeur de la bibliothèque voudrait pouvoir saisir les références complètes du livre (la date d'achat, son prix d'achat, la quantité achetée, le fournisseur, sa classification : policier, aventure.).


Cette saisie doit lui permettre de connaître le nombre total de livre en stock, le coût du stock, le nombre et le coût par type de livre, le nombre de livre par auteur avec le titre et la quantité en stock. Si vous avez d'autres statistiques à lui proposer, elles seront les bienvenues.


Un client est référencé par un numéro aléatoire généré par la base de données. Les emprunteurs doivent pouvoir être ajoutés, modifiés, supprimés.


Les règles de gestion sont les suivantes :

  • Une personne ne peut pas emprunter plus de 3 livres de la bibliothèque. Elle peut en emprunter 3 d'un coup ou les uns après les autres ;
  • Elle doit ramener un livre 15 jours après son emprunt. Le système doit pouvoir faire un état des livres empruntés non rentrés et éditer un listing avec les noms, prénoms, adresses et N° de téléphone des emprunteurs et la liste des livres qu'ils auraient du rendre. Ainsi, le bibliothécaire pourra téléphoner à la personne pour lui demander de rendre les ouvrages.

On doit pouvoir imprimer ou visualiser :

  • La liste complète des livres ainsi que leur quantité en stock ;
  • La liste des livres en prêt ;
  • La liste des inscrits n'ayant jamais emprunté un livre.