I. Introduction▲
L'absence de composants natifs aux bases de données sous une version personnelle est un des freins importants à ceux qui souhaitent s'initier à Delphi. Une solution consiste à passer par un autre langage. Ce langage est Python, un langage objet, open source et interprété très puissant. Les composants PythonForDelphi permettent d'utiliser des objets Python sous Delphi.
Ce tutoriel est destiné aux programmeurs Delphi d'une version personnelle qui souhaitent utiliser malgré tout des bases de données. Sachez qu'aucune connaissance en Python n'est exigée. Tout le code que vous écrirez sera exclusivement en Delphi.
En revanche, on ne pourra utiliser les composants du style (DBListBox, DBComboBox, DBGrid…) vu que l'on travaille sous une version personnelle de Delphi. Tout se fera par requête SQL et exploitation de ces requêtes.
Écrit en juillet 2005, ce tutoriel a été revu en novembre 2006 pour être compatible avec les dernières versions des logiciels/bibliothèques actuellement disponible (notamment python 2.5 et Firebird 2.0)
II. Installation des composants▲
Vous pouvez télécharger le source complet des exemples ici. Il a été écrit sous Delphi 7 édition personnelle, mais a également été testé sous Delphi 2005 édition personnelle (win 32). Ce fichier contient :
- le source de l'exemple ;
- l'exécutable de l'exemple ;
- le runtime FBruntimePython contenant le moteur Python et les dépendances pour Firebird ;
- le fichier FBBase.py, interface permettant de manipuler des bases Firebird.
Aperçu de l'interface de l'exemple :
II-A. Installation de la bibliothèque PythonForDelphi▲
La première chose est d'installer la bibliothèque PythonForDelphi. L'installation est très facile. Il suffit de récupérer l'installateur ici. La version utilisée pour ce tutoriel est la version 3.32. Ensuite lancez l'exécutable et suivez les instructions.
Le répertoire par défaut est c:\Program Files\PythonForDelphi :
Arrivé à cette fenêtre,
cliquez tout d'abord sur le bouton Edit Definition.inc, commentez la ligne 41 {$DEFINE PREFER_UNICODE} // this will convert a variant containing an OleStr to a UniCode Python string., puis enregistrez le fichier. Vous pourrez toujours remodifier ce fichier plus tard que vous retrouverez dans le répertoire ./Components/Sources/Core. C'est également ce fichier qui vous permettra de compiler vos programmes Delphi avec différentes versions de Python.
Sélectionnez ensuite votre version de Delphi et la version 2.5 de Python puis cliquez sur le bouton Compile. Cela installera les composants dans l'IDE de Delphi. Si vous avez une version personnelle de Delphi, une erreur apparaîtra dans les logs,
car tous les composants n'auront pas été installés (certains nécessitant une version professionnelle).
Arrivé à la fenêtre suivante, cliquez directement sur Cancel.
En ouvrant l'IDE de Delphi 7, vous aurez un nouvel onglet qui contiendra au moins ces icônes :
Et sous Delphi 2005 :
Présentation succincte des composants. Beaucoup sont encore présents pour raison historique.
Composant |
Description |
---|---|
PythonGUIInputOutput |
Ce composant permet de rediriger la sortie de l'exécution d'un code Python vers un TMemo ou un TRichEdit. |
AtomPythonEngine |
Composant déprécié à partir de Delphi 6. Utiliser le composant PythonEngine |
PythonEngine |
Moteur Python - permet d'utiliser d'autres composants de PythonForDelphi |
PythonInputOutput |
Ce composant permet de rediriger la sortie de l'exécution d'un code Python vers ce que l'on veut |
PythonType |
Ce composant permet de créer un nouveau type Objet. |
PythonModule |
Ce composant permet de créer un nouveau module. On peut également s'en servir pour stocker son propre code python qui sera inclus dans le .exe pour ne pas avoir à distribuer des fichiers .py en annexe. |
PythonDelphiVar |
Ce composant permet de créer des variables Python qui seront également accessibles depuis Delphi |
PyDelphiWrapper |
Ce composant (apparaissant dans la version 3.29) permet d'exporter des variables Delphi |
PythonDatabase |
Ce composant (présent avec version commerciale de Delphi) implémente les unités VCL DB et DBTables pour l'accès à une base de données |
II-B. Installation de Firebird 2.0▲
Le serveur utilisé était d'une version 2.0RC5. Vous pouvez le télécharger directement sur le site de Firebird.
II-C. Installation des dépendances Python▲
Vous avez à ce stade deux possibilités pour votre développement et pour le déploiement du logiciel.
Vous pouvez opter pour l'installation complète du langage Python et des librairies utiles (comptez 30 Mo en tout) ou bien simplement un runtime (4 Mo) à installer dans le répertoire de l'application.
Pour l'installation, c'est très simple, il suffit de récupérer les installateurs autoextractibles disponibles sur les sites dont vous trouverez les adresses ici.
Langages et librairies utilisés lors du développement de ce tutoriel :
- Python 2.5;
- kinterbasdb 3.2.0.
Si vous optez pour le runtime, il suffit de décompresser l'archive disponible ici dans le répertoire de l'application. Ce runtime (assez compliqué à créer) est figé dans le sens où il a été construit à partir de Python 2.5 et kinterbasdb 3.2.0 et qu'il n'est pas soumis à l'arrivée de nouvelles versions de ces librairies. Cependant des mises à jour seront faites si nécessaire.
Si vous pensez modifier les sources Python, pensez à installer Python 2.5 qui fournit un éditeur adapté à la gestion des sources Python.
Si de plus, vous souhaitez utiliser d'autres objets Python avec Delphi, il est préférable d'installer le langage Python plutôt que d'utiliser le runtime que je vous fournis.
II-D. Installation du fichier FBBase.py▲
Il suffit de copier ce fichier à la racine du répertoire de l'application. Ce fichier correspond à l'interface Python avec la base de données Firebird.
II-E. Tableau récapitulatif▲
Développement (version Runtime) |
Développement (version Full) |
Déploiement (version Runtime) |
Déploiement (version Full) |
---|---|---|---|
Delphi 7/2005 |
Delphi 7/2005 |
Exécutable de l'application |
Exécutable de l'application |
PythonForDelphi 3.32 |
PythonForDelphi 3.32 |
||
Firebird 2.0 |
Firebird 2.0 |
Firebird 2.0 |
Firebird 2.0 |
FBBase.py |
FBBase.py |
FBBase.py |
FBBase.py |
FBruntimePython |
Python 2.5 |
FBruntimePython |
Python 2.5 |
kinterbasdb 3.2.0 |
kinterbasdb 3.2.0 |
III. Python et les bases de données▲
III-A. L'API 2.0 de Python pour les bases de données (DBAPI)▲
III-A-1. Introduction▲
Il n'est pas possible d'accéder en natif aux bases de données avec Python. Cependant l'ajout de bibliothèques conformes au standard DBAPI permet d'accéder à différentes bases de données.
On peut citer par exemple les modules kinterbasdb pour accéder aux bases Firebird/Interbase, mySQLdb pour les bases Mysql, psycopg pour les bases PostgreSQL …
Ce standard présenté par la suite permet d'accéder à différents types de bases de données sans grandes modifications de votre code. Une explication au chapitre V vous guidera si vous optez pour une autre BDD.
III-A-2. La fonction connect▲
La fonction connect permet la connexion à une base de données. Le standard DBAPI recommande d'utiliser les paramètres suivants :
database : nom de la base de données à laquelle on veut se connecter ;
dsn : nom de la source de données utilisée pour la connexion ;
host : nom de la machine sur laquelle s'exécute la base de données ;
password : mot de passe pour la connexion ;
user : nom d'utilisateur pour la connexion.
La fonction connect renvoie un objet de la classe Connection.
Python est sensible à la casse. Les minuscules sont différentes des majuscules. Lorsque vous utiliserez des objets Python dans votre programme Delphi, il faudra également se souvenir que cette distinction est encore en vigueur.
import
kinterbasdb
con =
kinterbasdb.connect
(
host=
'localhost'
, database=
r'C:\Program Files\Firebird\Firebird_2_0\examples\empbuild\employee.fdb'
, \
user=
'sysdba'
, password=
'masterkey'
)
III-A-3. Classe Connection▲
Quatre méthodes de la classe Connection vont nous intéresser :
close() : ferme la connexion à la base de données et libère les ressources ;
commit() : applique la transaction courante à la base de données ;
cursor() : renvoie une instance de la classe Cursor ;
rollback() : annule la transaction courante sur la base de données.
cur =
con.cursor
(
)
III-A-4. Classe Cursor▲
Une instance de la classe Cursor représente l'ensemble des résultats d'une requête SQL.
Cette classe fournit de nombreuses méthodes. Nous ne présenterons que celles utilisées par la suite :
Close() : ferme le curseur et libère les ressources associées ;
execute(statement) : exécute une requête SQL statement sur la base de données ;
fetchall() : renvoie le résultat de la requête sous forme d'une liste de tuples.
Remarque: Un tuple est une séquence d'éléments immutables tandis qu'une liste est une séquence d'éléments mutables
cur.execute
(
'select * from Country'
)
l =
cur.fetchall
(
)
print
l
cur.close
(
)
III-B. Le module FBBase.py - interface avec la BDD Firebird▲
III-B-1. Introduction▲
Pour utiliser les modules fournis avec Python sur les bases de données, nous avions trois possibilités. La première possibilité serait d'utiliser les objets de la classe Connection et Cursor de façon brute. Mais en général, quelle que soit la base de données utilisée, on se crée une interface qui nous facilite l'utilisation de ces classes. Ici, cette interface peut se faire du côté de Delphi ou du côté de Python. J'ai opté pour la programmer en Python pour les raisons suivantes :
- il est plus simple de manipuler les objets Python avec Python qu'avec Delphi ;
- certains paramètres (notamment lors de la connexion à la base de données) entraînent une erreur de compilation sous Delphi, car termes non reconnus. Donc à moins de s'en sortir de manière peu propre, il était préférable de tout faire en Python et de gérer sous Delphi des objets Python que l'on maîtrise entièrement.
Néanmoins, l'interface proposée est une interface simple et minimaliste à l'exception de quelques fonctions. Elle pourra donc être complétée directement pour les besoins d'une base de données spécifique. Une autre possibilité est de se créer une nouvelle interface sous Delphi qui gérera la base de données en question en se liant à celle sous python qui fournit tous les outils unitaires à l'utilisation d'une base de données.
III-B-2. L'objet TFBConnection▲
import
kinterbasdb
try
:
if
kinterbasdb.FB_API_VER ==
20
: kinterbasdb.init
(
type_conv=
200
) ## Firebird 2.0.x
except
: pass
import
os.path
import
os
class
TFBConnection:
def
__init__
(
self, host=
'localhost'
, pathbase=
r'C:\Program Files\Firebird\Firebird_2_0\examples\empbuild\employee.fdb'
, \
user=
'sysdba'
, password=
'masterkey'
):
self.pathbase=
pathbase
self.host=
host
self.user=
user
self.password=
password
def
Initialize
(
self, host=
'localhost'
, pathbase=
r'C:\Program Files\Firebird\Firebird_2_0\examples\empbuild\employee.fdb'
, \
user=
'sysdba'
, password=
'masterkey'
):
self.pathbase=
pathbase
self.host=
host
self.user=
user
self.password=
password
def
Connect
(
self): self.con =
kinterbasdb.connect
(
host=
self.host, database=
self.pathbase, user=
self.user, password=
self.password)
def
Commit
(
self): self.con.commit
(
)
def
Cursor
(
self): return
TFBCursor
(
self)
def
Rollback
(
self): self.con.rollback
(
)
def
Close
(
self): self.con.close
(
)
## Fonctionnalite propre a Firebird
def
DropDatabase
(
self): self.con.drop_database
(
)
def
CreateDatabase
(
self):
if
self.host==
'localhost'
:
try
: os.makedirs
(
os.path.dirname
(
self.pathbase))
except
: pass
else
:
try
: os.makedirs
(
os.path.dirname
(
'
\\\\
'
+
self.host+
self.pathbase))
except
: pass
self.con =
kinterbasdb.create_database
(
"create database '"
+
self.pathbase+
"' user '"
+
self.user+
"' password '"
+
\
self.password+
"'"
)
Cette classe est une interface à la classe Connection et à la fonction connect présentée précédemment. Elle permet de gérer une connexion à une BDD en reprenant les principales fonctions.
Tout d'abord, les trois premières lignes servent à l'importation de modules, kinterbasdb pour la gestion des bases Firebird, os et os.path pour la gestion des fichiers.
Présentation des méthodes de la classe TFBConnection
Méthodes de la classe TFBConnection
- __init__(self, host, database, user, password) : méthode appelée à la création de l'objet. Initialise les paramètres de la classe ;
- Connect(self) : lance la connexion à la BDD ;
- Commit(self) : applique la transaction courante à la base de données ;
- Cursor(self) : renvoie un objet de la classe TFBCursor ;
- Rollback(self) : annule la transaction courante sur la base de données ;
- Close(self) : ferme la connexion à la base de données et libère les ressources ;
- DropDatabase(self) : efface la base de données ;
- CreateDatabase(self): Crée la base de données. On crée si nécessaire le répertoire où sera sauvegardée la base.
pyCon =
TFBConnection
(
)
pyCon.Connect
(
)
III-B-3. L'objet TFBCursor▲
class
TFBCursor
(
kinterbasdb.Cursor):
def
__init__
(
self, FBConnection): kinterbasdb.Cursor.__init__
(
self, FBConnection.con)
def
ExecuteElem
(
self, strreq): return
(
self.ExecuteListe
(
strreq))[0
]
def
ExecuteElem2
(
self, strreq): return
(
self.ExecuteTuple
(
strreq))[0
]
def
ExecuteTuple
(
self, strreq):
self.execute
(
strreq)
return
self.fetchall
(
)
def
ExecuteIter
(
self, strreq):
self.execute
(
strreq)
return
self.iter(
)
def
ExecuteListe
(
self, strreq):
listresult=
[]
for
laliste in
self.ExecuteTuple
(
strreq): listresult.append
(
laliste[0
])
return
listresult
## Fonctions clone de celles de base - permet d'avoir des references explicites dans ce code
def
Close
(
self): self.close
(
)
def
Execute
(
self, strreq): self.execute
(
strreq)
def
IterMap
(
self): return
self.itermap
(
)
def
RowCount
(
self): return
self.rowcount
(
)
def
FecthAll
(
self): return
self.fetchall
(
)
def
FecthOne
(
self): return
self.fetchone
(
)
def
Description
(
self): return
self.description
(
)
def
CallProc
(
self, procname, params=(
)): return
self.callproc
(
procname, params)
## Fonctionnalites supplementaires propres a Firebird
def
ExecuteElemMap
(
self, strreq): return
(
self.ExecuteMap
(
strreq))[0
]
def
ExecuteMap
(
self, strreq):
self.execute
(
strreq)
return
self.fetchallmap
(
)
def
ExecuteIterMap
(
self, strreq):
self.execute
(
strreq)
return
self.itermap
(
)
def
ListeTable
(
self):
return
self.ExecuteListe
(
"select rdb$relation_name from rdb$relations where "
+
\
"rdb$relation_name not like 'RDB$%';"
)
def
ListeChamp
(
self, strtable):
l =
self.ExecuteListe
(
"Select RDB$FIELD_NAME from RDB$RELATION_FIELDS "
+
\
"where RDB$RELATION_NAME = '"
+
strtable.upper
(
)+
"';"
)
for
i in
range(
len(
l)): l[i] =
l[i].strip
(
) ## La requete precedente laisse des espaces a la fin des noms
return
l
def
CreateGenerator
(
self, strgen, strtrig, strtable, strcolumn):
self.execute
(
"CREATE GENERATOR "
+
strgen+
";"
)
strSQL=
"CREATE TRIGGER "
+
strtrig+
" FOR "
+
strtable+
\
" ACTIVE BEFORE INSERT POSITION 0 "
+
\
"AS BEGIN "
+
\
"IF( NEW."
+
strcolumn+
" IS NULL ) THEN "
+
\
"NEW."
+
strcolumn+
" = GEN_ID("
+
strgen+
", 1 );"
+
\
"END"
self.execute
(
strSQL)
Cette classe est une interface à la classe Cursor présentée précédemment. Elle permet de manière simple d'exécuter des requêtes et de récupérer les résultats avec pour seule connaissance requise, le langage SQL.
Présentation de quelques méthodes de la classe TFBCursor
Méthodes de la classe TFBCursor
- __init__(self, FBConnection) : méthode appelée lors de la construction de l'objet ;
- Close(self) : ferme le curseur. Vous pouvez également utiliser close (sans le c majuscule) ;
- Execute(self, strreq) : exécute la requête SQL strreq. À utiliser sur des requêtes ne retournant aucun résultat tel que INSERT, UPDATE… Vous pouvez aussi utiliser la méthode execute (sans le e majuscule) ;
- ExecuteElem(self, strreq) : à utiliser pour récupérer le résultat unique d'une requête sur un seul champ ;
- ExecuteElem2(self, strreq) : à utiliser pour récupérer le tuple unique d'une requête sur un seul champ ;
- ExecuteTuple(self, strreq) : exécute la requête SQL strreq et renvoie le résultat sous forme d'une liste de tuples. À utiliser sur une requête SELECT ;
- ExecuteMap(self, strreq) : exécute la requête SQL strreq et renvoie le résultat sous forme d'une liste de map (ou dictionnaire). À utiliser sur une requête SELECT ;
- ExecuteListe(self, strreq) : exécute la requête SQL strreq et renvoie le résultat sous forme d'une liste de valeurs. À utiliser sur une requête SELECT qui ne contient qu'un seul champ de retour ;
- ListeTable(self) : retourne une liste des tables de la base de données. La requête utilisée n'est compatible qu'avec Firebird ;
- ListeChamp(self, strtable) : retourne une liste des champs de la table strtable de la base de données. La requête utilisée n'est compatible qu'avec Firebird ;
- CreateGenerator(self, strgen, strtrig, strtable, strcolumn) : permet d'appliquer à un champ une autoincrémentation. strtable et strcolumn correspondent à la table et au champ en question. strgen et strtrig correspondent au nom du générateur et du trigger.
Pour les requêtes du type SELECT, vous aurez le choix entre les méthodes ExecuteTuple et ExecuteMap. La première vous donnera un code plus concis, la deuxième un code plus clair.
>>>
pyCon =
TFBConnection
(
)
>>>
pyCon.Connect
(
)
>>>
pyCur =
pyCon.Cursor
(
)
>>>
lt =
pyCur.ExecuteTuple
(
'Select * from Country'
)
>>>
lm =
pyCur.ExecuteMap
(
'Select * from Country'
)
>>>
l =
pyCur.ExecuteListe
(
'Select CURRENCY from Country'
)
>>>
print
lt[5
]
(
'Italy'
, 'Lira'
)
>>>
print
lm[5
]
(
result set row with
COUNTRY =
Italy, CURRENCY =
Lira)
>>>
print
lm[5
]['COUNTRY'
]
Italy
>>>
print
l[5
]
Lira
IV. Programmation sous Delphi▲
Nous allons maintenant à partir de Delphi accéder à l'interface développée en Python.
Assurez-vous tout d'abord que vous avez installé Firebird et PythonForDelphi et que vous avez récupéré les sources du projet. Ces sources contiennent en plus le fichier FBBase.py et le runtime Python.
Ouvrez ensuite le projet AdminBase.dpr.
Si vous choisissez l'installation complète de Python, vous pouvez effacer le runtime (répertoire kinterbasdb et encodings et les fichiers .pyc et .pyd) ainsi que la dll Python25.dll (la suppression de la dll est obligatoire sinon l'application détectant cette dll ne cherchera le module kinterbasdb que dans ce répertoire (et ne le trouvera donc pas)).
IV-A. Connection à la base de données et initialisation des objets▲
Nous n'entrerons pas dans le détail de l'utilisation des composants fournis par PythonForDelphi, mais seulement pour ceux qui nous intéressent. Si vous souhaitez approfondir le sujet, vous trouverez des tutoriels sur le site de téléchargement de la bibliothèque et de nombreuses démos sont disponibles lors de l'installation des composants.
IV-A-1. PythonEngine▲
Ce composant fait le lien avec la machine virtuelle Python. Dans votre programme Python, il ne peut y en avoir qu'un seul en même temps. C'est le seul composant de la barre de composants que nous utiliserons. Sur la fiche, vous voyez ce composant avec une tête de Python nommé PE (nom usuel).
IV-A-2. Initialisation des objets▲
On remarquera dans les uses l'unité VarPyth (ajoutée à la « main »). Cette unité fournit des variables globales d'accès à des objets Python.
procedure
TfrmAdmin.FormCreate(Sender: TObject);
var
PyStrings:TStringList;
begin
Caption:='Visualisation d''une Base Firebird - Version '
+Version ;
// Récupération du code du module python FBBase.py
PyStrings:=TStringList.Create;
PyStrings.LoadFromFile('FBBase.py'
);
// Exécution du code en mémoire dans le moteur Python
PE.ExecStrings(PyStrings);
// Création d'un objet TFBBase permettant de manipuler la base de données
pyCon:=MainModule.TFBConnection();
PyStrings.Free;
end
;
Une première chose est de mettre en mémoire dans le moteur Python le code Python de l'interface FBBase.py. Pour cela la fonction PE.ExecStrings(pystr: Strings) permet d'exécuter le code source contenu dans pystr.
Tout objet défini dans pystr est alors directement accessible dans la variable globale MainModule.
MainModule.TFBConnection() appelle le constructeur __init__ de la classe TFBConnection et retourne une instance correspondante. Pour l'instant, notre instance pyCon est initialisée avec des valeurs par défaut (ceux définis dans la classe Python). Il s'agit donc des paramètres correspondant à la base de données que l'on a lors de l'installation de Firebird. Par contre, nous ne sommes pas encore connectés à cette base.
Tout objet Python est déclaré en Delphi en tant que variant.
IV-A-3. Connexion à la base de données▲
procedure
TfrmAdmin.btnConnectClick(Sender: TObject);
var
pyltables,pylchamps:variant;
pyCur:variant;
i:integer
;
begin
// connexion à la base de données avec les nouveaux paramètres
pyCon.host:=ehost.Text;
pyCon.database:=edatabase.Text;
pyCon.user:=euser.text;
pyCon.password:=epassword.Text;
try
pyCon.Connect() ;
...
end
;
On accède aux attributs/fonctions d'une instance Python de la même façon qu'en Delphi.
La première chose est de se connecter à la base avec pyCon.Connect().
En Python, le typage est dynamique; on peut donc associer à un attribut n'importe quel type et le changer à tout moment.
Encore une fois, il faut bien respecter la casse sur l'utilisation des attributs/fonctions de pyCon.
IV-B. Utilisation de requêtes SQL▲
IV-B-1. Exemple 1▲
procedure
TfrmAdmin.btnConnectClick(Sender: TObject);
var
pyltables,pylchamps:variant;
pyCur:variant;
i:integer
;
begin
...
// Création d'un curseur
pyCur:=pyCon.Cursor();
// On liste les tables de la base et on remplit un comboBox
pyltables := pyCur.ListeTable();
comboTable.Clear;
for
i:=0
to
len(pyltables)-1
do
comboTable.items.Add(pyltables.GetItem(i));
comboTable.ItemIndex:=0
;
// On liste les champs de la table sélectionnée et on remplit un combobox
pylchamps:= pyCur.ListeChamps(comboTable.Text);
comboChamp.Clear;
for
i:=0
to
len(pylchamps)-1
do
comboChamp.items.Add(pylchamps.GetItem(i));
combochamp.ItemIndex:=0
;
btnAfficherTuple.Enabled:=true
;
btnAfficherMap.Enabled:=true
;
pyCur.Close()
except
MessageDlg('Erreur : La connexion a la base de données n''a pu se faire'
, mtError, [mbOk], 0
);
end
;
end
;
procedure
TfrmAdmin.comboTableChange(Sender: TObject);
var
pylchamps:variant;
i:integer
;
pyCur:variant;
begin
pyCur:=pyCon.Cursor();
// Si on sélectionne une nouvelle table, on met à jour la comboBox des champs.
pylchamps:= pyCur.ListeChamp(comboTable.Text);
comboChamp.Clear;
for
i:=0
to
len(pylchamps)-1
do
comboChamp.items.Add(pylchamps.GetItem(i));
combochamp.ItemIndex:=0
;
pyCur.Close()
end
;
Voici le premier exemple de l'utilisation d'un objet TFBCursor.
Lorsque vous cliquez sur le bouton « Connecter », le programme remplit les combobox de l'encadré Requête avec le nom des tables de la base de données et le nom des champs de la table sélectionnée. Pour cela, on utilise le curseur pyCur et les fonctions vues auparavant ListeTable et ListeChamp.
Tout résultat retourné par une fonction Python sera récupéré dans une variable Delphi de type variant. Lorsque vous changez la table, la deuxième combobox est immédiatement modifiée pour recevoir les champs correspondants.
Cependant, Delphi ne permet pas d'utiliser la propriété d'index sur un variant si celui-ci n'est pas un vrai tableau de variant, c'est-à-dire qu'il ne sera pas possible d'accéder directement à pyltables[0]. En effet, notre variable représente une liste de string. Ce problème est contourné par l'unité VarPyth qui détecte des noms spéciaux accolés à une variable variant: GetItem, SetItem, Length… Ainsi on accédera à un élément avec la syntaxe pyltables.GetItem(0).
On notera également la fonction global len (équivalent à .Length()) qui permet de connaître la longueur d'une liste.
Je vous encourage à parcourir le fichier VarPyth.pas pour de plus amples explications.
IV-B-2. Exemple 2▲
Pour afficher le résultat de la requête, nous disposons de deux boutons. Le premier utilisera la fonction ExecuteTuple et le deuxième ExecuteMap. Le résultat sera cependant identique.
procedure
TfrmAdmin.btnAfficherTupleClick(Sender: TObject);
var
pyValue,pyChamps:variant;
pyCur:variant;
i,j:integer
;
begin
pyCur:=pyCon.Cursor();
// On récupère le nom des champs de la table et on l'affiche dans la StringGrid
pyChamps:=pyCur.ListeChamps(comboTable.Text);
sgTable.ColCount:=len(pyChamps);
for
j:=0
to
len(pyChamps)-1
do
begin
sgTable.Cells[j,0
]:=pyChamps.GetItem(j);
sgTable.ColWidths[j]:=100
;
end
;
// On récupère toutes les valeurs de la table sélectionnée avec tri selon le champ sélectionné
pyValue:=pyCur.ExecuteTuple('select * from '
+comboTable.Text+' order by '
+combochamp.Text);
sgTable.RowCount:=len(pyValue)+1
;
for
i:=0
to
len(pyValue)-1
do
for
j:=0
to
len(pyValue.GetItem(i))-1
do
sgTable.Cells[j,i+1
]:=pyValue.GetItem(i).GetItem(j);
pyCur.Close()
end
;
Dans ce deuxième exemple, on va afficher une table complète. Pour cela on exécute la requête pyCur.ExecuteTuple('select * from '+comboTable.Text+' order by '+combochamp.Text); qui nous retourne une liste de tuples (équivalent à une liste de listes). Pour accéder à un élément, il suffira de doubler l'utilisation du GetItem : pyValue.GetItem(i).GetItem(j)
procedure
TfrmAdmin.btnAfficherMapClick(Sender: TObject);
var
pyValue,pyChamps:variant;
pyCur:variant;
i,j:integer
;
begin
pyCur:=pyCon.Cursor();
// On récupère le nom des champs de la table et on l'affiche dans la StringGrid
pyChamps:=pyCur.ListeChamp(comboTable.Text);
sgTable.ColCount:=len(pyChamps);
for
j:=0
to
len(pyChamps)-1
do
begin
sgTable.Cells[j,0
]:=pyChamps.GetItem(j);
sgTable.ColWidths[j]:=100
;
end
;
// On récupère toutes les valeurs de la table sélectionnée avec tri selon le champ sélectionné
pyValue:=pyCur.ExecuteMap('select * from '
+comboTable.Text+' order by '
+combochamp.Text);
sgTable.RowCount:=len(pyValue)+1
;
for
i:=0
to
len(pyValue)-1
do
for
j:=0
to
len(pyValue.GetItem(i))-1
do
sgTable.Cells[j,i+1
]:=pyValue.GetItem(i).GetItem(pyChamps.GetItem(j));
pyCur.Close()
end
;
Cette fonction est identique à la précédente, excepté les lignes pyValue:=pyCur.ExecuteMap(…) et sgTable.Cells[j,i+1]:=pyValue.GetItem(i).GetItem(pyChamps.GetItem(j));. Pour accéder à un élément, on n'utilise plus un index numérique, mais le nom d'un champ (pour conserver la généricité de la fonction, le nom du champ se trouve donc dans pyChamps.GetItem(j)).
V. Pour aller plus loin▲
Ce tutoriel et les sources fournies ont été conçus pour fonctionner avec les bases Firebird. Cependant on a vu que d'autres bibliothèques Python pour l'accès à d'autres bases de données respectent le standard DBAPI. Il est donc envisageable d'utiliser la même interface Python pour d'autres bases de données.
Je propose donc une nouvelle interface pyBase.py qui fonctionnera avec les bases de données suivantes :
Firebird, MySQL
V-A. Interface pyBase.py▲
Vous pouvez télécharger cette interface ici. Le nom des classes a un peu changé afin d'avoir une nomenclature générale. Un paramètre supplémentaire name a été ajouté pour la construction de l'instance TpyConnection qui vous permettra de sélectionner le type de la base de données utilisée.
V-B. Déploiement de vos applications▲
Outre votre exécutable créé sous Delphi et les fichiers dépendants, il vous faudra comme précédemment copier ou installer d'autres fichiers.
V-B-1. Interface pyBase.py▲
Il vous suffit de copier ce fichier à la racine du répertoire de votre application
V-B-2. Dépendances Python▲
Cette fois-ci, je n'ai pas préparé de runtime. Vous ne serez pas obligé d'installer toutes les librairies, mais juste celles correspondantes aux bases de données souhaitées. Vous trouverez tous les liens nécessaires ici. À l'heure actuelle, il n'existe pas d'installateur pour MySQLdb compatible Python 2.5 n'étant pas disponible, vous devrez vous rabattre sur des versions compatibles 2.4 (y compris pour toutes les autres bibliothèques) :
- Python 2.4.4 (pour toutes les BDD) ;
- kinterbasdb 3.2.0 (Firebird) ;
- MySQLdb 1.2.1 (MySQL).
V-B-3. Ajout de nouvelles bases de données▲
Si vous voulez utiliser d'autres BDD, il vous faudra tout d'abord modifier le fichier pyBase.py :
- en premier lieu, ajout de l'importation du module correspondant au début du fichier ;
- modification de la fonction Connect de la classe TpyConnection.
Pour le déploiement, comme la construction du runtime est assez compliquée (j'y travaille pour essayer de le rendre plus simple), je vous conseille d'opter pour l'installation complète.
Voici une liste des modules à récupérer si vous souhaitez tester d'autres bases de données. Je n'ai pas testé ces modules, mais vous devriez trouver sur les sites la documentation nécessaire.
BDD |
Module |
Lien |
---|---|---|
Firebird/Interbase |
kinterbasdb |
|
MySQL |
MySQLdb |
|
ODBC |
mxODBC |
|
Oracle |
DCOracle2 |
|
MSSQL Server |
mssqldb |
|
DB2 |
DB2 |
ftp://people.linuxkorea.co.kr/pub/DB2/ [ftp://people.linuxkorea.co.kr/pub/DB2/] |
PostGreSQL |
psycopg |
|
SAP DB |
sapdbapi |
|
Sybase |
Sybase |
VI. Téléchargement▲
Voici le récapitulatif de ce que vous pouvez télécharger :
Fichier |
Description |
Taille |
Mode FTP |
Mode HTTP de secours |
---|---|---|---|---|
FBBase.py |
Interface Python/Firebird |
7 Ko |
||
pyBase.py |
Interface Python/Firebird/MySQL |
7 Ko |
||
FBAdminBase0.2.1.zip |
Source + exécutable de l'application sans les dépendances Python |
307 Ko |
||
FBRuntimePython.zip |
Runtime Python/Firebird |
1.63 Mo |
||
FBAdminBase0.2.1-FBRuntimePython-FBBase.py.zip |
Application complète + Runtime Python/Firebird + FBBase |
1.94 Mo |
VII. Conclusion▲
L'accès aux bases de données sous une version personnelle de Delphi se fait de manière très simple grâce à l'utilisation des composants PythonForDelphi. De plus aucune connaissance en Python n'est nécessaire, d'où l'intérêt pour les Delphiistes.