From 25dd11664cbdaf455705f4e2d5abcb90709cb994 Mon Sep 17 00:00:00 2001 From: Reto Trappitsch Date: Mon, 11 Mar 2024 15:27:16 +0100 Subject: [PATCH 1/7] add PYAPP_IS_GUI to spawn new process when opening app --- Cargo.toml | 1 + build.rs | 7 +++++++ docs/config.md | 6 ++++++ src/app.rs | 16 ++++++++++++++++ src/distribution.rs | 7 +++++++ src/process.rs | 21 +++++++++++++++++++-- 6 files changed, 56 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 1dd21a0..5b770b9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -58,6 +58,7 @@ passthrough = [ "PYAPP_EXPOSE_PYTHON", "PYAPP_EXPOSE_PYTHON_PATH", "PYAPP_FULL_ISOLATION", + "PYAPP_IS_GUI", "PYAPP_METADATA_TEMPLATE", "PYAPP_PASS_LOCATION", "PYAPP_PIP_ALLOW_CONFIG", diff --git a/build.rs b/build.rs index 47ea958..e4c5928 100644 --- a/build.rs +++ b/build.rs @@ -627,6 +627,13 @@ fn set_python_path(distribution_source: &str) { } else { set_runtime_variable(installation_variable, "bin/python3"); }; + + let variable_is_gui = "PYAPP_IS_GUI"; + if is_enabled(variable_is_gui) { + set_runtime_variable(variable_is_gui, "1"); + } else { + set_runtime_variable(variable_is_gui, "0"); + } } fn set_site_packages_path(distribution_source: &str) { diff --git a/docs/config.md b/docs/config.md index 0fd9a0d..cc2e81a 100644 --- a/docs/config.md +++ b/docs/config.md @@ -51,6 +51,12 @@ The following options are mutually exclusive: If none are set then the `PYAPP_EXEC_MODULE` option will default to the value of `PYAPP_PROJECT_NAME` with hyphens replaced by underscores. +## Graphical user interface (GUI) + +If you are packaging for windows on windows and your python execution calls a GUI, you can set `PYAPP_IS_GUI` to `true` or `1`. This will use `pythonw.exe` instead of `python.exe` to execute the application. If a GUI is run with `python.exe` on Windows, a console window will appear alongside the GUI. Details can be found [here](https://docs.python.org/3/using/windows.html#python-application). + +Even when you set `PYAPP_IS_GUI` to `1`, you can still run the application from the command line. Furthermore, PyApp specific functions (e.g., installation and setup) will still display a console window with status messages. Note however that running a GUI application with `pythonw.exe` means that all `stdout` and `stderr` output will be discarded. + ## Python distribution ### Known diff --git a/src/app.rs b/src/app.rs index 0815ba4..898148b 100644 --- a/src/app.rs +++ b/src/app.rs @@ -74,6 +74,12 @@ fn installation_python_path() -> String { env!("PYAPP__INSTALLATION_PYTHON_PATH").into() } +#[cfg(windows)] +fn installation_pythonw_path() -> String { + let python_path: String = env!("PYAPP__INSTALLATION_PYTHON_PATH").into(); + python_path.replace("python.exe", "pythonw.exe") +} + fn installation_site_packages_path() -> String { env!("PYAPP__INSTALLATION_SITE_PACKAGES_PATH").into() } @@ -180,6 +186,11 @@ pub fn pip_external() -> bool { env!("PYAPP_PIP_EXTERNAL") == "1" } +#[cfg(windows)] +pub fn app_is_gui() -> bool { + env!("PYAPP_IS_GUI") == "1" +} + pub fn full_isolation() -> bool { env!("PYAPP_FULL_ISOLATION") == "1" } @@ -204,6 +215,11 @@ pub fn python_path() -> PathBuf { install_dir().join(installation_python_path()) } +#[cfg(windows)] +pub fn pythonw_path() -> PathBuf { + install_dir().join(installation_pythonw_path()) +} + pub fn site_packages_path() -> PathBuf { install_dir().join(installation_site_packages_path()) } diff --git a/src/distribution.rs b/src/distribution.rs index f28b9f4..1063611 100644 --- a/src/distribution.rs +++ b/src/distribution.rs @@ -19,6 +19,13 @@ pub fn python_command(python: &PathBuf) -> Command { pub fn run_project() -> Result<()> { let mut command = python_command(&app::python_path()); + #[cfg(windows)] + { + if app::app_is_gui() { + command = python_command(&app::pythonw_path()); + } + } + if !app::exec_code().is_empty() { command.args(["-c", app::exec_code().as_str()]); } else if !app::exec_module().is_empty() { diff --git a/src/process.rs b/src/process.rs index 2296b7f..3a007e7 100644 --- a/src/process.rs +++ b/src/process.rs @@ -9,6 +9,9 @@ use anyhow::Result; use crate::terminal; +#[cfg(windows)] +use crate::app; + pub fn wait_for(mut command: Command, message: String) -> Result<(ExitStatus, String)> { let (mut reader, writer_stdout) = os_pipe::pipe()?; let writer_stderr = writer_stdout.try_clone()?; @@ -37,6 +40,20 @@ pub fn exec(mut command: Command) -> Result<()> { #[cfg(windows)] pub fn exec(mut command: Command) -> Result<()> { - let status = command.status()?; - exit(status.code().unwrap_or(1)); + if app::app_is_gui() { + let mut child = command.spawn()?; + match child.try_wait() { + Ok(Some(status)) => { + exit(status.code().unwrap_or(1)); + } + Ok(None) => { + // The child is still running + Ok(()) + } + Err(e) => Err(e.into()), + } + } else { + let status = command.status()?; + exit(status.code().unwrap_or(1)); + } } From 18f2b48de350621791f97f46cc24b9d2c70bec13 Mon Sep 17 00:00:00 2001 From: Reto Trappitsch Date: Sun, 17 Mar 2024 10:10:06 +0100 Subject: [PATCH 2/7] spawn a process on all OS instead of executing --- docs/config.md | 13 +++++++++++-- src/app.rs | 1 - src/embed/project | Bin 0 -> 6259 bytes src/process.rs | 34 +++++++++++++++++----------------- 4 files changed, 28 insertions(+), 20 deletions(-) diff --git a/docs/config.md b/docs/config.md index cc2e81a..d7d2b3f 100644 --- a/docs/config.md +++ b/docs/config.md @@ -53,9 +53,18 @@ If none are set then the `PYAPP_EXEC_MODULE` option will default to the value of ## Graphical user interface (GUI) -If you are packaging for windows on windows and your python execution calls a GUI, you can set `PYAPP_IS_GUI` to `true` or `1`. This will use `pythonw.exe` instead of `python.exe` to execute the application. If a GUI is run with `python.exe` on Windows, a console window will appear alongside the GUI. Details can be found [here](https://docs.python.org/3/using/windows.html#python-application). +If you are packaging a GUI, you can set `PYAPP_IS_GUI` to `true` or `1`. -Even when you set `PYAPP_IS_GUI` to `1`, you can still run the application from the command line. Furthermore, PyApp specific functions (e.g., installation and setup) will still display a console window with status messages. Note however that running a GUI application with `pythonw.exe` means that all `stdout` and `stderr` output will be discarded. +On windows, this will use `pythonw.exe` instead of `python.exe` to execute the application, which avoids a console window from appearing. +Note that running a GUI application with `pythonw.exe` means that all `stdout` and `stderr` output from your GUI will be discarded. + +On unix-like systems, `python` will be used for the execution. +PyApp will run your GUI by spawning a new process, such that the console window that calls the command terminates after successful spawning. + +!!!note + On macos, the console by default does not automatically close when processes have terminated. Thus, the console will stay open after the GUI is started, however, can be closed manually without interferring with the GUI. The default console behavior can be changed in the user settings to close after the last process terminated successfully, see, e.g., [here](https://stackoverflow.com/questions/5560167/osx-how-to-auto-close-terminal-window-after-the-exit-command-executed). + +Even when you set `PYAPP_IS_GUI` to `1`, you can still run the application from the command line. Furthermore, PyApp specific functions (e.g., installation and setup) will still display a console window with status messages. ## Python distribution diff --git a/src/app.rs b/src/app.rs index 898148b..51e09a1 100644 --- a/src/app.rs +++ b/src/app.rs @@ -186,7 +186,6 @@ pub fn pip_external() -> bool { env!("PYAPP_PIP_EXTERNAL") == "1" } -#[cfg(windows)] pub fn app_is_gui() -> bool { env!("PYAPP_IS_GUI") == "1" } diff --git a/src/embed/project b/src/embed/project index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..ab8fcec1ca240f91edea3f9c7f5d87060d7eb9af 100644 GIT binary patch literal 6259 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 literal 0 HcmV?d00001 diff --git a/src/process.rs b/src/process.rs index 3a007e7..7577d49 100644 --- a/src/process.rs +++ b/src/process.rs @@ -1,16 +1,13 @@ use std::io::Read; #[cfg(unix)] use std::os::unix::process::CommandExt; -#[cfg(windows)] +// #[cfg(windows)] use std::process::exit; use std::process::{Command, ExitStatus}; use anyhow::Result; -use crate::terminal; - -#[cfg(windows)] -use crate::app; +use crate::{app, terminal}; pub fn wait_for(mut command: Command, message: String) -> Result<(ExitStatus, String)> { let (mut reader, writer_stdout) = os_pipe::pipe()?; @@ -35,25 +32,28 @@ pub fn wait_for(mut command: Command, message: String) -> Result<(ExitStatus, St #[cfg(unix)] pub fn exec(mut command: Command) -> Result<()> { - Err(command.exec().into()) + if app::app_is_gui() { + exec_gui(command) + } else { + Err(command.exec().into()) + } } #[cfg(windows)] pub fn exec(mut command: Command) -> Result<()> { if app::app_is_gui() { - let mut child = command.spawn()?; - match child.try_wait() { - Ok(Some(status)) => { - exit(status.code().unwrap_or(1)); - } - Ok(None) => { - // The child is still running - Ok(()) - } - Err(e) => Err(e.into()), - } + exec_gui(command) } else { let status = command.status()?; exit(status.code().unwrap_or(1)); } } + +fn exec_gui(mut command: Command) -> Result<()> { + let mut child = command.spawn()?; + match child.try_wait() { + Ok(Some(status)) => exit(status.code().unwrap_or(1)), + Ok(None) => Ok(()), // The child is still running + Err(e) => Err(e.into()), + } +} From 48c32c5b79a8b2619369c4389970ed34fd9524c3 Mon Sep 17 00:00:00 2001 From: Ofek Lev Date: Sun, 24 Mar 2024 17:29:59 -0400 Subject: [PATCH 3/7] update docs --- docs/config.md | 16 +++++++--------- src/embed/project | Bin 6259 -> 0 bytes 2 files changed, 7 insertions(+), 9 deletions(-) delete mode 100644 src/embed/project diff --git a/docs/config.md b/docs/config.md index d7d2b3f..a5b1a72 100644 --- a/docs/config.md +++ b/docs/config.md @@ -51,20 +51,18 @@ The following options are mutually exclusive: If none are set then the `PYAPP_EXEC_MODULE` option will default to the value of `PYAPP_PROJECT_NAME` with hyphens replaced by underscores. -## Graphical user interface (GUI) +### GUI -If you are packaging a GUI, you can set `PYAPP_IS_GUI` to `true` or `1`. +If you are packaging a graphical user interface (GUI), you can set `PYAPP_IS_GUI` to `true` or `1`. -On windows, this will use `pythonw.exe` instead of `python.exe` to execute the application, which avoids a console window from appearing. -Note that running a GUI application with `pythonw.exe` means that all `stdout` and `stderr` output from your GUI will be discarded. +On Windows, this will use `pythonw.exe` instead of `python.exe` to execute [the application](https://docs.python.org/3/using/windows.html#python-application), which avoids a console window from appearing. Running a GUI application with `pythonw.exe` means that all `stdout` and `stderr` output from your GUI will be discarded. -On unix-like systems, `python` will be used for the execution. -PyApp will run your GUI by spawning a new process, such that the console window that calls the command terminates after successful spawning. +Otherwise, the application will execute as usual. PyApp will run your GUI by spawning a new process, such that the console window that calls runs the application terminates after successful spawning. -!!!note - On macos, the console by default does not automatically close when processes have terminated. Thus, the console will stay open after the GUI is started, however, can be closed manually without interferring with the GUI. The default console behavior can be changed in the user settings to close after the last process terminated successfully, see, e.g., [here](https://stackoverflow.com/questions/5560167/osx-how-to-auto-close-terminal-window-after-the-exit-command-executed). +Even when `PYAPP_IS_GUI` is enabled you can still run the application from the command line. Furthermore, PyApp-specific logic (e.g. installation and setup) will still display a console window with status messages. -Even when you set `PYAPP_IS_GUI` to `1`, you can still run the application from the command line. Furthermore, PyApp specific functions (e.g., installation and setup) will still display a console window with status messages. +!!! note + On macOS, the console by default does not automatically close when processes have terminated (however it can be closed manually without interferring with the GUI). The default console behavior [can be changed](https://stackoverflow.com/questions/5560167/osx-how-to-auto-close-terminal-window-after-the-exit-command-executed) in the user settings to close after the last process terminated successfully. ## Python distribution diff --git a/src/embed/project b/src/embed/project deleted file mode 100644 index ab8fcec1ca240f91edea3f9c7f5d87060d7eb9af..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6259 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 From d6f49fb207a7d21b5f3e3a5d268a0a7993d4e533 Mon Sep 17 00:00:00 2001 From: Ofek Lev Date: Sun, 24 Mar 2024 17:48:05 -0400 Subject: [PATCH 4/7] update --- docs/config.md | 4 ++-- src/app.rs | 2 +- src/distribution.rs | 2 +- src/process.rs | 3 +-- 4 files changed, 5 insertions(+), 6 deletions(-) diff --git a/docs/config.md b/docs/config.md index a5b1a72..e5a884c 100644 --- a/docs/config.md +++ b/docs/config.md @@ -57,12 +57,12 @@ If you are packaging a graphical user interface (GUI), you can set `PYAPP_IS_GU On Windows, this will use `pythonw.exe` instead of `python.exe` to execute [the application](https://docs.python.org/3/using/windows.html#python-application), which avoids a console window from appearing. Running a GUI application with `pythonw.exe` means that all `stdout` and `stderr` output from your GUI will be discarded. -Otherwise, the application will execute as usual. PyApp will run your GUI by spawning a new process, such that the console window that calls runs the application terminates after successful spawning. +Otherwise, the application will execute as usual. PyApp will run your GUI by spawning a new process, such that the console window that runs the application terminates after successful spawning. Even when `PYAPP_IS_GUI` is enabled you can still run the application from the command line. Furthermore, PyApp-specific logic (e.g. installation and setup) will still display a console window with status messages. !!! note - On macOS, the console by default does not automatically close when processes have terminated (however it can be closed manually without interferring with the GUI). The default console behavior [can be changed](https://stackoverflow.com/questions/5560167/osx-how-to-auto-close-terminal-window-after-the-exit-command-executed) in the user settings to close after the last process terminated successfully. + On macOS, the console by default does not automatically close when processes have terminated (however it can be closed manually without interferring with the GUI). The default console behavior [can be changed](https://stackoverflow.com/questions/5560167/osx-how-to-auto-close-terminal-window-after-the-exit-command-executed) in the user settings to close after the last process terminates successfully. ## Python distribution diff --git a/src/app.rs b/src/app.rs index 51e09a1..d53e3be 100644 --- a/src/app.rs +++ b/src/app.rs @@ -186,7 +186,7 @@ pub fn pip_external() -> bool { env!("PYAPP_PIP_EXTERNAL") == "1" } -pub fn app_is_gui() -> bool { +pub fn is_gui() -> bool { env!("PYAPP_IS_GUI") == "1" } diff --git a/src/distribution.rs b/src/distribution.rs index 1063611..41437a0 100644 --- a/src/distribution.rs +++ b/src/distribution.rs @@ -21,7 +21,7 @@ pub fn run_project() -> Result<()> { #[cfg(windows)] { - if app::app_is_gui() { + if app::is_gui() { command = python_command(&app::pythonw_path()); } } diff --git a/src/process.rs b/src/process.rs index 7577d49..8d99779 100644 --- a/src/process.rs +++ b/src/process.rs @@ -1,7 +1,6 @@ use std::io::Read; #[cfg(unix)] use std::os::unix::process::CommandExt; -// #[cfg(windows)] use std::process::exit; use std::process::{Command, ExitStatus}; @@ -41,7 +40,7 @@ pub fn exec(mut command: Command) -> Result<()> { #[cfg(windows)] pub fn exec(mut command: Command) -> Result<()> { - if app::app_is_gui() { + if app::is_gui() { exec_gui(command) } else { let status = command.status()?; From 62b6ef1a188218b03b90284a2b99cffd16b36ae0 Mon Sep 17 00:00:00 2001 From: Ofek Lev Date: Sun, 24 Mar 2024 17:52:49 -0400 Subject: [PATCH 5/7] Update process.rs --- src/process.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/process.rs b/src/process.rs index 8d99779..6f7e49e 100644 --- a/src/process.rs +++ b/src/process.rs @@ -31,7 +31,7 @@ pub fn wait_for(mut command: Command, message: String) -> Result<(ExitStatus, St #[cfg(unix)] pub fn exec(mut command: Command) -> Result<()> { - if app::app_is_gui() { + if app::is_gui() { exec_gui(command) } else { Err(command.exec().into()) From df38f0d7fbcdf746d663d33531348ea61e40adc9 Mon Sep 17 00:00:00 2001 From: Ofek Lev Date: Sun, 24 Mar 2024 18:17:33 -0400 Subject: [PATCH 6/7] . --- build.rs | 17 ++++++++++------- src/embed/project | 0 2 files changed, 10 insertions(+), 7 deletions(-) create mode 100644 src/embed/project diff --git a/build.rs b/build.rs index e4c5928..3ffb70c 100644 --- a/build.rs +++ b/build.rs @@ -627,13 +627,6 @@ fn set_python_path(distribution_source: &str) { } else { set_runtime_variable(installation_variable, "bin/python3"); }; - - let variable_is_gui = "PYAPP_IS_GUI"; - if is_enabled(variable_is_gui) { - set_runtime_variable(variable_is_gui, "1"); - } else { - set_runtime_variable(variable_is_gui, "0"); - } } fn set_site_packages_path(distribution_source: &str) { @@ -805,6 +798,15 @@ fn set_execution_mode() { } } +fn set_is_gui() { + let variable = "PYAPP_IS_GUI"; + if is_enabled(variable) { + set_runtime_variable(variable, "1"); + } else { + set_runtime_variable(variable, "0"); + } +} + fn set_isolation_mode() { let variable = "PYAPP_FULL_ISOLATION"; if is_enabled(variable) { @@ -938,6 +940,7 @@ fn main() { set_project(); set_distribution(); set_execution_mode(); + set_is_gui(); set_isolation_mode(); set_upgrade_virtualenv(); set_pip_external(); diff --git a/src/embed/project b/src/embed/project new file mode 100644 index 0000000..e69de29 From 6c4534630864ec18966c6bba644c60e8b1ec9b35 Mon Sep 17 00:00:00 2001 From: Ofek Lev Date: Sun, 24 Mar 2024 18:21:05 -0400 Subject: [PATCH 7/7] Update changelog.md --- docs/changelog.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index 800ca6c..766ee05 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -8,6 +8,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), ## Unreleased +***Added:*** + +- Add `PYAPP_IS_GUI` option to support graphical applications + ## 0.15.1 - 2024-03-03 ***Fixed:***