# main.py from functions import * from funct_inlay import * from funct_wp import * from funct_clp import * from funct_gen import * from funct_probe import * import tkinter as tk from tkinter import ttk import yaml folder_parsets = r"\\HOST-BL7\Mabi-Robotic-Produktion\40_PROJEKTE\30_Interne_Projekte\DMU50_Automation\40_SOFTWARE\CONFIGURATOR\DMU50_GUI_PY-main\dmu50_gui_py\_cfg_parsets" folder_pictures = "cfg_picture" folder_output = r"\\HOST-BL7\Mabi-Robotic-Produktion\40_PROJEKTE\30_Interne_Projekte\DMU50_Automation\40_SOFTWARE\CONFIGURATOR\DMU50_GUI_PY-main\dmu50_gui_py\_out" file_def = "def.yml" file_jobs = "jobs.yml" name_newparset = "" name_newjob = "" pic_sizelimit = 250 def _verify_job(job_name): if job_name in ("", name_newjob): messagebox.showwarning("No job selected", "Please select (or save) a job first.") return 0 else: return 1 def _deep_copy(save_dir, gen_dir): if os.path.isdir(save_dir): for name in os.listdir(save_dir): src = os.path.join(save_dir, name) dst = os.path.join(gen_dir, name) if os.path.isdir(src): shutil.copytree(src, dst, dirs_exist_ok=True) else: shutil.copy2(src, dst) def setup_group(frame, group_name, data, input_vars, selected_params, group_widgets): group_data = data['groups'][group_name] picture_name = group_data['cfg']['picture_name'] # Picture name defined in def.yml picture_path = os.path.join(folder_parsets, picture_name) # Full path to the picture param_values = get_existing_parameters(group_name, folder_parsets) param_values.append(name_newparset) param_var = selected_params[group_name] # Bind the dropdown to the corresponding selected_param entry multicolumn_idx = group_data['cfg'].get('multicolumn_idx', []) colspan = (len(multicolumn_idx)+1)*2 # Dropdown for parameter sets frame_parset = ttk.Frame(frame) frame_parset.grid(row=2, column=0, columnspan=colspan, pady=5, sticky="w") param_dropdown = ttk.Combobox(frame_parset, textvariable=param_var, values=param_values, width=14) group_widgets[group_name] = {"param_dropdown": param_dropdown} # Save for later reference source_path = f"{folder_pictures}/{data['general']['notavailablepic_name']}" # Dropdown field ttk.Label(frame_parset, text="Parameter-Set:", width=14).grid(row=0, column=0, padx=5, pady=5, sticky="w") param_dropdown.grid(row=0, column=1, padx=5, pady=5, sticky="w") # Function to update the picture based on inputs or use the init picture def update_inlay_picture(): grpnm = group_name all_group_vars = {group_name: {key: var.get() for key, var in group_vars.items()} for group_name, group_vars in input_vars.items()} # Use the source_path if the selected parameter set is the new parameter set placeholder # print(param_dropdown.get()) #DEBUG if param_dropdown.get() == name_newparset: picture_to_use = source_path else: picture_to_use = picture_path if grpnm == "inlay": generate_picture_inlay(all_group_vars, picture_to_use) elif grpnm == "wp": generate_picture_wp(all_group_vars, picture_to_use) elif grpnm == "clp": generate_picture_clp(all_group_vars, picture_to_use) elif grpnm == "probe": generate_picture_probe(all_group_vars, picture_to_use) group_image_tk = load_image(picture_to_use, pic_sizelimit) image_label.configure(image=group_image_tk) image_label.image = group_image_tk # Keep a reference to avoid garbage collection # Function to load a parameter set def load_selected_param(): selected_param = param_dropdown.get() if selected_param != name_newparset: param_data = load_yaml(group_name, selected_param, folder_parsets) for key, value in param_data.items(): input_vars[group_name][key].set(value) update_inlay_picture() # Function to save the current parameter set def save_current_param(): param_name = param_dropdown.get() if not param_name == name_newparset: if param_name not in param_values and param_name.strip(): param_values.insert(-1, param_name) # Insert before '<>' param_dropdown['values'] = param_values param_dropdown.set(param_name) if name_newparset not in param_values: param_values.append(name_newparset) # Ensure it's only appended once param_data = {key: var.get() for key, var in input_vars[group_name].items()} save_yaml(group_name, param_name, param_data, folder_parsets) update_inlay_picture() # Function to delete the current parameter set def delete_current_param(): param_name = param_dropdown.get() if param_name in param_values and param_name != name_newparset: confirm = messagebox.askyesno("Delete Parameter", f"Are you sure you want to delete '{param_name}'?") if confirm: delete_yaml(group_name, param_name, folder_parsets) param_values.remove(param_name) if name_newparset not in param_values: param_values.append(name_newparset) # Ensure it's only appended once # If there are any parameter sets left, select the next one; otherwise, show name_newparset if param_values and param_values[0] != name_newparset: param_dropdown['values'] = param_values param_dropdown.set(param_values[0]) # Select the first remaining parameter set load_selected_param() # Load the values of the first remaining parameter set else: # If no parameter sets left, show name_newparset param_dropdown['values'] = [name_newparset] param_dropdown.set(name_newparset) # Clear the input fields when no parameter sets are left for key in input_vars[group_name]: input_vars[group_name][key].set('') # Show original picture update_inlay_picture() # Detect and save a new parameter set if entered via dropdown (press Enter) def on_enter_new_param(event): selected_param = param_dropdown.get() if selected_param not in param_values and selected_param.strip(): param_values.insert(-1, selected_param) param_dropdown['values'] = param_values save_current_param() param_dropdown.set(selected_param) if name_newparset not in param_values: param_values.append(name_newparset) # Ensure it's only appended once update_inlay_picture() # Load and display the specific group picture group_image_tk = load_image(source_path, pic_sizelimit) image_label = ttk.Label(frame, image=group_image_tk) image_label.image = group_image_tk # Keep a reference to avoid garbage collection image_label.grid(row=0, column=0, columnspan=colspan, padx=10, pady=10, sticky="") # Center the image in the middle frame.grid_rowconfigure(0, minsize=230) # Add horizontal line ttk.Separator(frame, orient='horizontal').grid(row=1, column=0, columnspan=colspan, sticky="ew", pady=10) # buttons for save/load/delete ttk.Button(frame_parset, text="Save", width=12, command=save_current_param).grid(row=1, column=0, padx=5, pady=5, sticky="w") ttk.Button(frame_parset, text="Delete", width=12, command=delete_current_param).grid(row=1, column=1, padx=5, pady=5, sticky="w") # Add horizontal line ttk.Separator(frame, orient='horizontal').grid(row=3, column=0, columnspan=colspan, sticky="ew", pady=10) # Setup input fields below dropdown and buttons row = 0 col = 0 group_vars = {} frame_var = ttk.Frame(frame) # Kein Innenabstand mehr frame_var.grid(row=4, column=0, columnspan=colspan, padx=5, pady=5, sticky="nsew") for idx, (var_name, _) in enumerate(group_data['parameter'].items()): if idx+1 in multicolumn_idx: col += 2 row = 0 ttk.Label(frame_var, text=VarDescription(var_name), font=("TkDefaultFont", 8)).grid(row=row, column=col, padx=5, pady=5, sticky="e") var = tk.StringVar() group_vars[var_name] = var ttk.Entry(frame_var, textvariable=var, font=("TkDefaultFont", 8), width=7).grid(row=row, column=col + 1, padx=5, pady=5, sticky="w") row += 1 input_vars[group_name] = group_vars # Automatically select and load the first parameter set if param_values and param_values[0] != name_newparset: param_var.set(param_values[0]) load_selected_param() # Directly load the first parameter set else: param_var.set(name_newparset) # If no parameters available, set to default # Bind the dropdown selection event param_dropdown.bind("<>", lambda event: load_selected_param()) # Bind the Enter key to detect when a new parameter set is typed and saved param_dropdown.bind("", on_enter_new_param) # Automatically generate and display the picture at startup update_inlay_picture() def setup_jobs(frame, data, selected_job, selected_params, input_vars, group_widgets): # Load existing jobs from the jobs.yml file job_values = get_existing_parameters("jobs", folder_parsets) job_values.append(name_newjob) # Function to load selected job and update each group def load_selected_job(): job_name = selected_job.get() if job_name != name_newjob: job_data = load_yaml("jobs", job_name, folder_parsets) # Update the parameter sets for each group according to the job for group_name, param_set in job_data.items(): if group_name in selected_params: # Check if the parameter set exists for the group param_values = get_existing_parameters(group_name, folder_parsets) if param_set not in param_values: # If parameter set is missing, set to <> and clear fields selected_params[group_name].set(name_newparset) clear_input_fields(group_name) else: selected_params[group_name].set(param_set) # Manually trigger the parameter dropdown change for the group param_dropdown = group_widgets[group_name]["param_dropdown"] param_dropdown.set(param_set) # Update dropdown to show selected set param_dropdown.event_generate("<>") # Function to clear the input fields when a parameter set is missing def clear_input_fields(group_name): for key in input_vars[group_name]: input_vars[group_name][key].set('') # Clear the input fields # Function to save the current job def save_current_job(): job_name = selected_job.get() if not job_name == name_newjob: if job_name not in job_values and job_name.strip(): job_values.insert(-1, job_name) # Insert before '<>' selected_job.set(job_name) job_data = {group_name: selected_params[group_name].get() for group_name in data['groups']} save_yaml("jobs", job_name, job_data, folder_parsets) # Update job dropdown after saving a new job job_dropdown['values'] = job_values job_dropdown.set(job_name) # Function to delete the selected job def delete_current_job(): job_name = selected_job.get() if job_name in job_values and job_name != name_newjob: confirm = messagebox.askyesno("Delete Job", f"Are you sure you want to delete '{job_name}'?") if confirm: delete_yaml("jobs", job_name, folder_parsets) job_values.remove(job_name) selected_job.set(job_values[0] if job_values else name_newjob) # Update job dropdown after deletion job_dropdown['values'] = job_values load_selected_job() def upload_job_files(): job_name = selected_job.get() if not _verify_job(job_name): return job_safe = make_windows_safe(job_name) save_dir = os.path.join(folder_parsets, "files", job_safe) os.makedirs(save_dir, exist_ok=True) paths = filedialog.askopenfilenames( parent=frame, title=f"Attach file(s) to job: {job_name}" ) if not paths: return copied = 0 for src in paths: try: shutil.copy2(src, save_dir) copied += 1 except Exception as exc: messagebox.showerror("Copy failed", f"Could not copy:\n{src}\n\n{exc}") return messagebox.showinfo("Upload complete", f"{copied} file(s) copied to:\n{save_dir}") def show_job_files(): job_name = selected_job.get() if not _verify_job(job_name): return job_safe = make_windows_safe(job_name) target = os.path.join(folder_parsets, "files", job_safe) if not os.path.isdir(target) or not os.listdir(target): messagebox.showinfo("No files", f"No files found for job “{job_name}”.") return os.startfile(target) # Job dropdown and buttons for save/load/delete ttk.Label(frame, text="Job:").grid(row=0, column=0, padx=5, pady=5, sticky="w") job_dropdown = ttk.Combobox(frame, textvariable=selected_job, values=job_values) job_dropdown.grid(row=0, column=1, padx=5, pady=5) ttk.Button(frame, text="Save", command=save_current_job) \ .grid(row=0, column=2, padx=5, pady=5, sticky="w") ttk.Button(frame, text="Delete", command=delete_current_job) \ .grid(row=0, column=3, padx=5, pady=5, sticky="w") sep = ttk.Separator(frame, orient="vertical") sep.grid(row=0, column=4, sticky="ns", padx=8) # full height of the cell frame.grid_columnconfigure(4, minsize=15) # width of the line ttk.Button(frame, text="Upload Job-Files", command=upload_job_files) \ .grid(row=0, column=5, padx=5, pady=5, sticky="w") ttk.Button(frame, text="Show Job-Files", command=show_job_files) \ .grid(row=0, column=6, padx=5, pady=5, sticky="w") # Bind job selection to loading the selected job and updating the groups job_dropdown.bind("<>", lambda event: load_selected_job()) job_dropdown.bind("", lambda event: save_current_job()) # After setting up all UI components if job_values and job_values[0] != name_newjob: selected_job.set(job_values[0]) # Select the first job automatically load_selected_job() # Load the first job's parameters and update the corresponding parameter sets return load_selected_job def setup_evaluation(root, folder_pictures, data, folder_output, input_vars, selected_params, selected_job): # Create a frame for the EVALUATION section frame = ttk.LabelFrame(root, text="EVALUATION", padding=(10, 10)) frame.grid(row=2, column=0, columnspan=5, padx=10, pady=10, sticky="nsew") # Load and display the specific initial picture picture_path = f"{folder_pictures}/{data['general']['generatorpic_name']}" group_image_tk = load_image(picture_path, 100) # Assuming a function load_image exists and works as expected image_label = ttk.Label(frame, image=group_image_tk) image_label.image = group_image_tk # Keep a reference to avoid garbage collection image_label.grid(row=0, column=0, rowspan=2, padx=10, pady=10) # Function to generate configuration file based on current parameters def btn_gen_cfg_dmuaut1(): job_name = selected_job.get() if not _verify_job(job_name): return job_safe = make_windows_safe(job_name) save_dir = os.path.join(folder_parsets, "files", job_safe) gen_dir = gen_cfg_dmuaut(folder_output, input_vars, selected_params) print(save_dir) print(gen_dir) _deep_copy(save_dir, gen_dir) # def btn_gen_cfg_dmuaut2(): # gen_cfg_dmuaut(folder_output, input_vars, selected_params, filetransfer=True) # def btn_gen_cfg_inlay1(): # gen_cfg_inlay(folder_output, input_vars, selected_params) # def btn_gen_cfg_inlay2(): # gen_cfg_inlay(folder_output, input_vars, selected_params, filetransfer=True) # Place buttons in the vertical frame for operations button_frame = ttk.Frame(frame) button_frame.grid(row=0, column=1, padx=10, pady=10, sticky="n") button1 = ttk.Button(button_frame, text="GEN CFG_DMUAUT.SPF FILE", width=45, command=btn_gen_cfg_dmuaut1) button1.grid(row=0, column=0, padx=10, pady=5) # button2 = ttk.Button(button_frame, text="GEN+TRANSFER CFG_DMUAUT.SPF FILE", width=45, command=btn_gen_cfg_dmuaut2) #button2.grid(row=0, column=1, padx=10, pady=5) #button3 = ttk.Button(button_frame, text="GEN CFG_INLAY.SPF FILE", width=45, command=btn_gen_cfg_inlay1) #button3.grid(row=1, column=0, padx=5, pady=5) #button4 = ttk.Button(button_frame, text="GEN+TRANSFER CFG_INLAY.SPF FILE", width=45, command=btn_gen_cfg_inlay2) #button4.grid(row=1, column=1, padx=5, pady=5) # Drag-and-Drop File Field def main(): # Tkinter root setup root = tk.Tk() root.title("DMU50") root.geometry("+0+0") # Load object definitions from YAML with open(file_def, 'r') as file: data = yaml.safe_load(file) input_vars = {} selected_params = {} # Dictionary to track selected parameters for each group selected_job = tk.StringVar() group_widgets = {} # Dictionary to store widgets related to groups def setup_ui(): # ---- Setup Jobs UI frame = ttk.LabelFrame(root, text="Jobs", padding=(10, 10)) frame.grid(row=0, column=0, columnspan=len(data['groups']), padx=10, pady=10, sticky="nsew") load_selected_job = setup_jobs(frame, data, selected_job, selected_params, input_vars, group_widgets) # ---- Create the UI for each group for col_idx, group_name in enumerate(data['groups']): frame_group = ttk.LabelFrame(root, text=VarDescription(group_name), padding=(10, 10)) frame_group.grid(row=1, column=col_idx, padx=10, pady=10, sticky="nsew") selected_params[group_name] = tk.StringVar() setup_group(frame_group, group_name, data, input_vars, selected_params, group_widgets) root.grid_columnconfigure(list(range(col_idx + 1)), weight=1) root.grid_rowconfigure(0, weight=1) # ---- Setup the EVALUATION section setup_evaluation(root, folder_pictures, data, folder_output, input_vars, selected_params, selected_job) # After the UI is fully set up, load the selected job to update parameter lists load_selected_job() # Ensure this is called to load the job parameters # Show the window after setup is complete root.deiconify() # Initially hide the root window root.withdraw() # Delay UI setup by 1000 milliseconds (1 second) root.after(300, setup_ui) # Start the Tkinter event loop root.mainloop() if __name__ == "__main__": main()