Pyinstaller-Ausgabe komplett an ein tkinter-Textfeld schicken

Fragen zu Tkinter.
DMD-OL
User
Beiträge: 327
Registriert: Samstag 26. Dezember 2015, 16:21

In der Dokumentation von subprocess steht gar nichts von cwd.

Code: Alles auswählen

process = subprocess.Popen(['python', 'C:/Program Files/Python39/Scripts/pyinstaller.exe'],     # pyinstaller.exe
                           cwd='C:/Users/chris/Documents/PyCharm/Hot Run',                      # current working dir
                           stdout=subprocess.PIPE,
                           stderr=subprocess.PIPE,
                           universal_newlines=True)
stdout, stderr = process.communicate()
print(stdout, stderr)
process.kill()
damit erhalte ich:

Code: Alles auswählen

 usage: pyinstaller [-h] [-v] [-D] [-F] [--specpath DIR] [-n NAME]
                   [--add-data <SRC;DEST or SRC:DEST>]
                   [--add-binary <SRC;DEST or SRC:DEST>] [-p DIR]
                   [--hidden-import MODULENAME]
                   [--additional-hooks-dir HOOKSPATH]
                   [--runtime-hook RUNTIME_HOOKS] [--exclude-module EXCLUDES]
                   [--key KEY] [-d {all,imports,bootloader,noarchive}] [-s]
                   [--noupx] [--upx-exclude FILE] [-c] [-w]
                   [-i <FILE.ico or FILE.exe,ID or FILE.icns or "NONE">]
                   [--version-file FILE] [-m <FILE or XML>] [-r RESOURCE]
                   [--uac-admin] [--uac-uiaccess] [--win-private-assemblies]
                   [--win-no-prefer-redirects]
                   [--osx-bundle-identifier BUNDLE_IDENTIFIER]
                   [--runtime-tmpdir PATH] [--bootloader-ignore-signals]
                   [--distpath DIR] [--workpath WORKPATH] [-y]
                   [--upx-dir UPX_DIR] [-a] [--clean] [--log-level LEVEL]
                   scriptname [scriptname ...]
pyinstaller: error: the following arguments are required: scriptname


Process finished with exit code 0
Womit kann man denn jetzt die für pyinstaller fehlenden Parameter: 'main.py', '--onedir', '--onefile', '--windowed', .... übergeben?
Ist env dafür vorgesehen?
Sirius3
User
Beiträge: 18289
Registriert: Sonntag 21. Oktober 2012, 17:20

Ich weiß ja nicht in welcher Dokumentation gesucht hast? :roll:
https://docs.python.org/3/library/subpr ... onstructor

Exe-Dateien lassen sich wohl kaum mit dem Python-Interpreter ausführen. Und wie man Kommandozeilenparameter übergibt, sollte doch eigentlich klar sein??
DMD-OL
User
Beiträge: 327
Registriert: Samstag 26. Dezember 2015, 16:21

Ich liebe Gegenfragen :)
Nein. Muß ich die Parameter an subprocess.Popen oder kann ich stdout übergeben?
Das weiß ich eben NICHT.
Benutzeravatar
sparrow
User
Beiträge: 4540
Registriert: Freitag 17. April 2009, 10:28

Ich glaube, du bringst da Dinge grundlegende durcheinander.
Subprocess führt ein externes Programm aus. Wie würdest du das Programm denn ausführen, wenn du es nicht aus einem Python-Script ausführen würdest?
DMD-OL
User
Beiträge: 327
Registriert: Samstag 26. Dezember 2015, 16:21

Ich habs jetzt:

Code: Alles auswählen

process = subprocess.Popen(['C:/Program Files/Python39/Scripts/pyinstaller.exe',
                            'main.py',
                            '--onedir',
                            '--onefile',
                            '--windowed',
                            '--noconsole',
                            '--name=HOT RUNNER',
                            '--icon=C:/Users/chris/Documents/PyCharm/Hot Run/images/icon/minion.ico',
                            '--distpath=C:/Users/chris/Desktop/HOT RUNNER',
                            '--specpath=C:/Users/chris/Desktop/HOT RUNNER'
                            ],
                           cwd='C:/Users/chris/Desktop/HOT RUNNER',
                           stdout=subprocess.PIPE,
                           stderr=subprocess.PIPE,
                           universal_newlines=True)
stdout, stderr = process.communicate()
print(stdout, stderr)
process.kill()
Das läuft so wie es soll. Vorher kopiere ich alles erforderliche in den Ordner "C:/Users/chris/Desktop/HOT RUNNER".
Die executable wird dann in diesem Ordner erstellt.
DeaD_EyE schrieb:
Du könntest von deinem Programm aus den Pyinstaller mit Popen starten und stderr/stdout der PIPE zuweisen.
Dann warten bis der Prozess beendet ist und dann stdout und stderr lesen und die Daten in eine Variable übertragen (z.B. tkinter.StringVar).

Falls die Ausgabe gestreamt werden soll, wird es etwas komplizierter.
Ich möchte es jetzt gern versuchen, die Ausgabe noch zu streamen.
Hab aber natürlich noch keinen Plan wie...
Eine kleine Anregung der Herangehensweise bräuchte ich dann doch... :)
DMD-OL
User
Beiträge: 327
Registriert: Samstag 26. Dezember 2015, 16:21

VIELEN DANK AN EUCH.
Ich habe es jetzt komplett hinbekommen mit live-Ausgabe von stdout.
Ich benutze zwei while-Schleifen (poll() und stdout.readline()), die alles direkt ausspucken.
Danach beende ich mit kill() den Prozess.
:)
Sirius3
User
Beiträge: 18289
Registriert: Sonntag 21. Oktober 2012, 17:20

Hört sich nicht sehr gut an. stdout.readline blockiert, womit Deine ganze GUI einfriert. Ein Prozess beendet sich freiwillig, kill ist also falsch, dafür gibt es wait.
Benutzeravatar
__blackjack__
User
Beiträge: 14085
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Und falls man doch einen Prozess beenden will, würde man es auch erst einmal mit `terminate()` statt `kill()` machen, damit der Prozess die Möglichkeit bekommt sich selbst ordentlich zu beenden.
“Vir, intelligence has nothing to do with politics!” — Londo Mollari
DMD-OL
User
Beiträge: 327
Registriert: Samstag 26. Dezember 2015, 16:21

aktuell sieht es jetzt so aus:

Code: Alles auswählen

        
while process.poll() is None:
	line = process.stdout.readline()
            [...]
process.kill() 
Ich benutze .kill(), da der process per button gestartet wird. Und nach jedem durchlauf wird der gekillt, da man ja
dann wieder den button benutzt und damit wieder einen völlig neuen process beginnt (und beendet)
Is das scheiße?
Benutzeravatar
__blackjack__
User
Beiträge: 14085
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@DMD-OL: Das wäre aus genannten Gründen Scheisse — wenn der Prozess denn noch laufen würde. Wenn `poll()` einen Rückgabecode liefert, dann läuft der aber nicht mehr, denn sonst hätte er keinen Rückgabecode liefern können.

Statt `poll()` hätte man da auch einfach erst einmal eine ``for``-Schleife über `process.stdout` laufen lassen können. Und danach dann ein `process.wait()` um auf das Ende des Prozesses zu warten. Eventuell mit `timeout` und mit `after()` wiederholen, falls der Fall eintreten könnte, dass das externe Programm seine Standardausgabe schliesst, aber noch etwas länger irgend etwas macht bevor es sich beendet.
“Vir, intelligence has nothing to do with politics!” — Londo Mollari
DMD-OL
User
Beiträge: 327
Registriert: Samstag 26. Dezember 2015, 16:21

ok. Mit for-Schleife und wait verlier ich aber dann doch meine "live"-Übertragung, oder?
Hier wird alles sofort ins Textfeld geballert.
Benutzeravatar
__blackjack__
User
Beiträge: 14085
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@DMD-OL: Das geht weder mit ``while`` noch mit ``for`` weil beides die GUI blockiert. Das wir das jetzt schon wieder als Thema haben… 🙄
“Vir, intelligence has nothing to do with politics!” — Londo Mollari
Sirius3
User
Beiträge: 18289
Registriert: Sonntag 21. Oktober 2012, 17:20

Die "live"-Übertragung hast Du mit Deinem bisherigen Code auch nicht, jedenfalls nicht, wenn Du nicht noch viel mehr machst, als das, was Du bisher hier verraten hast.
Benutzeravatar
sparrow
User
Beiträge: 4540
Registriert: Freitag 17. April 2009, 10:28

Ganz nebenbei: Mich verwirrt der Grund des Programms. pyinstaller nutzt doch die spec-Datei um das Projekt zu konfigurieren. Warum dann eine Oberfläche, die nur einen Bruchteil der Oprionen fest verdrahtet als Aufrufargumente übergibt?
DMD-OL
User
Beiträge: 327
Registriert: Samstag 26. Dezember 2015, 16:21

Hi an alle.
Ich habe da nochmal ein kleines Problem.
In meinem Code möchte ich mir vorher ein Icon auswählen, das dann in die EXE-Datei übernommen wird.
DIE GROSSEN BUCHSTABEN VERWENDE ICH ZUR EINFACHEREN LESBARKEIT.
Das funktioniert auch alles, wenn ich aber nach der while-Schleife, die erstellte EXE in den Pfad TRG_EXE kopiere, wird
nicht das richtige Icon mitgenommen. Da stimmt wohl noch was mit der while-Schleife nicht?

Code: Alles auswählen

# SRC_ICN_FILE = "C:/Users/chris/Documents/PyCharm/Hot Run/images/icon/minion.ico"
SRC_ICN_FILE = "C:/Users/chris/Documents/PyCharm/Create EXE-File/images/icon.ico"
# SRC_ICN_FILE = "C:/Users/chris/Documents/PyCharm/Archiv/Tutorials/Space Invadors/images/icon/spaceship_32.ico"


TRG_FOLDER_PATH = "C:/Users/chris/Desktop/Space Invators" # Alles zur exe-Erstellung benötigte, wird hier her kopiert
TRG_BASE_NAME = "Space Invators"
TRG_ICN_FILE = "C:/Users/chris/Desktop/Space Invators/icon/icon.ico"  # Hierher wird das ausgewählte Icon kopiert

process = subprocess.Popen([spawn.find_executable('pyinstaller.exe'),
                            'main.py',
                            '--onefile',
                            '--windowed',
                            '--icon=' + TRG_ICN_FILE,
                            '--name=' + TRG_BASE_NAME,
                            ],
                           bufsize=1,
                           stdout=subprocess.PIPE,
                           stderr=subprocess.STDOUT,
                           cwd=TRG_FOLDER_PATH,  # Aktueller Arbeitsbereich ist der neue erstellte Ordner
                           universal_newlines=True)

while process.poll() is None:
    line = process.stdout.readline()
    if not line:
        break
    self.DISPLAY.insert(tk.END, line, 'red')
    self.DISPLAY.see(tk.END)
    self.DISPLAY.update_idletasks()

# Bis hier funktionierts. Das ausgewählte Icon wird jeweils richtig in die .exe eingebaut.
################################################################################################################

# Ab hier wird zwar die EXE richtig erstellt und kopiert, die EXE-Datei zeigt aber ein falsches Icon an (nicht das ausgewählte).
if process.returncode is None:
   print('Finished...')
   TRG_EXE = os.path.join(TRG_FOLDER_PATH, 'dist', TRG_BASE_NAME + '.exe')
    self.copy_additives(TRG_EXE, TRG_FOLDER_PATH)
    
################################################################################################################
Woran kann das liegen?
Sirius3
User
Beiträge: 18289
Registriert: Sonntag 21. Oktober 2012, 17:20

Das hat nichts mit der while-Schleife zu tun, die wie schon mehrfach geschrieben, eh falsch ist, weil das eine for-Schleife sein sollte:

Code: Alles auswählen

for line in process.stdout:
    self.display.insert(tk.END, line, "red")
Wobei diese Art der Schleife bei GUI-Programmen nicht verwendet werden darf, weil sie die eigentliche Ereignisschleife kaputt macht. Ein update_ideltasks sollte man erst gar nicht benutzen. Unter Unix könnte man wohl tkinter.createfilehandler verwenden, unter Windows wird aber einem nichts anderes übrig bleiben als mit einem separaten Thread und einer Queue zu arbeiten.
Antworten