@@ -52,7 +52,7 @@ def __init__(self, parent):
5252
5353 # Options list
5454 self .options = [
55- {"label" : "Update" , "callback" : self .update_and_check },
55+ {"label" : "Update Check " , "callback" : self .update_and_check },
5656 {"label" : "Logs" , "callback" : self .show_logs },
5757 {"label" : "License" , "callback" : self .show_license },
5858 {"label" : "About" , "callback" : self .show_about },
@@ -130,12 +130,18 @@ def show_about(self, _=None):
130130 )
131131
132132 def show_license (self , _ = None ):
133- """Open license file in a scrollable popup window (reusable)."""
133+ """Open license file in a scrollable popup window (resizable, with fixed footer)."""
134+ # --- Prevent duplicate windows ---
135+ if hasattr (self , "license_window" ) and self .license_window and self .license_window .winfo_exists ():
136+ self .license_window .lift ()
137+ self .license_window .focus_force ()
138+ return
139+
134140 license_paths_to_try = [
135141 "_internal/LICENSE.txt" ,
136142 "LICENSE.txt" ,
137143 os .path .join (os .path .dirname (__file__ ), "_internal" , "LICENSE.txt" ),
138- resource_path ("LICENSE.txt" )
144+ resource_path ("LICENSE.txt" ),
139145 ]
140146
141147 text = None
@@ -146,7 +152,6 @@ def show_license(self, _=None):
146152 text = f .read ()
147153 break
148154 except Exception :
149- # ignore and try next
150155 pass
151156
152157 if text is None :
@@ -155,85 +160,96 @@ def show_license(self, _=None):
155160 messagebox .showerror ("License" , error_txt )
156161 return
157162
158- # Reusable Toplevel license window
159163 win = tk .Toplevel (self .parent )
164+ self .license_window = win # keep reference
160165 win .title ("License" )
161166 win .geometry ("600x600" )
162167 win .transient (self .parent )
163168 win .resizable (True , True )
164169 center_relative_to_parent (win , self .parent )
170+ win .protocol ("WM_DELETE_WINDOW" , lambda : (win .destroy (), setattr (self , "license_window" , None )))
171+
172+ win .rowconfigure (0 , weight = 1 )
173+ win .columnconfigure (0 , weight = 1 )
165174
166- # Use a frame for padding and layout
167175 frame = ttk .Frame (win , padding = (8 , 8 , 8 , 8 ))
168- frame .pack (fill = "both" , expand = True )
176+ frame .grid (row = 0 , column = 0 , sticky = "nsew" )
177+ frame .rowconfigure (0 , weight = 1 )
178+ frame .columnconfigure (0 , weight = 1 )
169179
170- # Text widget + scrollbar
171180 text_box = tk .Text (frame , wrap = "word" , borderwidth = 0 )
172181 text_box .insert ("1.0" , text )
173- text_box .config (state = "disabled" ) # read-only
174- text_box .pack ( side = "left" , fill = "both" , expand = True )
182+ text_box .config (state = "disabled" )
183+ text_box .grid ( row = 0 , column = 0 , sticky = "nsew" )
175184
176185 scrollbar = ttk .Scrollbar (frame , orient = "vertical" , command = text_box .yview )
177- scrollbar .pack ( side = "right" , fill = "y " )
186+ scrollbar .grid ( row = 0 , column = 1 , sticky = "ns " )
178187 text_box .config (yscrollcommand = scrollbar .set )
179188
180- # Close button
181189 btn_frame = ttk .Frame (win )
182- btn_frame .pack (fill = "x" , pady = (6 , 8 ))
183- close_btn = ttk .Button (btn_frame , text = "Close" , command = win .destroy )
184- close_btn .pack (side = "right" , padx = (0 , 8 ))
190+ btn_frame .grid (row = 1 , column = 0 , sticky = "ew" , pady = (6 , 8 ))
191+ btn_frame .columnconfigure (0 , weight = 1 )
192+ ttk .Button (btn_frame , text = "Close" , command = win .destroy ).grid (row = 0 , column = 0 , sticky = "e" , padx = (0 , 8 ))
193+
185194
186195 def update_and_check (self , _ = None ):
187- if new_updates ():
196+ if new_updates (manual_check = True ):
188197 update ()
189198 else :
190199 messagebox .showinfo ("Update" , "No new updates available." )
191200
192201 def show_logs (self , _ = None ):
193- """Display live-updating logs in a scrollable window with timestamped header and copy button ."""
202+ """Display live-updating logs in a scrollable window with fixed footer buttons ."""
194203 win = tk .Toplevel (self .parent )
195204 win .title ("Application Logs" )
196- win .geometry ("400x500 " )
205+ win .geometry ("600x600 " )
197206 win .transient (self .parent )
198207 center_relative_to_parent (win , self .parent )
199208
209+ # Use grid instead of pack for better control
210+ win .rowconfigure (0 , weight = 1 )
211+ win .columnconfigure (0 , weight = 1 )
212+
200213 frame = ttk .Frame (win , padding = (8 , 8 , 8 , 8 ))
201- frame .pack (fill = "both" , expand = True )
214+ frame .grid (row = 0 , column = 0 , sticky = "nsew" )
215+ frame .rowconfigure (0 , weight = 1 )
216+ frame .columnconfigure (0 , weight = 1 )
202217
203- # Prepare header (once)
218+ # --- Header and text setup ---
204219 header = (
205220 f"=== { self .parent .title ()} ===\n "
206- f"Session started : { datetime .datetime .now ().strftime ('%Y-%m-%d %H:%M:%S' )} \n "
207- f"Python: { sys .version .split ("(" )[0 ]} \n "
221+ f"Logging Started : { datetime .datetime .now ().strftime ('%Y-%m-%d %H:%M:%S' )} \n "
222+ f"Python: { sys .version .split ()[0 ]} \n "
208223 f"Platform: { platform .system ()} ({ platform .machine ()} )\n "
209224 f"Config: { read_file (config_file ())} \n "
210225 f"{ '=' * 30 } \n \n "
211226 )
212227
213- # Combine header and current log contents
214228 initial_text = header + (self .log_stream .getvalue () or "(No logs yet)\n " )
215229
216230 text_box = tk .Text (frame , wrap = "word" )
217231 text_box .insert ("1.0" , initial_text )
218232 text_box .config (state = "disabled" )
219- text_box .pack ( side = "left" , fill = "both" , expand = True )
233+ text_box .grid ( row = 0 , column = 0 , sticky = "nsew" )
220234
221235 scrollbar = ttk .Scrollbar (frame , orient = "vertical" , command = text_box .yview )
222- scrollbar .pack ( side = "right" , fill = "y " )
236+ scrollbar .grid ( row = 0 , column = 1 , sticky = "ns " )
223237 text_box .config (yscrollcommand = scrollbar .set )
224238
239+ # --- Buttons at bottom (separate frame) ---
225240 btn_frame = ttk .Frame (win )
226- btn_frame .pack (fill = "x" , pady = (6 , 8 ))
241+ btn_frame .grid (row = 1 , column = 0 , sticky = "ew" , pady = (6 , 8 ))
242+ btn_frame .columnconfigure (0 , weight = 1 )
243+ btn_frame .columnconfigure (1 , weight = 1 )
227244
228245 def copy_logs ():
229- """Copy full logs (with header) to clipboard."""
230246 full_text = header + self .log_stream .getvalue ()
231247 win .clipboard_clear ()
232248 win .clipboard_append (full_text )
233249 messagebox .showinfo ("Logs" , "Logs copied to clipboard." )
234250
235- ttk .Button (btn_frame , text = "Copy Logs" , command = copy_logs ).pack ( side = "left " , padx = (8 , 0 ))
236- ttk .Button (btn_frame , text = "Close" , command = win .destroy ).pack ( side = "right " , padx = (0 , 8 ))
251+ ttk .Button (btn_frame , text = "Copy Logs" , command = copy_logs ).grid ( row = 0 , column = 0 , sticky = "w " , padx = (8 , 0 ))
252+ ttk .Button (btn_frame , text = "Close" , command = win .destroy ).grid ( row = 0 , column = 1 , sticky = "e " , padx = (0 , 8 ))
237253
238254 # --- Live Updating ---
239255 def refresh_logs ():
0 commit comments