-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmain.py
235 lines (184 loc) · 11.6 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
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
import customtkinter as ctk
from CTkMessagebox import CTkMessagebox as messagebox
from new import create_new_project_window
from edit import edit_project_window
import os
import json
from tkcalendar import DateEntry
theme = "dark-blue" # Premade themes are: blue, dark-blue, and green
ctk.set_default_color_theme(theme)
light_color_primary = "blue" # Text color, light mode
dark_color_primary = "#ECD08E" # Text color, dark mode
light_color_secondary = "gray"
dark_color_secondary = "black"
class App(ctk.CTk):
"""Woodworking Projects Manager, main application."""
def __init__(self):
super().__init__()
# A list to keep track of the child windows so we can cleanly exit the app
self.child_windows = []
# Bind the close event to fire our function
self.protocol("WM_DELETE_WINDOW", self.on_closing)
# Determine the paths for the required folders
current_folder_path = os.path.dirname(os.path.abspath(__file__))
projects_folder_path = os.path.join(current_folder_path, 'projects')
archives_folder_path = os.path.join(projects_folder_path, 'archives')
deleted_folder_path = os.path.join(projects_folder_path, 'deleted')
# Create the folders if they don't exist
os.makedirs(projects_folder_path, exist_ok=True)
os.makedirs(archives_folder_path, exist_ok=True)
os.makedirs(deleted_folder_path, exist_ok=True)
current_folder_path = os.path.dirname(os.path.abspath(__file__))
# Initialize radio_var here if it needs to be accessed early, need to move the def for radio buttons up
default_theme = "green" # This can be: blue, green, dark-blue. To use custom themes, see the CTk docs
self.radio_var = ctk.StringVar(value=default_theme)
# Load previous window position and theme if available
try:
with open('settings.json', 'r') as file:
settings = json.load(file)
self.geometry(f"{settings['width']}x{settings['height']}+{settings['x']}+{settings['y']}")
except FileNotFoundError:
self.geometry("500x425") # Default position
# Initialize projects list
self.projects = self.load_projects()
# Set up the application window
self.title("PyWood Project Manager")
self.geometry("500x425")
self.grid_columnconfigure((0, 1), weight=1)
# Configure UI components
self._configure_ui()
def edit_project_window(app_instance, filename, update_options_menu=None):
# Create a new window for editing a project
edit_window = ctk.CTk()
# Add this window to the main app's child_windows list
app_instance.child_windows.append(edit_window)
# Configure UI components
edit_window._configure_ui(filename, update_options_menu)
def edit_project(self): # Function to handle "Edit Project" button click
selected_project = self.options_menu_var.get() # Get selected project from CTkOptionMenu
# Determine the path to the projects folder
current_folder_path = os.path.dirname(os.path.abspath(__file__))
projects_folder_path = os.path.join(current_folder_path, 'projects')
# Construct the full path to the selected project file
filename = os.path.join(projects_folder_path, selected_project + '.json')
# Call edit_project_window function
edit_project_window(self, filename, update_options_menu=self.update_options_menu)
def _configure_ui(self):
"""Configure the UI components."""
self.label = ctk.CTkLabel(self, text="PyWood :: Project Manager", corner_radius=10, text_color=(light_color_primary, dark_color_primary), fg_color=(light_color_secondary, dark_color_secondary))
self.label.grid(row=0, column=0, padx=20, pady=20, sticky="ew", columnspan=2)
self.button_new = ctk.CTkButton(self, text="New Project", command=self.new_project, corner_radius=10, text_color=(light_color_primary, dark_color_primary))
self.button_new.grid(row=1, column=0, padx=20, pady=20, sticky="w", columnspan=2)
self.button_edit = ctk.CTkButton(self, text="Edit Project", command=self.edit_project, corner_radius=10, text_color=(light_color_primary, dark_color_primary))
self.button_edit.grid(row=3, column=0, padx=20, pady=20, sticky="w", columnspan=2)
self.button_archive = ctk.CTkButton(self, text="Archive Project", command=self.archive_project, corner_radius=10, text_color=(light_color_primary, dark_color_primary))
self.button_archive.grid(row=4, column=0, padx=20, pady=20, sticky="w", columnspan=2)
self.button_delete = ctk.CTkButton(self, text="Delete Project", command=self.delete_project, corner_radius=10, text_color=(light_color_primary, dark_color_primary), fg_color="#FF6666", hover_color="#FF3333")
self.button_delete.grid(row=5, column=0, padx=20, pady=20, sticky="w", columnspan=2)
self.button_empty_trash = ctk.CTkButton(self, text="Empty Trash", command=self.empty_trash, corner_radius=10, text_color=("white", "black"), fg_color="#FFCC99", hover_color="#FF9933")
self.button_empty_trash.grid(row=6, column=0, padx=20, pady=20, sticky="w", columnspan=2)
self.options_menu_var = ctk.StringVar(value=self.projects[0])
self.options_menu = ctk.CTkOptionMenu(self, values=self.projects, variable=self.options_menu_var, width=250, corner_radius=10, text_color=(light_color_primary, dark_color_primary))
self.options_menu.grid(row=1, column=1, padx=20, pady=20, sticky="e", columnspan=1)
def load_projects(self):
"""Load projects from the 'projects' folder."""
projects_path = os.path.join(os.path.dirname(__file__), 'projects')
try:
project_files = [f[:-5] for f in os.listdir(projects_path) if f.endswith('.json')]
return ["Projects:"] + project_files
except Exception as e:
return ["Projects:"]
def delete_project(self):
"""Delete the selected project from the list and move the file to the 'deleted' folder."""
selected_option = self.options_menu_var.get()
if selected_option and selected_option != "Projects:":
answer = messagebox(title="Confirm Deletion", message=f"Delete project {selected_option}?", icon="question", option_1="Yes", option_2="No")
response = answer.get()
if response == "Yes":
# Remove the selected project from the options menu
self.projects.remove(selected_option)
self.options_menu.configure(values=self.projects)
self.options_menu_var.set(self.projects[0])
# Move the corresponding file to the "deleted" folder
current_folder_path = os.path.join(os.path.dirname(__file__), 'projects')
deleted_folder_path = os.path.join(current_folder_path, 'deleted')
if not os.path.exists(deleted_folder_path):
os.mkdir(deleted_folder_path)
file_name = selected_option + ".json"
current_file_path = os.path.join(current_folder_path, file_name)
deleted_file_path = os.path.join(deleted_folder_path, file_name)
if os.path.exists(current_file_path):
os.replace(current_file_path, deleted_file_path) # Use os.replace to move the file
def update_options_menu(self):
"""Update the options menu with the latest projects list."""
self.projects = self.load_projects()
updated_values = self.projects.copy() # Create a copy of the projects list
self.options_menu.configure(values=updated_values) # Update the values in the CTkOptionMenu
self.options_menu_var.set(self.projects[0])
def new_project(self):
create_new_project_window(self, update_options_menu=self.update_options_menu)
# The above line passes the update_options_menu method of the main application class as an argument. This seems clunky but its the only way I could get the OptionMenu to update after saving.
def archive_project(self):
"""Archive the selected project by moving it to the 'archives' folder."""
selected_option = self.options_menu_var.get()
if selected_option and selected_option != "Projects:":
answer = messagebox(title="Confirm Archiving", message=f"Archive project {selected_option}?", icon="question", option_1="Yes", option_2="No")
response = answer.get()
if response == "Yes":
# Remove the selected project from the options menu
self.projects.remove(selected_option)
self.options_menu.configure(values=self.projects)
self.options_menu_var.set(self.projects[0])
# Move the corresponding file to the "archives" folder
current_folder_path = os.path.join(os.path.dirname(__file__), 'projects')
archives_folder_path = os.path.join(current_folder_path, 'archives')
if not os.path.exists(archives_folder_path):
os.mkdir(archives_folder_path)
file_name = selected_option + ".json"
current_file_path = os.path.join(current_folder_path, file_name)
archived_file_path = os.path.join(archives_folder_path, file_name)
if os.path.exists(current_file_path):
os.replace(current_file_path, archived_file_path) # Use os.replace to move the file
def empty_trash(self):
"""Empty the 'deleted' folder."""
current_folder_path = os.path.join(os.path.dirname(__file__), 'projects')
deleted_folder_path = os.path.join(current_folder_path, 'deleted')
if not os.path.exists(deleted_folder_path):
os.mkdir(deleted_folder_path)
folder_path = deleted_folder_path
answer = messagebox(title="Confirm Empty Trash", message=f"Empty the trash (deleted) folder?\nThis action is permanent.", icon="question", option_1="Yes", option_2="No")
response = answer.get()
if response == "Yes":
for filename in os.listdir(folder_path):
file_path = os.path.join(folder_path, filename)
try:
if os.path.isfile(file_path) or os.path.islink(file_path):
os.unlink(file_path)
except Exception as e:
messagebox(title="Error",message=f"Failed to delete %s. Reason: %s' % (file_path)")
def on_closing(self):
# Close all child windows
for child_window in self.child_windows:
child_window.destroy()
# Get the geometry string
geometry = self.geometry()
# Split the string into width, height, x, and y
width, rest = geometry.split('x')
height, x, y = rest.split('+')
# Save the position in a dictionary
information = {
'width': width,
'height': height,
'x': x,
'y': y,
}
# Determine the path to save the JSON file
current_folder_path = os.path.dirname(os.path.abspath(__file__))
file_path = os.path.join(current_folder_path, 'settings.json')
# Write the position to the file
with open(file_path, 'w') as file:
json.dump(information, file)
# Close the app process
quit()
app = App()
app.mainloop()