Compare commits
	
		
			3 Commits 
		
	
	
		
			027a88342e
			...
			b5124686bf
		
	
	| Author | SHA1 | Date | 
|---|---|---|
|  BodgeMaster | b5124686bf | |
|  BodgeMaster | b6cfc46fe4 | |
|  BodgeMaster | b55468f67d | 
|  | @ -1 +1,3 @@ | ||||||
| *.swp | *.swp | ||||||
|  | __pycache__ | ||||||
|  | .spyproject | ||||||
|  |  | ||||||
|  | @ -0,0 +1,73 @@ | ||||||
|  | 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.") | ||||||
|  | @ -0,0 +1,132 @@ | ||||||
|  | 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,164 +1,24 @@ | ||||||
| #!/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 sys, os, json, traceback | import gui_helper | ||||||
|  | from config import Config | ||||||
| 
 | 
 | ||||||
| ################################################################################ | ################################################################################ | ||||||
| # DEFINITIONS | # CONSTANTS | ||||||
| ################################################################################ | ################################################################################ | ||||||
| 
 | 
 | ||||||
| EXIT_SUCCESS=0 |  | ||||||
| EXIT_ERROR=1 |  | ||||||
| 
 |  | ||||||
| default_configuration = { | default_configuration = { | ||||||
|     "window geometry": "640x480" |     "window geometry": "640x480" | ||||||
| } | } | ||||||
| # empty configuration by default because new values will be added while trying to load them | configuration_file_path = os.path.join(os.path.expanduser("~"), "some_ide_config.json") | ||||||
| # 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 | ||||||
| ################################################################################ | ################################################################################ | ||||||
| 
 | 
 | ||||||
| # read configuration | configuration = Config(configuration_file_path, default_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 | ||||||
|  | @ -166,94 +26,18 @@ else: | ||||||
| 
 | 
 | ||||||
| main_window = tk.Tk() | main_window = tk.Tk() | ||||||
| main_window.title("IDE") | main_window.title("IDE") | ||||||
| main_window.geometry(get_configuration_value("window geometry")) | main_window.geometry(configuration.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 build_menu(structure_dict, menu): | def rebuild_menu(structure_dict): | ||||||
|     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) | ||||||
|     build_menu(menu_structure, menubar) |     gui_helper.build_menu(structure_dict, menubar) | ||||||
|     main_window.config(menu=menubar) |     main_window.config(menu=menubar) | ||||||
| 
 | 
 | ||||||
| rebuild_menu() | rebuild_menu(gui_helper.menu_structure) | ||||||
| 
 | 
 | ||||||
| def handle_exit(): | def handle_exit(): | ||||||
|     set_configuration_value("window geometry", main_window.geometry()) |     configuration.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) | ||||||
|  |  | ||||||
|  | @ -0,0 +1,27 @@ | ||||||
|  | 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