Compare commits
	
		
			No commits in common. "b5124686bf6a8f29838ac4280f0e3321a9b084de" and "027a88342e70367055f8ac2566cd2c3bb30d6011" have entirely different histories. 
		
	
	
		
			b5124686bf
			...
			027a88342e
		
	
		|  | @ -1,3 +1 @@ | ||||||
| *.swp | *.swp | ||||||
| __pycache__ |  | ||||||
| .spyproject |  | ||||||
|  |  | ||||||
							
								
								
									
										73
									
								
								config.py
								
								
								
								
							
							
						
						
									
										73
									
								
								config.py
								
								
								
								
							|  | @ -1,73 +0,0 @@ | ||||||
| import os, sys, json |  | ||||||
| import tkinter as tk |  | ||||||
| from tkinter import ttk |  | ||||||
| import gui_helper, util |  | ||||||
| 
 |  | ||||||
| class Config: |  | ||||||
|     def __init__(self, file_path, default_config): |  | ||||||
|         self.__file_path = file_path |  | ||||||
|         self.__default_config = default_config |  | ||||||
|         self.__current_config = {} |  | ||||||
| 
 |  | ||||||
|         if os.path.isfile(file_path): |  | ||||||
|             try: |  | ||||||
|                 config_file = open(self.__file_path, "r") |  | ||||||
|                 self.__current_config = json.loads(config_file.read()) |  | ||||||
|                 config_file.close() |  | ||||||
|             except: |  | ||||||
|                 util.error("An exception occurred while trying to load the configuration.", handle_gracefully=False) |  | ||||||
|         else: |  | ||||||
|             # config not found |  | ||||||
|             dialog_interaction_handler = gui_helper.Window_Interaction_Handler() |  | ||||||
| 
 |  | ||||||
|             dialog = tk.Tk() |  | ||||||
|             dialog.title("No configuration found") |  | ||||||
|             ttk.Label(dialog, text="No configuration found!").pack() |  | ||||||
|             buttons_frame = tk.Frame(dialog) |  | ||||||
|             buttons_frame.pack() |  | ||||||
|             ttk.Button(buttons_frame, text="Create", command=lambda: dialog_interaction_handler.interact("create", True, additional_action=dialog.destroy)).grid(column=0, row=0) |  | ||||||
|             ttk.Button(buttons_frame, text="Quit", command=lambda: dialog_interaction_handler.interact("create", False, additional_action=dialog.destroy)).grid(column=1, row=0) |  | ||||||
|             dialog.resizable(0,0) |  | ||||||
|             dialog.mainloop() |  | ||||||
| 
 |  | ||||||
|             if dialog_interaction_handler.get_result("create"): |  | ||||||
|                 self.__current_config = default_config |  | ||||||
|                 try: |  | ||||||
|                     config_file = open(self.__file_path, "w") |  | ||||||
|                     config_file.write(json.dumps(self.__current_config)) |  | ||||||
|                     config_file.close() |  | ||||||
|                 except: |  | ||||||
|                     util.warn("Failed to save initial config file.", is_exception=True) |  | ||||||
|                     dialog = tk.Tk() |  | ||||||
|                     dialog.title("Failed to save initial config file") |  | ||||||
|                     ttk.Label(dialog, text="Failed to save the initial config file.\n" + |  | ||||||
|                                     "The IDE can still run, but it is likely that all changes to the configuration will be lost where they would be saved otherwise.").pack() |  | ||||||
|                     ttk.Button(dialog, text="Continue", command=dialog.destroy).pack() |  | ||||||
|                     dialog.resizable(0,0) |  | ||||||
|                     dialog.mainloop() |  | ||||||
|             else: |  | ||||||
|                 util.error("No config present and user chose not to create one. Exiting.", is_exception=False, handle_gracefully=True) |  | ||||||
|                 # exit with success exit code anyway because this is not a program failure |  | ||||||
|                 sys.exit(util.EXIT_SUCCESS) |  | ||||||
| 
 |  | ||||||
|     def get_configuration_value(self, key): |  | ||||||
|         if not key in self.__current_config: |  | ||||||
|             util.info("Requested configuration value for "+str(key)+" not in configuration. Loading from default configuration.") |  | ||||||
|             try: |  | ||||||
|                 self.__current_config[key] = self.__default_config[key] |  | ||||||
|             except KeyError: |  | ||||||
|                 util.error("Requested an invalid configuration key.") |  | ||||||
|                 return None |  | ||||||
|         return self.__current_config[key] |  | ||||||
| 
 |  | ||||||
|     def set_configuration_value(self, key, value, save_to_disk=True): |  | ||||||
|         if not key in self.__current_config: |  | ||||||
|             util.info("Writing configuration for previously unknown key "+str(key)+".") |  | ||||||
|         self.__current_config[key]=value |  | ||||||
|         if save_to_disk: |  | ||||||
|             try: |  | ||||||
|                 config_file = open(self.__file_path, "w") |  | ||||||
|                 config_file.write(json.dumps(self.__current_config)) |  | ||||||
|                 config_file.close() |  | ||||||
|             except: |  | ||||||
|                 util.error("Failed to save config file.") |  | ||||||
							
								
								
									
										132
									
								
								gui_helper.py
								
								
								
								
							
							
						
						
									
										132
									
								
								gui_helper.py
								
								
								
								
							|  | @ -1,132 +0,0 @@ | ||||||
| import tkinter as tk |  | ||||||
| import util |  | ||||||
| 
 |  | ||||||
| # easy way to get data out of window events |  | ||||||
| class Window_Interaction_Handler: |  | ||||||
|     # constructor |  | ||||||
|     def __init__(self): |  | ||||||
|         self.__window_interactions = {} |  | ||||||
| 
 |  | ||||||
|     # add a result for an interaction event, saves results in reverse order by default, can optionally call another function |  | ||||||
|     def interact(self, name, result, reverse_order=True, additional_action=None, additional_action_parameters=()): |  | ||||||
|         if name in self.__window_interactions: |  | ||||||
|             if reverse_order: |  | ||||||
|                 self.__window_interactions[name] = [result] + self.__window_interactions[name] |  | ||||||
|             else: |  | ||||||
|                 self.__window_interactions[name] = self.__window_interactions[name] + [result] |  | ||||||
|         else: |  | ||||||
|             self.__window_interactions[name] = [result] |  | ||||||
| 
 |  | ||||||
|         if not additional_action==None: |  | ||||||
|             additional_action(*additional_action_parameters) |  | ||||||
| 
 |  | ||||||
|     # get first result for a given event from the list of results (newest (default) or oldest), removes the returned result from the list by default |  | ||||||
|     def get_result(self, name, remove=True): |  | ||||||
|         if name in self.__window_interactions and len(self.__window_interactions[name])>0: |  | ||||||
|             if remove: |  | ||||||
|                 result = self.__window_interactions[name].pop(0) |  | ||||||
|                 if len(self.__window_interactions[name])==0: |  | ||||||
|                     del self.__window_interactions[name] |  | ||||||
|                 return result |  | ||||||
|             else: |  | ||||||
|                 return self.__window_interactions[name][0] |  | ||||||
|         else: |  | ||||||
|             return None |  | ||||||
| 
 |  | ||||||
|     # get all results for a given event |  | ||||||
|     def get_results(self, name, clear=False): |  | ||||||
|         if name in self.__window_interactions and len(self.__window_interactions[name])>0: |  | ||||||
|             results = self.__window_interactions[name] |  | ||||||
|             if clear: |  | ||||||
|                 del self.__window_interactions[name] |  | ||||||
|             return results |  | ||||||
| 
 |  | ||||||
|     # clear results for a given event |  | ||||||
|     def clear(self, name): |  | ||||||
|         if name in self.__window_interactions: |  | ||||||
|             del self.__window_interactions[name] |  | ||||||
| 
 |  | ||||||
|     # destructor |  | ||||||
|     def __del__(self): |  | ||||||
|         if len(self.__window_interactions)>0: |  | ||||||
|             util.warn("__window_interactions not empty upon destruction of Window_Interaction_Handler:\n"+str(self.__window_interactions)) |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| def not_implemented(): |  | ||||||
|     util.warn("Not implemented!") |  | ||||||
| 
 |  | ||||||
| # format: |  | ||||||
| #   "":{} -> menu or submenu |  | ||||||
| #   "":function -> menu entry |  | ||||||
| #   "":None -> disabled menu entry |  | ||||||
| #   None:None -> separator |  | ||||||
| # |  | ||||||
| # Entries with ... at the end are supposed to open dialogs whereas entries without dots are supposed to take effect immediately |  | ||||||
| menu_structure = { |  | ||||||
|     "IDE": { |  | ||||||
|         "Preferences...": not_implemented, |  | ||||||
|         None: None, |  | ||||||
|         "Quit": not_implemented |  | ||||||
|     }, |  | ||||||
|     "Project": { |  | ||||||
|         "New": { |  | ||||||
|             "No known project types": None |  | ||||||
|         }, |  | ||||||
|         "Open...": not_implemented, |  | ||||||
|         "Close": { |  | ||||||
|             "No open projects": None |  | ||||||
|         }, |  | ||||||
|         None: None, |  | ||||||
|         "Preferences...": not_implemented, |  | ||||||
|         "Search...": not_implemented, |  | ||||||
|         "Build": not_implemented |  | ||||||
|     }, |  | ||||||
|     "File": { |  | ||||||
|         "New...": not_implemented, |  | ||||||
|         "Open...": not_implemented, |  | ||||||
|         "Save": not_implemented, |  | ||||||
|         "Close": not_implemented, |  | ||||||
|         None: None, |  | ||||||
|         "Rename...": not_implemented, |  | ||||||
|         "Move...": not_implemented, |  | ||||||
|         "View in File Explorer...": not_implemented |  | ||||||
|     }, |  | ||||||
|     "Edit": { |  | ||||||
|         "Cut": not_implemented, |  | ||||||
|         "Copy": not_implemented, |  | ||||||
|         "Paste": not_implemented, |  | ||||||
|         "Move code...": not_implemented, |  | ||||||
|         None: None, |  | ||||||
|         "Search and Replace...": not_implemented, |  | ||||||
|         None: None, |  | ||||||
|         "Format": not_implemented, |  | ||||||
|         "Indent": not_implemented, |  | ||||||
|         "Unindent": not_implemented, |  | ||||||
|         "Toggle Comment": not_implemented |  | ||||||
|     }, |  | ||||||
|     "View": { |  | ||||||
|         "Zoom in": not_implemented, |  | ||||||
|         "Zoom out": not_implemented, |  | ||||||
|         "Normal Size": not_implemented |  | ||||||
|     }, |  | ||||||
|     "Help": { |  | ||||||
|         "Manual...": not_implemented, |  | ||||||
|         "About IDE...": not_implemented, |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| def build_menu(structure_dict, menu): |  | ||||||
|     for entry in structure_dict: |  | ||||||
|         if structure_dict[entry]==None: |  | ||||||
|             if entry==None: |  | ||||||
|                 menu.add_separator() |  | ||||||
|             else: |  | ||||||
|                 menu.add_command(label=entry) |  | ||||||
|                 menu.entryconfig(entry, state="disabled") |  | ||||||
|         if isinstance(structure_dict[entry], dict): |  | ||||||
|             submenu = tk.Menu(menu, tearoff=False) |  | ||||||
|             build_menu(structure_dict[entry], submenu) |  | ||||||
|             menu.add_cascade(label=entry, menu=submenu) |  | ||||||
|         if callable(structure_dict[entry]): |  | ||||||
|             menu.add_command(label=entry, command=structure_dict[entry]) |  | ||||||
| 
 |  | ||||||
							
								
								
									
										238
									
								
								main.py
								
								
								
								
							
							
						
						
									
										238
									
								
								main.py
								
								
								
								
							|  | @ -1,24 +1,164 @@ | ||||||
| #!/usr/bin/python3 | #!/usr/bin/python3 | ||||||
| import os |  | ||||||
| import tkinter as tk | import tkinter as tk | ||||||
| from tkinter import ttk | from tkinter import ttk | ||||||
| import gui_helper | import sys, os, json, traceback | ||||||
| from config import Config |  | ||||||
| 
 | 
 | ||||||
| ################################################################################ | ################################################################################ | ||||||
| # CONSTANTS | # DEFINITIONS | ||||||
| ################################################################################ | ################################################################################ | ||||||
| 
 | 
 | ||||||
|  | EXIT_SUCCESS=0 | ||||||
|  | EXIT_ERROR=1 | ||||||
|  | 
 | ||||||
| default_configuration = { | default_configuration = { | ||||||
|     "window geometry": "640x480" |     "window geometry": "640x480" | ||||||
| } | } | ||||||
| configuration_file_path = os.path.join(os.path.expanduser("~"), "some_ide_config.json") | # empty configuration by default because new values will be added while trying to load them | ||||||
|  | # this allows the IDE to load old configuration files with missing keys | ||||||
|  | configuration = {} | ||||||
|  | home_directory = os.path.expanduser("~") | ||||||
|  | config_file_path = os.path.join(home_directory, "some_ide_config.json") | ||||||
|  | 
 | ||||||
|  | def info(message): | ||||||
|  |     # print info to sys.stderr because it isn’t really output, just debug information | ||||||
|  |     print("INFO: "+str(message), file=sys.stderr) | ||||||
|  |     traceback.print_stack() | ||||||
|  | 
 | ||||||
|  | def warn(message, is_exception=False): | ||||||
|  |     print("WARNING: "+str(message), file=sys.stderr) | ||||||
|  |     if is_exception: | ||||||
|  |         traceback.print_exc() | ||||||
|  |     else: | ||||||
|  |         traceback.print_stack() | ||||||
|  | 
 | ||||||
|  | def error(message, is_exception=True, handle_gracefully=True): | ||||||
|  |     print("ERROR: "+str(message), file=sys.stderr) | ||||||
|  |     if is_exception: | ||||||
|  |         traceback.print_exc() | ||||||
|  |     else: | ||||||
|  |         traceback.print_stack() | ||||||
|  |     if handle_gracefully: | ||||||
|  |         pass | ||||||
|  |     else: | ||||||
|  |         sys.exit(EXIT_ERROR) | ||||||
|  | 
 | ||||||
|  | def get_configuration_value(key): | ||||||
|  |     if key in configuration: | ||||||
|  |         pass | ||||||
|  |     else: | ||||||
|  |         info("Requested configuration value for "+str(key)+" not in configuration. Loading from default configuration.") | ||||||
|  |         try: | ||||||
|  |             configuration[key] = default_configuration[key] | ||||||
|  |         except KeyError: | ||||||
|  |             error("Requested an invalid configuration key.") | ||||||
|  |             return None | ||||||
|  |     return configuration[key] | ||||||
|  | 
 | ||||||
|  | def set_configuration_value(key, value, save_to_disk=True): | ||||||
|  |     if not key in configuration: | ||||||
|  |         info("Writing configuration for previously unknown key "+str(key)+".") | ||||||
|  |     configuration[key]=value | ||||||
|  |     try: | ||||||
|  |         config_file = open(config_file_path, "w") | ||||||
|  |         config_file.write(json.dumps(configuration)) | ||||||
|  |         config_file.close() | ||||||
|  |     except: | ||||||
|  |         error("Failed to save config file.") | ||||||
|  | 
 | ||||||
|  | # easy way to get data out of window events | ||||||
|  | class Window_Interaction_Handler: | ||||||
|  |     # constructor | ||||||
|  |     def __init__(self): | ||||||
|  |         self.__window_interactions = {} | ||||||
|  | 
 | ||||||
|  |     # add a result for an interaction event, saves results in reverse order by default, can optionally call another function | ||||||
|  |     def interact(self, name, result, reverse_order=True, additional_action=None, additional_action_parameters=()): | ||||||
|  |         if name in self.__window_interactions: | ||||||
|  |             if reverse_order: | ||||||
|  |                 self.__window_interactions[name] = [result] + self.__window_interactions[name] | ||||||
|  |             else: | ||||||
|  |                 self.__window_interactions[name] = self.__window_interactions[name] + [result] | ||||||
|  |         else: | ||||||
|  |             self.__window_interactions[name] = [result] | ||||||
|  | 
 | ||||||
|  |         if not additional_action==None: | ||||||
|  |             additional_action(*additional_action_parameters) | ||||||
|  | 
 | ||||||
|  |     # get first result for a given event from the list of results (newest (default) or oldest), removes the returned result from the list by default | ||||||
|  |     def get_result(self, name, remove=True): | ||||||
|  |         if name in self.__window_interactions and len(self.__window_interactions[name])>0: | ||||||
|  |             if remove: | ||||||
|  |                 result = self.__window_interactions[name].pop(0) | ||||||
|  |                 if len(self.__window_interactions[name])==0: | ||||||
|  |                     del self.__window_interactions[name] | ||||||
|  |                 return result | ||||||
|  |             else: | ||||||
|  |                 return self.__window_interactions[name][0] | ||||||
|  |         else: | ||||||
|  |             return None | ||||||
|  | 
 | ||||||
|  |     # get all results for a given event | ||||||
|  |     def get_results(self, name, clear=False): | ||||||
|  |         if name in self.__window_interactions and len(self.__window_interactions[name])>0: | ||||||
|  |             results = self.__window_interactions[name] | ||||||
|  |             if clear: | ||||||
|  |                 del self.__window_interactions[name] | ||||||
|  |             return results | ||||||
|  | 
 | ||||||
|  |     # clear results for a given event | ||||||
|  |     def clear(self, name): | ||||||
|  |         if name in self.__window_interactions: | ||||||
|  |             del self.__window_interactions[name] | ||||||
|  | 
 | ||||||
|  |     # destructor | ||||||
|  |     def __del__(self): | ||||||
|  |         if len(self.__window_interactions)>0: | ||||||
|  |             warn("__window_interactions not empty upon destruction of Window_Interaction_Handler:\n"+str(self.__window_interactions)) | ||||||
| 
 | 
 | ||||||
| ################################################################################ | ################################################################################ | ||||||
| # PROGRAM STARTUP | # PROGRAM STARTUP | ||||||
| ################################################################################ | ################################################################################ | ||||||
| 
 | 
 | ||||||
| configuration = Config(configuration_file_path, default_configuration) | # read configuration | ||||||
|  | if os.path.isfile(config_file_path): | ||||||
|  |     try: | ||||||
|  |         config_file = open(config_file_path, "r") | ||||||
|  |         configuration = json.loads(config_file.read()) | ||||||
|  |         config_file.close() | ||||||
|  |     except Exception: | ||||||
|  |         error("An exception occurred while trying to load the configuration.", handle_gracefully=False) | ||||||
|  | else: | ||||||
|  |     # config not found | ||||||
|  |     dialog_interaction_handler = Window_Interaction_Handler() | ||||||
|  | 
 | ||||||
|  |     dialog = tk.Tk() | ||||||
|  |     dialog.title("No configuration found") | ||||||
|  |     ttk.Label(dialog, text="No configuration found!").pack() | ||||||
|  |     buttons_frame = tk.Frame(dialog) | ||||||
|  |     buttons_frame.pack() | ||||||
|  |     ttk.Button(buttons_frame, text="Create", command=lambda: dialog_interaction_handler.interact("create", True, additional_action=dialog.destroy)).grid(column=0, row=0) | ||||||
|  |     ttk.Button(buttons_frame, text="Quit", command=lambda: dialog_interaction_handler.interact("create", False, additional_action=dialog.destroy)).grid(column=1, row=0) | ||||||
|  |     dialog.resizable(0,0) | ||||||
|  |     dialog.mainloop() | ||||||
|  | 
 | ||||||
|  |     if dialog_interaction_handler.get_result("create"): | ||||||
|  |         try: | ||||||
|  |             config_file = open(config_file_path, "w") | ||||||
|  |             config_file.write(json.dumps(default_configuration)) | ||||||
|  |             config_file.close() | ||||||
|  |         except: | ||||||
|  |             warn("Failed to save initial config file.", is_exception=True) | ||||||
|  |             dialog = tk.Tk() | ||||||
|  |             dialog.title("Failed to save initial config file") | ||||||
|  |             ttk.Label(dialog, text="Failed to save the initial config file.\n" + | ||||||
|  |                             "The IDE can still start up, but it is likely that all changes to the configuration will be lost where they would be saved otherwise.").pack() | ||||||
|  |             ttk.Button(dialog, text="Continue", command=dialog.destroy).pack() | ||||||
|  |             dialog.resizable(0,0) | ||||||
|  |             dialog.mainloop() | ||||||
|  |     else: | ||||||
|  |         error("No config present and user chose not to create one. Exiting.", is_exception=False, handle_gracefully=True) | ||||||
|  |         # exit with success exit code anyway because this is not a program failure | ||||||
|  |         sys.exit(EXIT_SUCCESS) | ||||||
| 
 | 
 | ||||||
| ################################################################################ | ################################################################################ | ||||||
| # PROGRAM MAIN WINDOW | # PROGRAM MAIN WINDOW | ||||||
|  | @ -26,18 +166,94 @@ configuration = Config(configuration_file_path, default_configuration) | ||||||
| 
 | 
 | ||||||
| main_window = tk.Tk() | main_window = tk.Tk() | ||||||
| main_window.title("IDE") | main_window.title("IDE") | ||||||
| main_window.geometry(configuration.get_configuration_value("window geometry")) | main_window.geometry(get_configuration_value("window geometry")) | ||||||
|  | 
 | ||||||
|  | def not_implemented(): | ||||||
|  |     warn("Not implemented!") | ||||||
|  | 
 | ||||||
|  | # format: | ||||||
|  | #   "":{} -> menu or submenu | ||||||
|  | #   "":function -> menu entry | ||||||
|  | #   "":None -> disabled menu entry | ||||||
|  | #   None:None -> separator | ||||||
|  | # | ||||||
|  | # Entries with ... at the end are supposed to open dialogs whereas entries without dots are supposed to take effect immediately | ||||||
|  | menu_structure = { | ||||||
|  |     "IDE": { | ||||||
|  |         "Preferences...": not_implemented, | ||||||
|  |         "Quit": not_implemented | ||||||
|  |     }, | ||||||
|  |     "Project": { | ||||||
|  |         "New": { | ||||||
|  |             "No known project types": None | ||||||
|  |         }, | ||||||
|  |         "Open...": not_implemented, | ||||||
|  |         "Close": { | ||||||
|  |             "No open projects": None | ||||||
|  |         }, | ||||||
|  |         None: None, | ||||||
|  |         "Preferences...": not_implemented, | ||||||
|  |         "Search...": not_implemented, | ||||||
|  |         "Build": not_implemented | ||||||
|  |     }, | ||||||
|  |     "File": { | ||||||
|  |         "New...": not_implemented, | ||||||
|  |         "Open...": not_implemented, | ||||||
|  |         "Save": not_implemented, | ||||||
|  |         "Close": not_implemented, | ||||||
|  |         None: None, | ||||||
|  |         "Rename...": not_implemented, | ||||||
|  |         "Move...": not_implemented, | ||||||
|  |         "View in File Explorer...": not_implemented | ||||||
|  |     }, | ||||||
|  |     "Edit": { | ||||||
|  |         "Cut": not_implemented, | ||||||
|  |         "Copy": not_implemented, | ||||||
|  |         "Paste": not_implemented, | ||||||
|  |         None: None, | ||||||
|  |         "Search and Replace...": not_implemented, | ||||||
|  |         None: None, | ||||||
|  |         "Format": not_implemented, | ||||||
|  |         "Indent": not_implemented, | ||||||
|  |         "Unindent": not_implemented, | ||||||
|  |         "Toggle Comment": not_implemented | ||||||
|  |     }, | ||||||
|  |     "View": { | ||||||
|  |         "Zoom in": not_implemented, | ||||||
|  |         "Zoom out": not_implemented, | ||||||
|  |         "Normal Size": not_implemented | ||||||
|  |     }, | ||||||
|  |     "Help": { | ||||||
|  |         "Manual...": not_implemented, | ||||||
|  |         "About IDE...": not_implemented, | ||||||
|  |     } | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
| menubar = None | menubar = None | ||||||
| def rebuild_menu(structure_dict): | def build_menu(structure_dict, menu): | ||||||
|  |     for entry in structure_dict: | ||||||
|  |         if structure_dict[entry]==None: | ||||||
|  |             if entry==None: | ||||||
|  |                 menu.add_separator() | ||||||
|  |             else: | ||||||
|  |                 menu.add_command(label=entry) | ||||||
|  |                 menu.entryconfig(entry, state="disabled") | ||||||
|  |         if isinstance(structure_dict[entry], dict): | ||||||
|  |             submenu = tk.Menu(menu, tearoff=False) | ||||||
|  |             build_menu(structure_dict[entry], submenu) | ||||||
|  |             menu.add_cascade(label=entry, menu=submenu) | ||||||
|  |         if callable(structure_dict[entry]): | ||||||
|  |             menu.add_command(label=entry, command=structure_dict[entry]) | ||||||
|  | 
 | ||||||
|  | def rebuild_menu(): | ||||||
|     menubar = tk.Menu(main_window) |     menubar = tk.Menu(main_window) | ||||||
|     gui_helper.build_menu(structure_dict, menubar) |     build_menu(menu_structure, menubar) | ||||||
|     main_window.config(menu=menubar) |     main_window.config(menu=menubar) | ||||||
| 
 | 
 | ||||||
| rebuild_menu(gui_helper.menu_structure) | rebuild_menu() | ||||||
| 
 | 
 | ||||||
| def handle_exit(): | def handle_exit(): | ||||||
|     configuration.set_configuration_value("window geometry", main_window.geometry()) |     set_configuration_value("window geometry", main_window.geometry()) | ||||||
|     main_window.destroy() |     main_window.destroy() | ||||||
| 
 | 
 | ||||||
| main_window.protocol("WM_DELETE_WINDOW", handle_exit) | main_window.protocol("WM_DELETE_WINDOW", handle_exit) | ||||||
|  |  | ||||||
							
								
								
									
										27
									
								
								util.py
								
								
								
								
							
							
						
						
									
										27
									
								
								util.py
								
								
								
								
							|  | @ -1,27 +0,0 @@ | ||||||
| import sys, traceback |  | ||||||
| 
 |  | ||||||
| EXIT_SUCCESS=0 |  | ||||||
| EXIT_ERROR=1 |  | ||||||
| 
 |  | ||||||
| def info(message): |  | ||||||
|     # print info to sys.stderr because it isn’t really output, just debug information |  | ||||||
|     print("INFO: "+str(message), file=sys.stderr) |  | ||||||
|     traceback.print_stack() |  | ||||||
| 
 |  | ||||||
| def warn(message, is_exception=False): |  | ||||||
|     print("WARNING: "+str(message), file=sys.stderr) |  | ||||||
|     if is_exception: |  | ||||||
|         traceback.print_exc() |  | ||||||
|     else: |  | ||||||
|         traceback.print_stack() |  | ||||||
| 
 |  | ||||||
| def error(message, is_exception=True, handle_gracefully=True): |  | ||||||
|     print("ERROR: "+str(message), file=sys.stderr) |  | ||||||
|     if is_exception: |  | ||||||
|         traceback.print_exc() |  | ||||||
|     else: |  | ||||||
|         traceback.print_stack() |  | ||||||
|     if handle_gracefully: |  | ||||||
|         pass |  | ||||||
|     else: |  | ||||||
|         sys.exit(EXIT_ERROR) |  | ||||||
		Reference in New Issue