Tutoriel wxWindows
de David BEECH traduction française par Patrick VALERI
Section C++ du site http://phenix.developez.com créé le 20 Novembre 2003
Remis à jour 17 Septembre 2004
Programmez avec wxWindows
Huitième Étape
Utilisation d'une base de données
Introduction
Maintenant, il est temps de rassembler ce que nous avons et d'essayer de construire quelque chose qui s'apparente à une « vraie » application. Ce travail pratique est nettement plus complexe que les travaux pratiques antérieurs et que les choses apprises précédemment, nous:
- apprendrons quelques bases sur ODBC,
- construirons une classe odbc simple,
- construirons une application rudimentaire qui combine notre travail antérieur, les classes odbc et la classe wxGrid.
Notre application ne sera pas particulièrement complexe mais ce sera une base utile pour plusieurs travaux concrets.
ODBC (Open Database Connectivity)
La société Microsoft est souvent accusée de toutes sortes d'horribles choses, quelquefois vraiment justifiés, cependant il y des cas où Microsoft contribut à une oeuvre d'une immense valeur pour le monde informatique, l'interface ODBC est un de ces cas.
ODBC définit une interface d'accès aux bases de données à la fois locale et distante. L'objectif est de finaliser une haute interopérabilité (je sais qu'il n'y devrait pas y avoir de tel mot, comme interopérabilité, mais c'est l'habitude majeure de Microsoft).En principe cela signifie qu'une application peut accéder à une DBMS (DataBase Management System ou SGBD en français) en changeant un composant qui est externe à l'application. Nous pouvons écrire un système compatible qui stockent ses données dans une DBMS et laisse l'utilisateur décider de quelle DBMS ils vont se servir. Nous pourrions développer et tester l'application en utilisant Postgresql mais ça sera déployé avec une base de données Oracle. Dommage, ce n'est pas aussi simple dans la réalité mais on en trouve la fin, spécialement si le développeur de l'application se fixe sur le langage standard de requête SQL ( Structured Query Language ).
ODBC fournit actuellement deux interfaces, indiquées ici en figure 2. La première est entre l'application que vous écrivez et le gestionnaire de pilote ODBC. Le gestionnaire de pilote est une partie logicielle qui lie l'application, via un second interface, à un pilote. Le pilote est une autre partie logicielle qui actuellement communique avec la DBMS et il y a précisément autant de pilote ODBC pour chaque base de données connues depuis les vieux formats comme Dbase jusqu'aux géants comme Oracle, Sybase puis, heureusement pour nous, PostgreSQL et MySQL.
Tout d'abord, nous allons nous intéresser à quelques questions:
Obtenir le(s) pilote(s) approprié(s).
Nous assurer que nous avons un gestionnaire de pilote. Ce n'est pas un problème sur plate-forme Windows puisqu'un gestionnaire de pilote ODBC fait partie du kit. Cela peut être une contrainte mineure sur plate-forme Linux puisque vous pouvez avoir à installer un gestionnaire de pilote. J'utilise UnixODBC.
Configurer le pilote pour pointer vers une DBMS ce qui signifie que vous devez avoir une DBMS quelque part. J'utilise à la fois PostgreSQL et MySQL sur Win2K et Linux.
J'utilise MyODBC pour MySQL sur Linux et Win2k à la fois et psqlODBC pour PostgreSQL sur Linux et Win2k à la fois. Et bien, pensais-je ainsi, cela fait bien longtemps que j'ai regardé cela pour la dernière fois. Après tout est-ce que vous vous préoccupez souvent de surveiller quand un logiciel fonctionne bien.
Voici une brève approche de la façon de configurer une connexion ODBC dans Windows. Se procurer et installer les pilotes ODBC dont vous avez besoin puis, en supposant que vous connaissez le chemin du DBMS, lancer la configuration des sources ODBC. Vous pouvez le lancer par Paramètres->Panneau de configuration->Outils d'administration. Vous pouvez voir cela Figure 3.
Les onglets concernés sont DSN(Data Source Name) - User or System. (Source de données Utilisateur ou Système). Un DSN est une référence à un lien de connexion DBMS. Un DSN utilisateur est valable pour un utilisateur donné, un DSN System est valable pour un ordinateur donné.
La figure 4 montre le DSN pour une base de données PostgreSQL. La source de donnée PG_DB_PM (C'est vous qui lui donné un nom), la base de données est tgproject, le serveur est 192.168.1.190 et le port de la dbms est 5432, c.a.d le port standard PostgreSQL.
La figure 5 montre le DSN pour une base de données MySQL. La source de données est SES Workshop, la base de données est gcdb, le serveur est localhost et le port est 3306, le port standard MySQL.
Vous pouvez voir que les dialogues sont également différents visuellement mais comportent essentiellement la même information, conséquence des différents auteurs, ce qui est ce à quoi vous devez vous attendre puisque cette interface de configuration est fournie par le pilote.
L'information dsn est enregistrée dans des fichiers *.ini et en plus, dans le cas de Windows, probablement dans le registre( Commentaire_6 ).
Linux a la même approche de configuration en particulier si vous utiliser le gestionnaire de pilote UnixODBC. J'ai trouvé cela plus facile bien que je puisse juste éditer le fichier odbc.ini, le problème majeur est d'être sûr que vous utilisez le bon puisque comme toute chose dans Linux, il y a un grand nombre d'options. Le fichier odbc.ini peut être dans le répertoire d'accueil de l'utilisateur (home directory) ou dans le répertoire d'installation de UnixODBC ou dans /etc.
Je vais vous laisser, livré à vous-même, puisqu'il est temps d'installer et de configurer les composants ODBC mais il existe une multitude d'aide par ailleurs.
Les classes ODBC
A propos des API
Je devais revoir les API brièvement, suffisamment pour nous permettre de démarrer l'écriture de notre application mais si vous voulez lire à propos d'ODBC dans tous les détails vous pouvez vous procurer les spécifications actuelles de Microsoft ou mieux aller sur le site SolidTech et télécharger les spécifications comme la collection des PDF's. Parcourez les pages des spécifications ODBC Microsoft.
Les API ODBC fournissent trois éléments clé que nous allons utiliser:
Un environnement de saisie: c'est une structure globale utilisée pour gérer les caractéristiques globales ODBC. C'est nécessaire mais n'est pas beaucoup utilisé sinon que nous allons travailler avec les pilotes et les gestionnaires de pilotes.
Une saisie de base de données ou de connexion: l'essentiel du travail est effectué avec cet élément, c'est le lien avec une base de données spécifique.
Une saisie de requêtes: cela caractérise les requêtes SQL qui sont exécutées à l'intérieur d'un contexte de maintien de connexion.
Il y a beaucoup d'autres éléments mais ceux-ci sont notre point d'entrée.
J'ai écrit une enveloppe ODBC qui consiste en deux classes:
Une classe de connexion ODBC: TQConnect, et
Une classe de Table ODBC: Table
L'interface pour la classe de connexion peut être vue ici , et l'implémentation ici . L'interface pour la classe de table peut être vue ici et l'implémentation ici .
J'ai commenté le code pour aider à son interprétation mais vous devez vous referez à la documentation des API ODBC dans le but de comprendre ce que font les appels aux API.
Le reste du tutoriel est dédié à l'utilisation de classes simples ODBC dans un exemple d'application.
L'application
En dehors des possibilités que nous avons utilisées auparavant dans ce tutoriel, l'application doit fournir maintenant de quoi :
Connecter une base de données ODBC
Envoyer une requête SQL à la base de données et
Voir les résultats des requêtes SQL dans une grille
Connexion à la base de données
Figure 6:
La Figure 6 ci-dessus montre comment le dialogue de connexion ODBC, un choix du menu dans pmMainFrame.
Vous pouvez voir le code de pmMainFrame ici . Le fichier interface peut être vu ici . Il peut être utile de le garder ouvert pendant que l'on regarde comment l'application connecte la base de données.
Les « événements » de connexion réveillent la méthode OnDBStart montré ci-dessous:
void pmMainframe::OnDBStart(wxCommandEvent & event)
{ SQLRETURN rc;
pmSRCdlg * getLogon =
new pmSRCdlg (this,-1,"Log on (dsn,uid,pwd)", wxPoint(100,100), wxSize(450, 300),
wxRESIZE_BORDER | wxDEFAULT_DIALOG_STYLE);
if (getLogon->ShowModal() == wxID_OK)
{ wxString connectStr = getLogon->GetValue();
wxLogMessage("Connecting to %s", connectStr.c_str());
rc = dbConnect(connectStr);
if (rc == SQL_SUCCESS)
wxLogMessage("Connect OK");
else
wxLogMessage("Connect failed. SQL return code %d", rc);
}
else
wxLogMessage("Connect declined");
}
Nous entrons un DSN, un nom utilisateur, un mot de passe et cliquons le bouton OK. Vous pouvez voir dans OnDBStart que nous utilisons quelques API ODBC. Nous avons une variable nommée rc de type SQLRETURN et nous utilisons une constante nommée SQL_SUCCESS. Nous appelons la fonction dbConnect avec l'argument chaîne de connexion avec la quelle nous obtenons un dialogue de connexion.
N'oubliez pas que vous avez besoin d'un DSN valable dans le but de vous connecter avec succès. Le dialogue de connexion ci-contre montre une connexion à un DSN nommé tgproject . Cela provient d'une base de données Postgresql située sur un serveur à un mètre ou bien plus loin de moi. Cela peut être à l'autre bout du monde.
La classe pmMainFrame contient deux membres liés à la base de données, theDatabase qui est un type TQConnect et theTable qui est un type Table. La classe TQConnect a une méthode de connexion :
SQLRETURN TQConnect::connect(const char * szDSN)
{
rc = SQLAllocConnect(henv, &hdbc);
if (rc == SQL_SUCCESS)
{ rc = SQLDriverConnect( hdbc, 0, (SQLCHAR *) szDSN,
SQL_NTS,
(SQLCHAR *) buf,
sizeof(buf),
&buflen,
SQL_DRIVER_COMPLETE);
cConnections++;
return rc;
}
else
return rc;
};
et ceci prend soin de connecter la base de données via l'API ODBC. Rappelez-vous ce que j'ai dit auparavant: vous devez lire la documentation des API pour comprendre les API ODBC. Je pourrais vous expliquer les API plus en détails mais cela prendrait la taille d'un petit tutoriel à lui tout seul.
Si nous contrôlons la connexion avec succès nous obtenons en retour un état SQL_SUCCESS que nous testons dans OnDBStart. Nous sommes prêts maintenant pour exécuter quelques requêtes SQL vers notre base de données.
Commentaire important:
David BEECH possède de nombreux ouvrages à son actif dont un tutoriel C++ et les STL mais à mon sens, la conception du programme devient trop compliqué :
- Le code mixe les bibliothèques MFC, les STL sans compter un code lourd en C.
- Alors que nous avions une vision pédagogique des programmes, on plonge trop vite dans les entrailles de l'ODBC. Ultérieurement, il serait profitable de se consacrer à cette étude.
- A l'exécution, il ne possède pas le même comportement, ou échoue à l'accès, en fonction du type de la base de données et avec le minimum de modification pour qu'il compile correctement(voir code).
Pour ceux que cela tente, la suite est ci-dessous sinon vous pouvez dérivez par ce lien ( Suite )
****************
Les requêtes aux bases de données
Figure 7:
Si nous choisissons SQL depuis le menu de la fenêtre principale (Figure7) nous voyons un simple dialogue comme montré ici.
Entrer juste une requête SQL valide ou erronée si vous voulez, et cliquez sur le bouton OK.
Sélectionnez le bouton OK aboutit à un appel à la méthode execSQL de votre connexion theDatabase . Cette méthode a deux arguments: la requête SQL et une référence à un objet Table, theTable . Les résultats de la requête SQL sont verrouillés dans un vecteur nommé tData dans theTable mais nous n'avons pas besoin de nous en préoccuper puisque nous obtenons les résultats via la méthode theTable .
Vous pouvez voir dans OnExecSQLGrid ci-dessous que nous utilisons theTable de deux façons. Nous appelons les méthodes getAttribs pour voir combien de lignes et de colonnes il y a et nous les remettons à une instance de classe nommée pmGridframe . C'est cela qui prend soin d'afficher le résultat actuel des requêtes SQL.
SQLRETURN pmMainframe::OnExecSQLGrid(wxCommandEvent & event)
{ SQLRETURN retCode = SQL_SUCCESS;
wxString cmd;
if ( GetConnectStatus() )
{ pmSQLdlg * getSQL =
new pmSQLdlg (this,-1,"SQL Command",lastCmd, wxPoint(100,100), wxSize(450, 300),
wxRESIZE_BORDER | wxDEFAULT_DIALOG_STYLE);
if (getSQL->ShowModal() == wxID_OK)
{ cmd = getSQL->GetValue();
lastCmd = cmd;
retCode = theDatabase.execSQL(cmd.c_str(), theTable);
if (retCode == SQL_SUCCESS)
{ attribs = theTable.getAttribs();
wxLogMessage("%d rows, %d columns", attribs.cRows, attribs.cColumns);
if (attribs.cRows > 0)
{ pmGridframe * x = new pmGridframe(this, "testing", theTable, 20,20,200,200);
x->Show(TRUE);
}
else
wxLogMessage("SQL returned zero rows");
}
}
else wxLogMessage("Failed - SQL return code %d", retCode);
}
else
wxLogMessage("Cannot execute SQL - we are disconnected.");
return retCode;
}
Visualiser les résultats des requêtes SQL
|
Ici vous voyez les données retournées par les requêtes SQL affichées dans l'instance wxGrid qui est contenue dans une instance wxFrame. |
L'implémentation de pmGridFrame peut être vue ici et l'interface ici .
Si vous regarder le code vous verrez avec surprise qu'il y en a peu. C'est un témoignage à quel point est utile le framework wxWindows. Nous avons créé une interface complexe pour une liste de résultat SQL programmée avec quelques appels simples.
Les lignes et colonnes de grilles peuvent être redimensionnées et les cellules rééditées, bien que je doive me hâter d'ajouter que nous n'avons pas implémenté cela dans cette application. Peut être dans le prochain tutoriel.
Les changements dans le Makefile
Le Makefile, que nous avons utilisé, a été revu et peut être vu ici .
Sommaire
Un petit rappel : En présentant cela, je n'ai montré que les détails importants. Vous devez vous rappeler que les exemples contiennent aussi d'autres codes pertinents, par exemple l'identifiant de fenêtres et la table d'événements. J'ai allégé les programmes d'exemples pour la présentation dans le but de garder de l'espace et de ne pas s'égarer..
Si vous souhaitez télécharger le code et les pages html cliquez ici .
Retour en début de document
Traduction de Patrick VALERI du document de David Beech
Copyright © 1999 - 2001 David Beech
Ce document est issu de http://phenix.developpez.com/ et reste la propriété exclusive de son auteur.
La copie, modification et/ou distribution par quelque moyen que ce soit est soumise à l'obtention préalable de l'autorisation de l'auteur.
Copyright © 2000-2005