spacr 0.2.32__py3-none-any.whl → 0.2.45__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/core.py +46 -59
- spacr/gui.py +20 -38
- spacr/gui_core.py +389 -474
- spacr/gui_elements.py +198 -56
- spacr/gui_utils.py +315 -57
- spacr/io.py +29 -38
- spacr/measure.py +6 -9
- spacr/plot.py +106 -0
- spacr/resources/icons/logo.pdf +2786 -6
- spacr/resources/icons/logo_spacr.png +0 -0
- spacr/resources/icons/logo_spacr_1.png +0 -0
- spacr/settings.py +1 -1
- spacr/utils.py +4 -24
- {spacr-0.2.32.dist-info → spacr-0.2.45.dist-info}/METADATA +4 -1
- {spacr-0.2.32.dist-info → spacr-0.2.45.dist-info}/RECORD +19 -17
- {spacr-0.2.32.dist-info → spacr-0.2.45.dist-info}/LICENSE +0 -0
- {spacr-0.2.32.dist-info → spacr-0.2.45.dist-info}/WHEEL +0 -0
- {spacr-0.2.32.dist-info → spacr-0.2.45.dist-info}/entry_points.txt +0 -0
- {spacr-0.2.32.dist-info → spacr-0.2.45.dist-info}/top_level.txt +0 -0
spacr/gui_core.py
CHANGED
@@ -1,6 +1,4 @@
|
|
1
|
-
import os, traceback, ctypes,
|
2
|
-
import matplotlib.pyplot as plt
|
3
|
-
matplotlib.use('Agg')
|
1
|
+
import os, traceback, ctypes, requests, csv, time, requests, re
|
4
2
|
import tkinter as tk
|
5
3
|
from tkinter import ttk
|
6
4
|
from tkinter import filedialog
|
@@ -9,15 +7,17 @@ from multiprocessing.sharedctypes import Synchronized
|
|
9
7
|
from tkinter import ttk, scrolledtext
|
10
8
|
from matplotlib.figure import Figure
|
11
9
|
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
|
12
|
-
from huggingface_hub import list_repo_files
|
13
10
|
import numpy as np
|
11
|
+
import psutil
|
12
|
+
import GPUtil
|
13
|
+
|
14
14
|
try:
|
15
15
|
ctypes.windll.shcore.SetProcessDpiAwareness(True)
|
16
16
|
except AttributeError:
|
17
17
|
pass
|
18
18
|
|
19
19
|
from .settings import set_default_train_test_model, get_measure_crop_settings, set_default_settings_preprocess_generate_masks, get_analyze_reads_default_settings, set_default_umap_image_settings
|
20
|
-
from .gui_elements import spacrProgressBar, spacrButton, spacrLabel, spacrFrame, spacrDropdownMenu ,set_dark_style
|
20
|
+
from .gui_elements import spacrProgressBar, spacrButton, spacrLabel, spacrFrame, spacrDropdownMenu ,set_dark_style
|
21
21
|
|
22
22
|
# Define global variables
|
23
23
|
q = None
|
@@ -32,143 +32,92 @@ fig_queue = None
|
|
32
32
|
|
33
33
|
thread_control = {"run_thread": None, "stop_requested": False}
|
34
34
|
|
35
|
-
def
|
36
|
-
global
|
37
|
-
|
38
|
-
|
39
|
-
if
|
40
|
-
|
41
|
-
thread_control["run_thread"].join()
|
42
|
-
thread_control["run_thread"] = None
|
43
|
-
|
44
|
-
def spacrFigShow(fig_queue=None):
|
45
|
-
"""
|
46
|
-
Replacement for plt.show() that queues figures instead of displaying them.
|
47
|
-
"""
|
48
|
-
fig = plt.gcf()
|
49
|
-
if fig_queue:
|
50
|
-
fig_queue.put(fig)
|
51
|
-
else:
|
52
|
-
fig.show()
|
53
|
-
plt.close(fig)
|
35
|
+
def toggle_settings(button_scrollable_frame):
|
36
|
+
global vars_dict
|
37
|
+
from .settings import categories
|
38
|
+
from .gui_utils import hide_all_settings
|
39
|
+
if vars_dict is None:
|
40
|
+
raise ValueError("vars_dict is not initialized.")
|
54
41
|
|
55
|
-
|
42
|
+
active_categories = set()
|
56
43
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
44
|
+
def toggle_category(settings):
|
45
|
+
for setting in settings:
|
46
|
+
if setting in vars_dict:
|
47
|
+
label, widget, _ = vars_dict[setting]
|
48
|
+
if widget.grid_info():
|
49
|
+
label.grid_remove()
|
50
|
+
widget.grid_remove()
|
51
|
+
else:
|
52
|
+
label.grid()
|
53
|
+
widget.grid()
|
65
54
|
|
66
|
-
|
67
|
-
|
68
|
-
|
55
|
+
def on_category_select(selected_category):
|
56
|
+
if selected_category == "Select Category":
|
57
|
+
return
|
58
|
+
if selected_category in categories:
|
59
|
+
toggle_category(categories[selected_category])
|
60
|
+
if selected_category in active_categories:
|
61
|
+
active_categories.remove(selected_category)
|
62
|
+
else:
|
63
|
+
active_categories.add(selected_category)
|
64
|
+
category_dropdown.update_styles(active_categories)
|
65
|
+
category_var.set("Select Category")
|
66
|
+
|
67
|
+
category_var = tk.StringVar()
|
68
|
+
non_empty_categories = [category for category, settings in categories.items() if any(setting in vars_dict for setting in settings)]
|
69
|
+
category_dropdown = spacrDropdownMenu(button_scrollable_frame.scrollable_frame, category_var, non_empty_categories, command=on_category_select)
|
70
|
+
category_dropdown.grid(row=0, column=4, sticky="ew", pady=2, padx=2)
|
71
|
+
vars_dict = hide_all_settings(vars_dict, categories)
|
72
|
+
|
73
|
+
def process_fig_queue():
|
74
|
+
global canvas, fig_queue, canvas_widget, parent_frame
|
75
|
+
|
76
|
+
def clear_canvas(canvas):
|
77
|
+
for ax in canvas.figure.get_axes():
|
78
|
+
ax.clear()
|
79
|
+
canvas.draw_idle()
|
69
80
|
|
70
81
|
try:
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
from .gui_utils import process_stdout_stderr
|
86
|
-
from .core import preprocess_generate_masks, generate_ml_scores, identify_masks_finetune, check_cellpose_models, analyze_recruitment, train_cellpose, compare_cellpose_masks, analyze_plaques, generate_dataset, apply_model_to_tar
|
87
|
-
from .io import generate_cellpose_train_test
|
88
|
-
from .measure import measure_crop
|
89
|
-
from .sim import run_multiple_simulations
|
90
|
-
from .deep_spacr import train_test_model
|
91
|
-
from .sequencing import analyze_reads, map_barcodes_folder, perform_regression
|
92
|
-
process_stdout_stderr(q)
|
93
|
-
|
94
|
-
print(f'run_function_gui settings_type: {settings_type}')
|
95
|
-
|
96
|
-
if settings_type == 'mask':
|
97
|
-
function = preprocess_generate_masks
|
98
|
-
imports = 2
|
99
|
-
elif settings_type == 'measure':
|
100
|
-
function = measure_crop
|
101
|
-
imports = 1
|
102
|
-
elif settings_type == 'simulation':
|
103
|
-
function = run_multiple_simulations
|
104
|
-
imports = 1
|
105
|
-
elif settings_type == 'sequencing':
|
106
|
-
function = analyze_reads
|
107
|
-
imports = 1
|
108
|
-
elif settings_type == 'classify':
|
109
|
-
function = train_test_model
|
110
|
-
imports = 2
|
111
|
-
elif settings_type == 'train_cellpose':
|
112
|
-
function = train_cellpose
|
113
|
-
imports = 1
|
114
|
-
elif settings_type == 'ml_analyze':
|
115
|
-
function = generate_ml_scores
|
116
|
-
imports = 2
|
117
|
-
elif settings_type == 'cellpose_masks':
|
118
|
-
function = identify_masks_finetune
|
119
|
-
imports = 1
|
120
|
-
elif settings_type == 'cellpose_all':
|
121
|
-
function = check_cellpose_models
|
122
|
-
imports = 1
|
123
|
-
elif settings_type == 'map_barcodes':
|
124
|
-
function = map_barcodes_folder
|
125
|
-
imports = 2
|
126
|
-
elif settings_type == 'regression':
|
127
|
-
function = perform_regression
|
128
|
-
imports = 2
|
129
|
-
elif settings_type == 'recruitment':
|
130
|
-
function = analyze_recruitment
|
131
|
-
imports = 2
|
132
|
-
else:
|
133
|
-
raise ValueError(f"Invalid settings type: {settings_type}")
|
134
|
-
try:
|
135
|
-
function_gui_wrapper(function, settings, q, fig_queue, imports)
|
82
|
+
while not fig_queue.empty():
|
83
|
+
clear_canvas(canvas)
|
84
|
+
fig = fig_queue.get_nowait()
|
85
|
+
for ax in fig.get_axes():
|
86
|
+
ax.set_xticks([]) # Remove x-axis ticks
|
87
|
+
ax.set_yticks([]) # Remove y-axis ticks
|
88
|
+
ax.xaxis.set_visible(False) # Hide the x-axis
|
89
|
+
ax.yaxis.set_visible(False) # Hide the y-axis
|
90
|
+
fig.tight_layout()
|
91
|
+
fig.set_facecolor('black')
|
92
|
+
canvas.figure = fig
|
93
|
+
fig_width, fig_height = canvas_widget.winfo_width(), canvas_widget.winfo_height()
|
94
|
+
fig.set_size_inches(fig_width / fig.dpi, fig_height / fig.dpi, forward=True)
|
95
|
+
canvas.draw_idle()
|
136
96
|
except Exception as e:
|
137
|
-
q.put(f"Error during processing: {e}")
|
138
97
|
traceback.print_exc()
|
139
98
|
finally:
|
140
|
-
|
141
|
-
|
142
|
-
def start_process(q=None, fig_queue=None, settings_type='mask'):
|
143
|
-
global thread_control, vars_dict
|
144
|
-
from .settings import check_settings, expected_types
|
99
|
+
after_id = canvas_widget.after(100, process_fig_queue)
|
100
|
+
parent_frame.after_tasks.append(after_id)
|
145
101
|
|
146
|
-
if q is None:
|
147
|
-
q = Queue()
|
148
|
-
if fig_queue is None:
|
149
|
-
fig_queue = Queue()
|
150
102
|
|
151
|
-
try:
|
152
|
-
settings = check_settings(vars_dict, expected_types, q)
|
153
|
-
except ValueError as e:
|
154
|
-
q.put(f"Error: {e}")
|
155
|
-
return
|
156
103
|
|
157
|
-
if thread_control.get("run_thread") is not None:
|
158
|
-
initiate_abort()
|
159
|
-
|
160
|
-
stop_requested = Value('i', 0)
|
161
|
-
thread_control["stop_requested"] = stop_requested
|
162
104
|
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
105
|
+
def set_globals(thread_control_var, q_var, console_output_var, parent_frame_var, vars_dict_var, canvas_var, canvas_widget_var, scrollable_frame_var, fig_queue_var, progress_bar_var, usage_bars_var):
|
106
|
+
global thread_control, q, console_output, parent_frame, vars_dict, canvas, canvas_widget, scrollable_frame, fig_queue, progress_bar, usage_bars
|
107
|
+
thread_control = thread_control_var
|
108
|
+
q = q_var
|
109
|
+
console_output = console_output_var
|
110
|
+
parent_frame = parent_frame_var
|
111
|
+
vars_dict = vars_dict_var
|
112
|
+
canvas = canvas_var
|
113
|
+
canvas_widget = canvas_widget_var
|
114
|
+
scrollable_frame = scrollable_frame_var
|
115
|
+
fig_queue = fig_queue_var
|
116
|
+
progress_bar = progress_bar_var
|
117
|
+
usage_bars = usage_bars_var
|
170
118
|
|
171
119
|
def import_settings(settings_type='mask'):
|
120
|
+
from .gui_utils import convert_settings_dict_for_gui, hide_all_settings
|
172
121
|
global vars_dict, scrollable_frame, button_scrollable_frame
|
173
122
|
from .settings import generate_fields
|
174
123
|
|
@@ -217,74 +166,23 @@ def import_settings(settings_type='mask'):
|
|
217
166
|
vars_dict = generate_fields(new_settings, scrollable_frame)
|
218
167
|
vars_dict = hide_all_settings(vars_dict, categories=None)
|
219
168
|
|
220
|
-
def
|
221
|
-
from torchvision import models as torch_models
|
222
|
-
torchvision_models = [name for name, obj in torch_models.__dict__.items() if callable(obj)]
|
223
|
-
chans = ['0', '1', '2', '3', '4', '5', '6', '7', '8', None]
|
224
|
-
chans_v2 = [0, 1, 2, 3, None]
|
225
|
-
variables = {}
|
226
|
-
special_cases = {
|
227
|
-
'metadata_type': ('combo', ['cellvoyager', 'cq1', 'nikon', 'zeis', 'custom'], 'cellvoyager'),
|
228
|
-
'channels': ('combo', ['[0,1,2,3]', '[0,1,2]', '[0,1]', '[0]'], '[0,1,2,3]'),
|
229
|
-
'channel_dims': ('combo', ['[0,1,2,3]', '[0,1,2]', '[0,1]', '[0]'], '[0,1,2,3]'),
|
230
|
-
'cell_mask_dim': ('combo', chans, None),
|
231
|
-
'cell_chann_dim': ('combo', chans, None),
|
232
|
-
'nucleus_mask_dim': ('combo', chans, None),
|
233
|
-
'nucleus_chann_dim': ('combo', chans, None),
|
234
|
-
'pathogen_mask_dim': ('combo', chans, None),
|
235
|
-
'pathogen_chann_dim': ('combo', chans, None),
|
236
|
-
'crop_mode': ('combo', ['cell', 'nucleus', 'pathogen', '[cell, nucleus, pathogen]', '[cell,nucleus, pathogen]'], ['cell']),
|
237
|
-
'magnification': ('combo', [20, 40, 60], 20),
|
238
|
-
'nucleus_channel': ('combo', chans_v2, None),
|
239
|
-
'cell_channel': ('combo', chans_v2, None),
|
240
|
-
'channel_of_interest': ('combo', chans_v2, None),
|
241
|
-
'pathogen_channel': ('combo', chans_v2, None),
|
242
|
-
'timelapse_mode': ('combo', ['trackpy', 'btrack'], 'trackpy'),
|
243
|
-
'train_mode': ('combo', ['erm', 'irm'], 'erm'),
|
244
|
-
'clustering': ('combo', ['dbscan', 'kmean'], 'dbscan'),
|
245
|
-
'reduction_method': ('combo', ['umap', 'tsne'], 'umap'),
|
246
|
-
'model_name': ('combo', ['cyto', 'cyto_2', 'cyto_3', 'nuclei'], 'cyto'),
|
247
|
-
'regression_type': ('combo', ['ols','gls','wls','rlm','glm','mixed','quantile','logit','probit','poisson','lasso','ridge'], 'ols'),
|
248
|
-
'timelapse_objects': ('combo', ['cell', 'nucleus', 'pathogen', 'cytoplasm', None], None),
|
249
|
-
'model_type': ('combo', torchvision_models, 'resnet50'),
|
250
|
-
'optimizer_type': ('combo', ['adamw', 'adam'], 'adamw'),
|
251
|
-
'schedule': ('combo', ['reduce_lr_on_plateau', 'step_lr'], 'reduce_lr_on_plateau'),
|
252
|
-
'loss_type': ('combo', ['focal_loss', 'binary_cross_entropy_with_logits'], 'focal_loss'),
|
253
|
-
'normalize_by': ('combo', ['fov', 'png'], 'png'),
|
254
|
-
'agg_type': ('combo', ['mean', 'median'], 'mean'),
|
255
|
-
'grouping': ('combo', ['mean', 'median'], 'mean'),
|
256
|
-
'min_max': ('combo', ['allq', 'all'], 'allq'),
|
257
|
-
'transform': ('combo', ['log', 'sqrt', 'square', None], None)
|
258
|
-
}
|
259
|
-
|
260
|
-
for key, value in settings.items():
|
261
|
-
if key in special_cases:
|
262
|
-
variables[key] = special_cases[key]
|
263
|
-
elif isinstance(value, bool):
|
264
|
-
variables[key] = ('check', None, value)
|
265
|
-
elif isinstance(value, int) or isinstance(value, float):
|
266
|
-
variables[key] = ('entry', None, value)
|
267
|
-
elif isinstance(value, str):
|
268
|
-
variables[key] = ('entry', None, value)
|
269
|
-
elif value is None:
|
270
|
-
variables[key] = ('entry', None, value)
|
271
|
-
elif isinstance(value, list):
|
272
|
-
variables[key] = ('entry', None, str(value))
|
273
|
-
else:
|
274
|
-
variables[key] = ('entry', None, str(value))
|
275
|
-
return variables
|
276
|
-
|
277
|
-
def setup_settings_panel(vertical_container, settings_type='mask', window_dimensions=[500, 1000]):
|
169
|
+
def setup_settings_panel(vertical_container, settings_type='mask'):
|
278
170
|
global vars_dict, scrollable_frame
|
279
171
|
from .settings import get_identify_masks_finetune_default_settings, set_default_analyze_screen, set_default_settings_preprocess_generate_masks, get_measure_crop_settings, set_default_train_test_model, get_analyze_reads_default_settings, set_default_umap_image_settings, generate_fields, get_perform_regression_default_settings, get_train_cellpose_default_settings, get_map_barcodes_default_settings, get_analyze_recruitment_default_settings, get_check_cellpose_models_default_settings
|
172
|
+
from .gui_utils import convert_settings_dict_for_gui, set_element_size
|
173
|
+
|
174
|
+
size_dict = set_element_size(vertical_container)
|
175
|
+
settings_width = size_dict['settings_width']
|
280
176
|
|
281
|
-
|
282
|
-
|
177
|
+
# Create a PanedWindow for the settings panel
|
178
|
+
settings_paned_window = tk.PanedWindow(vertical_container, orient=tk.HORIZONTAL)
|
179
|
+
vertical_container.add(settings_paned_window, stretch="always")
|
180
|
+
|
181
|
+
settings_frame = tk.Frame(settings_paned_window, width=settings_width)
|
182
|
+
settings_frame.pack_propagate(False) # Prevent the frame from resizing based on its children
|
183
|
+
|
184
|
+
settings_paned_window.add(settings_frame)
|
283
185
|
|
284
|
-
settings_frame = tk.Frame(vertical_container)
|
285
|
-
vertical_container.add(settings_frame, stretch="always")
|
286
|
-
settings_label = spacrLabel(settings_frame, text="Settings", anchor='center', justify='center', align="center")
|
287
|
-
settings_label.grid(row=0, column=0, pady=10, padx=10)
|
288
186
|
scrollable_frame = spacrFrame(settings_frame)
|
289
187
|
scrollable_frame.grid(row=1, column=0, sticky="nsew")
|
290
188
|
settings_frame.grid_rowconfigure(1, weight=1)
|
@@ -321,7 +219,7 @@ def setup_settings_panel(vertical_container, settings_type='mask', window_dimens
|
|
321
219
|
vars_dict = generate_fields(variables, scrollable_frame)
|
322
220
|
|
323
221
|
containers = [settings_frame]
|
324
|
-
widgets = [
|
222
|
+
widgets = [scrollable_frame]
|
325
223
|
|
326
224
|
style = ttk.Style(vertical_container)
|
327
225
|
_ = set_dark_style(style, containers=containers, widgets=widgets)
|
@@ -355,19 +253,50 @@ def setup_plot_section(vertical_container):
|
|
355
253
|
|
356
254
|
def setup_console(vertical_container):
|
357
255
|
global console_output
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
256
|
+
|
257
|
+
# Create a frame to hold the console and button sections
|
258
|
+
console_button_frame = tk.Frame(vertical_container)
|
259
|
+
vertical_container.add(console_button_frame, stretch="always")
|
260
|
+
|
261
|
+
# Create a PanedWindow for resizing height
|
262
|
+
console_paned_window = tk.PanedWindow(console_button_frame, orient=tk.VERTICAL, bg='black')
|
263
|
+
console_paned_window.pack(fill=tk.BOTH, expand=True)
|
264
|
+
|
265
|
+
# Create the main console frame using spacrFrame with textbox=True
|
266
|
+
console_frame = spacrFrame(console_paned_window, textbox=True)
|
267
|
+
console_paned_window.add(console_frame, stretch="always")
|
268
|
+
|
269
|
+
# Assign the scrollable frame (which is a Text widget) to console_output
|
270
|
+
console_output = console_frame.scrollable_frame
|
271
|
+
|
272
|
+
# Ensure the Text widget spans the entire console frame
|
273
|
+
console_output.grid(row=0, column=0, sticky="nsew")
|
274
|
+
|
275
|
+
# Configure the grid to allow expansion
|
276
|
+
console_frame.grid_rowconfigure(0, weight=1)
|
365
277
|
console_frame.grid_columnconfigure(0, weight=1)
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
278
|
+
|
279
|
+
# Create a lower frame to act as the anchor point
|
280
|
+
lower_frame = tk.Frame(console_paned_window)
|
281
|
+
console_paned_window.add(lower_frame, minsize=10) # Adjust minsize to ensure usability
|
282
|
+
|
283
|
+
# Dynamically adjust the height and width of the console_frame
|
284
|
+
def adjust_frame_size(event):
|
285
|
+
console_frame.update_idletasks()
|
286
|
+
new_width = console_paned_window.winfo_width()
|
287
|
+
new_height = console_paned_window.winfo_height()
|
288
|
+
console_frame.config(width=new_width, height=new_height)
|
289
|
+
console_output.config(width=new_width, height=new_height)
|
290
|
+
|
291
|
+
# Bind the configure event to dynamically adjust size
|
292
|
+
console_paned_window.bind("<Configure>", adjust_frame_size)
|
293
|
+
|
294
|
+
# Apply dark style to the PanedWindow and lower frame
|
295
|
+
style = ttk.Style()
|
296
|
+
set_dark_style(style, containers=[console_paned_window, lower_frame])
|
297
|
+
|
298
|
+
return console_output, console_frame
|
299
|
+
|
371
300
|
|
372
301
|
def setup_progress_frame(vertical_container):
|
373
302
|
global progress_output
|
@@ -387,260 +316,285 @@ def setup_progress_frame(vertical_container):
|
|
387
316
|
_ = set_dark_style(style, containers=containers, widgets=widgets)
|
388
317
|
return progress_output
|
389
318
|
|
390
|
-
def
|
391
|
-
global vars_dict,
|
392
|
-
|
393
|
-
|
394
|
-
dataset_subfolder = "plate1"
|
395
|
-
local_dir = os.path.join(os.path.expanduser("~"), "datasets") # Set to the home directory
|
396
|
-
|
397
|
-
# Download the dataset
|
398
|
-
try:
|
399
|
-
dataset_path = download_dataset(dataset_repo_id, dataset_subfolder, local_dir)
|
400
|
-
if 'src' in vars_dict:
|
401
|
-
vars_dict['src'][2].set(dataset_path)
|
402
|
-
q.put(f"Set source path to: {vars_dict['src'][2].get()}\n")
|
403
|
-
q.put(f"Dataset downloaded to: {dataset_path}\n")
|
404
|
-
except Exception as e:
|
405
|
-
q.put(f"Failed to download dataset: {e}\n")
|
406
|
-
|
407
|
-
# Download the settings files
|
408
|
-
try:
|
409
|
-
settings_path = download_dataset(settings_repo_id, "", local_dir)
|
410
|
-
q.put(f"Settings downloaded to: {settings_path}\n")
|
411
|
-
except Exception as e:
|
412
|
-
q.put(f"Failed to download settings: {e}\n")
|
413
|
-
|
414
|
-
def download_dataset(repo_id, subfolder, local_dir=None, retries=5, delay=5):
|
415
|
-
global q
|
416
|
-
"""
|
417
|
-
Downloads a dataset or settings files from Hugging Face and returns the local path.
|
418
|
-
|
419
|
-
Args:
|
420
|
-
repo_id (str): The repository ID (e.g., 'einarolafsson/toxo_mito' or 'einarolafsson/spacr_settings').
|
421
|
-
subfolder (str): The subfolder path within the repository (e.g., 'plate1' or the settings subfolder).
|
422
|
-
local_dir (str): The local directory where the files will be saved. Defaults to the user's home directory.
|
423
|
-
retries (int): Number of retry attempts in case of failure.
|
424
|
-
delay (int): Delay in seconds between retries.
|
425
|
-
|
426
|
-
Returns:
|
427
|
-
str: The local path to the downloaded files.
|
428
|
-
"""
|
429
|
-
if local_dir is None:
|
430
|
-
local_dir = os.path.join(os.path.expanduser("~"), "datasets")
|
431
|
-
|
432
|
-
local_subfolder_dir = os.path.join(local_dir, subfolder if subfolder else "settings")
|
433
|
-
if not os.path.exists(local_subfolder_dir):
|
434
|
-
os.makedirs(local_subfolder_dir)
|
435
|
-
elif len(os.listdir(local_subfolder_dir)) > 0:
|
436
|
-
q.put(f"Files already downloaded to: {local_subfolder_dir}")
|
437
|
-
return local_subfolder_dir
|
438
|
-
|
439
|
-
attempt = 0
|
440
|
-
while attempt < retries:
|
441
|
-
try:
|
442
|
-
files = list_repo_files(repo_id, repo_type="dataset")
|
443
|
-
subfolder_files = [file for file in files if file.startswith(subfolder) or (subfolder == "" and file.endswith('.csv'))]
|
444
|
-
|
445
|
-
for file_name in subfolder_files:
|
446
|
-
for download_attempt in range(retries):
|
447
|
-
try:
|
448
|
-
url = f"https://huggingface.co/datasets/{repo_id}/resolve/main/{file_name}?download=true"
|
449
|
-
response = requests.get(url, stream=True)
|
450
|
-
response.raise_for_status()
|
451
|
-
|
452
|
-
local_file_path = os.path.join(local_subfolder_dir, os.path.basename(file_name))
|
453
|
-
with open(local_file_path, 'wb') as file:
|
454
|
-
for chunk in response.iter_content(chunk_size=8192):
|
455
|
-
file.write(chunk)
|
456
|
-
q.put(f"Downloaded file: {file_name}")
|
457
|
-
break
|
458
|
-
except (requests.HTTPError, requests.Timeout) as e:
|
459
|
-
q.put(f"Error downloading {file_name}: {e}. Retrying in {delay} seconds...")
|
460
|
-
time.sleep(delay)
|
461
|
-
else:
|
462
|
-
raise Exception(f"Failed to download {file_name} after multiple attempts.")
|
463
|
-
|
464
|
-
return local_subfolder_dir
|
465
|
-
|
466
|
-
except (requests.HTTPError, requests.Timeout) as e:
|
467
|
-
q.put(f"Error downloading files: {e}. Retrying in {delay} seconds...")
|
468
|
-
attempt += 1
|
469
|
-
time.sleep(delay)
|
470
|
-
|
471
|
-
raise Exception("Failed to download files after multiple attempts.")
|
319
|
+
def setup_button_section(horizontal_container, settings_type='mask', run=True, abort=True, download=True, import_btn=True):
|
320
|
+
global thread_control, parent_frame, button_frame, button_scrollable_frame, run_button, abort_button, download_dataset_button, import_button, q, fig_queue, vars_dict, progress_bar
|
321
|
+
from .gui_utils import set_element_size, download_hug_dataset
|
322
|
+
from .settings import categories
|
472
323
|
|
473
|
-
def setup_button_section(horizontal_container, settings_type='mask', window_dimensions=[500, 1000], run=True, abort=True, download=True, import_btn=True):
|
474
|
-
global button_frame, button_scrollable_frame, run_button, abort_button, download_dataset_button, import_button, q, fig_queue, vars_dict, progress_bar
|
475
|
-
from .settings import descriptions
|
476
324
|
|
477
|
-
|
478
|
-
|
325
|
+
size_dict = set_element_size(horizontal_container)
|
326
|
+
button_section_height = size_dict['panel_height']
|
327
|
+
button_frame = tk.Frame(horizontal_container, height=button_section_height)
|
328
|
+
|
329
|
+
# Prevent the frame from resizing based on the child widget
|
330
|
+
button_frame.pack_propagate(False)
|
479
331
|
|
480
|
-
button_frame = tk.Frame(horizontal_container)
|
481
332
|
horizontal_container.add(button_frame, stretch="always", sticky="nsew")
|
482
|
-
button_frame
|
483
|
-
button_frame.grid_rowconfigure(1, weight=1)
|
484
|
-
button_frame.grid_columnconfigure(0, weight=1)
|
485
|
-
|
486
|
-
categories_label = spacrLabel(button_frame, text="Categories", anchor='center', justify='center', align="center")
|
487
|
-
categories_label.grid(row=0, column=0, pady=10, padx=10)
|
488
|
-
button_scrollable_frame = spacrFrame(button_frame)
|
333
|
+
button_scrollable_frame = spacrFrame(button_frame, scrollbar=False)
|
489
334
|
button_scrollable_frame.grid(row=1, column=0, sticky="nsew")
|
490
|
-
|
491
|
-
widgets = [categories_label, button_scrollable_frame.scrollable_frame]
|
335
|
+
widgets = [button_scrollable_frame.scrollable_frame]
|
492
336
|
|
493
337
|
btn_col = 0
|
494
|
-
btn_row =
|
338
|
+
btn_row = 0
|
495
339
|
|
496
340
|
if run:
|
497
341
|
print(f'settings_type: {settings_type}')
|
498
|
-
run_button = spacrButton(button_scrollable_frame.scrollable_frame, text="run", command=lambda: start_process(q, fig_queue, settings_type))
|
342
|
+
run_button = spacrButton(button_scrollable_frame.scrollable_frame, text="run", command=lambda: start_process(q, fig_queue, settings_type), show_text=False, size=size_dict['btn_size'], animation=False)
|
499
343
|
run_button.grid(row=btn_row, column=btn_col, pady=5, padx=5, sticky='ew')
|
500
344
|
widgets.append(run_button)
|
501
|
-
|
345
|
+
btn_col += 1
|
502
346
|
|
503
347
|
if abort and settings_type in ['mask', 'measure', 'classify', 'sequencing', 'umap']:
|
504
|
-
abort_button = spacrButton(button_scrollable_frame.scrollable_frame, text="abort", command=initiate_abort)
|
348
|
+
abort_button = spacrButton(button_scrollable_frame.scrollable_frame, text="abort", command=lambda: initiate_abort(), show_text=False, size=size_dict['btn_size'], animation=False)
|
505
349
|
abort_button.grid(row=btn_row, column=btn_col, pady=5, padx=5, sticky='ew')
|
506
350
|
widgets.append(abort_button)
|
507
|
-
|
351
|
+
btn_col += 1
|
508
352
|
|
509
353
|
if download and settings_type in ['mask']:
|
510
|
-
download_dataset_button = spacrButton(button_scrollable_frame.scrollable_frame, text="download", command=download_hug_dataset)
|
354
|
+
download_dataset_button = spacrButton(button_scrollable_frame.scrollable_frame, text="download", command=lambda: download_hug_dataset(q, vars_dict), show_text=False, size=size_dict['btn_size'], animation=False)
|
511
355
|
download_dataset_button.grid(row=btn_row, column=btn_col, pady=5, padx=5, sticky='ew')
|
512
356
|
widgets.append(download_dataset_button)
|
513
|
-
|
357
|
+
btn_col += 1
|
514
358
|
|
515
359
|
if import_btn:
|
516
|
-
import_button = spacrButton(button_scrollable_frame.scrollable_frame, text="settings", command=lambda: import_settings(settings_type))
|
360
|
+
import_button = spacrButton(button_scrollable_frame.scrollable_frame, text="settings", command=lambda: import_settings(settings_type), show_text=False, size=size_dict['btn_size'], animation=False)
|
517
361
|
import_button.grid(row=btn_row, column=btn_col, pady=5, padx=5, sticky='ew')
|
518
362
|
widgets.append(import_button)
|
519
363
|
btn_row += 1
|
520
364
|
|
521
365
|
# Add the progress bar under the settings category menu
|
522
366
|
progress_bar = spacrProgressBar(button_scrollable_frame.scrollable_frame, orient='horizontal', mode='determinate')
|
523
|
-
progress_bar.grid(row=
|
367
|
+
progress_bar.grid(row=btn_row, column=0, columnspan=5, pady=5, padx=5, sticky='ew')
|
368
|
+
progress_bar.set_label_position() # Set the label position after grid placement
|
524
369
|
widgets.append(progress_bar)
|
525
370
|
|
526
371
|
if vars_dict is not None:
|
527
372
|
toggle_settings(button_scrollable_frame)
|
528
373
|
|
529
|
-
|
530
|
-
|
531
|
-
description_frame.grid_columnconfigure(0, weight=1)
|
532
|
-
description_frame.grid_rowconfigure(0, weight=1) # Add this line to make the row expandable
|
374
|
+
style = ttk.Style(horizontal_container)
|
375
|
+
_ = set_dark_style(style, containers=[button_frame], widgets=widgets)
|
533
376
|
|
534
|
-
|
535
|
-
description_label.grid(row=0, column=0, pady=50, padx=20, sticky='nsew')
|
536
|
-
description_text = descriptions.get(settings_type, "No description available for this module.")
|
537
|
-
description_label.config(text=description_text)
|
377
|
+
return button_scrollable_frame
|
538
378
|
|
539
|
-
|
540
|
-
|
541
|
-
|
379
|
+
def setup_usage_panel(horizontal_container):
|
380
|
+
global usage_bars
|
381
|
+
from .gui_utils import set_element_size
|
382
|
+
|
383
|
+
def update_usage(ram_bar, vram_bar, gpu_bar, usage_bars, parent_frame):
|
384
|
+
# Update RAM usage
|
385
|
+
ram_usage = psutil.virtual_memory().percent
|
386
|
+
ram_bar['value'] = ram_usage
|
387
|
+
|
388
|
+
# Update GPU and VRAM usage
|
389
|
+
gpus = GPUtil.getGPUs()
|
390
|
+
if gpus:
|
391
|
+
gpu = gpus[0]
|
392
|
+
vram_usage = gpu.memoryUtil * 100
|
393
|
+
gpu_usage = gpu.load * 100
|
394
|
+
vram_bar['value'] = vram_usage
|
395
|
+
gpu_bar['value'] = gpu_usage
|
396
|
+
|
397
|
+
# Update CPU usage for each core
|
398
|
+
cpu_percentages = psutil.cpu_percent(percpu=True)
|
399
|
+
for bar, usage in zip(usage_bars[3:], cpu_percentages):
|
400
|
+
bar['value'] = usage
|
401
|
+
|
402
|
+
# Schedule the function to run again after 1000 ms (1 second)
|
403
|
+
parent_frame.after(1000, update_usage, ram_bar, vram_bar, gpu_bar, usage_bars, parent_frame)
|
404
|
+
|
405
|
+
size_dict = set_element_size(horizontal_container)
|
406
|
+
usage_panel_height = size_dict['panel_height']
|
407
|
+
usage_frame = tk.Frame(horizontal_container, height=usage_panel_height)
|
408
|
+
horizontal_container.add(usage_frame, stretch="always", sticky="nsew")
|
409
|
+
|
410
|
+
usage_frame.grid_rowconfigure(0, weight=0)
|
411
|
+
usage_frame.grid_rowconfigure(1, weight=1)
|
412
|
+
usage_frame.grid_columnconfigure(0, weight=1)
|
413
|
+
usage_frame.grid_columnconfigure(1, weight=1)
|
414
|
+
|
415
|
+
usage_scrollable_frame = spacrFrame(usage_frame, scrollbar=False)
|
416
|
+
usage_scrollable_frame.grid(row=1, column=0, sticky="nsew", columnspan=2)
|
417
|
+
widgets = [usage_scrollable_frame.scrollable_frame]
|
418
|
+
usage_bars = []
|
419
|
+
max_elements_per_column = 6
|
420
|
+
row = 0
|
421
|
+
col = 0
|
422
|
+
|
423
|
+
# Initialize RAM, VRAM, and GPU bars as None
|
424
|
+
ram_bar, vram_bar, gpu_bar = None, None, None
|
425
|
+
|
426
|
+
# Try adding RAM bar
|
427
|
+
try:
|
428
|
+
ram_info = psutil.virtual_memory()
|
429
|
+
ram_label_text = f"RAM"
|
430
|
+
label = ttk.Label(usage_scrollable_frame.scrollable_frame, text=ram_label_text, anchor='w')
|
431
|
+
label.grid(row=row, column=2 * col, pady=5, padx=5, sticky='w')
|
432
|
+
ram_bar = spacrProgressBar(usage_scrollable_frame.scrollable_frame, orient='horizontal', mode='determinate', length=size_dict['bar_size'], label=False)
|
433
|
+
ram_bar.grid(row=row, column=2 * col + 1, pady=5, padx=5, sticky='ew')
|
434
|
+
widgets.append(label)
|
435
|
+
widgets.append(ram_bar)
|
436
|
+
usage_bars.append(ram_bar)
|
437
|
+
row += 1
|
438
|
+
except Exception as e:
|
439
|
+
print(f"Could not add RAM usage bar: {e}")
|
542
440
|
|
543
|
-
|
441
|
+
# Try adding VRAM and GPU usage bars
|
442
|
+
try:
|
443
|
+
gpus = GPUtil.getGPUs()
|
444
|
+
if gpus:
|
445
|
+
gpu = gpus[0]
|
446
|
+
vram_label_text = f"VRAM"
|
447
|
+
label = ttk.Label(usage_scrollable_frame.scrollable_frame, text=vram_label_text, anchor='w')
|
448
|
+
label.grid(row=row, column=2 * col, pady=5, padx=5, sticky='w')
|
449
|
+
vram_bar = spacrProgressBar(usage_scrollable_frame.scrollable_frame, orient='horizontal', mode='determinate', length=size_dict['bar_size'], label=False)
|
450
|
+
vram_bar.grid(row=row, column=2 * col + 1, pady=5, padx=5, sticky='ew')
|
451
|
+
widgets.append(label)
|
452
|
+
widgets.append(vram_bar)
|
453
|
+
usage_bars.append(vram_bar)
|
454
|
+
row += 1
|
455
|
+
|
456
|
+
gpu_label_text = f"GPU"
|
457
|
+
label = ttk.Label(usage_scrollable_frame.scrollable_frame, text=gpu_label_text, anchor='w')
|
458
|
+
label.grid(row=row, column=2 * col, pady=5, padx=5, sticky='w')
|
459
|
+
gpu_bar = spacrProgressBar(usage_scrollable_frame.scrollable_frame, orient='horizontal', mode='determinate', length=size_dict['bar_size'], label=False)
|
460
|
+
gpu_bar.grid(row=row, column=2 * col + 1, pady=5, padx=5, sticky='ew')
|
461
|
+
widgets.append(label)
|
462
|
+
widgets.append(gpu_bar)
|
463
|
+
usage_bars.append(gpu_bar)
|
464
|
+
row += 1
|
465
|
+
except Exception as e:
|
466
|
+
print(f"Could not add VRAM or GPU usage bars: {e}")
|
544
467
|
|
545
|
-
|
546
|
-
|
468
|
+
# Add CPU core usage bars
|
469
|
+
try:
|
470
|
+
cpu_cores = psutil.cpu_count(logical=True)
|
471
|
+
cpu_freq = psutil.cpu_freq()
|
472
|
+
|
473
|
+
for core in range(cpu_cores):
|
474
|
+
if row > 0 and row % max_elements_per_column == 0:
|
475
|
+
col += 1
|
476
|
+
row = 0
|
477
|
+
label = ttk.Label(usage_scrollable_frame.scrollable_frame, text=f"Core {core+1}", anchor='w')
|
478
|
+
label.grid(row=row, column=2 * col, pady=2, padx=5, sticky='w')
|
479
|
+
bar = spacrProgressBar(usage_scrollable_frame.scrollable_frame, orient='horizontal', mode='determinate', length=size_dict['bar_size'], label=False)
|
480
|
+
bar.grid(row=row, column=2 * col + 1, pady=2, padx=5, sticky='ew')
|
481
|
+
widgets.append(label)
|
482
|
+
widgets.append(bar)
|
483
|
+
usage_bars.append(bar)
|
484
|
+
row += 1
|
485
|
+
except Exception as e:
|
486
|
+
print(f"Could not add CPU core usage bars: {e}")
|
547
487
|
|
548
488
|
style = ttk.Style(horizontal_container)
|
549
|
-
_ = set_dark_style(style, containers=
|
489
|
+
_ = set_dark_style(style, containers=[usage_frame], widgets=widgets)
|
550
490
|
|
551
|
-
|
491
|
+
if ram_bar is None:
|
492
|
+
ram_bar = spacrProgressBar(usage_scrollable_frame.scrollable_frame, orient='horizontal', mode='determinate', length=size_dict['bar_size'], label=False)
|
493
|
+
if vram_bar is None:
|
494
|
+
vram_bar = spacrProgressBar(usage_scrollable_frame.scrollable_frame, orient='horizontal', mode='determinate', length=size_dict['bar_size'], label=False)
|
495
|
+
if gpu_bar is None:
|
496
|
+
gpu_bar = spacrProgressBar(usage_scrollable_frame.scrollable_frame, orient='horizontal', mode='determinate', length=size_dict['bar_size'], label=False)
|
552
497
|
|
498
|
+
update_usage(ram_bar, vram_bar, gpu_bar, usage_bars, usage_frame)
|
499
|
+
return usage_scrollable_frame, usage_bars
|
553
500
|
|
554
|
-
def
|
555
|
-
|
556
|
-
|
501
|
+
def setup_help_section(horizontal_container, settings_type='mask'):
|
502
|
+
from .settings import descriptions
|
503
|
+
from .gui_utils import set_element_size
|
557
504
|
|
558
|
-
|
559
|
-
|
560
|
-
- vars_dict: dict, The dictionary containing the settings and their corresponding widgets.
|
561
|
-
"""
|
505
|
+
size_dict = set_element_size(horizontal_container)
|
506
|
+
help_section_height = size_dict['panel_height']
|
562
507
|
|
563
|
-
|
564
|
-
|
508
|
+
# Create the frame for the help section
|
509
|
+
description_frame = tk.Frame(horizontal_container, height=help_section_height)
|
510
|
+
description_frame.pack_propagate(False)
|
565
511
|
|
566
|
-
|
567
|
-
|
568
|
-
|
569
|
-
|
570
|
-
# Initially hide all settings
|
571
|
-
for setting in settings:
|
572
|
-
if setting in vars_dict:
|
573
|
-
label, widget, _ = vars_dict[setting]
|
574
|
-
label.grid_remove()
|
575
|
-
widget.grid_remove()
|
576
|
-
return vars_dict
|
512
|
+
# Add the description frame to the horizontal container
|
513
|
+
horizontal_container.add(description_frame, stretch="always", sticky="nsew")
|
514
|
+
description_frame.grid_columnconfigure(0, weight=1)
|
515
|
+
description_frame.grid_rowconfigure(0, weight=1) # Ensure the text widget row is expandable
|
577
516
|
|
578
|
-
|
579
|
-
|
580
|
-
|
517
|
+
style_out = set_dark_style(ttk.Style())
|
518
|
+
bg_color = style_out['bg_color']
|
519
|
+
fg_color = style_out['fg_color']
|
581
520
|
|
582
|
-
|
583
|
-
|
521
|
+
# Insert the description text
|
522
|
+
description_text = descriptions.get(settings_type, "No description available for this module.")
|
523
|
+
first_line, *rest_of_description = description_text.split('\n', 1)
|
584
524
|
|
585
|
-
|
525
|
+
# Create the first line label
|
526
|
+
first_line_label = ttk.Label(description_frame, text=first_line, anchor='w', background=bg_color, foreground=fg_color)
|
527
|
+
first_line_label.grid(row=0, column=0, sticky='ew')
|
586
528
|
|
587
|
-
|
588
|
-
|
589
|
-
|
590
|
-
label, widget, _ = vars_dict[setting]
|
591
|
-
if widget.grid_info():
|
592
|
-
label.grid_remove()
|
593
|
-
widget.grid_remove()
|
594
|
-
else:
|
595
|
-
label.grid()
|
596
|
-
widget.grid()
|
529
|
+
# Create the text widget with the appropriate background and foreground colors
|
530
|
+
description_text_widget = tk.Text(description_frame, wrap="word", bg=bg_color, fg=fg_color, bd=0, highlightthickness=0)
|
531
|
+
description_text_widget.grid(row=1, column=0, sticky="nsew")
|
597
532
|
|
598
|
-
|
599
|
-
|
600
|
-
|
601
|
-
|
602
|
-
toggle_category(categories[selected_category])
|
603
|
-
if selected_category in active_categories:
|
604
|
-
active_categories.remove(selected_category)
|
605
|
-
else:
|
606
|
-
active_categories.add(selected_category)
|
607
|
-
category_dropdown.update_styles(active_categories)
|
608
|
-
category_var.set("Select Category")
|
533
|
+
# Insert the rest of the description text
|
534
|
+
if rest_of_description:
|
535
|
+
description_text_widget.insert("1.0", rest_of_description[0].lstrip())
|
536
|
+
description_text_widget.config(state="disabled") # Make the text widget read-only
|
609
537
|
|
610
|
-
|
611
|
-
|
612
|
-
|
613
|
-
category_dropdown.grid(row=7, column=0, sticky="ew", pady=2, padx=2)
|
614
|
-
vars_dict = hide_all_settings(vars_dict, categories)
|
538
|
+
def update_wraplength(event):
|
539
|
+
new_width = event.width - 20 # Adjust as needed
|
540
|
+
description_text_widget.config(width=new_width)
|
615
541
|
|
616
|
-
|
617
|
-
global canvas, fig_queue, canvas_widget, parent_frame
|
542
|
+
description_text_widget.bind('<Configure>', update_wraplength)
|
618
543
|
|
619
|
-
|
620
|
-
|
621
|
-
|
622
|
-
|
544
|
+
# Apply dark style
|
545
|
+
style = ttk.Style(horizontal_container)
|
546
|
+
_ = set_dark_style(style, containers=[description_frame], widgets=[description_text_widget, first_line_label])
|
547
|
+
|
548
|
+
return description_frame
|
549
|
+
|
550
|
+
def initiate_abort():
|
551
|
+
global thread_control, q, parent_frame
|
552
|
+
if thread_control.get("run_thread") is not None:
|
553
|
+
try:
|
554
|
+
q.put("Aborting processes...")
|
555
|
+
thread_control.get("run_thread").terminate()
|
556
|
+
thread_control["run_thread"] = None
|
557
|
+
q.put("Processes aborted.")
|
558
|
+
except Exception as e:
|
559
|
+
q.put(f"Error aborting process: {e}")
|
560
|
+
|
561
|
+
thread_control = {"run_thread": None, "stop_requested": False}
|
562
|
+
|
563
|
+
|
564
|
+
def start_process(q=None, fig_queue=None, settings_type='mask'):
|
565
|
+
global thread_control, vars_dict, parent_frame
|
566
|
+
from .settings import check_settings, expected_types
|
567
|
+
from .gui_utils import run_function_gui
|
568
|
+
|
569
|
+
if q is None:
|
570
|
+
q = Queue()
|
571
|
+
if fig_queue is None:
|
572
|
+
fig_queue = Queue()
|
623
573
|
|
624
574
|
try:
|
625
|
-
|
626
|
-
|
627
|
-
|
628
|
-
|
629
|
-
|
630
|
-
|
631
|
-
|
632
|
-
|
633
|
-
|
634
|
-
|
635
|
-
|
636
|
-
|
637
|
-
|
638
|
-
|
639
|
-
|
640
|
-
|
641
|
-
|
642
|
-
|
643
|
-
|
575
|
+
settings = check_settings(vars_dict, expected_types, q)
|
576
|
+
except ValueError as e:
|
577
|
+
q.put(f"Error: {e}")
|
578
|
+
return
|
579
|
+
|
580
|
+
if thread_control.get("run_thread") is not None:
|
581
|
+
initiate_abort()
|
582
|
+
|
583
|
+
stop_requested = Value('i', 0)
|
584
|
+
thread_control["stop_requested"] = stop_requested
|
585
|
+
|
586
|
+
process_args = (settings_type, settings, q, fig_queue, stop_requested)
|
587
|
+
if settings_type in [
|
588
|
+
'mask', 'measure', 'simulation', 'sequencing', 'classify', 'cellpose_dataset',
|
589
|
+
'train_cellpose', 'ml_analyze', 'cellpose_masks', 'cellpose_all', 'map_barcodes',
|
590
|
+
'regression', 'recruitment', 'plaques', 'cellpose_compare', 'vision_scores',
|
591
|
+
'vision_dataset'
|
592
|
+
]:
|
593
|
+
thread_control["run_thread"] = Process(target=run_function_gui, args=process_args)
|
594
|
+
else:
|
595
|
+
q.put(f"Error: Unknown settings type '{settings_type}'")
|
596
|
+
return
|
597
|
+
thread_control["run_thread"].start()
|
644
598
|
|
645
599
|
def process_console_queue():
|
646
600
|
global q, console_output, parent_frame, progress_bar
|
@@ -658,13 +612,15 @@ def process_console_queue():
|
|
658
612
|
console_output.see(tk.END)
|
659
613
|
|
660
614
|
# Check if the message contains progress information
|
661
|
-
if clean_message.startswith("Progress"):
|
615
|
+
if clean_message.startswith("Progress:"):
|
662
616
|
try:
|
663
617
|
# Extract the progress information
|
664
|
-
match = re.search(r'(\d+)/(\d+)', clean_message)
|
618
|
+
match = re.search(r'Progress: (\d+)/(\d+), operation_type: ([\w\s]*)(.*)', clean_message)
|
665
619
|
if match:
|
666
620
|
current_progress = int(match.group(1))
|
667
621
|
total_progress = int(match.group(2))
|
622
|
+
operation_type = match.group(3).strip()
|
623
|
+
time_info = match.group(4).strip()
|
668
624
|
|
669
625
|
# Add the task to the completed set
|
670
626
|
process_console_queue.completed_tasks.append(current_progress)
|
@@ -678,19 +634,18 @@ def process_console_queue():
|
|
678
634
|
progress_bar['value'] = unique_progress_count
|
679
635
|
|
680
636
|
# Extract and update additional information
|
681
|
-
|
682
|
-
|
683
|
-
progress_bar.operation_type = operation_match.group(1)
|
637
|
+
if operation_type:
|
638
|
+
progress_bar.operation_type = operation_type
|
684
639
|
|
685
|
-
time_image_match = re.search(r'Time/image: ([\d.]+) sec',
|
640
|
+
time_image_match = re.search(r'Time/image: ([\d.]+) sec', time_info)
|
686
641
|
if time_image_match:
|
687
642
|
progress_bar.time_image = float(time_image_match.group(1))
|
688
643
|
|
689
|
-
time_batch_match = re.search(r'Time/batch: ([\d.]+) sec',
|
644
|
+
time_batch_match = re.search(r'Time/batch: ([\d.]+) sec', time_info)
|
690
645
|
if time_batch_match:
|
691
646
|
progress_bar.time_batch = float(time_batch_match.group(1))
|
692
647
|
|
693
|
-
time_left_match = re.search(r'Time_left: ([\d.]+) min',
|
648
|
+
time_left_match = re.search(r'Time_left: ([\d.]+) min', time_info)
|
694
649
|
if time_left_match:
|
695
650
|
progress_bar.time_left = float(time_left_match.group(1))
|
696
651
|
|
@@ -703,74 +658,32 @@ def process_console_queue():
|
|
703
658
|
process_console_queue.completed_tasks.clear()
|
704
659
|
except Exception as e:
|
705
660
|
print(f"Error parsing progress message: {e}")
|
706
|
-
|
661
|
+
|
707
662
|
after_id = console_output.after(100, process_console_queue)
|
708
663
|
parent_frame.after_tasks.append(after_id)
|
709
664
|
|
710
|
-
def set_globals(q_var, console_output_var, parent_frame_var, vars_dict_var, canvas_var, canvas_widget_var, scrollable_frame_var, fig_queue_var, progress_bar_var):
|
711
|
-
global q, console_output, parent_frame, vars_dict, canvas, canvas_widget, scrollable_frame, fig_queue, progress_bar
|
712
|
-
q = q_var
|
713
|
-
console_output = console_output_var
|
714
|
-
parent_frame = parent_frame_var
|
715
|
-
vars_dict = vars_dict_var
|
716
|
-
canvas = canvas_var
|
717
|
-
canvas_widget = canvas_widget_var
|
718
|
-
scrollable_frame = scrollable_frame_var
|
719
|
-
fig_queue = fig_queue_var
|
720
|
-
progress_bar = progress_bar_var
|
721
|
-
|
722
|
-
def create_containers(parent_frame):
|
723
|
-
vertical_container = tk.PanedWindow(parent_frame, orient=tk.VERTICAL)
|
724
|
-
horizontal_container = tk.PanedWindow(vertical_container, orient=tk.HORIZONTAL)
|
725
|
-
settings_frame = tk.Frame(horizontal_container)
|
726
|
-
return vertical_container, horizontal_container, settings_frame
|
727
|
-
|
728
|
-
def setup_frame(parent_frame):
|
729
|
-
style = ttk.Style(parent_frame)
|
730
|
-
vertical_container, horizontal_container, settings_frame = create_containers(parent_frame)
|
731
|
-
containers = [vertical_container, horizontal_container, settings_frame]
|
732
|
-
|
733
|
-
set_dark_style(style, parent_frame, containers)
|
734
|
-
set_default_font(parent_frame, font_name="Helvetica", size=8)
|
735
|
-
|
736
|
-
vertical_container.grid(row=0, column=0, sticky=tk.NSEW)
|
737
|
-
vertical_container.add(horizontal_container, stretch="always")
|
738
|
-
horizontal_container.grid_columnconfigure(0, weight=1)
|
739
|
-
horizontal_container.grid_columnconfigure(1, weight=1)
|
740
|
-
settings_frame.grid_rowconfigure(0, weight=0)
|
741
|
-
settings_frame.grid_rowconfigure(1, weight=1)
|
742
|
-
settings_frame.grid_columnconfigure(0, weight=1)
|
743
|
-
horizontal_container.add(settings_frame, stretch="always", sticky="nsew")
|
744
|
-
|
745
|
-
return parent_frame, vertical_container, horizontal_container
|
746
|
-
|
747
665
|
def initiate_root(parent, settings_type='mask'):
|
748
|
-
global q, fig_queue, parent_frame, scrollable_frame, button_frame, vars_dict, canvas, canvas_widget, button_scrollable_frame, progress_bar
|
749
|
-
from .gui_utils import main_thread_update_function
|
666
|
+
global q, fig_queue, thread_control, parent_frame, scrollable_frame, button_frame, vars_dict, canvas, canvas_widget, button_scrollable_frame, progress_bar
|
667
|
+
from .gui_utils import main_thread_update_function, setup_frame, set_element_size
|
750
668
|
from .gui import gui_app
|
751
669
|
set_start_method('spawn', force=True)
|
752
670
|
print("Initializing root with settings_type:", settings_type)
|
753
671
|
|
754
672
|
parent_frame = parent
|
755
|
-
parent_frame.update_idletasks()
|
756
|
-
frame_width = int(parent_frame.winfo_width())
|
757
|
-
frame_height = int(parent_frame.winfo_height())
|
758
|
-
print(frame_width, frame_height)
|
759
|
-
dims = [frame_width, frame_height]
|
760
673
|
|
761
|
-
if not
|
762
|
-
|
674
|
+
if not isinstance(parent_frame, (tk.Tk, tk.Toplevel)):
|
675
|
+
parent_window = parent_frame.winfo_toplevel()
|
676
|
+
else:
|
677
|
+
parent_window = parent_frame
|
678
|
+
|
679
|
+
parent_window.update_idletasks()
|
763
680
|
|
764
|
-
|
765
|
-
|
766
|
-
try:
|
767
|
-
widget.destroy()
|
768
|
-
except tk.TclError as e:
|
769
|
-
print(f"Error destroying widget: {e}")
|
681
|
+
if not hasattr(parent_window, 'after_tasks'):
|
682
|
+
parent_window.after_tasks = []
|
770
683
|
|
771
684
|
q = Queue()
|
772
685
|
fig_queue = Queue()
|
773
|
-
parent_frame, vertical_container, horizontal_container = setup_frame(parent_frame)
|
686
|
+
parent_frame, vertical_container, horizontal_container, settings_container = setup_frame(parent_frame)
|
774
687
|
|
775
688
|
if settings_type == 'annotate':
|
776
689
|
from .app_annotate import initiate_annotation_app
|
@@ -779,17 +692,19 @@ def initiate_root(parent, settings_type='mask'):
|
|
779
692
|
from .app_make_masks import initiate_make_mask_app
|
780
693
|
initiate_make_mask_app(horizontal_container)
|
781
694
|
else:
|
782
|
-
scrollable_frame, vars_dict = setup_settings_panel(
|
783
|
-
button_scrollable_frame = setup_button_section(horizontal_container, settings_type, window_dimensions=dims)
|
695
|
+
scrollable_frame, vars_dict = setup_settings_panel(settings_container, settings_type)
|
784
696
|
canvas, canvas_widget = setup_plot_section(vertical_container)
|
785
|
-
console_output = setup_console(vertical_container)
|
786
|
-
|
787
|
-
|
697
|
+
console_output, console_frame = setup_console(vertical_container)
|
698
|
+
button_scrollable_frame = setup_button_section(horizontal_container, settings_type)
|
699
|
+
_, usage_bars = setup_usage_panel(horizontal_container)
|
700
|
+
_ = setup_help_section(horizontal_container, settings_type)
|
701
|
+
|
702
|
+
set_globals(thread_control, q, console_output, parent_frame, vars_dict, canvas, canvas_widget, scrollable_frame, fig_queue, progress_bar, usage_bars)
|
703
|
+
q.put(f"Console")
|
788
704
|
process_console_queue()
|
789
705
|
process_fig_queue()
|
790
|
-
after_id =
|
791
|
-
|
706
|
+
after_id = parent_window.after(100, lambda: main_thread_update_function(parent_window, q, fig_queue, canvas_widget))
|
707
|
+
parent_window.after_tasks.append(after_id)
|
792
708
|
|
793
709
|
print("Root initialization complete")
|
794
710
|
return parent_frame, vars_dict
|
795
|
-
|