1+ # -*- coding: utf-8 -*- 
2+ 
3+ from  concurrent .futures  import  thread 
4+ from  importlib  import  reload 
5+ import  sys 
6+ import  os 
7+ import  threading 
8+ import  time 
9+ from  PySide6 .QtWidgets  import  (
10+     QApplication ,
11+     QWidget ,
12+     QPushButton ,
13+     QVBoxLayout ,
14+     QHBoxLayout ,
15+     QLabel ,
16+     QSpacerItem ,
17+     QGridLayout ,
18+     QSizePolicy ,
19+     QGroupBox 
20+ )
21+ from  PySide6 .QtGui  import  QIcon , QPainter , QPixmap , QPalette 
22+ from  PySide6 .QtCore  import  QSize , Qt , QSettings 
23+ import  multiprocessing 
24+ from  qfluentwidgets  import  Theme , setTheme 
25+  # Keep for freeze_support, but remove direct Process usage 
26+ from  settings .update_settings_gui  import  UpdateDialog 
27+ from  settings .settings_gui  import  SettingsDialog 
28+ from  con  import  CON  # Import CON instance for theme settings 
29+ # Encoding settings have been moved to debug_logger for handling 
30+ # --- Helper function to create placeholder icons --- 
31+ # Since we cannot directly generate .icns files, we create PNG files as examples. 
32+ # Please place the AppIcon.icns and zip.icns files in the same directory as this script. 
33+ def  create_placeholder_icon (path : str , color : str , text : str ):
34+     """Create a simple PNG placeholder icon if the icon file does not exist.""" 
35+     if  not  os .path .exists (path ):
36+         pixmap  =  QPixmap (128 , 128 )
37+         pixmap .fill (color )
38+         painter  =  QPainter (pixmap )
39+         painter .setPen ("white" )
40+         font  =  painter .font ()
41+         font .setPointSize (48 )
42+         
43+         painter .setFont (font )
44+         # Qt.AlignCenter is enum value 1 
45+         painter .drawText (pixmap .rect (), Qt .AlignmentFlag .AlignCenter , text )
46+         painter .end ()
47+         pixmap .save (path )
48+         print (f"Note: '{ path }  )
49+         return  True 
50+     # If it's an .icns file, use it directly 
51+     elif  path .endswith (".icns" ) and  os .path .exists (path ):
52+         return  True 
53+     # If a non-.icns placeholder file exists, consider it successful 
54+     elif  not  path .endswith (".icns" ) and  os .path .exists (path ):
55+         return  True 
56+     return  False 
57+ 
58+ class  IconButtonsWindow (QWidget ):
59+     
60+     def  _load_qss_file (self , filename ):
61+         """Load QSS content from external file""" 
62+         qss_path  =  os .path .join (os .path .dirname (__file__ ), 'qss' , filename )
63+         try :
64+             with  open (qss_path , 'r' , encoding = 'utf-8' ) as  f :
65+                 return  f .read ()
66+         except  FileNotFoundError :
67+             print (f"Warning: QSS file not found: { qss_path }  )
68+             return  "" 
69+         except  Exception  as  e :
70+             print (f"Error loading QSS file { qss_path } { e }  )
71+             return  "" 
72+ 
73+     @property  
74+     def  LIGHT_QSS (self ):
75+         """Load light theme QSS from external file""" 
76+         return  self ._load_qss_file ('launcher_light.qss' )
77+ 
78+     @property  
79+     def  DARK_QSS (self ):
80+         """Load dark theme QSS from external file""" 
81+         return  self ._load_qss_file ('launcher_dark.qss' )
82+ 
83+     
84+     def  __init__ (self , q_app : QApplication ):
85+         super ().__init__ ()
86+         self ._q_app  =  q_app  # Store QApplication instance 
87+         self .setWindowTitle ("Converter" )
88+         # Load theme setting immediately 
89+         self .settings  =  QSettings ("MyCompany" , "ConverterApp" )
90+         self .theme_setting  =  self .settings .value ("theme" , 0 , type = int )
91+ 
92+         self .path =  os .path .dirname (os .path .abspath (__file__ ))
93+         # Define paths for icon files 
94+         self .app_icon_path  =  os .path .join (self .path ,"AppIcon.png" )
95+         self .appd_icon_path  =  os .path .join (self .path ,"AppIcond.png" )
96+         self .zip_icon_path  =  os .path .join (self .path ,"zip.png" )
97+         self .zipd_icon_path  =  os .path .join (self .path ,"zipd.png" )
98+ 
99+         # Check if icon files exist and create placeholders if needed 
100+         if  not  os .path .exists (self .app_icon_path ):
101+             print ("Note: AppIcon.png file not found. Will try to create a PNG placeholder icon." )
102+             create_placeholder_icon (self .app_icon_path , "dodgerblue" , "App" )
103+         if  not  os .path .exists (self .appd_icon_path ):
104+             print ("Note: AppIcond.png file not found. Will try to create a PNG placeholder icon." )
105+             create_placeholder_icon (self .appd_icon_path , "darkblue" , "AppD" ) # Changed placeholder color for dark mode app icon 
106+         
107+         if  not  os .path .exists (self .zip_icon_path ):
108+             print ("Note: zip.png file not found. Will try to create a PNG placeholder icon." )
109+             create_placeholder_icon (self .zip_icon_path , "gray" , "Zip" )
110+ 
111+         if  not  os .path .exists (self .zipd_icon_path ):
112+             print ("Note: zipd.png file not found. Will try to create a PNG placeholder icon." )
113+             create_placeholder_icon (self .zipd_icon_path , "dimgray" , "ZipD" )
114+ 
115+         self .init_ui ()
116+         # Apply theme based on settings or initial system detection 
117+         self ._apply_system_theme_from_settings () 
118+     
119+     def  _apply_system_theme (self , is_dark_mode ): # This method will now be primarily for paletteChanged signal 
120+         # Only apply system theme if setting is System Default 
121+         if  self .settings .value ("theme" , 0 , type = int ) ==  0 :
122+             self ._apply_theme (is_dark_mode )
123+ 
124+     def  _apply_system_theme_from_settings (self ):
125+         theme_setting  =  self .settings .value ("theme" , 0 , type = int )
126+         if  self ._q_app :
127+             if  theme_setting  ==  0 : # System Default 
128+                 is_dark_mode  =  self ._q_app .palette ().color (QPalette .ColorRole .Window ).lightnessF () <  0.5 
129+                 self ._apply_theme (is_dark_mode )
130+            
131+ 
132+     def  _apply_theme (self , is_dark_mode ):
133+         if  is_dark_mode :
134+             self .setStyleSheet (self .DARK_QSS )
135+             if  hasattr (self , 'button_zip' ):
136+                 self .button_zip .setIcon (QIcon (self .zipd_icon_path ))
137+             if  hasattr (self , 'button_app' ):
138+                 self .button_app .setIcon (QIcon (self .appd_icon_path ))
139+         else :
140+             self .setStyleSheet (self .LIGHT_QSS )
141+             if  hasattr (self , 'button_zip' ):
142+                 self .button_zip .setIcon (QIcon (self .zip_icon_path ))
143+             if  hasattr (self , 'button_app' ):
144+                 self .button_app .setIcon (QIcon (self .app_icon_path ))
145+         
146+         # Notify all sub-widgets to update theme 
147+         self .update_sub_widgets_theme (is_dark_mode )
148+     
149+     def  update_sub_widgets_theme (self , is_dark_mode ):
150+         """Notify all sub-widgets to update theme""" 
151+         # Update settings dialog theme (if already created) 
152+         if  hasattr (self , '_settings_dialog' ) and  self ._settings_dialog :
153+             self ._settings_dialog .apply_theme (is_dark_mode )
154+     
155+     def  init_ui (self ):
156+         # Create main layout 
157+         main_layout  =  QVBoxLayout ()
158+         
159+         main_layout .setSpacing (25 )  # Increased spacing for better visual separation 
160+         main_layout .setContentsMargins (40 , 35 , 40 , 35 )  # Better margins 
161+         
162+         # Add title 
163+         title_label  =  QLabel ("Converter" )
164+         title_label .setObjectName ("title_label" )
165+         title_label .setAlignment (Qt .AlignmentFlag .AlignCenter )
166+         main_layout .addWidget (title_label )
167+ 
168+         # --- Image Converter Group --- 
169+         image_group  =  QGroupBox ("Image Converter" )
170+         image_group .setObjectName ("image_group" )
171+         image_layout  =  QVBoxLayout (image_group )
172+         image_layout .setSpacing (10 )
173+         image_layout .setContentsMargins (15 , 15 , 15 , 15 )
174+         
175+         # Image Converter Button 
176+         app_icon  =  QIcon (self .app_icon_path )
177+         self .button_app  =  QPushButton ("Image Converter" )
178+         self .button_app .setObjectName ("button_app" )
179+         self .button_app .setIcon (app_icon )
180+         self .button_app .setIconSize (QSize (40 , 40 ))  # Consistent icon size 
181+         self .button_app .setMinimumHeight (55 )  # Consistent height 
182+         self .button_app .clicked .connect (run_image_app )
183+         
184+         # Center the button 
185+         app_button_layout  =  QHBoxLayout ()
186+         app_button_layout .addStretch ()
187+         app_button_layout .addWidget (self .button_app )
188+         app_button_layout .addStretch ()
189+         image_layout .addLayout (app_button_layout )
190+         
191+         # Description for Image Converter - moved below button 
192+         image_desc  =  QLabel ("Convert PNG images to ICNS format for macOS applications" )
193+         image_desc .setObjectName ("description_label" )
194+         image_desc .setAlignment (Qt .AlignmentFlag .AlignCenter )
195+         image_desc .setWordWrap (True )
196+         image_layout .addWidget (image_desc )
197+         
198+         main_layout .addWidget (image_group )
199+ 
200+         # --- Archive Converter Group --- 
201+         archive_group  =  QGroupBox ("Archive Converter" )
202+         archive_group .setObjectName ("archive_group" )
203+         archive_layout  =  QVBoxLayout (archive_group )
204+         archive_layout .setSpacing (10 )
205+         archive_layout .setContentsMargins (15 , 15 , 15 , 15 )
206+         
207+         # Archive Converter Button 
208+         zip_icon  =  QIcon (self .zip_icon_path )
209+         self .button_zip  =  QPushButton (" Archive Converter" )
210+         self .button_zip .setObjectName ("button_zip" )
211+         self .button_zip .setIcon (zip_icon )
212+         self .button_zip .setIconSize (QSize (40 , 40 ))  # Consistent icon size 
213+         self .button_zip .setMinimumHeight (55 )  # Consistent height 
214+         self .button_zip .clicked .connect (run_zip_app )
215+         
216+         # Center the button 
217+         zip_button_layout  =  QHBoxLayout ()
218+         zip_button_layout .addStretch ()
219+         zip_button_layout .addWidget (self .button_zip )
220+         zip_button_layout .addStretch ()
221+         archive_layout .addLayout (zip_button_layout )
222+         
223+         # Description for Archive Converter - moved below button 
224+         archive_desc  =  QLabel ("Create and extract ZIP, RAR, and 7Z archive files" )
225+         archive_desc .setObjectName ("description_label" )
226+         archive_desc .setAlignment (Qt .AlignmentFlag .AlignCenter )
227+         archive_desc .setWordWrap (True )
228+         archive_layout .addWidget (archive_desc )
229+         
230+         main_layout .addWidget (archive_group )
231+         
232+         # Add vertical space 
233+         main_layout .addSpacerItem (QSpacerItem (20 , 40 , QSizePolicy .Policy .Minimum , QSizePolicy .Policy .Expanding ))
234+ 
235+         # Settings button - positioned below zip button 
236+         settings_button  =  QPushButton (QIcon .fromTheme ("preferences-system" ), " Settings" )
237+         settings_button .setObjectName ("settings_button" )
238+         settings_button .setIconSize (QSize (20 , 20 ))
239+         settings_button .clicked .connect (self .show_settings )
240+ 
241+         settings_button_layout  =  QHBoxLayout ()
242+         settings_button_layout .addStretch ()
243+         settings_button_layout .addWidget (settings_button )
244+         settings_button_layout .addStretch ()
245+         main_layout .addLayout (settings_button_layout )
246+ 
247+         # Set the main layout for the window 
248+         self .setLayout (main_layout )
249+ 
250+     def  show_settings (self ):
251+         settings_dialog  =  SettingsDialog (self )
252+         self ._settings_dialog  =  settings_dialog   # Save dialog reference 
253+         settings_dialog .show () # Use show() instead of exec() to keep dialog non-modal 
254+ 
255+ def  run_zip ():
256+     from  arc_gui  import  ZipAppRunner 
257+     app_runner  =  ZipAppRunner ()
258+     app_runner .MainLoop ()
259+ def  run_image ():
260+     from  image_converter  import  ICNSConverterApp 
261+     app_runner  =  ICNSConverterApp ()
262+     app_runner .MainLoop ()
263+ def  run_zip_app ():
264+     multiprocessing .Process (target = run_zip ).start ()
265+ def  run_image_app ():
266+     multiprocessing .Process (target = run_image ).start ()
267+ 
268+ 
269+ 
270+ if  __name__  ==  "__main__" :
271+     multiprocessing .freeze_support ()
272+     app  =  QApplication (sys .argv )
273+     
274+     # Initialize debug logger 
275+     try :
276+         from  support .debug_logger  import  debug_logger 
277+         debug_logger .setup_logger ()
278+         if  debug_logger .is_debug_enabled ():
279+             print ("Debug mode enabled - logging to ~/.converter/log" )
280+     except  Exception  as  e :
281+         print (f"Failed to initialize debug logger: { e }  )
282+     
283+     from  support .toggle  import  theme_manager 
284+     theme_manager .start ()
285+     setTheme (Theme .AUTO )
286+     window  =  IconButtonsWindow (q_app = app )
287+     window .show ()
288+     # Connect to palette changes for real-time theme switching ONLY if setting is System Default 
289+     app .paletteChanged .connect (lambda : window ._apply_system_theme (app .palette ().color (QPalette .ColorRole .Window ).lightnessF () <  0.5 ))
290+     exit_code  =  app .exec ()
291+     
292+     # Cleanup debug logger 
293+     try :
294+         from  support .debug_logger  import  debug_logger 
295+         debug_logger .restore_output ()
296+     except :
297+         pass 
298+     
299+     theme_manager .stop ()
300+     sys .exit (exit_code )
0 commit comments