forked from socketteer/loom
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmain.py
172 lines (139 loc) · 5.85 KB
/
main.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
import os
import tkinter as tk
import traceback
from collections import defaultdict
from pprint import pprint
from tkinter import ttk, messagebox
from ttkthemes import ThemedStyle
from controller import Controller
from model import TreeModel, EMPTY_TREE
from util.custom_tks import ClosableNotebook
from util.util import json_open, json_create
from util.util_tk import create_menubar
from view.colors import darkmode
import PIL.Image
import PIL.ImageTk
class Application:
# Create the application window
def __init__(self, width, height):
# Create the root
self.root = tk.Tk()
self.root.geometry("%dx%d+50+30" % (width, height))
self.root.title("Read tree")
# App icon :). Save or will be garbage collected
self.icon = PIL.ImageTk.PhotoImage(PIL.Image.open("static/zoneplate.png"))
self.root.tk.call('wm', 'iconphoto', self.root._w, self.icon)
# Dark mode
style = ThemedStyle(self.root)
if darkmode:
style.set_theme("black")
# Create the notebook and add a tab to it
# self.close_icon = build_notebook_style()
self.notebook = ClosableNotebook(self.root)
self.notebook.pack(fill=tk.BOTH, expand=1)
self.tabs = []
# Load app data
self.app_data_file = os.path.join(os.getcwd(), "data/", ".app_data.json")
self.app_data = None
self.initialize_app_state()
# Bind Button-1 to tab click so tabs can be closed
self.notebook.bind('<Button-1>', self.tab_click)
self.notebook.bind()
# Do final root prep
self.root.update_idletasks()
# Put the app into the foreground
self.root.attributes('-topmost', True)
self.root.update()
self.root.attributes('-topmost', False)
def initialize_app_state(self):
try:
self.app_data = json_open(self.app_data_file) # if os.path.isfile(self.app_data_file) else {}
for tab_data in self.app_data["tabs"]:
self.create_tab(filename=tab_data["filename"])
except Exception as e:
print("Failed to load with app data")
print(str(e))
print(traceback.format_exc())
self.app_data = {}
if len(self.tabs) == 0:
print("Opening a blank tab")
self.create_tab()
self.set_tab_names()
def update_app_data(self):
self.set_tab_names()
self.app_data = {
"tabs": [
{"filename": t.state.tree_filename}
for t in self.tabs
]
}
json_create(self.app_data_file, self.app_data)
# Create a tab
def create_tab(self, filename=None, event=None):
# if len(self.tabs) > 0:
# messagebox.showwarning("Error", "Only use one tab right now. hehe")
# return
tab = Controller(self.root)
self.tabs.append(tab)
self.notebook.add(tab.display.frame, text=f"Tab {len(self.tabs)}")
# Build the menu bar
self.build_menus()
tab.state.register_callback(tab.state.io_update, self.update_app_data)
if filename is not None:
print("opening", filename)
tab.state.open_tree(filename)
else:
tab.state.load_tree_data(EMPTY_TREE)
def close_tab(self, event=None, index=None):
index = self.notebook.index("current") if index is None else index
self.notebook.forget(index)
self.tabs.pop(index)
if len(self.tabs) == 0:
self.create_tab()
# If the user clicks a close button, get the tab at that position and close it
def tab_click(self, event):
if "close" in event.widget.identify(event.x, event.y):
index = self.notebook.index(f"@{event.x},{event.y}")
self.close_tab(index=index)
self.build_menus()
def set_tab_names(self):
for i, t in enumerate(self.tabs):
name = os.path.splitext(os.path.basename(t.state.tree_filename))[0] \
if t.state.tree_filename else f"Tab {i+1}"
self.notebook.tab(i, text=name)
# Build the applications menubar
# TODO Splitting between here and tab is bad. Move this to the tab
def build_menus(self):
if hasattr(self, "menu"):
self.menu.destroy()
menu_list = defaultdict(list, {
"File": [
#('New Tab', 'Ctrl+N', '<Control-n>', self.create_tab),
('Open', 'O', None, lambda event=None: self.forward_command(Controller.open_tree)),
('Import subtree', 'Ctrl+Shift+O', None, lambda event=None: self.forward_command(Controller.import_tree)),
('Save', 'S', None, lambda event=None: self.forward_command(Controller.save_tree)),
('Save As...', 'Ctrl+S', '<Control-s>', lambda event=None: self.forward_command(Controller.save_tree_as)),
('Export text', 'Ctrl+Shift+X', '<Control-Shift-KeyPress-X>',
lambda event=None: self.forward_command(Controller.export_text)),
('Close Tab', None, None, self.close_tab),
('Quit', 'Ctrl+Q', '<Control-q>', self.quit_app)
]
})
for menu, items in self.tabs[self.notebook.index("current")].build_menus().items():
menu_list[menu].extend(items)
self.menu = create_menubar(self.root, menu_list)
# Forward the given command to the current display controller
def forward_command(self, command):
if len(self.tabs) == 0:
messagebox.showwarning("Error", "There is no tree open.")
else:
command(self.tabs[self.notebook.index("current")])
def quit_app(self, event=None):
self.root.destroy()
# Let the application run
def main(self):
self.root.mainloop()
# Create the display application and run it
if __name__ == "__main__":
app = Application(1200, 675)
app.main()