From f6634309847e0c16aa4568b8bf332d89a6c64484 Mon Sep 17 00:00:00 2001 From: Brett Date: Sat, 16 Dec 2023 22:20:24 -0600 Subject: [PATCH] Fix py2app (#69) * Initial commit of py2app code * Use environment variable RESOURCE_PATH when running as a bundle * Move config files to system config location --- main.py | 1 + resources/{icons => }/AddProduct.png | Bin resources/{icons => }/Adobe After Effects.png | Bin resources/{icons => }/Blender.png | Bin resources/{icons => }/Console.png | Bin resources/{icons => }/Document.png | Bin resources/{icons => }/Download.png | Bin resources/{icons => }/FFmpeg.png | Bin resources/{icons => }/Gear.png | Bin resources/{icons => }/GreenCircle.png | Bin resources/{icons => }/Monitor.png | Bin resources/{icons => }/RedSquare.png | Bin resources/{icons => }/SearchFolder.png | Bin resources/{icons => }/Server.png | Bin resources/{icons => }/SoftwareInstaller.png | Bin resources/{icons => }/StopSign.png | Bin resources/{icons => }/Synchronize.png | Bin resources/{icons => }/Trash.png | Bin resources/{icons => }/linux.png | Bin resources/{icons => }/macos.png | Bin resources/{icons => }/windows.png | Bin setup.py | 21 ++++++++++ src/api/api_server.py | 4 +- src/init.py | 5 +-- src/ui/main_window.py | 21 +++++----- src/ui/widgets/statusbar.py | 4 +- src/utilities/config.py | 38 ++++++++++++++++++ src/utilities/misc_helper.py | 24 +++++++---- 28 files changed, 93 insertions(+), 25 deletions(-) rename resources/{icons => }/AddProduct.png (100%) rename resources/{icons => }/Adobe After Effects.png (100%) rename resources/{icons => }/Blender.png (100%) rename resources/{icons => }/Console.png (100%) rename resources/{icons => }/Document.png (100%) rename resources/{icons => }/Download.png (100%) rename resources/{icons => }/FFmpeg.png (100%) rename resources/{icons => }/Gear.png (100%) rename resources/{icons => }/GreenCircle.png (100%) rename resources/{icons => }/Monitor.png (100%) rename resources/{icons => }/RedSquare.png (100%) rename resources/{icons => }/SearchFolder.png (100%) rename resources/{icons => }/Server.png (100%) rename resources/{icons => }/SoftwareInstaller.png (100%) rename resources/{icons => }/StopSign.png (100%) rename resources/{icons => }/Synchronize.png (100%) rename resources/{icons => }/Trash.png (100%) rename resources/{icons => }/linux.png (100%) rename resources/{icons => }/macos.png (100%) rename resources/{icons => }/windows.png (100%) create mode 100644 setup.py diff --git a/main.py b/main.py index 814cf87..b82800a 100755 --- a/main.py +++ b/main.py @@ -1,4 +1,5 @@ #!/usr/bin/env python3 + from src import init if __name__ == '__main__': diff --git a/resources/icons/AddProduct.png b/resources/AddProduct.png similarity index 100% rename from resources/icons/AddProduct.png rename to resources/AddProduct.png diff --git a/resources/icons/Adobe After Effects.png b/resources/Adobe After Effects.png similarity index 100% rename from resources/icons/Adobe After Effects.png rename to resources/Adobe After Effects.png diff --git a/resources/icons/Blender.png b/resources/Blender.png similarity index 100% rename from resources/icons/Blender.png rename to resources/Blender.png diff --git a/resources/icons/Console.png b/resources/Console.png similarity index 100% rename from resources/icons/Console.png rename to resources/Console.png diff --git a/resources/icons/Document.png b/resources/Document.png similarity index 100% rename from resources/icons/Document.png rename to resources/Document.png diff --git a/resources/icons/Download.png b/resources/Download.png similarity index 100% rename from resources/icons/Download.png rename to resources/Download.png diff --git a/resources/icons/FFmpeg.png b/resources/FFmpeg.png similarity index 100% rename from resources/icons/FFmpeg.png rename to resources/FFmpeg.png diff --git a/resources/icons/Gear.png b/resources/Gear.png similarity index 100% rename from resources/icons/Gear.png rename to resources/Gear.png diff --git a/resources/icons/GreenCircle.png b/resources/GreenCircle.png similarity index 100% rename from resources/icons/GreenCircle.png rename to resources/GreenCircle.png diff --git a/resources/icons/Monitor.png b/resources/Monitor.png similarity index 100% rename from resources/icons/Monitor.png rename to resources/Monitor.png diff --git a/resources/icons/RedSquare.png b/resources/RedSquare.png similarity index 100% rename from resources/icons/RedSquare.png rename to resources/RedSquare.png diff --git a/resources/icons/SearchFolder.png b/resources/SearchFolder.png similarity index 100% rename from resources/icons/SearchFolder.png rename to resources/SearchFolder.png diff --git a/resources/icons/Server.png b/resources/Server.png similarity index 100% rename from resources/icons/Server.png rename to resources/Server.png diff --git a/resources/icons/SoftwareInstaller.png b/resources/SoftwareInstaller.png similarity index 100% rename from resources/icons/SoftwareInstaller.png rename to resources/SoftwareInstaller.png diff --git a/resources/icons/StopSign.png b/resources/StopSign.png similarity index 100% rename from resources/icons/StopSign.png rename to resources/StopSign.png diff --git a/resources/icons/Synchronize.png b/resources/Synchronize.png similarity index 100% rename from resources/icons/Synchronize.png rename to resources/Synchronize.png diff --git a/resources/icons/Trash.png b/resources/Trash.png similarity index 100% rename from resources/icons/Trash.png rename to resources/Trash.png diff --git a/resources/icons/linux.png b/resources/linux.png similarity index 100% rename from resources/icons/linux.png rename to resources/linux.png diff --git a/resources/icons/macos.png b/resources/macos.png similarity index 100% rename from resources/icons/macos.png rename to resources/macos.png diff --git a/resources/icons/windows.png b/resources/windows.png similarity index 100% rename from resources/icons/windows.png rename to resources/windows.png diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..b9fc1f1 --- /dev/null +++ b/setup.py @@ -0,0 +1,21 @@ +""" +This is a setup.py script generated by py2applet + +Usage: + python setup.py py2app +""" +import glob + +from setuptools import setup + +APP = ['main.py'] +DATA_FILES = [('config', glob.glob('config/*.*')), + ('resources', glob.glob('resources/*.*'))] +OPTIONS = {} + +setup( + app=APP, + data_files=DATA_FILES, + options={'py2app': OPTIONS}, + setup_requires=['py2app'], +) diff --git a/src/api/api_server.py b/src/api/api_server.py index 28494c2..3aa26ab 100755 --- a/src/api/api_server.py +++ b/src/api/api_server.py @@ -26,7 +26,7 @@ from src.engines.engine_manager import EngineManager from src.render_queue import RenderQueue, JobNotFoundError from src.utilities.config import Config from src.utilities.misc_helper import system_safe_path, current_system_os, current_system_cpu, \ - current_system_os_version, config_dir + current_system_os_version from src.utilities.server_helper import generate_thumbnail_for_job from src.utilities.zeroconf_server import ZeroconfServer @@ -55,7 +55,7 @@ def sorted_jobs(all_jobs, sort_by_date=True): @server.route('/') @server.route('/index') def index(): - with open(system_safe_path(os.path.join(config_dir(), 'presets.yaml'))) as f: + with open(system_safe_path(os.path.join(Config.config_dir(), 'presets.yaml'))) as f: render_presets = yaml.load(f, Loader=yaml.FullLoader) return render_template('index.html', all_jobs=sorted_jobs(RenderQueue.all_jobs()), diff --git a/src/init.py b/src/init.py index 772c6de..3ca5402 100644 --- a/src/init.py +++ b/src/init.py @@ -25,9 +25,8 @@ def run() -> int: """ # Load Config YAML - config_dir = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), 'config') - Config.load_config(system_safe_path(os.path.join(config_dir, 'config.yaml'))) - + Config.setup_config_dir() + Config.load_config(system_safe_path(os.path.join(Config.config_dir(), 'config.yaml'))) logging.basicConfig(format='%(asctime)s: %(levelname)s: %(module)s: %(message)s', datefmt='%d-%b-%y %H:%M:%S', level=Config.server_log_level.upper()) diff --git a/src/ui/main_window.py b/src/ui/main_window.py index 5caaa3e..3228a3b 100644 --- a/src/ui/main_window.py +++ b/src/ui/main_window.py @@ -401,7 +401,7 @@ class MainWindow(QMainWindow): for hostname in found_servers: if hostname not in current_server_list: properties = ZeroconfServer.get_hostname_properties(hostname) - image_path = os.path.join(resources_dir(), 'icons', f"{properties.get('system_os', 'Monitor')}.png") + image_path = os.path.join(resources_dir(), f"{properties.get('system_os', 'Monitor')}.png") list_widget = QListWidgetItem(QIcon(image_path), hostname) self.server_list_view.addItem(list_widget) @@ -438,23 +438,22 @@ class MainWindow(QMainWindow): # Top Toolbar Buttons self.topbar.add_button( - "New Job", f"{resources_directory}/icons/AddProduct.png", self.new_job) + "Console", f"{resources_directory}/Console.png", self.open_console_window) self.topbar.add_button( - "Engines", f"{resources_directory}/icons/SoftwareInstaller.png", self.engine_browser) - self.topbar.add_button( - "Console", f"{resources_directory}/icons/Console.png", self.open_console_window) + "Engines", f"{resources_directory}/SoftwareInstaller.png", self.engine_browser) self.topbar.add_separator() self.topbar.add_button( - "Stop Job", f"{resources_directory}/icons/StopSign.png", self.stop_job) + "Stop Job", f"{resources_directory}/StopSign.png", self.stop_job) self.topbar.add_button( - "Delete Job", f"{resources_directory}/icons/Trash.png", self.delete_job) + "Delete Job", f"{resources_directory}/Trash.png", self.delete_job) self.topbar.add_button( - "Render Log", f"{resources_directory}/icons/Document.png", self.job_logs) + "Render Log", f"{resources_directory}/Document.png", self.job_logs) self.topbar.add_button( - "Download", f"{resources_directory}/icons/Download.png", self.download_files) + "Download", f"{resources_directory}/Download.png", self.download_files) self.topbar.add_button( - "Open Files", f"{resources_directory}/icons/SearchFolder.png", self.open_files) - + "Open Files", f"{resources_directory}/SearchFolder.png", self.open_files) + self.topbar.add_button( + "New Job", f"{resources_directory}/AddProduct.png", self.new_job) self.addToolBar(Qt.ToolBarArea.TopToolBarArea, self.topbar) # -- Toolbar Buttons -- # diff --git a/src/ui/widgets/statusbar.py b/src/ui/widgets/statusbar.py index 46a50b0..c36a437 100644 --- a/src/ui/widgets/statusbar.py +++ b/src/ui/widgets/statusbar.py @@ -34,7 +34,7 @@ class StatusBar(QStatusBar): while True: new_status = proxy.status() new_image_name = image_names.get(new_status, 'Synchronize.png') - image_path = os.path.join(resources_dir(), 'icons', new_image_name) + image_path = os.path.join(resources_dir(), new_image_name) self.label.setPixmap((QPixmap(image_path).scaled(16, 16, Qt.AspectRatioMode.KeepAspectRatio))) # add download status @@ -54,7 +54,7 @@ class StatusBar(QStatusBar): # Create a label that holds an image self.label = QLabel() - image_path = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))), 'resources', 'icons', + image_path = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))), 'resources', 'RedSquare.png') pixmap = (QPixmap(image_path).scaled(16, 16, Qt.AspectRatioMode.KeepAspectRatio)) self.label.setPixmap(pixmap) diff --git a/src/utilities/config.py b/src/utilities/config.py index e1b2089..97d84cf 100644 --- a/src/utilities/config.py +++ b/src/utilities/config.py @@ -1,5 +1,6 @@ import os import yaml +from src.utilities.misc_helper import current_system_os, copy_directory_contents class Config: @@ -34,3 +35,40 @@ class Config: cls.port_number = cfg.get('port_number', cls.port_number) cls.enable_split_jobs = cfg.get('enable_split_jobs', cls.enable_split_jobs) cls.download_timeout_seconds = cfg.get('download_timeout_seconds', cls.download_timeout_seconds) + + @classmethod + def config_dir(cls): + # Setup the config path + if current_system_os() == 'macos': + local_config_path = os.path.expanduser('~/Library/Application Support/Zordon') + elif current_system_os() == 'windows': + local_config_path = os.path.join(os.environ['APPDATA'], 'Zordon') + else: + local_config_path = os.path.expanduser('~/.config/Zordon') + return local_config_path + + @classmethod + def setup_config_dir(cls): + # Setup the config path + local_config_dir = cls.config_dir() + if os.path.exists(local_config_dir): + return + + try: + # Create the local configuration directory + os.makedirs(local_config_dir) + + # Determine the template path + resource_environment_path = os.environ.get('RESOURCEPATH') + if resource_environment_path: + template_path = os.path.join(resource_environment_path, 'config') + else: + template_path = os.path.join( + os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))), 'config') + + # Copy contents from the template to the local configuration directory + copy_directory_contents(template_path, local_config_dir) + + except Exception as e: + print(f"An error occurred while setting up the config directory: {e}") + raise \ No newline at end of file diff --git a/src/utilities/misc_helper.py b/src/utilities/misc_helper.py index 25f3c0d..d53cc73 100644 --- a/src/utilities/misc_helper.py +++ b/src/utilities/misc_helper.py @@ -1,6 +1,7 @@ import logging import os import platform +import shutil import socket import subprocess from datetime import datetime @@ -127,15 +128,24 @@ def current_system_cpu(): def resources_dir(): - resources_directory = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))), - 'resources') - return resources_directory + resource_environment_path = os.environ.get('RESOURCEPATH', None) + if resource_environment_path: # running inside resource bundle + return os.path.join(resource_environment_path, 'resources') + else: + return os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))), 'resources') -def config_dir(): - config_directory = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))), - 'config') - return config_directory +def copy_directory_contents(src_dir, dst_dir): + """ + Copy the contents of the source directory (src_dir) to the destination directory (dst_dir). + """ + for item in os.listdir(src_dir): + src_path = os.path.join(src_dir, item) + dst_path = os.path.join(dst_dir, item) + if os.path.isdir(src_path): + shutil.copytree(src_path, dst_path, dirs_exist_ok=True) + else: + shutil.copy2(src_path, dst_path) def is_localhost(comparison_hostname):