I. Introduction

Pour mettre au point ce tuto, j'ai installé deux fois Ubuntu 10.10 :

  • l'un que j'ai laissé “brut d'installation” ;
  • l'autre sur lequel j'ai installé tout ce qu'il fallait pour faire fonctionner un programme Python-PyQt4 complexe (avec QtSql).

Mon but est de pouvoir transporter sur la version “brute d'installation”, le programme traité par cx_Freeze.

II. Que faut-il installer pour qu'un programme PyQt4 fonctionne ?

Ubuntu 10.10 est installé avec Python 2.6.6. Il est possible d'installer Python 2.7, voire Python 3.1, mais je ne l'ai pas fait par prudence : certains autres programmes, y compris du système, fonctionnent avec la version de Python préinstallée.

Pour qu'un programme PyQt4 fonctionne, il faut peu de choses : le paquet python-qt4.

Il faut, bien sûr ajouter les autres modules utilisés par votre programme. Par exemple :

  • python-qt4-sql (accès aux bases de données SQL avec QtSql) ;
  • libqt4-sql-sqlite (pilote SQL pour SQLite3 ; il y en a d'autres pour MySQL, PostgreSQL, etc.) ;
  • python-qt4-phonon (multimédia).

Et c'est tout ! Avec synaptic, c'est l'affaire de deux minutes.

Une chose importante : en s'installant, python-qt4 installe aussi, au titre des dépendances, python-sip. Il est important de ne pas installer d'autres “sip”, car PyQt4 est configuré avec une version précise de sip et la bibliothèque correspondante, sip.so, devra accompagner votre programme “standalone”.

III. Installation de cx_Freeze sur Ubuntu 10.10

cx_Freeze est un paquet qui existe sous Ubuntu 10.10 et la première chose qu'on pense à faire est de l'installer. C'est en général une bonne pratique, mais pas ici : la version fournie, la 4.0.1, est insuffisante pour l'option dont on aura besoin, bin_path_includes, qui permettra d'intégrer des bibliothèques supplémentaires et qui n'est apparu qu'avec la version 4.2.1. On va donc télécharger le code source et l'intégrer par compilation :

  • installer, à cause de la compilation, le paquet python-dev. À noter que le compilateur gcc est déjà préinstallé ;
  • télécharger le code source cx_Freeze-4.2.2.tar.gz sur le site de cx_Freeze ;
  • désarchiver ; cela donne un répertoire cx_Freeze-4.2.2 dans lequel il y a tout. Vous le placez où vous voulez dans votre /home ;
  • faire venir une console et se placer dans le répertoire cx_Freeze-4.2.2 (cd /home/..chemin../cx_Freeze-4.2.2) ;
  • le fichier README.txt dit clairement (et très succinctement !) ce qu'il faut faire (j'ai ajouté le 'sudo' qui manquait) :
 
Sélectionnez
python setup.py build
sudo python setup.py install

Il vient un grand nombre de messages, ne vous laissez pas impressionner : il suffit de vérifier qu'il n'y a pas d'erreur !

Voilà, vous avez la version de cx_Freeze la plus récente !

IV. Configuration du cx_Freeze pour PyQt4

Il existe plusieurs façons d'utiliser cx_Freeze, j'ai choisi l'utilisation de setup.py (même méthode que pour py2exe sous Windows). L'avantage est que, si on se débrouille bien, le même setup.py pourra être utilisé sous Windows et sous Linux.

J'utilise un “modèle” de setup.py que je fais évoluer au gré de mes programmes et, il faut bien le dire, au gré de mes (nombreux et longs) tâtonnements.

Voilà les particularités des options de setup.py pour des programmes complexes PyQt4 avec QtSql (se reporter au code plus bas) :

  • si votre programme comporte des modules à intégrer, placés dans des sous-répertoires, il faut citer ces sous-répertoires dans l'option “path” ;
  • il faut dire que la bibliothèque sip.so soit intégrée avec l'option “includes”: [“sip”] ;
  • les fichiers et répertoires à copier en plus (comme les fichiers d'aide) sont mentionnés avec l'option “include_files”. Chaque copie demandée est un tuple composé du chemin source (absolu ou relatif) et du chemin destination (toujours relatif). Par exemple, pour recopier un répertoire comportant les fichiers d'aide, “include_files”: [(“aide”, “aide”)]. Notez que les options comportent un blanc souligné et pas un tiret comme dit dans la notice de cx_Freeze ;
  • c'est le cas pour QtSql: dans la version “standalone”, les pilotes SQL seront cherchés dans le sous-répertoire “sqldrivers” (j'ai mis une demi-journée à trouver cela…). Il faut donc demander la copie des drivers avec l'option “include_files”: [(”/usr/lib/qt4/plugins/sqldrivers”,”sqldrivers”)].

Et c'est tout : avec ça, vous récupérez un répertoire avec tout ce qu'il faut pour une exécution “standalone” sur un Linux dans lequel il n'y a ni Python, ni PyQt4 !

Voilà un exemple de code du setup.py que j'ai utilisé pour un logiciel de concours photo pour un photo-club (environ 10 000 lignes de code et utilisation de QtSql).

Pour que le setup.py puisse, sans modification, servir sous Windows et sous Linux, il faudra utiliser à plusieurs endroits les tests habituels de plateforme (if sys.platform == “win32” ou if sys.platform == “linux2”,) puisque certaines adresses de bibliothèques seront différentes. Peut-être que des adaptations mineures permettraient l'utilisation sur Mac OSX et plus (Solaris, par exemple) mais je n'ai aucune possibilité d'essayer.

 
Sélectionnez
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Python 2.7
# 02/2011
 
import sys, os
from cx_Freeze import setup, Executable
 
#############################################################################
# préparation des options 
 
# chemins de recherche des modules
path = sys.path + ["biblio", "consultations", "etatbase", "etiquettes", 
                   "jugement", "publications", "retourcolis", "saisiebord", 
                   "verifications", "vuetable"]
 
# options d'inclusion/exclusion des modules
includes = ["sip"]
excludes = []
packages = []
 
# copier les fichiers et/ou répertoires et leur contenu
includefiles = [("aide", "aide")]
if sys.platform == "linux2":
    includefiles += [(r"/usr/lib/qt4/plugins/sqldrivers","sqldrivers")]
elif sys.platform == "win32":
    includefiles += [(r"C:\Python27\Lib\site-packages\PyQt4\plugins\sqldrivers","sqldrivers")]
else:
    pass
 
# inclusion éventuelle de bibliothèques supplémentaires
binpathincludes = []
if sys.platform == "linux2":
    # pour que les bibliothèques de /usr/lib soient copiées aussi
    binpathincludes += ["/usr/lib"]
 
# construction du dictionnaire des options
options = {"path": path,
           "includes": includes,
           "excludes": excludes,
           "packages": packages,
           "include_files": includefiles,
           "bin_path_includes": binpathincludes
           }
 
#############################################################################
# préparation des cibles
base = None
if sys.platform == "win32":
    base = "Win32GUI"
 
cible_1 = Executable(
    script = "concoursphotos.pyw",
    base = base,
    compress = True,
    icon = None,
    )
 
#############################################################################
# création du setup
setup(
    name = "concoursphotos",
    version = "1",
    description = "Traitement de concours photo sous Windows et Linux",
    author = "Tyrtamos",
    options = {"build_exe": options},
    executables = [cible_1]
    )

Une fois le setup.py écrit et mis dans la racine du répertoire du programme à traiter (/chemin par exemple) :

  • on fait venir une console et on se place dans ce répertoire (cd /chemin) ;
  • on fait alors python setup.py build ;
  • une longue liste de messages indique ce que cx_Freeze fait. Il faut seulement vérifier qu'il n'y a pas d'erreur. Sinon, il faut modifier le setup.py jusqu'à ce que les erreurs aient disparu ;
  • dans le répertoire dans lequel on est (/chemin), il vient le sous-répertoire build\exe.linux-i686-2.6 dans lequel il y a la version “standalone” pouvant être diffusée. Vous pouvez, bien entendu, la proposer sous forme d'archive en un seul fichier ;
  • il ne reste plus qu'à l'essayer sur un Linux sans Python ni PyQt4. On le copie donc et on peut le lancer. Dans un premier temps, il faut le lancer à partir d'une console pour avoir tous les éventuels messages d'erreur ;
  • si c'est OK, on peut alors le lancer directement avec une icône (à créer) du bureau, voire l'intégrer dans le menu des applications.

V. Remerciements

Merci à dourouc05 et à ClaudeLELOUP pour leur relecture !