@@ -921,13 +921,77 @@ def gen():
921921 globals ()['tqdm' ] = orig_tqdm
922922
923923def launch_tk_gui ():
924+
925+ def on_run ():
926+ # Gather args from widgets
927+ run_args = {}
928+ for key , label , typ , * opts in fields :
929+ if typ == 'file' :
930+ run_args [key ] = widgets [key ].get ()
931+ elif typ == 'bool' :
932+ run_args [key ] = widgets [key ].get ()
933+ elif typ == 'choice' :
934+ run_args [key ] = widgets [key ].get ()
935+ elif typ == 'int' :
936+ try :
937+ run_args [key ] = int (widgets [key ].get ())
938+ except Exception :
939+ run_args [key ] = 0
940+
941+ # Validate required fields
942+ if not run_args ['catalog' ]:
943+ messagebox .showerror ("Error" , "Catalog file is required." )
944+ return
945+ # Save config
946+ save_config (run_args )
947+ # Disable UI
948+ for w in widgets .values ():
949+ if hasattr (w , 'config' ):
950+ w .config (state = 'disabled' )
951+ progress1 ['value' ] = 0
952+ progress2 ['value' ] = 0
953+ status ['text' ] = "Running..."
954+ root .update ()
955+
956+ # Run in thread to avoid blocking UI
957+ def run_job ():
958+ try :
959+ def prog_cb (bar_id , i , total ):
960+ if bar_id == 0 :
961+ progress1 ['maximum' ] = total
962+ progress1 ['value' ] = i
963+ status1 ['text' ] = f"Processing images { i } /{ total } "
964+ elif bar_id == 1 :
965+ progress2 ['maximum' ] = total
966+ progress2 ['value' ] = i
967+ status2 ['text' ] = f"Waiting for threads { i } /{ total } "
968+ root .update_idletasks ()
969+ run_with_args (run_args , progress_callback = prog_cb )
970+ status ['text' ] = "Done!"
971+ except Exception as e :
972+ status ['text' ] = f"Error: { e } "
973+ messagebox .showerror ("Error" , str (e ))
974+ finally :
975+ for w in widgets .values ():
976+ if hasattr (w , 'config' ):
977+ w .config (state = 'normal' )
978+ threading .Thread (target = run_job , daemon = True ).start ()
979+
924980 args = get_arg_defaults ()
925981 root = tk .Tk ()
926982 root .title ("Lightroom Face Metadata Sync" )
927983 try :
928984 root .iconbitmap ('Lightroom_Face_to_Metadata.ico' )
929985 except Exception :
930986 pass # Ignore if icon file is missing or not supported
987+
988+ # Set minimum window size (width x height)
989+ root .minsize (500 , 500 )
990+
991+ # Allow columns to expand with window resize
992+ root .columnconfigure (0 , weight = 0 )
993+ root .columnconfigure (1 , weight = 3 )
994+ root .columnconfigure (2 , weight = 0 )
931995
932996 fields = [
933997 ('catalog' , 'Lightroom Catalog (.lrcat)' , 'file' ),
@@ -944,11 +1008,11 @@ def launch_tk_gui():
9441008 widgets = {}
9451009 row = 0
9461010 for key , label , typ , * opts in fields :
947- tk .Label (root , text = label ).grid (row = row , column = 0 , sticky = 'w' )
1011+ tk .Label (root , text = label ).grid (row = row , column = 0 , sticky = 'w' , padx = ( 10 , 0 ) )
9481012 if typ == 'file' :
9491013 entry = tk .Entry (root , width = 50 )
950- entry .insert (0 , str (args .get (key , '' ))) # <-- fix here
951- entry .grid (row = row , column = 1 )
1014+ entry .insert (0 , str (args .get (key , '' )))
1015+ entry .grid (row = row , column = 1 , sticky = 'ew' , padx = ( 0 , 5 ) )
9521016 def browse (entry = entry , key = key ):
9531017 if key == 'catalog' :
9541018 f = filedialog .askopenfilename (filetypes = [('Lightroom Catalog' , '*.lrcat' )])
@@ -964,17 +1028,17 @@ def browse(entry=entry, key=key):
9641028 entry .delete (0 , tk .END )
9651029 entry .insert (0 , f )
9661030 btn = tk .Button (root , text = "Browse" , command = browse )
967- btn .grid (row = row , column = 2 )
1031+ btn .grid (row = row , column = 2 , padx = ( 0 , 10 ) )
9681032 widgets [key ] = entry
9691033 elif typ == 'bool' :
9701034 var = tk .BooleanVar (value = bool (args .get (key , False )))
9711035 chk = tk .Checkbutton (root , variable = var )
972- chk .grid (row = row , column = 1 , sticky = 'w' )
1036+ chk .grid (row = row , column = 1 , sticky = 'w' , padx = ( 0 , 5 ) )
9731037 widgets [key ] = var
9741038 elif typ == 'choice' :
9751039 var = tk .StringVar (value = args .get (key , opts [0 ][0 ]))
9761040 combo = ttk .Combobox (root , textvariable = var , values = opts [0 ], state = 'readonly' )
977- combo .grid (row = row , column = 1 , sticky = 'w' )
1041+ combo .grid (row = row , column = 1 , sticky = 'w' , padx = ( 0 , 5 ) )
9781042 widgets [key ] = var
9791043 elif typ == 'int' :
9801044 val = args .get (key , 0 )
@@ -984,90 +1048,45 @@ def browse(entry=entry, key=key):
9841048 val = 0
9851049 var = tk .StringVar (value = str (val ))
9861050 entry = tk .Entry (root , textvariable = var , width = 10 )
987- entry .grid (row = row , column = 1 , sticky = 'w' )
1051+ entry .grid (row = row , column = 1 , sticky = 'w' , padx = ( 0 , 5 ) )
9881052 widgets [key ] = var
9891053 row += 1
9901054
9911055 # Add two progress bars
9921056 label1 = tk .Label (root , text = "Processing images" )
993- label1 .grid (row = row , column = 0 , sticky = 'w' )
1057+ label1 .grid (row = row , column = 0 , sticky = 'w' , padx = ( 5 , 0 ) )
9941058 row += 1
9951059 progress1 = ttk .Progressbar (root , orient = 'horizontal' , length = 400 , mode = 'determinate' )
996- progress1 .grid (row = row , column = 0 , columnspan = 3 , pady = 5 )
1060+ progress1 .grid (row = row , column = 0 , columnspan = 3 , pady = 5 , sticky = 'ew' , padx = 10 )
1061+ root .rowconfigure (row , weight = 0 )
9971062 row += 1
9981063 status1 = tk .Label (root , text = "" )
999- status1 .grid (row = row , column = 0 , columnspan = 3 )
1064+ status1 .grid (row = row , column = 0 , columnspan = 3 , sticky = 'ew' , padx = 10 )
10001065 row += 1
10011066 label2 = tk .Label (root , text = "Waiting for threads" )
1002- label2 .grid (row = row , column = 0 , sticky = 'w' )
1067+ label2 .grid (row = row , column = 0 , sticky = 'w' , padx = ( 5 , 0 ) )
10031068 row += 1
10041069 progress2 = ttk .Progressbar (root , orient = 'horizontal' , length = 400 , mode = 'determinate' )
1005- progress2 .grid (row = row , column = 0 , columnspan = 3 , pady = 5 )
1070+ progress2 .grid (row = row , column = 0 , columnspan = 3 , pady = 5 , sticky = 'ew' , padx = 10 )
1071+ root .rowconfigure (row , weight = 0 )
10061072 row += 1
10071073 status2 = tk .Label (root , text = "" )
1008- status2 .grid (row = row , column = 0 , columnspan = 3 )
1074+ status2 .grid (row = row , column = 0 , columnspan = 3 , sticky = 'ew' , padx = 10 )
10091075 row += 1
10101076 status = tk .Label (root , text = "" )
1011- status .grid (row = row , column = 0 , columnspan = 3 )
1077+ status .grid (row = row , column = 0 , columnspan = 3 , sticky = 'ew' , padx = 10 )
10121078
1013- def on_run ():
1014- # Gather args from widgets
1015- run_args = {}
1016- for key , label , typ , * opts in fields :
1017- if typ == 'file' :
1018- run_args [key ] = widgets [key ].get ()
1019- elif typ == 'bool' :
1020- run_args [key ] = widgets [key ].get ()
1021- elif typ == 'choice' :
1022- run_args [key ] = widgets [key ].get ()
1023- elif typ == 'int' :
1024- try :
1025- run_args [key ] = int (widgets [key ].get ())
1026- except Exception :
1027- run_args [key ] = 0
1028-
1029- # Validate required fields
1030- if not run_args ['catalog' ]:
1031- messagebox .showerror ("Error" , "Catalog file is required." )
1032- return
1033- # Save config
1034- save_config (run_args )
1035- # Disable UI
1036- for w in widgets .values ():
1037- if hasattr (w , 'config' ):
1038- w .config (state = 'disabled' )
1039- progress1 ['value' ] = 0
1040- progress2 ['value' ] = 0
1041- status ['text' ] = "Running..."
1042- root .update ()
1043- # Run in thread to avoid blocking UI
1044- def run_job ():
1045- try :
1046- def prog_cb (bar_id , i , total ):
1047- if bar_id == 0 :
1048- progress1 ['maximum' ] = total
1049- progress1 ['value' ] = i
1050- status1 ['text' ] = f"Processing images { i } /{ total } "
1051- elif bar_id == 1 :
1052- progress2 ['maximum' ] = total
1053- progress2 ['value' ] = i
1054- status2 ['text' ] = f"Waiting for threads { i } /{ total } "
1055- root .update_idletasks ()
1056- run_with_args (run_args , progress_callback = prog_cb )
1057- status ['text' ] = "Done!"
1058- except Exception as e :
1059- status ['text' ] = f"Error: { e } "
1060- messagebox .showerror ("Error" , str (e ))
1061- finally :
1062- for w in widgets .values ():
1063- if hasattr (w , 'config' ):
1064- w .config (state = 'normal' )
1065- threading .Thread (target = run_job , daemon = True ).start ()
1079+ button_frame = tk .Frame (root )
1080+ button_frame .grid (row = row + 2 , column = 0 , columnspan = 3 , sticky = 'ew' , pady = 10 , padx = 10 )
1081+ root .rowconfigure (row + 2 , weight = 1 )
1082+ button_frame .columnconfigure (0 , weight = 1 )
1083+ button_frame .columnconfigure (1 , weight = 1 )
1084+
1085+ run_btn = tk .Button (button_frame , text = "Run" , command = on_run )
1086+ run_btn .grid (row = 0 , column = 0 , padx = 5 , sticky = 'ew' )
1087+ quit_btn = tk .Button (button_frame , text = "Quit" , command = root .destroy )
1088+ quit_btn .grid (row = 0 , column = 1 , padx = 5 , sticky = 'ew' )
10661089
1067- run_btn = tk .Button (root , text = "Run" , command = on_run )
1068- run_btn .grid (row = row + 2 , column = 0 , pady = 10 )
1069- quit_btn = tk .Button (root , text = "Quit" , command = root .destroy )
1070- quit_btn .grid (row = row + 2 , column = 1 , pady = 10 )
10711090 root .mainloop ()
10721091
10731092if __name__ == '__main__' :
0 commit comments