[modernisation de la page le 9/8/2014]
Faire un joli programme qui marche est une (bonne) chose, le diffuser en est une autre.
Il faut en effet que le PC du nouvel utilisateur dispose déjà des programmes nécessaires de Python, de PyQt4 (, etc…), avec les bonnes versions, sans lesquels votre programme ne fonctionnera pas (et vous porterez le chapeau…).
Alors, il est important de pouvoir diffuser votre programme, accompagné de toutes les bibliothèques qui lui permettront de fonctionner correctement, même si l'utilisateur n'a rien installé du tout! C'est l'objet du présent tuto.
On va utiliser le logiciel cx_freeze pour cela (http://cx-freeze.sourceforge.net/). Il a l'avantage d'être multiplateforme (Windows-Linux). Il faudra bien sûr l'utiliser sous Windows pour avoir la version Windows et sous Linux pour avoir la version Linux, car les bibliothèques ne sont pas les mêmes.
On va étudier ici l'utilisation de cx_freeze sur Windows (XP, Vista, 7 ou 8).
Pour qu'un programme PyQt4 fonctionne sous Windows, il faut peu de choses:
Pour Windows, ces 2 logiciels existent sous forme de binaires, et sont donc très faciles à installer.
Et c'est tout!
cx_freeze est aussi présenté sous forme de binaire facile à installer.
Téléchargement ici: http://cx-freeze.sourceforge.net/ (choisir de préférence une verson >=4.2.1).
Voilà, vous avez le cx_freeze le plus récent!
Il existe plusieurs façons d'utiliser cx_freeze, et j'ai choisi l'utilisation de setup.py (même méthode que pour py2exe sous Windows). L'avantage est que, si on 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:
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 Windows dans lequel il n'y a ni Python, ni PyQt4!
Voilà un exemple.
On a un projet dans un répertoire. Ce répertoire contiendra:
Les 2 programmes programme1.pyw sont identiques, sauf le nom de leur fichier et le titre de leur fenêtre. Cela permettra de tester la construction de 2 fenêtres différentes avec un seul setup.
Voilà le code du 1er programme programme1.pyw:
#!/usr/bin/python # -*- coding: utf-8 -*- # Python 3 import sys from PyQt4 import QtCore, QtGui ############################################################################# class Fenetre(QtGui.QWidget): def __init__(self, parent=None): super(Fenetre, self).__init__(parent) self.resize(400, 300) self.setWindowTitle("Programme 1") self.bouton = QtGui.QPushButton(u"bouton", self) self.bouton.clicked.connect(self.clictest) posit = QtGui.QGridLayout() posit.addWidget(self.bouton, 0, 0) self.setLayout(posit) def clictest(self): QtGui.QMessageBox.information(self, u"Pour information", u"Le bouton a été cliqué") def closeEvent(self, event): reponse = QtGui.QMessageBox.question(self, "Fermeture de la fenêtre", "Etes-vous sûr(e) de vouloir quitter?", QtGui.QMessageBox.Yes, QtGui.QMessageBox.No) if reponse == QtGui.QMessageBox.Yes: event.accept() else: event.ignore() ############################################################################# if __name__ == "__main__": app = QtGui.QApplication(sys.argv) # mettre la même icone pour toutes les fenêtres de l'application #app.setWindowIcon(QtGui.QIcon('icone.png')) # mettre le style des fenêtres style = 'Plastique' app.setStyle(QtGui.QStyleFactory.create(style)) app.setPalette(QtGui.QApplication.style().standardPalette()) # pour assurer la traduction automatique du conversationnel à la locale locale = QtCore.QLocale.system().name() translator = QtCore.QTranslator () if getattr(sys, 'frozen', False): # exécution de la version exécutable avec cx_freeze (ou équivalent): #=>les fichiers de traduction doivent se trouver dans "translations" reptrad = "translations" else: # exécution par l'interpréteur normal reptrad = QtCore.QLibraryInfo.location(QtCore.QLibraryInfo.TranslationsPath) translator.load("qt_" + locale, reptrad) app.installTranslator(translator) # fen = Fenetre() fen.setAttribute(QtCore.Qt.WA_DeleteOnClose) fen.show() sys.exit(app.exec_())
le fichier cx_freeze.bat ne servira qu“à lancer le traitement par cx_freeze. On prendra l'explorateur Windows, et on naviguera jusqu'au répertoire du projet, et on double-cliquera sur ce nom. Une console s'affichera avec toutes les lignes crées par cx_freeze: on vérifiera l'absence d'erreur, et sinon, on saura modifier le setup pour corriger. Voilà le contenu du cx_freeze.bat:
python setup.py build pause
Le setup.py contient les instructions destinées au traitement par cx_freeze.
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, …) mais je n'ai pas essayé.
#!/usr/bin/python # -*- coding: utf-8 -*- # Python 3 """ Pas d'accent dans le setup.py, ni dans la description, ni dans les commentaires Icone sous Windows: il faut: => un xxx.ico pour integration dans le exe, avec "icon=xxx.ico" => un xxx.png pour integration avec PyQt4 + demander la copie avec includefiles. """ import sys, os from cx_Freeze import setup, Executable ############################################################################# # preparation des options # chemins de recherche des modules path = sys.path # options d'inclusion/exclusion des modules includes = [] excludes = [] packages = [] # copier les fichiers et/ou repertoires et leur contenu: includefiles = [] # recopier l'icone.png de la fenetre ici if sys.platform == "linux2": includefiles += [(r"/usr/lib/qt4/plugins","plugins")] includefiles += [(r"/usr/share/qt4/translations","translations")] elif sys.platform == "win32": includefiles += [(r"E:\Python34\Lib\site-packages\PyQt4\plugins","plugins")] includefiles += [(r"E:\Python34\Lib\site-packages\PyQt4\translations","translations")] else: pass binpathincludes = [] if sys.platform == "linux2": # Linux: pour que les bibliotheques de /usr/lib soient copiees 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, "create_shared_zip": False, "include_in_shared_zip": False, "compressed": False } # pour inclure sous Windows les dll system necessaires if sys.platform == "win32": options["include_msvcr"] = True ############################################################################# # preparation des cibles base = None if sys.platform == "win32": # plateforme Windows base = "Win32GUI" # pour les programmes graphiques #base = "Console" # pour les programmes en console icone = None if sys.platform == "win32": icone = None # mettre ici l'icone.ico pour integration dans l'exe cible_1 = Executable( script = "programme1.pyw", base = base, compress = False, copyDependentFiles = True, appendScriptToExe = True, appendScriptToLibrary = False, icon = icone ) cible_2 = Executable( script = "programme2.pyw", base = base, compress = False, copyDependentFiles = True, appendScriptToExe = True, appendScriptToLibrary = False, icon = icone ) ############################################################################# # creation du setup setup( name = "Programme", version = "1.00", description = "essai", author = "Tyrtamos", options = {"build_exe": options}, executables = [cible_1, cible_2] )
Une fois le setup.py écrit et mis dans la racine du répertoire du programme à traiter, on double-clique sur cx_freeze.bat. Une console s'affiche, le traitement se déroule et les programmes ”.exe“ se construisent dans le sous-répertoire “build\exe.win32-3.4”. Nous auront ici programme1.exe et programme2.exe, accompagnés de toutes les bibliothèques nécessaires.
Dans tous les cas, il faudra lire attentivement la longue liste du traitement dans la console pour vérifier qu'il n'y a pas d'erreur. Sinon, il faudra modifier le setup.py jusqu'à ce que les erreurs aient disparu.
Il ne reste plus qu'à essayer de lancer les programmes ”.exe“. Ce serait bien de le faire sur un autre PC qui n'a pas Python. Enfin, dans un premier temps, il faut lancer les programmes ”.exe“ à partir de la 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.
On peut aussi aller plus loin en encapsulant les fichiers obtenus dans un programme d'installation comme “innosetup” (http://www.jrsoftware.org/isinfo.php). Vous obtiendrez alors un programme diffusé par un seul fichier qui s'installera et se désinstallera comme n'importe quel logiciel Windows: les utilisateurs se sauront même pas que c'est du Python…
Amusez-vous bien!