spacr 0.1.0__py3-none-any.whl → 0.1.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- spacr/gui.py +5 -5
- spacr/gui_2.py +106 -36
- spacr/gui_classify_app.py +3 -3
- spacr/gui_mask_app.py +34 -11
- spacr/gui_measure_app.py +32 -17
- spacr/gui_utils.py +96 -29
- {spacr-0.1.0.dist-info → spacr-0.1.1.dist-info}/METADATA +1 -1
- {spacr-0.1.0.dist-info → spacr-0.1.1.dist-info}/RECORD +12 -12
- {spacr-0.1.0.dist-info → spacr-0.1.1.dist-info}/LICENSE +0 -0
- {spacr-0.1.0.dist-info → spacr-0.1.1.dist-info}/WHEEL +0 -0
- {spacr-0.1.0.dist-info → spacr-0.1.1.dist-info}/entry_points.txt +0 -0
- {spacr-0.1.0.dist-info → spacr-0.1.1.dist-info}/top_level.txt +0 -0
spacr/gui.py
CHANGED
@@ -59,10 +59,10 @@ class MainApp(tk.Tk):
|
|
59
59
|
|
60
60
|
# Load the logo image
|
61
61
|
if not self.load_logo(logo_frame):
|
62
|
-
tk.Label(logo_frame, text="Logo not found", bg="black", fg="white", font=('
|
62
|
+
tk.Label(logo_frame, text="Logo not found", bg="black", fg="white", font=('Helvetica', 24, tkFont.NORMAL)).pack(padx=10, pady=10)
|
63
63
|
|
64
64
|
# Add SpaCr text below the logo with padding for sharper text
|
65
|
-
tk.Label(logo_frame, text="SpaCr", bg="black", fg="#008080", font=('
|
65
|
+
tk.Label(logo_frame, text="SpaCr", bg="black", fg="#008080", font=('Helvetica', 24, tkFont.NORMAL)).pack(padx=10, pady=10)
|
66
66
|
|
67
67
|
# Create a frame for the buttons and descriptions
|
68
68
|
buttons_frame = tk.Frame(self.content_frame, bg="black")
|
@@ -72,10 +72,10 @@ class MainApp(tk.Tk):
|
|
72
72
|
app_func, app_desc = app_data
|
73
73
|
|
74
74
|
# Create custom button with text
|
75
|
-
button = CustomButton(buttons_frame, text=app_name, command=lambda app_name=app_name: self.load_app(app_name))
|
75
|
+
button = CustomButton(buttons_frame, text=app_name, command=lambda app_name=app_name: self.load_app(app_name), font=('Helvetica', 12))
|
76
76
|
button.grid(row=i, column=0, pady=10, padx=10, sticky="w")
|
77
77
|
|
78
|
-
description_label = tk.Label(buttons_frame, text=app_desc, bg="black", fg="white", wraplength=800, justify="left", font=('
|
78
|
+
description_label = tk.Label(buttons_frame, text=app_desc, bg="black", fg="white", wraplength=800, justify="left", font=('Helvetica', 10, tkFont.NORMAL))
|
79
79
|
description_label.grid(row=i, column=1, pady=10, padx=10, sticky="w")
|
80
80
|
|
81
81
|
# Ensure buttons have a fixed width
|
@@ -131,7 +131,7 @@ class MainApp(tk.Tk):
|
|
131
131
|
|
132
132
|
app_frame = tk.Frame(self.content_frame, bg="black")
|
133
133
|
app_frame.pack(fill=tk.BOTH, expand=True)
|
134
|
-
selected_app_func(app_frame
|
134
|
+
selected_app_func(app_frame)#, self.winfo_width(), self.winfo_height())
|
135
135
|
|
136
136
|
def clear_frame(self, frame):
|
137
137
|
for widget in frame.winfo_children():
|
spacr/gui_2.py
CHANGED
@@ -1,52 +1,93 @@
|
|
1
|
-
import
|
1
|
+
import tkinter as tk
|
2
|
+
from tkinter import ttk
|
3
|
+
from tkinter import font as tkFont
|
2
4
|
from PIL import Image, ImageTk
|
3
5
|
import os
|
4
6
|
import requests
|
5
7
|
|
6
|
-
|
8
|
+
# Import your GUI apps
|
9
|
+
from .gui_mask_app import initiate_mask_root
|
10
|
+
from .gui_measure_app import initiate_measure_root
|
11
|
+
from .annotate_app import initiate_annotation_app_root
|
12
|
+
from .mask_app import initiate_mask_app_root
|
13
|
+
from .gui_classify_app import initiate_classify_root
|
14
|
+
|
15
|
+
from .gui_utils import CustomButton, style_text_boxes
|
16
|
+
|
17
|
+
class MainApp(tk.Tk):
|
7
18
|
def __init__(self):
|
8
19
|
super().__init__()
|
9
20
|
self.title("SpaCr GUI Collection")
|
10
|
-
self.geometry("
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
21
|
+
self.geometry("1100x1500")
|
22
|
+
self.configure(bg="black")
|
23
|
+
#self.attributes('-fullscreen', True)
|
24
|
+
|
25
|
+
style = ttk.Style()
|
26
|
+
style_text_boxes(style)
|
27
|
+
|
28
|
+
self.gui_apps = {
|
29
|
+
"Mask": (initiate_mask_root, "Generate cellpose masks for cells, nuclei and pathogen images."),
|
30
|
+
"Measure": (initiate_measure_root, "Measure single object intensity and morphological feature. Crop and save single object image"),
|
31
|
+
"Annotate": (initiate_annotation_app_root, "Annotation single object images on a grid. Annotations are saved to database."),
|
32
|
+
"Make Masks": (initiate_mask_app_root, "Adjust pre-existing Cellpose models to your specific dataset for improved performance"),
|
33
|
+
"Classify": (initiate_classify_root, "Train Torch Convolutional Neural Networks (CNNs) or Transformers to classify single object images.")
|
34
|
+
}
|
35
|
+
|
36
|
+
self.selected_app = tk.StringVar()
|
17
37
|
self.create_widgets()
|
18
38
|
|
19
39
|
def create_widgets(self):
|
20
|
-
|
21
|
-
self
|
40
|
+
# Create the menu bar
|
41
|
+
#create_menu_bar(self)
|
42
|
+
# Create a canvas to hold the selected app and other elements
|
43
|
+
self.canvas = tk.Canvas(self, bg="black", highlightthickness=0, width=4000, height=4000)
|
44
|
+
self.canvas.grid(row=0, column=0, sticky="nsew")
|
45
|
+
self.grid_rowconfigure(0, weight=1)
|
46
|
+
self.grid_columnconfigure(0, weight=1)
|
47
|
+
# Create a frame inside the canvas to hold the main content
|
48
|
+
self.content_frame = tk.Frame(self.canvas, bg="black")
|
49
|
+
self.content_frame.pack(fill=tk.BOTH, expand=True)
|
50
|
+
# Create startup screen with buttons for each GUI app
|
51
|
+
self.create_startup_screen()
|
22
52
|
|
23
|
-
|
53
|
+
def create_startup_screen(self):
|
54
|
+
self.clear_frame(self.content_frame)
|
55
|
+
|
56
|
+
# Create a frame for the logo and description
|
57
|
+
logo_frame = tk.Frame(self.content_frame, bg="black")
|
24
58
|
logo_frame.pack(pady=20, expand=True)
|
25
59
|
|
60
|
+
# Load the logo image
|
26
61
|
if not self.load_logo(logo_frame):
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
62
|
+
tk.Label(logo_frame, text="Logo not found", bg="black", fg="white", font=('Helvetica', 24)).pack(padx=10, pady=10)
|
63
|
+
|
64
|
+
# Add SpaCr text below the logo with padding for sharper text
|
65
|
+
tk.Label(logo_frame, text="SpaCr", bg="black", fg="#008080", font=('Helvetica', 24)).pack(padx=10, pady=10)
|
66
|
+
|
67
|
+
# Create a frame for the buttons and descriptions
|
68
|
+
buttons_frame = tk.Frame(self.content_frame, bg="black")
|
69
|
+
buttons_frame.pack(pady=10, expand=True, padx=10)
|
70
|
+
|
71
|
+
for i, (app_name, app_data) in enumerate(self.gui_apps.items()):
|
72
|
+
app_func, app_desc = app_data
|
73
|
+
|
74
|
+
# Create custom button with text
|
75
|
+
button = CustomButton(buttons_frame, text=app_name, command=lambda app_name=app_name: self.load_app(app_name, app_func), font=('Helvetica', 12))
|
76
|
+
button.grid(row=i, column=0, pady=10, padx=10, sticky="w")
|
77
|
+
|
78
|
+
description_label = tk.Label(buttons_frame, text=app_desc, bg="black", fg="white", wraplength=800, justify="left", font=('Helvetica', 12))
|
79
|
+
description_label.grid(row=i, column=1, pady=10, padx=10, sticky="w")
|
80
|
+
|
81
|
+
# Ensure buttons have a fixed width
|
82
|
+
buttons_frame.grid_columnconfigure(0, minsize=150)
|
83
|
+
# Ensure descriptions expand as needed
|
84
|
+
buttons_frame.grid_columnconfigure(1, weight=1)
|
44
85
|
|
45
86
|
def load_logo(self, frame):
|
46
87
|
def download_image(url, save_path):
|
47
88
|
try:
|
48
89
|
response = requests.get(url, stream=True)
|
49
|
-
response.raise_for_status()
|
90
|
+
response.raise_for_status() # Raise an HTTPError for bad responses
|
50
91
|
with open(save_path, 'wb') as f:
|
51
92
|
for chunk in response.iter_content(chunk_size=8192):
|
52
93
|
f.write(chunk)
|
@@ -57,34 +98,63 @@ class MainApp(ctk.CTk):
|
|
57
98
|
|
58
99
|
try:
|
59
100
|
img_path = os.path.join(os.path.dirname(__file__), 'logo_spacr.png')
|
101
|
+
print(f"Trying to load logo from {img_path}")
|
60
102
|
logo_image = Image.open(img_path)
|
61
103
|
except (FileNotFoundError, Image.UnidentifiedImageError):
|
104
|
+
print(f"File {img_path} not found or is not a valid image. Attempting to download from GitHub.")
|
62
105
|
if download_image('https://raw.githubusercontent.com/EinarOlafsson/spacr/main/spacr/logo_spacr.png', img_path):
|
63
106
|
try:
|
107
|
+
print(f"Downloaded file size: {os.path.getsize(img_path)} bytes")
|
64
108
|
logo_image = Image.open(img_path)
|
65
109
|
except Image.UnidentifiedImageError as e:
|
110
|
+
print(f"Downloaded file is not a valid image: {e}")
|
66
111
|
return False
|
67
112
|
else:
|
68
113
|
return False
|
69
114
|
except Exception as e:
|
115
|
+
print(f"An error occurred while loading the logo: {e}")
|
70
116
|
return False
|
71
|
-
|
72
117
|
try:
|
73
|
-
logo_image = logo_image.resize((
|
118
|
+
logo_image = logo_image.resize((800, 800), Image.Resampling.LANCZOS)
|
74
119
|
logo_photo = ImageTk.PhotoImage(logo_image)
|
75
|
-
logo_label =
|
120
|
+
logo_label = tk.Label(frame, image=logo_photo, bg="black")
|
76
121
|
logo_label.image = logo_photo # Keep a reference to avoid garbage collection
|
77
122
|
logo_label.pack()
|
78
123
|
return True
|
79
124
|
except Exception as e:
|
125
|
+
print(f"An error occurred while processing the logo image: {e}")
|
80
126
|
return False
|
81
127
|
|
82
|
-
def
|
83
|
-
|
128
|
+
def load_app_v1(self, app_name):
|
129
|
+
selected_app_func, _ = self.gui_apps[app_name]
|
130
|
+
self.clear_frame(self.content_frame)
|
131
|
+
|
132
|
+
app_frame = tk.Frame(self.content_frame, bg="black")
|
133
|
+
app_frame.pack(fill=tk.BOTH, expand=True)
|
134
|
+
selected_app_func(app_frame)
|
135
|
+
|
136
|
+
def load_app(root, app_name, app_func):
|
137
|
+
if hasattr(root, 'current_app_id'):
|
138
|
+
root.after_cancel(root.current_app_id)
|
139
|
+
root.current_app_id = None
|
140
|
+
|
141
|
+
# Clear the current content frame
|
142
|
+
for widget in root.content_frame.winfo_children():
|
143
|
+
widget.destroy()
|
144
|
+
|
145
|
+
# Initialize the selected app
|
146
|
+
app_frame = tk.Frame(root.content_frame, bg="black")
|
147
|
+
app_frame.pack(fill=tk.BOTH, expand=True)
|
148
|
+
app_func(app_frame)
|
149
|
+
|
150
|
+
def clear_frame(self, frame):
|
151
|
+
for widget in frame.winfo_children():
|
152
|
+
widget.destroy()
|
153
|
+
|
84
154
|
|
85
155
|
def gui_app():
|
86
156
|
app = MainApp()
|
87
157
|
app.mainloop()
|
88
158
|
|
89
159
|
if __name__ == "__main__":
|
90
|
-
gui_app()
|
160
|
+
gui_app()
|
spacr/gui_classify_app.py
CHANGED
@@ -69,13 +69,13 @@ def import_settings(scrollable_frame):
|
|
69
69
|
vars_dict = generate_fields(new_settings, scrollable_frame)
|
70
70
|
|
71
71
|
#@log_function_call
|
72
|
-
def initiate_classify_root(parent_frame)
|
72
|
+
def initiate_classify_root(parent_frame):
|
73
73
|
global vars_dict, q, canvas, fig_queue, canvas_widget, thread_control
|
74
74
|
|
75
75
|
style = ttk.Style(parent_frame)
|
76
76
|
set_dark_style(style)
|
77
77
|
style_text_boxes(style)
|
78
|
-
set_default_font(parent_frame, font_name="
|
78
|
+
set_default_font(parent_frame, font_name="Helvetica", size=8)
|
79
79
|
|
80
80
|
parent_frame.configure(bg='#333333')
|
81
81
|
parent_frame.grid_rowconfigure(0, weight=1)
|
@@ -179,7 +179,7 @@ def gui_classify():
|
|
179
179
|
root = tk.Tk()
|
180
180
|
root.geometry("1000x800")
|
181
181
|
root.title("SpaCer: generate masks")
|
182
|
-
initiate_classify_root(root,
|
182
|
+
initiate_classify_root(root),
|
183
183
|
create_menu_bar(root)
|
184
184
|
root.mainloop()
|
185
185
|
|
spacr/gui_mask_app.py
CHANGED
@@ -97,19 +97,23 @@ def import_settings(scrollable_frame):
|
|
97
97
|
vars_dict = generate_fields(new_settings, scrollable_frame)
|
98
98
|
|
99
99
|
#@log_function_call
|
100
|
-
def initiate_mask_root(parent_frame
|
100
|
+
def initiate_mask_root(parent_frame):
|
101
101
|
global vars_dict, q, canvas, fig_queue, canvas_widget, thread_control, advanced_var, scrollable_frame
|
102
102
|
|
103
103
|
style = ttk.Style(parent_frame)
|
104
104
|
set_dark_style(style)
|
105
105
|
style_text_boxes(style)
|
106
|
-
set_default_font(parent_frame, font_name="
|
106
|
+
set_default_font(parent_frame, font_name="Helvetica", size=8)
|
107
107
|
parent_frame.configure(bg='black')
|
108
108
|
parent_frame.grid_rowconfigure(0, weight=1)
|
109
109
|
parent_frame.grid_columnconfigure(0, weight=1)
|
110
110
|
|
111
111
|
fig_queue = Queue()
|
112
112
|
|
113
|
+
# Initialize after_tasks if not already done
|
114
|
+
if not hasattr(parent_frame, 'after_tasks'):
|
115
|
+
parent_frame.after_tasks = []
|
116
|
+
|
113
117
|
def _process_fig_queue():
|
114
118
|
global canvas
|
115
119
|
try:
|
@@ -130,14 +134,20 @@ def initiate_mask_root(parent_frame, width, height):
|
|
130
134
|
except Exception as e:
|
131
135
|
traceback.print_exc()
|
132
136
|
finally:
|
133
|
-
canvas_widget.after(100, _process_fig_queue)
|
137
|
+
after_id = canvas_widget.after(100, _process_fig_queue)
|
138
|
+
parent_frame.after_tasks.append(after_id)
|
134
139
|
|
135
140
|
def _process_console_queue():
|
136
141
|
while not q.empty():
|
137
142
|
message = q.get_nowait()
|
138
143
|
console_output.insert(tk.END, message)
|
139
144
|
console_output.see(tk.END)
|
140
|
-
console_output.after(100, _process_console_queue)
|
145
|
+
after_id = console_output.after(100, _process_console_queue)
|
146
|
+
parent_frame.after_tasks.append(after_id)
|
147
|
+
|
148
|
+
# Clear previous content if any
|
149
|
+
for widget in parent_frame.winfo_children():
|
150
|
+
widget.destroy()
|
141
151
|
|
142
152
|
vertical_container = tk.PanedWindow(parent_frame, orient=tk.HORIZONTAL)
|
143
153
|
vertical_container.grid(row=0, column=0, sticky=tk.NSEW)
|
@@ -172,17 +182,17 @@ def initiate_mask_root(parent_frame, width, height):
|
|
172
182
|
run_button.grid(row=45, column=0, pady=10, padx=10)
|
173
183
|
abort_button = CustomButton(scrollable_frame.scrollable_frame, text="Abort", command=initiate_abort)
|
174
184
|
abort_button.grid(row=45, column=1, pady=10, padx=10)
|
175
|
-
progress_label = ttk.Label(scrollable_frame.scrollable_frame, text="Processing: 0%", background="black", foreground="white")
|
185
|
+
progress_label = ttk.Label(scrollable_frame.scrollable_frame, text="Processing: 0%", background="black", foreground="white")
|
176
186
|
progress_label.grid(row=50, column=0, columnspan=2, sticky="ew", pady=(5, 0), padx=10)
|
177
187
|
|
178
188
|
# Plot Canvas Section
|
179
|
-
plot_frame = tk.PanedWindow(vertical_container, orient=tk.VERTICAL)
|
189
|
+
plot_frame = tk.PanedWindow(vertical_container, orient=tk.VERTICAL)
|
180
190
|
vertical_container.add(plot_frame, stretch="always")
|
181
|
-
figure = Figure(figsize=(30, 4), dpi=100, facecolor='black')
|
191
|
+
figure = Figure(figsize=(30, 4), dpi=100, facecolor='black')
|
182
192
|
plot = figure.add_subplot(111)
|
183
193
|
plot.plot([], []) # This creates an empty plot.
|
184
194
|
plot.axis('off')
|
185
|
-
canvas = FigureCanvasTkAgg(figure, master=plot_frame)
|
195
|
+
canvas = FigureCanvasTkAgg(figure, master=plot_frame)
|
186
196
|
canvas.get_tk_widget().configure(cursor='arrow', background='black', highlightthickness=0)
|
187
197
|
canvas_widget = canvas.get_tk_widget()
|
188
198
|
plot_frame.add(canvas_widget, stretch="always")
|
@@ -206,15 +216,28 @@ def initiate_mask_root(parent_frame, width, height):
|
|
206
216
|
_process_console_queue()
|
207
217
|
_process_fig_queue()
|
208
218
|
|
209
|
-
parent_frame.after(100, lambda: main_thread_update_function(parent_frame, q, fig_queue, canvas_widget, progress_label))
|
219
|
+
after_id = parent_frame.after(100, lambda: main_thread_update_function(parent_frame, q, fig_queue, canvas_widget, progress_label))
|
220
|
+
parent_frame.after_tasks.append(after_id)
|
210
221
|
|
211
222
|
return parent_frame, vars_dict
|
212
223
|
|
213
224
|
def gui_mask():
|
214
225
|
root = tk.Tk()
|
215
226
|
root.geometry("1000x800")
|
216
|
-
root.title("
|
217
|
-
|
227
|
+
root.title("SpaCr: generate masks")
|
228
|
+
|
229
|
+
# Clear previous content if any
|
230
|
+
if hasattr(root, 'content_frame'):
|
231
|
+
for widget in root.content_frame.winfo_children():
|
232
|
+
widget.destroy()
|
233
|
+
root.content_frame.grid_forget()
|
234
|
+
else:
|
235
|
+
root.content_frame = tk.Frame(root)
|
236
|
+
root.content_frame.grid(row=1, column=0, sticky="nsew")
|
237
|
+
root.grid_rowconfigure(1, weight=1)
|
238
|
+
root.grid_columnconfigure(0, weight=1)
|
239
|
+
|
240
|
+
initiate_mask_root(root.content_frame)
|
218
241
|
create_menu_bar(root)
|
219
242
|
root.mainloop()
|
220
243
|
|
spacr/gui_measure_app.py
CHANGED
@@ -86,29 +86,24 @@ def initiate_abort():
|
|
86
86
|
thread_control["run_thread"] = None
|
87
87
|
|
88
88
|
#@log_function_call
|
89
|
-
def
|
90
|
-
global vars_dict
|
91
|
-
|
92
|
-
csv_file_path = filedialog.askopenfilename(filetypes=[("CSV files", "*.csv")])
|
93
|
-
csv_settings = read_settings_from_csv(csv_file_path)
|
94
|
-
variables = measure_variables()
|
95
|
-
new_settings = update_settings_from_csv(variables, csv_settings)
|
96
|
-
vars_dict = generate_fields(new_settings, scrollable_frame)
|
97
|
-
|
98
|
-
#@log_function_call
|
99
|
-
def initiate_measure_root(parent_frame):#, width, height):
|
89
|
+
def initiate_measure_root(parent_frame):
|
100
90
|
global vars_dict, q, canvas, fig_queue, canvas_widget, thread_control, variables, advanced_var, scrollable_frame
|
101
91
|
|
102
92
|
style = ttk.Style(parent_frame)
|
103
93
|
set_dark_style(style)
|
104
94
|
style_text_boxes(style)
|
105
|
-
set_default_font(parent_frame, font_name="
|
95
|
+
set_default_font(parent_frame, font_name="Helvetica", size=8)
|
106
96
|
|
107
97
|
parent_frame.configure(bg='black')
|
108
98
|
parent_frame.grid_rowconfigure(0, weight=1)
|
109
99
|
parent_frame.grid_columnconfigure(0, weight=1)
|
100
|
+
|
110
101
|
fig_queue = Queue()
|
111
102
|
|
103
|
+
# Initialize after_tasks if not already done
|
104
|
+
if not hasattr(parent_frame, 'after_tasks'):
|
105
|
+
parent_frame.after_tasks = []
|
106
|
+
|
112
107
|
def _process_fig_queue():
|
113
108
|
global canvas
|
114
109
|
try:
|
@@ -129,14 +124,20 @@ def initiate_measure_root(parent_frame):#, width, height):
|
|
129
124
|
except Exception as e:
|
130
125
|
traceback.print_exc()
|
131
126
|
finally:
|
132
|
-
canvas_widget.after(100, _process_fig_queue)
|
127
|
+
after_id = canvas_widget.after(100, _process_fig_queue)
|
128
|
+
parent_frame.after_tasks.append(after_id)
|
133
129
|
|
134
130
|
def _process_console_queue():
|
135
131
|
while not q.empty():
|
136
132
|
message = q.get_nowait()
|
137
133
|
console_output.insert(tk.END, message)
|
138
134
|
console_output.see(tk.END)
|
139
|
-
console_output.after(100, _process_console_queue)
|
135
|
+
after_id = console_output.after(100, _process_console_queue)
|
136
|
+
parent_frame.after_tasks.append(after_id)
|
137
|
+
|
138
|
+
# Clear previous content if any
|
139
|
+
for widget in parent_frame.winfo_children():
|
140
|
+
widget.destroy()
|
140
141
|
|
141
142
|
vertical_container = tk.PanedWindow(parent_frame, orient=tk.HORIZONTAL)
|
142
143
|
vertical_container.grid(row=0, column=0, sticky=tk.NSEW)
|
@@ -205,15 +206,29 @@ def initiate_measure_root(parent_frame):#, width, height):
|
|
205
206
|
_process_console_queue()
|
206
207
|
_process_fig_queue()
|
207
208
|
|
208
|
-
parent_frame.after(100, lambda: main_thread_update_function(parent_frame, q, fig_queue, canvas_widget, progress_label))
|
209
|
+
after_id = parent_frame.after(100, lambda: main_thread_update_function(parent_frame, q, fig_queue, canvas_widget, progress_label))
|
210
|
+
parent_frame.after_tasks.append(after_id)
|
209
211
|
|
210
212
|
return parent_frame, vars_dict
|
211
213
|
|
214
|
+
|
212
215
|
def gui_measure():
|
213
216
|
root = tk.Tk()
|
214
217
|
root.geometry("1000x800")
|
215
|
-
root.title("
|
216
|
-
|
218
|
+
root.title("SpaCr: measure objects")
|
219
|
+
|
220
|
+
# Clear previous content if any
|
221
|
+
if hasattr(root, 'content_frame'):
|
222
|
+
for widget in root.content_frame.winfo_children():
|
223
|
+
widget.destroy()
|
224
|
+
root.content_frame.grid_forget()
|
225
|
+
else:
|
226
|
+
root.content_frame = tk.Frame(root)
|
227
|
+
root.content_frame.grid(row=1, column=0, sticky="nsew")
|
228
|
+
root.grid_rowconfigure(1, weight=1)
|
229
|
+
root.grid_columnconfigure(0, weight=1)
|
230
|
+
|
231
|
+
initiate_measure_root(root.content_frame)
|
217
232
|
create_menu_bar(root)
|
218
233
|
root.mainloop()
|
219
234
|
|
spacr/gui_utils.py
CHANGED
@@ -40,21 +40,27 @@ class ToolTip:
|
|
40
40
|
self.tooltip_window.destroy()
|
41
41
|
self.tooltip_window = None
|
42
42
|
|
43
|
-
|
44
43
|
def load_app(root, app_name, app_func):
|
45
|
-
#
|
46
|
-
root
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
#
|
52
|
-
|
53
|
-
|
54
|
-
|
44
|
+
# Cancel all scheduled after tasks
|
45
|
+
if hasattr(root, 'after_tasks'):
|
46
|
+
for task in root.after_tasks:
|
47
|
+
root.after_cancel(task)
|
48
|
+
root.after_tasks = []
|
49
|
+
|
50
|
+
# Clear the current content frame
|
51
|
+
if hasattr(root, 'content_frame'):
|
52
|
+
for widget in root.content_frame.winfo_children():
|
53
|
+
widget.destroy()
|
54
|
+
else:
|
55
|
+
root.content_frame = tk.Frame(root)
|
56
|
+
root.content_frame.grid(row=1, column=0, sticky="nsew")
|
57
|
+
root.grid_rowconfigure(1, weight=1)
|
58
|
+
root.grid_columnconfigure(0, weight=1)
|
55
59
|
|
56
|
-
|
60
|
+
# Initialize the new app in the content frame
|
61
|
+
app_func(root.content_frame)
|
57
62
|
|
63
|
+
def create_menu_bar(root):
|
58
64
|
from .gui_mask_app import initiate_mask_root
|
59
65
|
from .gui_measure_app import initiate_measure_root
|
60
66
|
from .annotate_app import initiate_annotation_app_root
|
@@ -68,6 +74,10 @@ def create_menu_bar(root):
|
|
68
74
|
"Make Masks": initiate_mask_app_root,
|
69
75
|
"Classify": initiate_classify_root
|
70
76
|
}
|
77
|
+
|
78
|
+
def load_app_wrapper(app_name, app_func):
|
79
|
+
load_app(root, app_name, app_func)
|
80
|
+
|
71
81
|
# Create the menu bar
|
72
82
|
menu_bar = tk.Menu(root, bg="#008080", fg="white")
|
73
83
|
# Create a "SpaCr Applications" menu
|
@@ -75,7 +85,7 @@ def create_menu_bar(root):
|
|
75
85
|
menu_bar.add_cascade(label="SpaCr Applications", menu=app_menu)
|
76
86
|
# Add options to the "SpaCr Applications" menu
|
77
87
|
for app_name, app_func in gui_apps.items():
|
78
|
-
app_menu.add_command(label=app_name, command=lambda app_name=app_name, app_func=app_func:
|
88
|
+
app_menu.add_command(label=app_name, command=lambda app_name=app_name, app_func=app_func: load_app_wrapper(app_name, app_func))
|
79
89
|
# Add a separator and an exit option
|
80
90
|
app_menu.add_separator()
|
81
91
|
app_menu.add_command(label="Exit", command=root.quit)
|
@@ -83,7 +93,7 @@ def create_menu_bar(root):
|
|
83
93
|
root.config(menu=menu_bar)
|
84
94
|
|
85
95
|
class CustomButton(tk.Frame):
|
86
|
-
def __init__(self, parent, text="", command=None, *args, **kwargs):
|
96
|
+
def __init__(self, parent, text="", command=None, font=None, *args, **kwargs):
|
87
97
|
super().__init__(parent, *args, **kwargs)
|
88
98
|
self.text = text
|
89
99
|
self.command = command
|
@@ -93,8 +103,8 @@ class CustomButton(tk.Frame):
|
|
93
103
|
|
94
104
|
self.button_bg = self.create_rounded_rectangle(0, 0, 150, 50, radius=20, fill="#800080")
|
95
105
|
|
96
|
-
#
|
97
|
-
self.font_style = tkFont.Font(family="
|
106
|
+
# Use the passed font or default to Helvetica if not provided
|
107
|
+
self.font_style = font if font else tkFont.Font(family="Helvetica", size=12, weight=tkFont.NORMAL)
|
98
108
|
self.button_text = self.canvas.create_text(75, 25, text=self.text, fill="white", font=self.font_style)
|
99
109
|
|
100
110
|
self.bind("<Enter>", self.on_enter)
|
@@ -137,7 +147,7 @@ class CustomButton(tk.Frame):
|
|
137
147
|
x1, y1]
|
138
148
|
|
139
149
|
return self.canvas.create_polygon(points, **kwargs, smooth=True)
|
140
|
-
|
150
|
+
|
141
151
|
class ToggleSwitch(ttk.Frame):
|
142
152
|
def __init__(self, parent, text="", variable=None, command=None, *args, **kwargs):
|
143
153
|
super().__init__(parent, *args, **kwargs)
|
@@ -226,15 +236,60 @@ class ToggleSwitch(ttk.Frame):
|
|
226
236
|
|
227
237
|
return self.canvas.create_polygon(points, **kwargs, smooth=True)
|
228
238
|
|
229
|
-
def set_default_font(root, font_name="
|
239
|
+
def set_default_font(root, font_name="Helvetica", size=12):
|
230
240
|
default_font = (font_name, size)
|
231
241
|
root.option_add("*Font", default_font)
|
232
242
|
root.option_add("*TButton.Font", default_font)
|
233
243
|
root.option_add("*TLabel.Font", default_font)
|
234
244
|
root.option_add("*TEntry.Font", default_font)
|
235
245
|
|
246
|
+
def check_and_download_font_v1():
|
247
|
+
font_name = "Helvetica"
|
248
|
+
font_dir = "fonts"
|
249
|
+
font_path = os.path.join(font_dir, "OpenSans-Regular.ttf")
|
250
|
+
|
251
|
+
# Check if the font is already available
|
252
|
+
available_fonts = list(tkFont.families())
|
253
|
+
if font_name not in available_fonts:
|
254
|
+
print(f"Font '{font_name}' not found. Downloading...")
|
255
|
+
if not os.path.exists(font_dir):
|
256
|
+
os.makedirs(font_dir)
|
257
|
+
|
258
|
+
if not os.path.exists(font_path):
|
259
|
+
url = "https://github.com/google/fonts/blob/main/apache/opensans/OpenSans-Regular.ttf?raw=true"
|
260
|
+
response = requests.get(url)
|
261
|
+
with open(font_path, "wb") as f:
|
262
|
+
f.write(response.content)
|
263
|
+
|
264
|
+
# Load the font
|
265
|
+
try:
|
266
|
+
tkFont.nametofont("TkDefaultFont").configure(family=font_name, size=10)
|
267
|
+
tkFont.nametofont("TkTextFont").configure(family=font_name, size=10)
|
268
|
+
tkFont.nametofont("TkHeadingFont").configure(family=font_name, size=12)
|
269
|
+
except tk.TclError:
|
270
|
+
tkFont.nametofont("TkDefaultFont").configure(family="Helvetica", size=10)
|
271
|
+
tkFont.nametofont("TkTextFont").configure(family="Helvetica", size=10)
|
272
|
+
tkFont.nametofont("TkHeadingFont").configure(family="Helvetica", size=12)
|
273
|
+
else:
|
274
|
+
tkFont.nametofont("TkDefaultFont").configure(family=font_name, size=10)
|
275
|
+
tkFont.nametofont("TkTextFont").configure(family=font_name, size=10)
|
276
|
+
tkFont.nametofont("TkHeadingFont").configure(family=font_name, size=12)
|
277
|
+
|
278
|
+
def style_text_boxes_v1(style):
|
279
|
+
check_and_download_font()
|
280
|
+
font_style = tkFont.Font(family="Helvetica", size=10) # Define the Helvetica font
|
281
|
+
style.configure('TEntry', padding='5 5 5 5', borderwidth=1, relief='solid', fieldbackground='#000000', foreground='#ffffff', font=font_style)
|
282
|
+
style.configure('TCombobox', fieldbackground='#000000', background='#000000', foreground='#ffffff', font=font_style)
|
283
|
+
style.configure('Custom.TButton', padding='10 10 10 10', borderwidth=1, relief='solid', background='#008080', foreground='#ffffff', font=font_style)
|
284
|
+
style.map('Custom.TButton',
|
285
|
+
background=[('active', '#66b2b2'), ('disabled', '#004d4d'), ('!disabled', '#008080')],
|
286
|
+
foreground=[('active', '#ffffff'), ('disabled', '#888888')])
|
287
|
+
style.configure('Custom.TLabel', padding='5 5 5 5', borderwidth=1, relief='flat', background='#000000', foreground='#ffffff', font=font_style)
|
288
|
+
style.configure('TCheckbutton', background='#333333', foreground='#ffffff', indicatoron=False, relief='flat', font=font_style)
|
289
|
+
style.map('TCheckbutton', background=[('selected', '#555555'), ('active', '#555555')])
|
290
|
+
|
236
291
|
def check_and_download_font():
|
237
|
-
font_name = "
|
292
|
+
font_name = "Helvetica"
|
238
293
|
font_dir = "fonts"
|
239
294
|
font_path = os.path.join(font_dir, "OpenSans-Regular.ttf")
|
240
295
|
|
@@ -257,9 +312,9 @@ def check_and_download_font():
|
|
257
312
|
tkFont.nametofont("TkTextFont").configure(family=font_name, size=10)
|
258
313
|
tkFont.nametofont("TkHeadingFont").configure(family=font_name, size=12)
|
259
314
|
except tk.TclError:
|
260
|
-
tkFont.nametofont("TkDefaultFont").configure(family="
|
261
|
-
tkFont.nametofont("TkTextFont").configure(family="
|
262
|
-
tkFont.nametofont("TkHeadingFont").configure(family="
|
315
|
+
tkFont.nametofont("TkDefaultFont").configure(family="Helvetica", size=10)
|
316
|
+
tkFont.nametofont("TkTextFont").configure(family="Helvetica", size=10)
|
317
|
+
tkFont.nametofont("TkHeadingFont").configure(family="Helvetica", size=12)
|
263
318
|
else:
|
264
319
|
tkFont.nametofont("TkDefaultFont").configure(family=font_name, size=10)
|
265
320
|
tkFont.nametofont("TkTextFont").configure(family=font_name, size=10)
|
@@ -267,7 +322,7 @@ def check_and_download_font():
|
|
267
322
|
|
268
323
|
def style_text_boxes(style):
|
269
324
|
check_and_download_font()
|
270
|
-
font_style = tkFont.Font(family="
|
325
|
+
font_style = tkFont.Font(family="Helvetica", size=10) # Define the Helvetica font
|
271
326
|
style.configure('TEntry', padding='5 5 5 5', borderwidth=1, relief='solid', fieldbackground='#000000', foreground='#ffffff', font=font_style)
|
272
327
|
style.configure('TCombobox', fieldbackground='#000000', background='#000000', foreground='#ffffff', font=font_style)
|
273
328
|
style.configure('Custom.TButton', padding='10 10 10 10', borderwidth=1, relief='solid', background='#008080', foreground='#ffffff', font=font_style)
|
@@ -279,7 +334,6 @@ def style_text_boxes(style):
|
|
279
334
|
style.map('TCheckbutton', background=[('selected', '#555555'), ('active', '#555555')])
|
280
335
|
|
281
336
|
|
282
|
-
|
283
337
|
def read_settings_from_csv(csv_file_path):
|
284
338
|
settings = {}
|
285
339
|
with open(csv_file_path, newline='') as csvfile:
|
@@ -337,7 +391,7 @@ class ScrollableFrame(ttk.Frame):
|
|
337
391
|
canvas.pack(side="left", fill="both", expand=True)
|
338
392
|
scrollbar.pack(side="right", fill="y")
|
339
393
|
|
340
|
-
class
|
394
|
+
class StdoutRedirector_v1(object):
|
341
395
|
def __init__(self, text_widget):
|
342
396
|
self.text_widget = text_widget
|
343
397
|
|
@@ -348,6 +402,21 @@ class StdoutRedirector(object):
|
|
348
402
|
def flush(self):
|
349
403
|
pass
|
350
404
|
|
405
|
+
class StdoutRedirector:
|
406
|
+
def __init__(self, text_widget):
|
407
|
+
self.text_widget = text_widget
|
408
|
+
|
409
|
+
def write(self, string):
|
410
|
+
try:
|
411
|
+
if self.text_widget.winfo_exists():
|
412
|
+
self.text_widget.insert(tk.END, string)
|
413
|
+
self.text_widget.see(tk.END)
|
414
|
+
except tk.TclError:
|
415
|
+
pass # Handle or log the error as needed
|
416
|
+
|
417
|
+
def flush(self):
|
418
|
+
pass
|
419
|
+
|
351
420
|
def check_mask_gui_settings(vars_dict):
|
352
421
|
settings = {}
|
353
422
|
for key, var in vars_dict.items():
|
@@ -804,8 +873,6 @@ def generate_fields(variables, scrollable_frame):
|
|
804
873
|
row += 1
|
805
874
|
return vars_dict
|
806
875
|
|
807
|
-
|
808
|
-
|
809
876
|
class TextRedirector(object):
|
810
877
|
def __init__(self, widget, queue):
|
811
878
|
self.widget = widget
|
@@ -834,7 +901,7 @@ def create_dark_mode(root, style, console_output):
|
|
834
901
|
style.map('TCombobox', fieldbackground=[('readonly', input_bg)], selectbackground=[('readonly', input_bg)], foreground=[('readonly', dark_text)])
|
835
902
|
|
836
903
|
if console_output != None:
|
837
|
-
console_output.config(bg=dark_bg, fg=light_text, insertbackground=light_text) #, font=("
|
904
|
+
console_output.config(bg=dark_bg, fg=light_text, insertbackground=light_text) #, font=("Helvetica", 12)
|
838
905
|
root.configure(bg=dark_bg)
|
839
906
|
|
840
907
|
def set_dark_style(style):
|
@@ -1025,4 +1092,4 @@ def run_multiple_simulations_wrapper(settings, q, fig_queue):
|
|
1025
1092
|
q.put(errorMessage) # Send the error message to the GUI via the queue
|
1026
1093
|
traceback.print_exc()
|
1027
1094
|
finally:
|
1028
|
-
plt.show = original_show # Restore the original plt.show function
|
1095
|
+
plt.show = original_show # Restore the original plt.show function
|
@@ -10,13 +10,13 @@ spacr/deep_spacr.py,sha256=N0o7ILD2p1FTfU4DFxnpjs00xjLhwib-ev0XGqA6muU,37035
|
|
10
10
|
spacr/foldseek.py,sha256=YIP1d4Ci6CeA9jSyiv-HTDbNmAmcSM9Y_DaOs7wYzLY,33546
|
11
11
|
spacr/get_alfafold_structures.py,sha256=ehx_MQgb12k3hFecP6cYVlm5TLO8iWjgevy8ESyS3cw,3544
|
12
12
|
spacr/graph_learning.py,sha256=1tR-ZxvXE3dBz1Saw7BeVFcrsUFu9OlUZeZVifih9eo,13070
|
13
|
-
spacr/gui.py,sha256=
|
14
|
-
spacr/gui_2.py,sha256=
|
15
|
-
spacr/gui_classify_app.py,sha256=
|
16
|
-
spacr/gui_mask_app.py,sha256=
|
17
|
-
spacr/gui_measure_app.py,sha256=
|
13
|
+
spacr/gui.py,sha256=ugBksLGOHdtOLlEuRyyc59TrkYKu3rDf8JxEgiBSVao,6536
|
14
|
+
spacr/gui_2.py,sha256=ZAI5quQYbhQJ40vK0NCqU_UMSPLkpfeQpomBWUSM0fc,6946
|
15
|
+
spacr/gui_classify_app.py,sha256=W_epjHsM3P9JfYDWFre694r9suXR_oEtBLvs6WAE_po,7860
|
16
|
+
spacr/gui_mask_app.py,sha256=Lmz1_PLUSuYYLWp36xnYSkKXqEn2bgaHIpW0uOeq4gQ,10403
|
17
|
+
spacr/gui_measure_app.py,sha256=kB-BL0_6vGo5MWND7e2OdLTz4MPa77K9tPYu3eDwBnk,10079
|
18
18
|
spacr/gui_sim_app.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
19
|
-
spacr/gui_utils.py,sha256=
|
19
|
+
spacr/gui_utils.py,sha256=FFFpDzlNyolv1iQtawwD_acctvUpMsFsbVMCrdwyuCM,53167
|
20
20
|
spacr/io.py,sha256=IoERqSwoxJrInYl-E0WfwFOEDZXFdJofk5DmpbyLGWM,112077
|
21
21
|
spacr/logger.py,sha256=7Zqr3TuuOQLWT32gYr2q1qvv7x0a2JhLANmZcnBXAW8,670
|
22
22
|
spacr/mask_app.py,sha256=jlKmj_evveIkkyH3PYEcAshcLXN0DOPWB1oc4hAwq9E,44201
|
@@ -32,9 +32,9 @@ spacr/version.py,sha256=axH5tnGwtgSnJHb5IDhiu4Zjk5GhLyAEDRe-rnaoFOA,409
|
|
32
32
|
spacr/models/cp/toxo_plaque_cyto_e25000_X1120_Y1120.CP_model,sha256=z8BbHWZPRnE9D_BHO0fBREE85c1vkltDs-incs2ytXQ,26566572
|
33
33
|
spacr/models/cp/toxo_plaque_cyto_e25000_X1120_Y1120.CP_model_settings.csv,sha256=fBAGuL_B8ERVdVizO3BHozTDSbZUh1yFzsYK3wkQN68,420
|
34
34
|
spacr/models/cp/toxo_pv_lumen.CP_model,sha256=2y_CindYhmTvVwBH39SNILF3rI3x9SsRn6qrMxHy3l0,26562451
|
35
|
-
spacr-0.1.
|
36
|
-
spacr-0.1.
|
37
|
-
spacr-0.1.
|
38
|
-
spacr-0.1.
|
39
|
-
spacr-0.1.
|
40
|
-
spacr-0.1.
|
35
|
+
spacr-0.1.1.dist-info/LICENSE,sha256=SR-2MeGc6SCM1UORJYyarSWY_A-JaOMFDj7ReSs9tRM,1083
|
36
|
+
spacr-0.1.1.dist-info/METADATA,sha256=f4CaWxwjyeC2yAEeYl-3J50QgVGZqTY9dBX9r66LyTM,5157
|
37
|
+
spacr-0.1.1.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
38
|
+
spacr-0.1.1.dist-info/entry_points.txt,sha256=xncHsqD9MI5wj0_p4mgZlrB8dHm_g_qF0Ggo1c78LqY,315
|
39
|
+
spacr-0.1.1.dist-info/top_level.txt,sha256=GJPU8FgwRXGzKeut6JopsSRY2R8T3i9lDgya42tLInY,6
|
40
|
+
spacr-0.1.1.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|