From 476d82e6a9fa19e8d4be141b3575738c6859ae82 Mon Sep 17 00:00:00 2001 From: Alejandro Sirgo Rica Date: Fri, 13 Dec 2024 13:54:15 +0100 Subject: ogclient-systray: add new systray program for ogclient Add make.bat for an easier building process in Windows. This script generates ogclient.exe and ogclient-systray binaries in a ./dist directory. Add ogclient-systray program. This python program polls the existence of the ogclient process and shows a systray if the ogclient service is active. Update utils/create_version_file.py to generate information for the systray binary. --- make.bat | 8 ++++ systray/ogclient-systray | 99 ++++++++++++++++++++++++++++++++++++++++++++ utils/create_version_file.py | 19 ++++++--- 3 files changed, 120 insertions(+), 6 deletions(-) create mode 100644 make.bat create mode 100644 systray/ogclient-systray diff --git a/make.bat b/make.bat new file mode 100644 index 0000000..ac38f04 --- /dev/null +++ b/make.bat @@ -0,0 +1,8 @@ +:: Create version_info.txt +python utils\create_version_file.py + +:: Build the service binary with clean +pyinstaller --onefile --noconsole --version-file=ogclient-version-info.txt --clean ogclient + +:: Build the systray binary with clean +pyinstaller --onefile --noconsole --version-file=systray-version-info.txt --clean systray\ogclient-systray diff --git a/systray/ogclient-systray b/systray/ogclient-systray new file mode 100644 index 0000000..aa517bd --- /dev/null +++ b/systray/ogclient-systray @@ -0,0 +1,99 @@ +#!/usr/bin/python3 + +# +# Copyright (C) 2020-2024 Soleta Networks +# +# This program is free software: you can redistribute it and/or modify it under +# the terms of the GNU Affero General Public License as published by the +# Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. + +import time +import multiprocessing as mp +import psutil + +from PIL import Image, ImageDraw +from pystray import Icon, Menu, MenuItem + +SERVICE_POLL_INTERVAL = 5 + + +def _create_default_image(): + """ + Creates a default image for the tray icon. Use in case + no favicon.ico is found. The image will be a blue circle. + """ + width = height = 250 + circle_color = (45, 158, 251) + + # Create a new image with a transparent background + image = Image.new('RGBA', (width, height), (0, 0, 0, 0)) + dc = ImageDraw.Draw(image) + + # Draw circle + circle_radius = min(width, height) // 2 - 10 + circle_center = (width // 2, height // 2) + dc.ellipse( + (circle_center[0] - circle_radius, circle_center[1] - circle_radius, + circle_center[0] + circle_radius, circle_center[1] + circle_radius), + fill=circle_color + ) + return image + + +def create_icon_image(): + try: + image = Image.open(r'./favicon.ico') + image = Image.composite(image, Image.new('RGB', image.size, 'white'), image) + except FileNotFoundError: + image = _create_default_image() + return image + + +def create_systray(): + menu = Menu(MenuItem('ogclient service running', + lambda icon, item: None)) + icon = Icon('ogClient', create_icon_image(), menu=menu) + icon.run() + + +def is_ogclient_service_active(): + service_name = 'ogclient' + try: + service = psutil.win_service_get(service_name) + service = service.as_dict() + return service.get('status') == 'running' + except Exception: + return False + + +def _session_check_loop(): + systray_process = None + try: + while True: + session_status = is_ogclient_service_active() + is_systray_active = systray_process and systray_process.is_alive() + + if session_status and not is_systray_active: + systray_process = mp.Process(target=create_systray, daemon=True) + systray_process.start() + elif not session_status and is_systray_active: + systray_process.terminate() + systray_process.join() + systray_process = None + + time.sleep(SERVICE_POLL_INTERVAL) + except KeyboardInterrupt: + if systray_process and systray_process.is_alive(): + systray_process.terminate() + systray_process.join() + + +def main(): + mp.freeze_support() + mp.set_start_method('spawn') + _session_check_loop() + + +if __name__ == "__main__": + main() diff --git a/utils/create_version_file.py b/utils/create_version_file.py index 3d0a295..4664766 100644 --- a/utils/create_version_file.py +++ b/utils/create_version_file.py @@ -30,12 +30,12 @@ VSVersionInfo( '040904B0', [ StringStruct('CompanyName', 'Soleta Networks'), - StringStruct('FileDescription', 'ogClient - OpenGnsys Client Application'), + StringStruct('FileDescription', '{appname} - OpenGnsys Client Application'), StringStruct('FileVersion', '{version}'), - StringStruct('InternalName', 'ogclient'), + StringStruct('InternalName', '{appname}'), StringStruct('LegalCopyright', 'Copyright © {year} Soleta Networks'), - StringStruct('OriginalFilename', 'ogclient.exe'), - StringStruct('ProductName', 'ogClient'), + StringStruct('OriginalFilename', '{appname}.exe'), + StringStruct('ProductName', '{appname}'), StringStruct('ProductVersion', '{version}') ] ) @@ -72,6 +72,13 @@ if __name__ == "__main__": version = get_git_version() major, minor, patch = version_to_tuple(version) current_year = datetime.now().year - version_file = version_template.format(major=major, minor=minor, patch=patch, version=version, year=current_year) - with open('version_info.txt', 'w', encoding='utf-8') as f: + version_file = version_template.format(major=major, minor=minor, + patch=patch, version=version, + year=current_year, appname='ogclient') + with open('ogclient-version-info.txt', 'w', encoding='utf-8') as f: + f.write(version_file) + version_file = version_template.format(major=major, minor=minor, + patch=patch, version=version, + year=current_year, appname='ogclient-systray') + with open('systray-version-info.txt', 'w', encoding='utf-8') as f: f.write(version_file) -- cgit v1.2.3-18-g5258