154 lines
6.1 KiB
Python
154 lines
6.1 KiB
Python
#!/usr/bin/python3
|
||
import tkinter as tk
|
||
from tkinter import ttk
|
||
import sys, os, json, traceback
|
||
|
||
################################################################################
|
||
# DEFINITIONS
|
||
################################################################################
|
||
|
||
EXIT_SUCCESS=0
|
||
EXIT_ERROR=1
|
||
|
||
default_configuration = {
|
||
"window geometry": "640x480"
|
||
}
|
||
# 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 = {}
|
||
|
||
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]
|
||
|
||
# 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 START
|
||
################################################################################
|
||
|
||
# read configuration
|
||
home_directory = os.path.expanduser("~")
|
||
config_file_path = os.path.join(home_directory, "some_ide_config.json")
|
||
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. 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)
|
||
|
||
main_window = tk.Tk()
|
||
main_window.geometry(get_configuration_value("window geometry"))
|
||
main_window.mainloop()
|