Ein wxPython-Programm mit cx_Freeze erstellen

Gute Links und Tutorials könnt ihr hier posten.
Antworten
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5555
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Oberhofen im Inntal (Tirol)
Kontaktdaten:

Ein wxPython-Programm mit cx_Freeze erstellen
(unter Windows 2000)

Mit cx_Freeze kann man Python-Programme auf Computern laufen lassen auf denen kein Python installiert ist. Und das auch noch unter Windows und Linux. Natürlich muss man das Programmpaket für Linux unter Linux erstellen und das für Windows unter Windows. Aber man hat zumindest schon mal ein einziges Werkzeug für beide Betriebssysteme.

http://starship.python.net/crew/atuining/cx_Freeze/

Ziel dieser Anleitung ist es, ein Python-Program dessen GUI mit wxPython erstellt wurde, für einen Computer, auf dem kein Python und kein wxPython installiert wurde, ausführbar zu machen. So sollte dann unser Ergebnis aussehen:

Bild

Installieren von cx_Freeze

In der Anleitung steht, dass man das Binärpaket einfach in einen Ordner entpacken soll. Ich habe mich dafür entschieden, cx_Freeze unterhalb des Python-Ordners (bei mir C:\Python24) zu entpacken. Wichtig ist, dass beim Entpacken die Ordnerstruktur erhalten bleibt. cx_Freeze liegt jetzt im Ordner ``C:\Python24\cx_Freeze-3.0.2``.

Noch schnell die Versionsnummer entfernen: ``C:\Python24\cx_Freeze``

Die Ordnerstruktur sieht jetzt so aus:

Code: Alles auswählen

C:\
  Python24
    cx_Freeze
      bases
      initscripts
Jetzt muss der cx_Freeze-Ordner noch zum Pfad hinzugefügt werden. Das geht über die Systemsteuerung:

Start -> Einstellungen -> Systemsteuerung -> System ->
Erweitert -> Umgebungsvariablen -> Benutzervariablen


Dazu sucht man in der Liste den Namen "path", klickt auf Bearbeiten und hängt an den vorhandenen Pfad ``;C:\Python24\cx_Freeze`` an. Man beachte den Strichpunkt um den Pfad von den anderen Pfaden zu trennen.

Wenn das erledigt ist, kann man schon mal testen, ob cx_Freeze funktioniert. Einfach in die DOS-Eingabeaufforderung wechseln (Start -> Ausführen -> cmd), ``FreezePython`` eingeben und mit ENTER bestätigen. cx_Freeze sollte sich jetzt zumindest mit einer Benutzerinformation melden.


Das Beispiel

Ich habe mir dafür ein kleines Beispiel ausgedacht, das aus zwei Python-Modulen besteht. Das eine Modul liest die installierten Python-Versionen aus der Windows-Registry aus. Das andere Modul zeigt den oder die ausgelesenen Pfade in einem Fenster an.

Beide Module kommen in einen eigens dafür erstellten Projektordner. Bei mir ist das der Ordner ``C:\pythonbeispiel``.

C:\pythonbeispiel\get_pythonversions.py

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: iso-8859-1 -*-

import _winreg as winreg


#----------------------------------------------------------------------
def get_python_versions():
    """
    Gibt alle installierten Python-Versionen unter Windows zurück.

    Dazu werden die Unterschlüssel des Registry-Schlüssels
    "HKEY_LOCAL_MACHINE\SOFTWARE\Python\PythonCore\"
    ausgelesen.
    """

    # Registry-Key öffnen
    try:
        key = winreg.OpenKey(
            winreg.HKEY_LOCAL_MACHINE,
            "SOFTWARE\\Python\\PythonCore"
        )
    except WindowsError:
        return None

    try:
        # Unterschlüssel durchlaufen
        index = 0
        versions = []
        while True:
            try:
                keyvalue = winreg.EnumKey(key, index)
                versions.append(keyvalue)
                index += 1
            except WindowsError:
                break
    finally:
        # Key schließen
        winreg.CloseKey(key)

    # Zurück geben
    return versions


def get_python_path(version = "2.4"):
    """
    Gibt den Pfad zur übergebenen Pythonversion zurück.

    Dazu wird der Registry-Pfad
    "HKEY_LOCAL_MACHINE\SOFTWARE\Python\PythonCore\<version>\InstallPath\"
    ausgelesen.
    """

    # Registry-Key öffnen
    try:
        key = winreg.OpenKey(
            winreg.HKEY_LOCAL_MACHINE,
            "SOFTWARE\\Python\\PythonCore\\%s\\InstallPath" % version
        )
    except WindowsError:
        return None

    try:
        try:
            # Pfad zur Python-Installation auslesen
            path = winreg.QueryValueEx(key, "")[0]
        except WindowsError:
            path = ""
    finally:
        # Schlüssel schließen
        winreg.CloseKey(key)

    # Zurück geben
    return path


#----------------------------------------------------------------------
if __name__ == "__main__":

    versions = get_python_versions()
    for version in versions:
        print "Python", version, "->", get_python_path(version)
C:\pythonbeispiel\get_pythonversions_gui.py

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: iso-8859-1 -*-

import wx
import get_pythonversions
import sys


#----------------------------------------------------------------------
class MyFrame(wx.Frame):

    #----------------------------------------------------------------------
    def __init__(
        self, parent = None, id = -1, title = "Pythonversionen",
        size = wx.Size(400, 200)
    ):

        wx.Frame.__init__(self, parent, id, title, size = size)

        # Menüleiste erstellen
        menubar = wx.MenuBar()
        self.SetMenuBar(menubar)

        # Datei-Menü
        mnu_file = wx.Menu()
        menubar.Append(mnu_file, u"&Datei")

        mnu_f_close = mnu_file.Append(
            id = -1, text = u"Sch&ließen", help = u"Schließt dieses Fenster."
        )
        self.Bind(wx.EVT_MENU, self.close_frame, mnu_f_close)

        # Panel erstellen und Schrift zuweisen
        panel = wx.Panel(self)
        font = wx.Font(
            pointSize = 10,
            family = wx.FONTFAMILY_SWISS,
            style = wx.FONTSTYLE_NORMAL,
            weight = wx.FONTWEIGHT_NORMAL,
        )
        panel.SetFont(font)

        # Sizer vorbereiten
        vbox1 = wx.BoxSizer(wx.VERTICAL)
        vbox2 = wx.BoxSizer(wx.VERTICAL)
        vbox1.Add(vbox2, 1, wx.EXPAND | wx.ALL, border = 2)
        panel.SetSizer(vbox1)

        # StaticText
        label = wx.StaticText(panel, -1, " Pythonversionen")
        vbox2.Add(label, flag = wx.ALL, border = 2)

        # ListCtrl
        listctrl = wx.ListCtrl(
            panel, -1, style = wx.LC_REPORT
        )
        vbox2.Add(listctrl, 1, wx.EXPAND | wx.ALL, border = 2)
        self.fill_listctrl(listctrl)

        # Statusleiste erstellen und zuweisen
        statusbar = wx.StatusBar(self)
        self.SetStatusBar(statusbar)

        # Ausrichten, zentrieren und anzeigen
        self.Center()
        self.Show()

    #----------------------------------------------------------------------
    def close_frame(self, event = None):
        """
        Schließt dieses Fenster
        """

        self.Close()

    #----------------------------------------------------------------------
    def fill_listctrl(self, listctrl):
        """
        Füllt das Listensteuerelement mit den Python-Versionen
        """

        # Spalten erstellen
        listctrl.InsertColumn(0, "Version")
        listctrl.InsertColumn(1, "Pfad")

        # Pythonversionen auslesen
        versions = get_pythonversions.get_python_versions()

        # Jede Pythonversion durchlaufen, den Pfad eruieren und eintragen
        if versions:
            for version in versions:
                path = get_pythonversions.get_python_path(version) or ""
                new_index = listctrl.InsertStringItem(sys.maxint, "Python " + version)
                listctrl.SetStringItem(new_index, 1, path)

            # Spaltenbreiten festlegen
            listctrl.SetColumnWidth(0, wx.LIST_AUTOSIZE)
            listctrl.SetColumnWidth(1, wx.LIST_AUTOSIZE)


#----------------------------------------------------------------------
class MyApp(wx.App):

    #----------------------------------------------------------------------
    def OnInit(self):

        # Frame erstellen
        frame = MyFrame()

        # Hier geht es erst weiter, wenn alle Fenster geschlossen wurden.
        self.MainLoop()

        return True


#----------------------------------------------------------------------
if __name__ == "__main__":
    app = MyApp(redirect = True)
Das Erstellen der Ausführbaren Datei

Ich erstelle mir meistens eine CMD-Datei (Stapelverarbeitungsdatei) in die ich alle Befehle schreibe, die zum Erstellen notwendig sind. So auch hier.

Die CMD-Datei kümmert sich um diese Dinge:

- Falls der Zielordner bereits existiert, wird er gelöscht.
- Der Zielordner wird erstellt.
- In den Zielordner wird die zusätzlich benötigte Datei "msvcr71.dll" opiert.
- FreezePython mit den erforderlichen Parametern starten.

C:\pythonbeispiel\make_exe_win.cmd

Code: Alles auswählen

REM  --------------------------------------------------
REM   Erstellt aus dem PYTHONFILE eine ausführbare EXE
REM  --------------------------------------------------

SET PROJECTDIR="C:\pythonbeispiel"
SET PYTHONFILE="get_pythonversions_gui.py"

CD /D %PROJECTDIR%

RD /S /Q dist
MD dist
MD dist\win

CP %SystemRoot%\system32\msvcr71.dll dist\win\

FreezePython.exe --install-dir=dist\win --base-name=Win32GUI.exe %PYTHONFILE%

PAUSE
Führt man diese Datei aus, wird alles, was zum Ausführen des Programmes *get_pythonversions_gui.py" benötigt wird in den Unterordner ``dist\win`` kopiert. Man muss diesen Ordner jetzt nur noch zum fremden Computer kopieren und die Datei *get_pythonversions_gui.exe* ausführen.

Üblicherweise lässt man so etwas von einem Setup-Programm wie zum Beispiel **InnoSetup** (http://www.jrsoftware.org/isinfo.php) erledigen. Dann kann man auch gleich einen Eintrag ins Startmenü erstellen lassen.


Einfaches Setup mit InnoSetup

Wenn ich schon dabei bin, dann kann ich auch noch schnell ein Setup dafür erstellen.

InnoSetup erhält man über die Website http://www.jrsoftware.org/isinfo.php.

Sobald man InnoSetup startet, fragt einen der Assistent nach den wichtigsten Informationen die InnoSetup braucht. Danach lässt man InnoSetup das Setup generieren.

Hier ein Beispiel, wie ich es eingestellt habe.

C:\pythonbeispiel\innosetup.iss
; Script generated by the Inno Setup Script Wizard.
; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES!

[Setup]
AppName=Get Python Versions
AppVerName=Get Python Versions 0.1
AppPublisher=Gerolds Penz
DefaultDirName={pf}\get_python_versions
DefaultGroupName=Get Python Versions
AllowNoIcons=yes
OutputDir=C:\pythonbeispiel\setup\win
OutputBaseFilename=get_python_versions_setup
Compression=lzma
SolidCompression=yes

[Languages]
Name: "english"; MessagesFile: "compiler:Default.isl"
Name: "german"; MessagesFile: "compiler:Languages\German.isl"

[Tasks]
Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked
Name: "quicklaunchicon"; Description: "{cm:CreateQuickLaunchIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked

[Files]
Source: "C:\pythonbeispiel\dist\win\get_pythonversions_gui.exe"; DestDir: "{app}"; Flags: ignoreversion
Source: "C:\pythonbeispiel\dist\win\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs
; NOTE: Don't use "Flags: ignoreversion" on any shared system files

[Icons]
Name: "{group}\Get Python Versions"; Filename: "{app}\get_pythonversions_gui.exe"
Name: "{group}\{cm:UninstallProgram,Get Python Versions}"; Filename: "{uninstallexe}"
Name: "{userdesktop}\Get Python Versions"; Filename: "{app}\get_pythonversions_gui.exe"; Tasks: desktopicon
Name: "{userappdata}\Microsoft\Internet Explorer\Quick Launch\Get Python Versions"; Filename: "{app}\get_pythonversions_gui.exe"; Tasks: quicklaunchicon

[Run]
Filename: "{app}\get_pythonversions_gui.exe"; Description: "{cm:LaunchProgram,Get Python Versions}"; Flags: nowait postinstall skipifsilent
Keine Angst, diese Informationen sammelt alle der Assistent. Man muss bei so kleinen Programmen selten etwas an den Einstellungen verändern.

So, dass war´s fürs Erste. Viel Spaß beim Nachmachen. :D

lg
Gerold
:-)

Edit: sys.maxint hinzugefügt
Zuletzt geändert von gerold am Mittwoch 18. Februar 2009, 14:05, insgesamt 3-mal geändert.
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
JR
User
Beiträge: 286
Registriert: Montag 20. Februar 2006, 16:43
Wohnort: Berlin

Hallo Gerold!

Deine Anleitung ist super!

Ich arbeite permanent mit wxPython und lasse meine Skripte mit FreezePython kompilieren.
Weißt du, weshalb meine Anwendung, wenn ich die Skripte ausführe im Xp-Stil "gelayoutet" wird, aber nach dem Kompilieren mit FreezePython nicht mehr genauso ausschaut? Das Design ähnelt nach FreezePython eher dem Aussehen von Windows 2000 Oberflächen.

Ich finde die abgerundeten Objekte und Hintergrundfarben beispielsweise von Notebooks einfach nach dem Design von Windows XP schöner.

Viele Grüße aus Berlin
Jamil
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5555
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Oberhofen im Inntal (Tirol)
Kontaktdaten:

JR hat geschrieben:Das Design ähnelt nach FreezePython eher dem Aussehen von Windows 2000 Oberflächen.
Hi Jamil!

Mit "py2exe" haben sie das gleiche Problem. Vielleicht hilft dir dieser Link weiter: http://www.python-forum.de/post-46657.html#46657

lg
Gerold
:-)
Zuletzt geändert von gerold am Montag 23. Oktober 2006, 11:48, insgesamt 1-mal geändert.
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
JR
User
Beiträge: 286
Registriert: Montag 20. Februar 2006, 16:43
Wohnort: Berlin

Superb!!

Also danke Gerold, ging sofort.

Die Lösung:

Erstellt die *.exe-Datei z.B. hallo.exe und legt ins selbe Verzeichnis eine Datei namens hallo.exe.manifest mit folgendem Inhalt:

Code: Alles auswählen

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>  
<assembly  
   xmlns="urn:schemas-microsoft-com:asm.v1"  
   manifestVersion="1.0"> 
 <dependency> 
  <dependentAssembly> 
 <assemblyIdentity 
   type="win32" 
   name="Microsoft.Windows.Common-Controls" 
   version="6.0.0.0" 
   publicKeyToken="6595b64144ccf1df" 
   language="*" 
   processorArchitecture="x86" 
 /> 
  </dependentAssembly> 
 </dependency> 
</assembly>
abaum
User
Beiträge: 18
Registriert: Freitag 12. Januar 2007, 21:53
Kontaktdaten:

Ist es auch möglich, der erzeugten *.exe (im vorigen Beispiel hallo.exe) ein eigenes Icon zuzuweisen?
JR
User
Beiträge: 286
Registriert: Montag 20. Februar 2006, 16:43
Wohnort: Berlin

HI!

Ja das geht auf jedem Fall.

Bisher habe ich das entweder mit Resource Hacker http://www.zdnet.de/downloads/prg/k/y/de0DKY-wc.html gemacht.

Oder:
Wenn du mit NSIS (Nullsoft Install Studio) einen Installer erstellst, kannst du im Script eine *.ico-Datei für die *.exe angeben.

Grüße
Jamil[/url]
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Hab den Sourcecode (ohne den wx Teil) in's Wiki übertragen: [wiki]Python Installation abfragen (Win)[/wiki]

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5555
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Oberhofen im Inntal (Tirol)
Kontaktdaten:

Hallo!

So wie es aussieht, funktioniert cx_freeze auch mit Tkinter-Programmen.

Siehe:
- http://www.python-forum.de/post-80525.html#80525
- http://sebsauvage.net/python/snyppets/#tkinter_cxfreeze

mfg
Gerold
:-)
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
Antworten