ofek / pyapp

Runtime installer for Python applications
https://ofek.dev/pyapp/
1.1k stars 24 forks source link

Add `PYAPP_IS_GUI` option #97

Closed trappitsch closed 3 months ago

trappitsch commented 3 months ago

Related to issue #96

This draft PR is an idea to solve the issue that when packaging a GUI and running it on Windows, a console window will be opened along with running the GUI. The PR is currently incomplete and needs the following (at least):

For macos: According to here, pythonw only needs to be used when running on Python versions <3.9.

Opening this as a draft as a start for discussion.

ofek commented 3 months ago

Do you mind, after you make the changes, giving me an example? Truthfully I've never used a Python GUI on Windows so I don't quite understand the issues we are solving.

trappitsch commented 3 months ago

Happy to! I'll work out some changed and then make a more detailed write up on why and what. Thanks!

trappitsch commented 3 months ago

Okay, this seems to be working now. The docs are still missing and I need to test this on macos, but here's a more detailed write up on the issue

Problem

I packaged this GUI with PyApp. On Linux (and presumably macos - to be tested) running the app by double clicking it works great, the GUI pops up, all fine. On windows however, along with the app, a console window shows up like here:

windows_gui_issue

The problem, turns out, is really multiple problems:

Solution (for now?)

Setting the top-level crate argument (RFC 1665) doesn't work for the purpose here, since a PyApp packaged program has to target the console subsystem (otherwise, installation messages, etc., disappear into nowhere).

Thus, the easy fix (but see below) is to run GUIs simply with pythonw.exe instead of python.exe. This can now be accomplished by setting PYAPP_IS_GUI = 1, which will change the execution to pythonw.exe (on windows only). This by itself still shows the console. The reason for this is that PyApp runs the command in process.rs with command.status(), which will wait for the child to finish. I had to change this to command.spawn() (for the GUI case only) and then do some error checking and exits. If the spawned child lives and does not die immediately, Ok(()) will be returned, upon which the console closes. During the spawning process, the console still briefly blinks on the screen, but I think this would be acceptable.

Is this pretty?

Well, it's prettier, but not ideal. The process still only displays as python in the task manager and any icons, etc., associated with the program disappear, since these configurations if present, are attached to the PyApp binary (which closed after spawning the GUI). A prettier solution would probably be to try and write a launcher, as discussed here. This is something I want to look into, but I'm not sure yet on how implement it. However, the way the current solution is written (by using an env variable), I think, is flexible enough in order to build on from here.

Apologies for the wordy explanation, I hope this makes things clearer. Let me know what you think. I'd understand if you think this is not a worthy enough improvement and/or outside of the scope of PyApp, however, I think GUI support could be really fun for many applications!

trappitsch commented 3 months ago

Quick update: Tested this on macos and the console popped up there too. I'll fix that too, update docs, and keep you posted!

ofek commented 3 months ago

Thank you!

trappitsch commented 3 months ago

Okay, this is now working on macos as well, with the caveat that by default, the macos console does not close after the last command is terminated (noted in docs). Otherwise, all working as described above in windows, linux, and macos. I also cleaned up the commit history a bit, hope this helps. Let me know what you think and thanks!

ofek commented 3 months ago

Beautiful, I will be fixing the branch up and merging soon!

image

trappitsch commented 3 months ago

Awesome, thanks! I'm glad you enjoy the feature :)