From 20846ca78ccdeb07dc7529445fde2868a3ce6c37 Mon Sep 17 00:00:00 2001 From: Etoh Date: Wed, 11 Oct 2023 22:12:38 +0100 Subject: [PATCH] Build Windows client console exe (#450) (#631) * Add Utils support for Windows console * Avoid GraphicalUI if Windows Console * Don't show GuiConfig for EXE console * Add enter-to-exit prompt when EXE console is missing args * Build syncplayConsole EXE * Fix isWindowsConsole() check * Limit stderr->blackhole code to Windows client console EXEs --- buildPy2exe.py | 4 +--- syncplay/ui/ConfigurationGetter.py | 6 ++++-- syncplay/ui/__init__.py | 12 +++++++----- syncplay/utils.py | 24 ++++++++++++++++++++++++ 4 files changed, 36 insertions(+), 10 deletions(-) diff --git a/buildPy2exe.py b/buildPy2exe.py index d023740..22a4f02 100755 --- a/buildPy2exe.py +++ b/buildPy2exe.py @@ -673,9 +673,7 @@ info = dict( "icon_resources": [(1, "syncplay\\resources\\icon.ico")], 'dest_base': "Syncplay"}, ], - console=['syncplayServer.py'], - # *** If you wish to make the Syncplay client use console mode (for --no-gui to work) then comment out the above two lines and uncomment the following line: - # console=['syncplayServer.py', {"script":"syncplayClient.py", "icon_resources":[(1, "resources\\icon.ico")], 'dest_base': "Syncplay"}], + console=['syncplayServer.py', {"script":"syncplayClient.py", "icon_resources":[(1, "syncplay\\resources\\icon.ico")], 'dest_base': "SyncplayConsole"}], options={ 'py2exe': { diff --git a/syncplay/ui/ConfigurationGetter.py b/syncplay/ui/ConfigurationGetter.py index f0a7f32..496c682 100755 --- a/syncplay/ui/ConfigurationGetter.py +++ b/syncplay/ui/ConfigurationGetter.py @@ -408,10 +408,12 @@ class ConfigurationGetter(object): sys.exit() def _promptForMissingArguments(self, error=None): - if self._config['noGui']: + if self._config['noGui'] or utils.isWindowsConsole(): if error: print("{}!".format(error)) print(getMessage("missing-arguments-error")) + if utils.isWindowsConsole(): + input(getMessage("enter-to-exit-prompt")) sys.exit() else: from syncplay.ui.GuiConfiguration import GuiConfiguration @@ -550,7 +552,7 @@ class ConfigurationGetter(object): # Arguments not validated yet - booleans are still text values if self._config['language']: setLanguage(self._config['language']) - if (self._config['forceGuiPrompt'] == "True" or not self._config['file']) and not self._config['noGui']: + if (self._config['forceGuiPrompt'] == "True" or not self._config['file']) and not self._config['noGui'] and not utils.isWindowsConsole(): self._forceGuiPrompt() self._checkConfig() self._saveConfig(iniPath) diff --git a/syncplay/ui/__init__.py b/syncplay/ui/__init__.py index 562ad43..8c80cfa 100755 --- a/syncplay/ui/__init__.py +++ b/syncplay/ui/__init__.py @@ -1,19 +1,21 @@ import os +from syncplay.utils import isWindowsConsole if "QT_PREFERRED_BINDING" not in os.environ: os.environ["QT_PREFERRED_BINDING"] = os.pathsep.join( ["PySide6", "PySide2", "PySide", "PyQt5", "PyQt4"] ) -try: - from syncplay.ui.gui import MainWindow as GraphicalUI -except ImportError: - pass +if not isWindowsConsole(): + try: + from syncplay.ui.gui import MainWindow as GraphicalUI + except ImportError: + pass from syncplay.ui.consoleUI import ConsoleUI def getUi(graphical=True, passedBar=None): - if graphical: + if graphical and not isWindowsConsole(): ui = GraphicalUI(passedBar=passedBar) else: ui = ConsoleUI() diff --git a/syncplay/utils.py b/syncplay/utils.py index 71572f1..ea9bec3 100755 --- a/syncplay/utils.py +++ b/syncplay/utils.py @@ -37,6 +37,8 @@ def isMacOS(): def isBSD(): return constants.OS_BSD in sys.platform or sys.platform.startswith(constants.OS_DRAGONFLY) +def isWindowsConsole(): + return os.path.basename(sys.executable) == "SyncplayConsole.exe" def retry(ExceptionToCheck, tries=4, delay=3, backoff=2, logger=None): """Retry calling the decorated function using an exponential backoff. @@ -225,6 +227,28 @@ def blackholeStdoutForFrozenWindow(): sys.stdout = Blackhole() del Blackhole + elif isWindowsConsole(): + class Blackhole(object): + softspace = 0 + + def write(self, text): + pass + + def flush(self): + pass + + class Stderr(object): + softspace = 0 + _file = None + _error = None + + def flush(self): + if self._file is not None: + self._file.flush() + + sys.stderr = Blackhole() + del Blackhole + def truncateText(unicodeText, maxLength): try: