From 12904e843ceb4a6e949b0a9c00b801d5e71784c3 Mon Sep 17 00:00:00 2001 From: punix Date: Wed, 2 Jul 2025 12:40:08 +0200 Subject: [PATCH] 29.06.2025 (show changelog) --- .gitignore | 24 +++ Changelog | 14 +- locale/de/LC_MESSAGES/lxtoolsinstaller.mo | Bin 5789 -> 5993 bytes lx-icons/16/settings.png | Bin 0 -> 575 bytes lx-icons/16/wg_vpn.png | Bin 0 -> 846 bytes lxtools_installer.py | 128 +++++++------ lxtools_installer.spec | 38 ++++ manager.py | 221 ++++++++++++++++++++-- message.py | 2 +- 9 files changed, 357 insertions(+), 70 deletions(-) create mode 100644 lx-icons/16/settings.png create mode 100644 lx-icons/16/wg_vpn.png create mode 100644 lxtools_installer.spec diff --git a/.gitignore b/.gitignore index e0b46ee..df72d0d 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,27 @@ debug.log .idea .vscode __pycache__ + +# Build-Artefakte +build/ +dist/ +certs/ +*.spec.bak +lxtools_installer +lxtools_installer.AppImage +lxtools_installer_compat +build_compatible.sh +build_local.sh +clean_build.sh +start_builder.sh +test_extract.py +test_paths.py +test_resources.py +manager_fixed.py + +# Docker-Build +docker_build/ +debug_docker.sh +Dockerfile.nuitka +DOCKER_BUILD_ANLEITUNG.md +nuitka_builder.py diff --git a/Changelog b/Changelog index c1e20ba..c5f082a 100644 --- a/Changelog +++ b/Changelog @@ -2,10 +2,20 @@ Changelog for LXTools installer ## [Unreleased] - - language set auto detection - - + + ### Added +29.06.2025 + +- add methode sigi, clean_files and remove_lxtools_files + for remove files and dirs on close lxtools_installer + +- fix message dialog on font and padding + +- add methods check polkit and check Networkmanager is installed + and view in header is result false + ### Added 23-06-2025 diff --git a/locale/de/LC_MESSAGES/lxtoolsinstaller.mo b/locale/de/LC_MESSAGES/lxtoolsinstaller.mo index 27eb7b952791c7534e9373234d7f1d9c83db02ab..191cb60a906b678b729d5b562df39ffc404b9fa9 100644 GIT binary patch delta 2511 zcmZXUTWnNS6oz+8xfM!p(-sBnDF~&MmbS>PKn0|gsx++_5;a5*?QUn7=}dFxj15Sg z2aPYFi8)}5mqsHGK!U-LXf!0oln3KAlK3Dbm?kC$<-r)#5b^T;GpB^g&aCt8+gW?9 zf9*4GHC=7aUa2TLZfHH&V(e(4F>!dahzITDEMw~6DOd%+fMswJmc!p*1uU9v%pzC= zHC_wvhFx$zbYU?}LcMno))c3t?nV-ffoQ?GWZW% z4wvFh2knA7s1Iu2AXFe@a1}fn&VLA1ny=wqa1v$}`Cs8eghI(-JyakqP?7F~3glTx z66SSC?aYaA{ydbQ%TR$|hf=hV0kN70)VLNZ(AJP$(qGa%$;3*Sf{g{nyawwTUmy(! zUW28ugopMofwiy|>XP+A1(blggfBn^`Zgpta~dkZ51{;h1GmCIOVM8uuA|_p`8FO> zpbtvwC!h{`4mxlg>cCH+6uAT?=?$oo%qPtiunj8H$D!tvuoE7G{LCdDO5|FWjvUI1 zYPSeV@-}!c?1efg15wo+3;7OIrtd-3@^dK1KSC+_2h;(zJT$)+%3mjx5(nTen0=m3 z3!N{aHvSEl!cxLlU~RA&?uOcT7)sJJP>w!=lKvv(XMX0<1pkE!u!&Dd>)lZPT`2$Q z{5We~r6b8t!Zq-HD9I+Fq@Rr}y3O@aF!7!{fVx| zq~LwnOiblc0#aoQ=ikvbGouLgpP(9T!yd-kF}2-!&H^bbUNF(#W;OX+}%9!9H%7Bzg1r`mdo!{#Xv!CP+8fKo!U1o z$NhGY@SFiV7)$b^?+i|y_Z-uc(=$v~^CqunSnJ*ST2DK%F5Hr@_nk{w3A zMwZSR@g~j>W`dXJRR@+iod}j$8_B=}Ng2ELv>2*xJy@B|KC~>}9q)u$^6JXF*U{ zk;Dgsz6^vA#0TvINDPrC#Dw~w`$7m25_}<1q7OEriBS_xG-CYyw*x^#^rUBg-#0Vg zch32rZ??DMr<(IOYo;ADSP%XX?w1&2!z)>Hn1y8_g_yMeeb|EjSCF2>CvInE8ir4!i=j@wc!7 znwgUu&JJloJzpMjGpuEv4Ck-H81v(>g81fB2FmCvoDIK$s_-YMil>l`l&ObNxDu+8 z9;k$OKqWQ+wf+^zQu8K^zyK5=@5o4?*p76y_E16a!^+8A|H!Ai0=Q8b^;z zHPrnW)J9!U3HC!NmxjbPhr;<0sLJ0BIR>S`CAb8B37e)EGhO;OaifJYEVvU^!h=u? z-iD9B_n>9aQCgPz(1#wc-eDgy*3uy%FC35kAR0f-DNK1?qVpBv+G$YTZF7#gF9~ zXu~mR!5a{D%pW03iK-q(LdGGFOc#`teNajr4tX3dWPTP(iED5(yakuR)x50rNw^Z` z_cLf^a1Khs38;m?KxJOer5RdKlJ`QjU>DRzuR|$#G@O47RrzK3IJ^#3;8Z>(B^-lN zVk<=Pyy<74ULAt-;TcG5a|QBezUCsyeuHXB1rxPu3DkRCkQn9#sI&AEMwiUHT6rUUcopfMbp`kDzY|Q2}vzk9OuXH zt}SQubU4%3C7Vk8qOA-mNIu$C?4#y)Oy@#}vm2LEGAXnae+plP_uwn=b+}}ft;Y5H zptjvH{kp3yd5eqs)Pdh?>M8#Uinb|aJJe^SPihUm9+%{5pU%#+_;g(7LWivrm#J+V zG$`xHWKzyxzwe}7%W-|%8@o6oGA)ReM}mg(HDihLJtbwW%`K~~-V68>G2%jVQ(_nWG7R$*A|?sZmJB&mf5y%WFVaiey)xN%VtG_#Wkx+ zvdzKHnnfLbw&NGZ2M9fw^cW+B36pbd&$sTG*#Det?rnKj)HddQXc5?j5WV(Z!k&`9O9l`#(`7^D|WWR#Z ox|eJ3BT3L0ZJ$(ZPH-Sv*?6B<{?#&$n;V>sE|_8mpGQyr4K|K_DgXcg diff --git a/lx-icons/16/settings.png b/lx-icons/16/settings.png new file mode 100644 index 0000000000000000000000000000000000000000..5a09d743e5384996a1199ac0d9a8f46523a8d570 GIT binary patch literal 575 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Ea{HEjtmSN`?>!lvI6;x#X;^) zj5kl})B`yR$sR$z3=CCj3=9n|3=F@3LJcn%7)lKo7+xhXFj&oCU=S~uvn$XBD8ZKG z?d}4kf#9d}Zjf%y0*}aI1_nK45N51cYF`EvWH0gbb!C6aCd;d%yJVJK1JK51PZ!4! zi_>c-@AYDK6lvXmcTLBpYym|!Clwan4l}b`O9kW|#p<~(-#EjZD=3}I-1LK?>qcj* zW{A)QHxc2;;G7F8yCS*MQ%nQ{_uQ3vx3l=&Ok=kDAtyh}D5&>`hIcH8pMSA^2E(ia zwhufk7$!BmYIO44m3$V=ViF`(Jk*i&cE(&H1MSt$#m^ z<(E__vpvBpDqGYr=VZRtwYQBv3$&glxUg|*`VnaQ*KmQRbZl?X#hnc1Bn}Y%A_4oyn0t!!|bk$3?f( zoxCy{D4^000SaNLh0L01FcU01FcV0GgZ_00007 zbV*G`2k8bH3J5r$Hx=;!00NRpL_t(I%Z<}tNK;`L$MNqOMp_q%Nr#h_MHy};DEJ2z zgs`jhCJLf1t+X5KDqL6H2%V6M=q7^7$bo@{_$Q5+W*KU!nK2TZ|DD@(xYU|wbMEOP z7!phRT|e*VeP8%J3jc_%Y8lC;C#zAEbzf_nZuPpq$C^_%9f$|AfTFakRi`W^CyLUt zvk<^JJj}Pw&d#yB_u7CiVA$$)OB8rme&J@$`E%ApRYg5;02BpBPY*MLgT!r*=TKfQ z=7Iv&4THtGIhNdR-dwEEtEVQe#{r09O3E%N0bpTvmWJ|jWMhM3Yc+|QM(?wF25dIY z*4C10v5;!9U{8oo05Ab8zw1^2LWV(O)fLvhI@q==5pzKy5)s17%Pcmx(sJuo^zV_t znm-CeR#q1PM*I3$?du2N*qPI~Cnr%;Q`uKo2*C8?I$TaC0Ad*PQ2>|;8U{08MgYjL zUc+2mjCXt-lgUI{US9O(`LQv6t*ij7ZU#pE9uI4NKLFd}6Ic%hagB_iC<+swhoV3z z5MX(65ny;T@P1)xYE3n#1K=7O0!86?WhK7(c_JH;D3Fwrf^TllWA(a6qkz@x7H4m7 z^U?f#0QQ*8B<$S5>)UtuP;cW%ZtkC#?cc}2%uGh@_Ls4mSKx9w#s2iE=&B}fZ8ll* zc*O7ZioLE*bXAj)?rxcKI7C;qw3zj6tf_esG7S0H+9tPia^=b8%kt<_h1@SKlW+a~ z5)Or=v8Lt$Tl9Z#UrVb?L?mb!vM@U`b!KVr@O Y25ZtvcJhLMJOBUy07*qoM6N<$f|Z bool: + """Check if network manager is installed""" + os_system = Detector.get_os() + deb = ["Debian", "Ubuntu", "Linux Mint", "Pop!_OS"] + arch = ["Arch Linux", "Manjaro", "EndeavourOS", "ArcoLinux", "Garuda Linux"] + if os_system in deb: + result = subprocess.run( + ["apt", "list", "--installed", "|", "grep", "polkit"], + capture_output=True, + text=True, + check=False, + ) + if result.returncode == 0: + return True + else: + return False + + elif os_system in arch: + result = subprocess.run( + ["pacman", "-Qs", "polkit"], + capture_output=True, + text=True, + check=False, + ) + if result.returncode == 0: + return True + else: + return False + + elif os_system == "Fedora": + result = subprocess.run( + ["dnf", "list", "--installed", "|", "grep", "polkit"], + capture_output=True, + text=True, + check=False, + ) + if result.returncode == 0: + return True + else: + return False + + elif os_system == "SUSE Tumbleweed" or os_system == "SUSE Leap": + result = subprocess.run( + ["zypper", "search", "--installed-only", "|", "grep", "polkit"], + capture_output=True, + text=True, + check=False, + ) + if result.returncode == 0: + return True + else: + return False + + @staticmethod + def get_networkmanager() -> bool: + """Check if network manager is installed""" + os_system = Detector.get_os() + deb = ["Debian", "Ubuntu", "Linux Mint", "Pop!_OS"] + arch = ["Arch Linux", "Manjaro", "EndeavourOS", "ArcoLinux", "Garuda Linux"] + if os_system in deb: + result = subprocess.run( + ["apt", "list", "--installed", "|", "grep", "network-manager"], + capture_output=True, + text=True, + check=False, + ) + if result.returncode == 0: + return True + else: + return False + + elif os_system in arch: + result = subprocess.run( + ["pacman", "-Qs", "networkmanager"], + capture_output=True, + text=True, + check=False, + ) + if result.returncode == 0: + return True + else: + return False + + elif os_system == "Fedora": + result = subprocess.run( + ["dnf", "list", "--installed", "|", "grep", "NetworkManager"], + capture_output=True, + text=True, + check=False, + ) + if result.returncode == 0: + return True + else: + return False + + elif os_system == "SUSE Tumbleweed" or os_system == "SUSE Leap": + result = subprocess.run( + ["zypper", "search", "--installed-only", "|", "grep", "networkmanager"], + capture_output=True, + text=True, + check=False, + ) + if result.returncode == 0: + return True + else: + return False + class Theme: @staticmethod @@ -180,7 +291,7 @@ class Image: def __init__(self): self.images = {} - def load_image(self, image_key, fallback_paths=None) -> None | tk.PhotoImage: + def load_image(self, image_key, fallback_paths=None) -> Optional[tk.PhotoImage]: """Load PNG image using tk.PhotoImage with fallback options""" if image_key in self.images: return self.images[image_key] @@ -228,7 +339,7 @@ class Image: self.images[image_key] = photo return photo except tk.TclError as e: - print(f"{LocaleStrings.MSGP["fail_load_image"]}{path}: {e}") + print(f"{LocaleStrings.MSGP['fail_load_image']}{path}: {e}") continue # Return None if no image found @@ -239,7 +350,7 @@ class AppManager: def __init__(self): self.projects = LXToolsAppConfig.PROJECTS - def get_project_info(self, project_key) -> dict | None: + def get_project_info(self, project_key) -> Optional[dict]: """Get project information by key""" return self.projects.get(project_key) @@ -281,10 +392,10 @@ class AppManager: # Debug logging print(LocaleStrings.MSGP["logviewer_check"]) - print(f"{LocaleStrings.MSGP["symlink_exist"]}{symlink_exists}") - print(f"{LocaleStrings.MSGP["executable_exist"]}{executable_exists}") - print(f"{LocaleStrings.MSGP["is_executable"]}{executable_is_executable}") - print(f"{LocaleStrings.MSGP["final_result"]}{is_installed}") + print(f"{LocaleStrings.MSGP['symlink_exist']}{symlink_exists}") + print(f"{LocaleStrings.MSGP['executable_exist']}{executable_exists}") + print(f"{LocaleStrings.MSGP['is_executable']}{executable_is_executable}") + print(f"{LocaleStrings.MSGP['final_result']}{is_installed}") return is_installed @@ -310,7 +421,7 @@ class AppManager: return version return "Unknown" except Exception as e: - print(f"{LocaleStrings.MSGP["get_version_error"]}{project_key}: {e}") + print(f"{LocaleStrings.MSGP['get_version_error']}{project_key}: {e}") return "Unknown" def get_latest_version(self, project_key) -> str: @@ -417,12 +528,98 @@ class LxTools: y = (screen_height - height) // 2 window.geometry(f"{width}x{height}+{x}+{y}") + @staticmethod + def clean_files(tmp_dir: Path = None, file: Path = None) -> None: + """ + Deletes temporary files and directories for cleanup when exiting the application. + + This method safely removes an optional directory defined by `AppConfig.TEMP_DIR` + and a single file to free up resources at the end of the program's execution. + All operations are performed securely, and errors such as `FileNotFoundError` + are ignored if the target files or directories do not exist. + :param tmp_dir: (Path, optional): Path to the temporary directory that should be deleted. + If `None`, the value of `AppConfig.TEMP_DIR` is used. + :param file: (Path, optional): Path to the file that should be deleted. + If `None`, no additional file will be deleted. + + Returns: + None: The method does not return any value. + """ + + if tmp_dir is not None: + shutil.rmtree(tmp_dir, ignore_errors=True) + try: + if file is not None: + Path.unlink(file) + + except FileNotFoundError: + pass + + @staticmethod + def sigi(file_path: Optional[Path] = None, file: Optional[Path] = None) -> None: + """ + Function for cleanup after a program interruption + + :param file: Optional - File to be deleted + :param file_path: Optional - Directory to be deleted + """ + + def signal_handler(signum: int, frame: Any) -> NoReturn: + """ + Determines clear text names for signal numbers and handles signals + + Args: + signum: The signal number + frame: The current stack frame + + Returns: + NoReturn since the function either exits the program or continues execution + """ + + signals_to_names_dict: Dict[int, str] = dict( + (getattr(signal, n), n) + for n in dir(signal) + if n.startswith("SIG") and "_" not in n + ) + + signal_name: str = signals_to_names_dict.get( + signum, f"Unnamed signal: {signum}" + ) + + # End program for certain signals, report to others only reception + if signum in (signal.SIGINT, signal.SIGTERM): + exit_code: int = 1 + logging.error( + f"\nSignal {signal_name} {signum} received. => Aborting with exit code {exit_code}.", + exc_info=True, + ) + LxTools.clean_files(file_path, file) + logging.info("Breakdown by user...") + sys.exit(exit_code) + else: + logging.info(f"Signal {signum} received and ignored.") + LxTools.clean_files(file_path, file) + logging.error("Process unexpectedly ended...") + + # Register signal handlers for various signals + signal.signal(signal.SIGINT, signal_handler) + signal.signal(signal.SIGTERM, signal_handler) + signal.signal(signal.SIGHUP, signal_handler) + + @staticmethod + def remove_lxtools_files() -> None: + if getattr(sys, "_MEIPASS", None) is not None: + shutil.rmtree("./locale") + shutil.rmtree("./TK-Themes") + shutil.rmtree("./lx-icons") + class LXToolsAppConfig: @staticmethod def extract_data_files() -> None: if getattr(sys, "_MEIPASS", None) is not None: + os.makedirs("/tmp/lxtools", exist_ok=True) # Liste der Quellordner (entspricht dem "datas"-Eintrag in lxtools_installer.spec) source_dirs = [ os.path.join(sys._MEIPASS, "locale"), # für locale/... @@ -437,7 +634,7 @@ class LXToolsAppConfig: for source_dir in source_dirs: group_name = os.path.basename( source_dir - ) # Erhält den Gruppen-Name (z. B. 'locale', 'TK-Themes') + ) # Erhält den Gruppen-Name (z. B. 'locale', 'TK-Themes') for root, dirs, files in os.walk(source_dir): for file in files: @@ -476,15 +673,15 @@ class LXToolsAppConfig: pass return gettext.gettext - VERSION = "1.1.6" + VERSION = "1.1.7" APP_NAME = "lxtoolsinstaller" WINDOW_WIDTH = 450 WINDOW_HEIGHT = 580 - # Working directory WORK_DIR = os.getcwd() ICONS_DIR = os.path.join(WORK_DIR, "lx-icons") THEMES_DIR = os.path.join(WORK_DIR, "TK-Themes") + TEMP_DIR = "/tmp/lxtools" # Locale settings LOCALE_DIR = "./locale/" @@ -618,6 +815,8 @@ class LocaleStrings: "progress": _("Progress"), "refresh2": _("Status refresh completed"), "python_check": _("Python not installed"), + "polkit_check": _("Please install Polkit!"), + "networkmanager_check": _("Please install Networkmanager!"), } # MSGC = Strings on Cards diff --git a/message.py b/message.py index b953563..be1a6a7 100644 --- a/message.py +++ b/message.py @@ -129,7 +129,7 @@ class MessageDialog: self.window = tk.Toplevel(master) self.window.grab_set() self.window.resizable(False, False) - ttk.Style().configure("TButton", font=("Helvetica", 11), padding=5) + ttk.Style().configure("TButton") self.buttons_widgets = [] self.current_button_index = 0 self._load_icons()