Programm fertig, wie bekomme ich jetzt ein Programmpaket?

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
Hellstorm
User
Beiträge: 231
Registriert: Samstag 22. Juni 2013, 15:01

Hallo,

ich habe jetzt mein recht einfaches Programm in PyQt fertig und würde das jetzt auch gerne produktiv einsetzen. Manuelles starten von der Kommandozeile funktioniert auch, aber auf Dauer scheint mir diese Art des Startens etwas unbequem. Ich würde das Programm jetzt gerne unter Linux (und nachher auch unter Windows) irgendwie vernünftig strukturieren und bequem mit einer .desktop-Datei ausführbar machen, so wie ich das von anderen Programmen gewöhnt bin.

Im Moment sieht meine Ordnerstruktur folgendermaßen aus:

Code: Alles auswählen

~/programm/
	  gui/window1.ui
	  gui/window2.ui
	  icons/programm_icon.svg
	  programm.py
	  modul.py
	  ...weitere Dateien
Wenn ich das Programm jetzt aus diesem Ordner heraus mit ./programm.py aufrufe, klappt es auch ohne Probleme (Die Pfade sind ja alle angepasst). Aber wenn ich jetzt z.B. aus ~ direkt das Programm mit programm/programm.py aufrufe, dann meckert er, dass er gui/window1.ui nicht finden kann (Wird wohl daran liegen, dass er das dann in ~/gui/window1.ui anstatt ~/programm/window1.ui sucht).


Wie genau sollte ich das jetzt machen, dass ich praktisch ein „Komplettpaket“ habe, in dem die Pfade in sich alle stimmig sind? Ich habe bei anderen Python-Programmen gesehen, dass es eine programm/__init__.py gibt und eine ausführbare Datei außerhalb dieses Ordners erstellt wird (z.B. in ~/bin), die dann einfach programm importiert. Das scheint mir auch sinnvoll, allerdings funktioniert das in meinem Falle nicht so genau. Ich sehe dort auch das Problem, dass diese Programme alle systemweit direkt unter /usr/lib/python/site-packages installiert werden (und so systemweit als Python-Modul importieren kann), was ich bei mir (noch) nicht machen möchte (ich will mir da erst ein eigenes Paket für den Paketmanager erstellen, damit ich das sauber installieren, aktualisieren und deinstallieren kann).

In einem nächsten Schritt würde ich mir natürlich eine setup.py erstellen, aber zuerst muss ich glaube ich die Programmstruktur manuell richtig erstellen, oder?

Danke!
Sirius3
User
Beiträge: 18260
Registriert: Sonntag 21. Oktober 2012, 17:20

@Hellstorm: Du kannst Dateien relativ zur Programmdatei finden, indem Du die spezielle Modul-Variable __file__ benutzt, die den aktuelle Dateinamen enthält:

Code: Alles auswählen

import os
BASE_PATH = os.path.abspath(os.path.dirname(__file__))
GUI_WINDOW1 = os.path.join(BASE_PATH, 'gui', 'window1.ui')
Hellstorm
User
Beiträge: 231
Registriert: Samstag 22. Juni 2013, 15:01

Das heißt, ich würde für alle Nicht-Python-Dateien, die ich irgendwo im Programm importiere, am besten erst einmal so eine Auflistung machen?

Ich probier das mal aus, danke :)

Edit: Tatsächlich, jetzt funktioniert es. Danke!
Jetzt muss ich mich nur noch um die richtige Ordnerstrukturierung kümmern.
Benutzeravatar
MagBen
User
Beiträge: 799
Registriert: Freitag 6. Juni 2014, 05:56
Wohnort: Bremen
Kontaktdaten:

Wenn Dein Programm auf einem (fast) beliebigen Windows-Rechner laufen soll, ohne dass eine spezielle Python-Installation dafür erforderlich sein muss, dann kanst Du das mit py2exe erreichen.
a fool with a tool is still a fool, www.magben.de, YouTube
Hellstorm
User
Beiträge: 231
Registriert: Samstag 22. Juni 2013, 15:01

Danke für den Tipp! Das habe ich sogar schon mal versucht, aber dann wurde der Ordner 90 MB groß... Schien mir etwas viel bei eigentlich unter 20 KiB Programmcode. Aber gut, ist ja klar, da die ganzen Qt-Bibliotheken eingebunden werden mussten. Aber vielleicht kann man das noch optimieren. Als nächster Schritt steht hier aber zuerst einmal ein Arch-Paket an, so dass ich das Programm bequem installieren und aktualisieren kann. Danach kümmer ich mich um Windows.

Äh, da hab ich direkt eine Frage zum Programmicon unter Linux: Ich bräuchte das Icon ja einmal für die .desktop-Datei und einmal intern für das Anwendungsfenster. Muss ich das Icon dann doppelt haben oder wie kann man das am besten lösen?
Hellstorm
User
Beiträge: 231
Registriert: Samstag 22. Juni 2013, 15:01

Ich habe jetzt etwas weitergespielt und in dem Programmordner eine __init__.py erstellt. In der __init__.py habe ich jetzt eine einzige Funktion namens run(), in der folgendes steht:

Code: Alles auswählen

from meintollesprogramm import gui
gui.main()
Diesen Programmordner würde ich dann anschließend in den site-packages-Ordner kopieren, oder?
Und in /usr/local/bin würde ich eine Datei namens programm (ohne .py) erstellen, die folgendes macht:
#!/usr/bin/env python3
import meintollesprogramm
meintollesprogramm.run()
Stimmt das so? Aber irgendwie zeigt er mir dann kein Icon an, wieso ist das denn so? Und ich kann im Ordner meintollesprogramm jetzt das Programm nicht mehr manuell starten.

Irgendwie verstehe ich immer noch nicht ganz, wie ich denn ein vernünftiges kleines GUI-Programm für Linux entwerfen soll :D All die Programme die ich bereits im Paketmanager finde sind viel zu komplex um sich daran ein Vorbild zu nehmen... :(
Benutzeravatar
snafu
User
Beiträge: 6854
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

@Hellstorm: Also eigentlich erstellt man eine ``setup.py``, die z.B. auf Basis von dem mit Python ausgelieferten ``distutils``-Paket die nötigen Python-Dateien kompiliert und an die passende Stelle kopiert. Schau dir evtl mal ein paar ``setup.py``-Dateien diverser Projekte an. Zu ``distutils`` gibt es auch ein Tutorial in der offiziellen Python-Doku.
Benutzeravatar
snafu
User
Beiträge: 6854
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Hellstorm hat geschrieben:Und in /usr/local/bin würde ich eine Datei namens programm (ohne .py) erstellen, die folgendes macht:
#!/usr/bin/env python3
import meintollesprogramm
meintollesprogramm.run()
Stimmt das so?
Ich hätte den Code zum Aufruf des eigentlichen Programms wohl eher so geschrieben:

Code: Alles auswählen

import meintollesprogramm

if __name__ == '__main__':
    meintollesprogramm.main()
Und diese Datei kannst du dann in einem ``bin``-Unterverzeichnis innerhalb deines Projektes ablegen. Bei ``distutils`` kann man angeben, welches die Skripte (also zum Start von der Kommandozeile gedachte Dateien) sind und die werden dann in den Ordner für ausführbare Dateien des jeweiligen Betriebssystems kopiert, wenn man das Projekt installiert.
Hellstorm
User
Beiträge: 231
Registriert: Samstag 22. Juni 2013, 15:01

Hallo,

danke für deine Antwort. Ich habe mir in der Zwischenzeit auch etwas durchgelesen, unter anderem zu setuptools. Das erstellt ja diese ausführbare Datei automatisch, so wie ich das gesehen habe.

Dort habe ich auch gelesen, dass man diesen Trick mit __file__ eher nicht machen sollte, sondern das Problem mit Hilfe von pkg_ressources lösen sollte. Wenn man das so macht, kann man wohl auch Egg-Dateien erstellen. Oder ist davon abzuraten?
Das mit dem __file__ sieht mir nämlich echt ein wenig komisch aus und mich wundert, dass es da keine „sauberere“ Lösung für gibt.
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Von Egg-Dateien ist abzuraten, ja. pkg_resources kann man sich überlegen, wenn man sich setuptools einhandeln will.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Hellstorm
User
Beiträge: 231
Registriert: Samstag 22. Juni 2013, 15:01

Naja, wenn es ohne geht, wäre natürlich ohne auch gut. Aber mir geht es vor allem um Erstellung der ausführbaren Datei.
Naja, ich informier mich mal noch mal etwas weiter.
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

Wurde gerade hier auf der EuroPython in einem Lightning Talk vorgestellt: Python Packaging User Guide

mutetella
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
Hellstorm
User
Beiträge: 231
Registriert: Samstag 22. Juni 2013, 15:01

Hallo,

so wie ich das jetzt verstanden habe, erstelle ich also trotzdem eine setup.py und schreibe die dort ganz normal wie bei allen setuptools-Tutorials, oder? Praktisch wie hier. Anschließend würde ich das aber nicht mit python setup.py installieren, sondern mit „pip install .“ oder mir vorher mit „python setup.py sdist“ ein sdist erstellen und das anschließend mit „pip install blabla.tar.gz“ installieren können, oder?
Aber da bekomme ich trotzdem noch einen *.egg-info-Ordner, nur mit dem Unterschied, dass das Programm selber unter site-packages nicht mehr in einem Unterordner ist.

Stimmt meine setup.py eigentlich soweit als Minimalbeispiel? Ich habe dort allerdings das Problem, dass ich dort PyQt5 nicht als requirement nennen kann, da er es bei PyPi nicht findet. Mir scheint es als ob man PyQt eher durch den Paketmanager der Distribution installieren muss (scheint mir auch logisch, braucht ja Qt). Aber ohne den Eintrag würde das Programm z.B. nicht in einer virtualenv laufen. Oder gehen GUI-Programme gar nicht in Qt?

Code: Alles auswählen

from setuptools import setup, find_packages

setup(
    name="Uploader",
    version="0.1",
    packages=["uploader"],
    install_requires=['paramiko',
        ],
    package_data={
        'uploader': ['gui/upload_summary.ui',
                     'gui/uploader_gui.ui',
                     'icons/uploader_icon.svg'],
        },
    entry_points={
        'console_scripts': [
            'uploader_gui=uploader:run_gui',
            ],
        },
)
Also das Programm läuft soweit dann zumindest in einer virtuellen Umgebung, wenn ich die mit --system-site-packages erstelle. Gibt es ansonsten noch irgendetwas zu beachten (außer das Feintuning der setup.py)?
Hellstorm
User
Beiträge: 231
Registriert: Samstag 22. Juni 2013, 15:01

Wie kann ich denn in die sdist beispielsweise eine .desktop-Datei hinzufügen? Ich habe versucht das mit data_files zu erreichen, es in package_data einzutragen, irgendwie funktioniert nichts. Setuptools kopiert automatisch beispielsweise die README.rst in die .tar.gz-Datei, aber ich kriege es beim besten Willen nicht hin, dort eine .desktop-Datei hineinzukopieren.

Oder soll ich mein Programm „manuell“ verpacken? Ich kann mir gar nicht vorstellen, dass setuptools so dermaßen ungeeignet für GUI-Anwendungen ist :K
BlackJack

@Hellstorm: Mal davon abgesehen das es kein Problem sein sollte beliebige Dateien mit in das Archiv packen zu lassen, ist `setup.py` nicht der Weg den die meisten Endbenutzer kennen/gehen um *Programme* zu installieren. Da gehen Endnutzer davon aus, dass das über den Mechanismus geht, mit dem sie auch all ihre anderen Programme installieren. Also Installer (EXE oder MSI) unter Windows, passende Pakete für die Distribution bei Linux (DEB, RPM, …), oder … keine Ahnung was dass bei MacOS ist.
Hellstorm
User
Beiträge: 231
Registriert: Samstag 22. Juni 2013, 15:01

Naja, ich entwickel das gerade für Arch. Dort wird in der PKGBUILD aber im Grunde auch nur die setup.py aufgerufen. Ich habe dort jetzt manuell die desktop-Datei eingefügt, das funktioniert auch. Dass ich dann für Windows beispielsweise den Nullsoft-Installer benutzen muss, ist mir auch klar.

Wofür ich das nur gefragt habe ist folgender Grund: Normalerweise wird ja auch einfach der Quelltext weiterverteilt (bzw. ich möchte den bei meinem simplen Programm einfach für mich selber aufbewahren). Und dort soll dann eben das gesamte Programm drin enthalten sein, und dazu gehört dann auch diese desktop-Datei. Wenn ich ein anderes Programm im Internet herunterladen, dann ist die dort schon enthalten. Ich frage mich nun nur, ob das andere Programme eben manuell packen oder auch diese sdist-Methode nutzen. Wenn nicht, dann frage ich mich natürlich, wofür die denn gut sein soll. Nur für Python-Bibliotheken, die keine anderen Dateien brauchen?
BlackJack

@Hellstorm: Für Dich selbst hast Du doch hoffentlich das Projekt in einer Versionsverwaltung. `sdist` sehe ich eigentlich nur als ”Ziel” für den Python Package Index. Wenn ich den Quelltext verteilen möchte, dann hoste ich das Repository von der Versionsverwaltung irgendwo, bzw. erstelle *davon* ein Archiv mit dem Quelltext.
Hellstorm
User
Beiträge: 231
Registriert: Samstag 22. Juni 2013, 15:01

Ja ich benutze Git. Habe jetzt gesehen dass man mit mit git archive -o datei.tar.gz HEAD auch einfach einen Tarball machen kann. Dann mach ich das jetzt so :)
Antworten