spacr 0.2.41__py3-none-any.whl → 0.2.46__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 -66
- spacr/gui.py +20 -38
- spacr/gui_core.py +327 -625
- spacr/gui_elements.py +175 -24
- spacr/gui_utils.py +324 -62
- spacr/io.py +43 -46
- 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 +0 -2
- spacr/utils.py +4 -24
- {spacr-0.2.41.dist-info → spacr-0.2.46.dist-info}/METADATA +1 -1
- {spacr-0.2.41.dist-info → spacr-0.2.46.dist-info}/RECORD +18 -16
- {spacr-0.2.41.dist-info → spacr-0.2.46.dist-info}/LICENSE +0 -0
- {spacr-0.2.41.dist-info → spacr-0.2.46.dist-info}/WHEEL +0 -0
- {spacr-0.2.41.dist-info → spacr-0.2.46.dist-info}/entry_points.txt +0 -0
- {spacr-0.2.41.dist-info → spacr-0.2.46.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,14 +7,10 @@ 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
|
14
|
-
|
15
|
-
import psutil, gpustat
|
11
|
+
import psutil
|
16
12
|
import GPUtil
|
17
|
-
|
18
|
-
from time import sleep
|
19
|
-
|
13
|
+
import tkinter.font as tkFont
|
20
14
|
|
21
15
|
try:
|
22
16
|
ctypes.windll.shcore.SetProcessDpiAwareness(True)
|
@@ -24,7 +18,7 @@ except AttributeError:
|
|
24
18
|
pass
|
25
19
|
|
26
20
|
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
|
27
|
-
from .gui_elements import spacrProgressBar, spacrButton, spacrLabel, spacrFrame, spacrDropdownMenu ,set_dark_style
|
21
|
+
from .gui_elements import spacrProgressBar, spacrButton, spacrLabel, spacrFrame, spacrDropdownMenu ,set_dark_style
|
28
22
|
|
29
23
|
# Define global variables
|
30
24
|
q = None
|
@@ -39,143 +33,92 @@ fig_queue = None
|
|
39
33
|
|
40
34
|
thread_control = {"run_thread": None, "stop_requested": False}
|
41
35
|
|
42
|
-
def
|
43
|
-
global
|
44
|
-
|
45
|
-
|
46
|
-
if
|
47
|
-
|
48
|
-
thread_control["run_thread"].join()
|
49
|
-
thread_control["run_thread"] = None
|
50
|
-
|
51
|
-
def spacrFigShow(fig_queue=None):
|
52
|
-
"""
|
53
|
-
Replacement for plt.show() that queues figures instead of displaying them.
|
54
|
-
"""
|
55
|
-
fig = plt.gcf()
|
56
|
-
if fig_queue:
|
57
|
-
fig_queue.put(fig)
|
58
|
-
else:
|
59
|
-
fig.show()
|
60
|
-
plt.close(fig)
|
36
|
+
def toggle_settings(button_scrollable_frame):
|
37
|
+
global vars_dict
|
38
|
+
from .settings import categories
|
39
|
+
from .gui_utils import hide_all_settings
|
40
|
+
if vars_dict is None:
|
41
|
+
raise ValueError("vars_dict is not initialized.")
|
61
42
|
|
62
|
-
|
43
|
+
active_categories = set()
|
63
44
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
45
|
+
def toggle_category(settings):
|
46
|
+
for setting in settings:
|
47
|
+
if setting in vars_dict:
|
48
|
+
label, widget, _ = vars_dict[setting]
|
49
|
+
if widget.grid_info():
|
50
|
+
label.grid_remove()
|
51
|
+
widget.grid_remove()
|
52
|
+
else:
|
53
|
+
label.grid()
|
54
|
+
widget.grid()
|
72
55
|
|
73
|
-
|
74
|
-
|
75
|
-
|
56
|
+
def on_category_select(selected_category):
|
57
|
+
if selected_category == "Select Category":
|
58
|
+
return
|
59
|
+
if selected_category in categories:
|
60
|
+
toggle_category(categories[selected_category])
|
61
|
+
if selected_category in active_categories:
|
62
|
+
active_categories.remove(selected_category)
|
63
|
+
else:
|
64
|
+
active_categories.add(selected_category)
|
65
|
+
category_dropdown.update_styles(active_categories)
|
66
|
+
category_var.set("Select Category")
|
67
|
+
|
68
|
+
category_var = tk.StringVar()
|
69
|
+
non_empty_categories = [category for category, settings in categories.items() if any(setting in vars_dict for setting in settings)]
|
70
|
+
category_dropdown = spacrDropdownMenu(button_scrollable_frame.scrollable_frame, category_var, non_empty_categories, command=on_category_select)
|
71
|
+
category_dropdown.grid(row=0, column=4, sticky="ew", pady=2, padx=2)
|
72
|
+
vars_dict = hide_all_settings(vars_dict, categories)
|
73
|
+
|
74
|
+
def process_fig_queue():
|
75
|
+
global canvas, fig_queue, canvas_widget, parent_frame
|
76
|
+
|
77
|
+
def clear_canvas(canvas):
|
78
|
+
for ax in canvas.figure.get_axes():
|
79
|
+
ax.clear()
|
80
|
+
canvas.draw_idle()
|
76
81
|
|
77
82
|
try:
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
from .gui_utils import process_stdout_stderr
|
93
|
-
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
|
94
|
-
from .io import generate_cellpose_train_test
|
95
|
-
from .measure import measure_crop
|
96
|
-
from .sim import run_multiple_simulations
|
97
|
-
from .deep_spacr import train_test_model
|
98
|
-
from .sequencing import analyze_reads, map_barcodes_folder, perform_regression
|
99
|
-
process_stdout_stderr(q)
|
100
|
-
|
101
|
-
print(f'run_function_gui settings_type: {settings_type}')
|
102
|
-
|
103
|
-
if settings_type == 'mask':
|
104
|
-
function = preprocess_generate_masks
|
105
|
-
imports = 2
|
106
|
-
elif settings_type == 'measure':
|
107
|
-
function = measure_crop
|
108
|
-
imports = 1
|
109
|
-
elif settings_type == 'simulation':
|
110
|
-
function = run_multiple_simulations
|
111
|
-
imports = 1
|
112
|
-
elif settings_type == 'sequencing':
|
113
|
-
function = analyze_reads
|
114
|
-
imports = 1
|
115
|
-
elif settings_type == 'classify':
|
116
|
-
function = train_test_model
|
117
|
-
imports = 2
|
118
|
-
elif settings_type == 'train_cellpose':
|
119
|
-
function = train_cellpose
|
120
|
-
imports = 1
|
121
|
-
elif settings_type == 'ml_analyze':
|
122
|
-
function = generate_ml_scores
|
123
|
-
imports = 2
|
124
|
-
elif settings_type == 'cellpose_masks':
|
125
|
-
function = identify_masks_finetune
|
126
|
-
imports = 1
|
127
|
-
elif settings_type == 'cellpose_all':
|
128
|
-
function = check_cellpose_models
|
129
|
-
imports = 1
|
130
|
-
elif settings_type == 'map_barcodes':
|
131
|
-
function = map_barcodes_folder
|
132
|
-
imports = 2
|
133
|
-
elif settings_type == 'regression':
|
134
|
-
function = perform_regression
|
135
|
-
imports = 2
|
136
|
-
elif settings_type == 'recruitment':
|
137
|
-
function = analyze_recruitment
|
138
|
-
imports = 2
|
139
|
-
else:
|
140
|
-
raise ValueError(f"Invalid settings type: {settings_type}")
|
141
|
-
try:
|
142
|
-
function_gui_wrapper(function, settings, q, fig_queue, imports)
|
83
|
+
while not fig_queue.empty():
|
84
|
+
clear_canvas(canvas)
|
85
|
+
fig = fig_queue.get_nowait()
|
86
|
+
for ax in fig.get_axes():
|
87
|
+
ax.set_xticks([]) # Remove x-axis ticks
|
88
|
+
ax.set_yticks([]) # Remove y-axis ticks
|
89
|
+
ax.xaxis.set_visible(False) # Hide the x-axis
|
90
|
+
ax.yaxis.set_visible(False) # Hide the y-axis
|
91
|
+
fig.tight_layout()
|
92
|
+
fig.set_facecolor('black')
|
93
|
+
canvas.figure = fig
|
94
|
+
fig_width, fig_height = canvas_widget.winfo_width(), canvas_widget.winfo_height()
|
95
|
+
fig.set_size_inches(fig_width / fig.dpi, fig_height / fig.dpi, forward=True)
|
96
|
+
canvas.draw_idle()
|
143
97
|
except Exception as e:
|
144
|
-
q.put(f"Error during processing: {e}")
|
145
98
|
traceback.print_exc()
|
146
99
|
finally:
|
147
|
-
|
100
|
+
after_id = canvas_widget.after(100, process_fig_queue)
|
101
|
+
parent_frame.after_tasks.append(after_id)
|
148
102
|
|
149
|
-
def start_process(q=None, fig_queue=None, settings_type='mask'):
|
150
|
-
global thread_control, vars_dict
|
151
|
-
from .settings import check_settings, expected_types
|
152
103
|
|
153
|
-
if q is None:
|
154
|
-
q = Queue()
|
155
|
-
if fig_queue is None:
|
156
|
-
fig_queue = Queue()
|
157
|
-
|
158
|
-
try:
|
159
|
-
settings = check_settings(vars_dict, expected_types, q)
|
160
|
-
except ValueError as e:
|
161
|
-
q.put(f"Error: {e}")
|
162
|
-
return
|
163
104
|
|
164
|
-
if thread_control.get("run_thread") is not None:
|
165
|
-
initiate_abort()
|
166
|
-
|
167
|
-
stop_requested = Value('i', 0)
|
168
|
-
thread_control["stop_requested"] = stop_requested
|
169
105
|
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
106
|
+
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):
|
107
|
+
global thread_control, q, console_output, parent_frame, vars_dict, canvas, canvas_widget, scrollable_frame, fig_queue, progress_bar, usage_bars
|
108
|
+
thread_control = thread_control_var
|
109
|
+
q = q_var
|
110
|
+
console_output = console_output_var
|
111
|
+
parent_frame = parent_frame_var
|
112
|
+
vars_dict = vars_dict_var
|
113
|
+
canvas = canvas_var
|
114
|
+
canvas_widget = canvas_widget_var
|
115
|
+
scrollable_frame = scrollable_frame_var
|
116
|
+
fig_queue = fig_queue_var
|
117
|
+
progress_bar = progress_bar_var
|
118
|
+
usage_bars = usage_bars_var
|
177
119
|
|
178
120
|
def import_settings(settings_type='mask'):
|
121
|
+
from .gui_utils import convert_settings_dict_for_gui, hide_all_settings
|
179
122
|
global vars_dict, scrollable_frame, button_scrollable_frame
|
180
123
|
from .settings import generate_fields
|
181
124
|
|
@@ -224,74 +167,23 @@ def import_settings(settings_type='mask'):
|
|
224
167
|
vars_dict = generate_fields(new_settings, scrollable_frame)
|
225
168
|
vars_dict = hide_all_settings(vars_dict, categories=None)
|
226
169
|
|
227
|
-
def
|
228
|
-
from torchvision import models as torch_models
|
229
|
-
torchvision_models = [name for name, obj in torch_models.__dict__.items() if callable(obj)]
|
230
|
-
chans = ['0', '1', '2', '3', '4', '5', '6', '7', '8', None]
|
231
|
-
chans_v2 = [0, 1, 2, 3, None]
|
232
|
-
variables = {}
|
233
|
-
special_cases = {
|
234
|
-
'metadata_type': ('combo', ['cellvoyager', 'cq1', 'nikon', 'zeis', 'custom'], 'cellvoyager'),
|
235
|
-
'channels': ('combo', ['[0,1,2,3]', '[0,1,2]', '[0,1]', '[0]'], '[0,1,2,3]'),
|
236
|
-
'channel_dims': ('combo', ['[0,1,2,3]', '[0,1,2]', '[0,1]', '[0]'], '[0,1,2,3]'),
|
237
|
-
'cell_mask_dim': ('combo', chans, None),
|
238
|
-
'cell_chann_dim': ('combo', chans, None),
|
239
|
-
'nucleus_mask_dim': ('combo', chans, None),
|
240
|
-
'nucleus_chann_dim': ('combo', chans, None),
|
241
|
-
'pathogen_mask_dim': ('combo', chans, None),
|
242
|
-
'pathogen_chann_dim': ('combo', chans, None),
|
243
|
-
'crop_mode': ('combo', ['cell', 'nucleus', 'pathogen', '[cell, nucleus, pathogen]', '[cell,nucleus, pathogen]'], ['cell']),
|
244
|
-
'magnification': ('combo', [20, 40, 60], 20),
|
245
|
-
'nucleus_channel': ('combo', chans_v2, None),
|
246
|
-
'cell_channel': ('combo', chans_v2, None),
|
247
|
-
'channel_of_interest': ('combo', chans_v2, None),
|
248
|
-
'pathogen_channel': ('combo', chans_v2, None),
|
249
|
-
'timelapse_mode': ('combo', ['trackpy', 'btrack'], 'trackpy'),
|
250
|
-
'train_mode': ('combo', ['erm', 'irm'], 'erm'),
|
251
|
-
'clustering': ('combo', ['dbscan', 'kmean'], 'dbscan'),
|
252
|
-
'reduction_method': ('combo', ['umap', 'tsne'], 'umap'),
|
253
|
-
'model_name': ('combo', ['cyto', 'cyto_2', 'cyto_3', 'nuclei'], 'cyto'),
|
254
|
-
'regression_type': ('combo', ['ols','gls','wls','rlm','glm','mixed','quantile','logit','probit','poisson','lasso','ridge'], 'ols'),
|
255
|
-
'timelapse_objects': ('combo', ['cell', 'nucleus', 'pathogen', 'cytoplasm', None], None),
|
256
|
-
'model_type': ('combo', torchvision_models, 'resnet50'),
|
257
|
-
'optimizer_type': ('combo', ['adamw', 'adam'], 'adamw'),
|
258
|
-
'schedule': ('combo', ['reduce_lr_on_plateau', 'step_lr'], 'reduce_lr_on_plateau'),
|
259
|
-
'loss_type': ('combo', ['focal_loss', 'binary_cross_entropy_with_logits'], 'focal_loss'),
|
260
|
-
'normalize_by': ('combo', ['fov', 'png'], 'png'),
|
261
|
-
'agg_type': ('combo', ['mean', 'median'], 'mean'),
|
262
|
-
'grouping': ('combo', ['mean', 'median'], 'mean'),
|
263
|
-
'min_max': ('combo', ['allq', 'all'], 'allq'),
|
264
|
-
'transform': ('combo', ['log', 'sqrt', 'square', None], None)
|
265
|
-
}
|
266
|
-
|
267
|
-
for key, value in settings.items():
|
268
|
-
if key in special_cases:
|
269
|
-
variables[key] = special_cases[key]
|
270
|
-
elif isinstance(value, bool):
|
271
|
-
variables[key] = ('check', None, value)
|
272
|
-
elif isinstance(value, int) or isinstance(value, float):
|
273
|
-
variables[key] = ('entry', None, value)
|
274
|
-
elif isinstance(value, str):
|
275
|
-
variables[key] = ('entry', None, value)
|
276
|
-
elif value is None:
|
277
|
-
variables[key] = ('entry', None, value)
|
278
|
-
elif isinstance(value, list):
|
279
|
-
variables[key] = ('entry', None, str(value))
|
280
|
-
else:
|
281
|
-
variables[key] = ('entry', None, str(value))
|
282
|
-
return variables
|
283
|
-
|
284
|
-
def setup_settings_panel(vertical_container, settings_type='mask', window_dimensions=[500, 1000]):
|
170
|
+
def setup_settings_panel(vertical_container, settings_type='mask'):
|
285
171
|
global vars_dict, scrollable_frame
|
286
172
|
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
|
173
|
+
from .gui_utils import convert_settings_dict_for_gui, set_element_size
|
174
|
+
|
175
|
+
size_dict = set_element_size(vertical_container)
|
176
|
+
settings_width = size_dict['settings_width']
|
287
177
|
|
288
|
-
|
289
|
-
|
178
|
+
# Create a PanedWindow for the settings panel
|
179
|
+
settings_paned_window = tk.PanedWindow(vertical_container, orient=tk.HORIZONTAL)
|
180
|
+
vertical_container.add(settings_paned_window, stretch="always")
|
181
|
+
|
182
|
+
settings_frame = tk.Frame(settings_paned_window, width=settings_width)
|
183
|
+
settings_frame.pack_propagate(False) # Prevent the frame from resizing based on its children
|
184
|
+
|
185
|
+
settings_paned_window.add(settings_frame)
|
290
186
|
|
291
|
-
settings_frame = tk.Frame(vertical_container)
|
292
|
-
vertical_container.add(settings_frame, stretch="always")
|
293
|
-
settings_label = spacrLabel(settings_frame, text="Settings", anchor='center', justify='center', align="center")
|
294
|
-
settings_label.grid(row=0, column=0, pady=10, padx=10)
|
295
187
|
scrollable_frame = spacrFrame(settings_frame)
|
296
188
|
scrollable_frame.grid(row=1, column=0, sticky="nsew")
|
297
189
|
settings_frame.grid_rowconfigure(1, weight=1)
|
@@ -328,7 +220,7 @@ def setup_settings_panel(vertical_container, settings_type='mask', window_dimens
|
|
328
220
|
vars_dict = generate_fields(variables, scrollable_frame)
|
329
221
|
|
330
222
|
containers = [settings_frame]
|
331
|
-
widgets = [
|
223
|
+
widgets = [scrollable_frame]
|
332
224
|
|
333
225
|
style = ttk.Style(vertical_container)
|
334
226
|
_ = set_dark_style(style, containers=containers, widgets=widgets)
|
@@ -362,19 +254,44 @@ def setup_plot_section(vertical_container):
|
|
362
254
|
|
363
255
|
def setup_console(vertical_container):
|
364
256
|
global console_output
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
257
|
+
from .gui_elements import set_dark_style
|
258
|
+
|
259
|
+
# Apply dark style and get style output
|
260
|
+
style = ttk.Style()
|
261
|
+
style_out = set_dark_style(style)
|
262
|
+
|
263
|
+
# Create a PanedWindow to hold the main content and console sections
|
264
|
+
main_paned_window = tk.PanedWindow(vertical_container, orient=tk.VERTICAL, bg=style_out['bg_color'])
|
265
|
+
vertical_container.add(main_paned_window, stretch="always")
|
266
|
+
|
267
|
+
# Create the main content frame
|
268
|
+
main_content_frame = tk.Frame(main_paned_window, bg=style_out['bg_color'])
|
269
|
+
main_paned_window.add(main_content_frame, stretch="always")
|
270
|
+
|
271
|
+
# Create a frame to hold the console and button sections
|
272
|
+
console_button_frame = tk.Frame(main_paned_window, bg=style_out['bg_color'])
|
273
|
+
main_paned_window.add(console_button_frame, stretch="always")
|
274
|
+
|
275
|
+
# Create the main console frame
|
276
|
+
console_frame = tk.Frame(console_button_frame, bg=style_out['bg_color'])
|
277
|
+
console_frame.pack(fill=tk.BOTH, expand=True)
|
278
|
+
|
279
|
+
# Create the scrollable frame (which is a Text widget) with white text
|
280
|
+
family = style_out['font_family']
|
281
|
+
size = style_out['font_size'] -2
|
282
|
+
font = tkFont.Font(family=family, size=size)
|
283
|
+
console_output = tk.Text(console_frame, bg=style_out['bg_color'], fg=style_out['fg_color'], font=font)
|
284
|
+
console_output.pack(fill=tk.BOTH, expand=True)
|
285
|
+
|
286
|
+
# Configure the grid to allow expansion
|
287
|
+
console_frame.grid_rowconfigure(0, weight=1)
|
372
288
|
console_frame.grid_columnconfigure(0, weight=1)
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
289
|
+
|
290
|
+
# Create a lower frame to act as the anchor point
|
291
|
+
lower_frame = tk.Frame(console_button_frame, bg=style_out['bg_color'])
|
292
|
+
lower_frame.pack(fill=tk.X, expand=False)
|
293
|
+
|
294
|
+
return console_output, console_frame
|
378
295
|
|
379
296
|
def setup_progress_frame(vertical_container):
|
380
297
|
global progress_output
|
@@ -394,139 +311,54 @@ def setup_progress_frame(vertical_container):
|
|
394
311
|
_ = set_dark_style(style, containers=containers, widgets=widgets)
|
395
312
|
return progress_output
|
396
313
|
|
397
|
-
def download_hug_dataset():
|
398
|
-
global vars_dict, q
|
399
|
-
dataset_repo_id = "einarolafsson/toxo_mito"
|
400
|
-
settings_repo_id = "einarolafsson/spacr_settings"
|
401
|
-
dataset_subfolder = "plate1"
|
402
|
-
local_dir = os.path.join(os.path.expanduser("~"), "datasets") # Set to the home directory
|
403
|
-
|
404
|
-
# Download the dataset
|
405
|
-
try:
|
406
|
-
dataset_path = download_dataset(dataset_repo_id, dataset_subfolder, local_dir)
|
407
|
-
if 'src' in vars_dict:
|
408
|
-
vars_dict['src'][2].set(dataset_path)
|
409
|
-
q.put(f"Set source path to: {vars_dict['src'][2].get()}\n")
|
410
|
-
q.put(f"Dataset downloaded to: {dataset_path}\n")
|
411
|
-
except Exception as e:
|
412
|
-
q.put(f"Failed to download dataset: {e}\n")
|
413
|
-
|
414
|
-
# Download the settings files
|
415
|
-
try:
|
416
|
-
settings_path = download_dataset(settings_repo_id, "", local_dir)
|
417
|
-
q.put(f"Settings downloaded to: {settings_path}\n")
|
418
|
-
except Exception as e:
|
419
|
-
q.put(f"Failed to download settings: {e}\n")
|
420
|
-
|
421
|
-
def download_dataset(repo_id, subfolder, local_dir=None, retries=5, delay=5):
|
422
|
-
global q
|
423
|
-
"""
|
424
|
-
Downloads a dataset or settings files from Hugging Face and returns the local path.
|
425
|
-
|
426
|
-
Args:
|
427
|
-
repo_id (str): The repository ID (e.g., 'einarolafsson/toxo_mito' or 'einarolafsson/spacr_settings').
|
428
|
-
subfolder (str): The subfolder path within the repository (e.g., 'plate1' or the settings subfolder).
|
429
|
-
local_dir (str): The local directory where the files will be saved. Defaults to the user's home directory.
|
430
|
-
retries (int): Number of retry attempts in case of failure.
|
431
|
-
delay (int): Delay in seconds between retries.
|
432
|
-
|
433
|
-
Returns:
|
434
|
-
str: The local path to the downloaded files.
|
435
|
-
"""
|
436
|
-
if local_dir is None:
|
437
|
-
local_dir = os.path.join(os.path.expanduser("~"), "datasets")
|
438
|
-
|
439
|
-
local_subfolder_dir = os.path.join(local_dir, subfolder if subfolder else "settings")
|
440
|
-
if not os.path.exists(local_subfolder_dir):
|
441
|
-
os.makedirs(local_subfolder_dir)
|
442
|
-
elif len(os.listdir(local_subfolder_dir)) > 0:
|
443
|
-
q.put(f"Files already downloaded to: {local_subfolder_dir}")
|
444
|
-
return local_subfolder_dir
|
445
|
-
|
446
|
-
attempt = 0
|
447
|
-
while attempt < retries:
|
448
|
-
try:
|
449
|
-
files = list_repo_files(repo_id, repo_type="dataset")
|
450
|
-
subfolder_files = [file for file in files if file.startswith(subfolder) or (subfolder == "" and file.endswith('.csv'))]
|
451
|
-
|
452
|
-
for file_name in subfolder_files:
|
453
|
-
for download_attempt in range(retries):
|
454
|
-
try:
|
455
|
-
url = f"https://huggingface.co/datasets/{repo_id}/resolve/main/{file_name}?download=true"
|
456
|
-
response = requests.get(url, stream=True)
|
457
|
-
response.raise_for_status()
|
458
|
-
|
459
|
-
local_file_path = os.path.join(local_subfolder_dir, os.path.basename(file_name))
|
460
|
-
with open(local_file_path, 'wb') as file:
|
461
|
-
for chunk in response.iter_content(chunk_size=8192):
|
462
|
-
file.write(chunk)
|
463
|
-
q.put(f"Downloaded file: {file_name}")
|
464
|
-
break
|
465
|
-
except (requests.HTTPError, requests.Timeout) as e:
|
466
|
-
q.put(f"Error downloading {file_name}: {e}. Retrying in {delay} seconds...")
|
467
|
-
time.sleep(delay)
|
468
|
-
else:
|
469
|
-
raise Exception(f"Failed to download {file_name} after multiple attempts.")
|
470
|
-
|
471
|
-
return local_subfolder_dir
|
472
|
-
|
473
|
-
except (requests.HTTPError, requests.Timeout) as e:
|
474
|
-
q.put(f"Error downloading files: {e}. Retrying in {delay} seconds...")
|
475
|
-
attempt += 1
|
476
|
-
time.sleep(delay)
|
477
|
-
|
478
|
-
raise Exception("Failed to download files after multiple attempts.")
|
479
|
-
|
480
|
-
|
481
|
-
|
482
314
|
def setup_button_section(horizontal_container, settings_type='mask', run=True, abort=True, download=True, import_btn=True):
|
483
|
-
global button_frame, button_scrollable_frame, run_button, abort_button, download_dataset_button, import_button, q, fig_queue, vars_dict, progress_bar
|
484
|
-
from .gui_utils import set_element_size
|
315
|
+
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
|
316
|
+
from .gui_utils import set_element_size, download_hug_dataset
|
317
|
+
from .settings import categories
|
318
|
+
|
485
319
|
size_dict = set_element_size(horizontal_container)
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
button_frame.
|
320
|
+
button_section_height = size_dict['panel_height']
|
321
|
+
button_frame = tk.Frame(horizontal_container, height=button_section_height)
|
322
|
+
|
323
|
+
# Prevent the frame from resizing based on the child widget
|
324
|
+
#button_frame.pack_propagate(False)
|
491
325
|
|
492
|
-
|
493
|
-
|
494
|
-
button_scrollable_frame = spacrFrame(button_frame)
|
326
|
+
horizontal_container.add(button_frame, stretch="always", sticky="nsew")
|
327
|
+
button_scrollable_frame = spacrFrame(button_frame, scrollbar=False)
|
495
328
|
button_scrollable_frame.grid(row=1, column=0, sticky="nsew")
|
496
|
-
|
497
|
-
widgets = [categories_label, button_scrollable_frame.scrollable_frame]
|
329
|
+
widgets = [button_scrollable_frame.scrollable_frame]
|
498
330
|
|
499
331
|
btn_col = 0
|
500
|
-
btn_row =
|
332
|
+
btn_row = 0
|
501
333
|
|
502
334
|
if run:
|
503
|
-
|
504
|
-
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'])
|
335
|
+
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)
|
505
336
|
run_button.grid(row=btn_row, column=btn_col, pady=5, padx=5, sticky='ew')
|
506
337
|
widgets.append(run_button)
|
507
|
-
|
338
|
+
btn_col += 1
|
508
339
|
|
509
340
|
if abort and settings_type in ['mask', 'measure', 'classify', 'sequencing', 'umap']:
|
510
|
-
abort_button = spacrButton(button_scrollable_frame.scrollable_frame, text="abort", command=initiate_abort, show_text=False, size=size_dict['btn_size'])
|
341
|
+
abort_button = spacrButton(button_scrollable_frame.scrollable_frame, text="abort", command=lambda: initiate_abort(), show_text=False, size=size_dict['btn_size'], animation=False)
|
511
342
|
abort_button.grid(row=btn_row, column=btn_col, pady=5, padx=5, sticky='ew')
|
512
343
|
widgets.append(abort_button)
|
513
|
-
|
344
|
+
btn_col += 1
|
514
345
|
|
515
346
|
if download and settings_type in ['mask']:
|
516
|
-
download_dataset_button = spacrButton(button_scrollable_frame.scrollable_frame, text="download", command=download_hug_dataset, show_text=False, size=size_dict['btn_size'])
|
347
|
+
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)
|
517
348
|
download_dataset_button.grid(row=btn_row, column=btn_col, pady=5, padx=5, sticky='ew')
|
518
349
|
widgets.append(download_dataset_button)
|
519
|
-
|
350
|
+
btn_col += 1
|
520
351
|
|
521
352
|
if import_btn:
|
522
|
-
import_button = spacrButton(button_scrollable_frame.scrollable_frame, text="settings", command=lambda: import_settings(settings_type),show_text=False, size=size_dict['btn_size'])
|
353
|
+
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)
|
523
354
|
import_button.grid(row=btn_row, column=btn_col, pady=5, padx=5, sticky='ew')
|
524
355
|
widgets.append(import_button)
|
525
356
|
btn_row += 1
|
526
357
|
|
527
358
|
# Add the progress bar under the settings category menu
|
528
359
|
progress_bar = spacrProgressBar(button_scrollable_frame.scrollable_frame, orient='horizontal', mode='determinate')
|
529
|
-
progress_bar.grid(row=
|
360
|
+
progress_bar.grid(row=btn_row, column=0, columnspan=6, pady=5, padx=5, sticky='ew')
|
361
|
+
progress_bar.set_label_position() # Set the label position after grid placement
|
530
362
|
widgets.append(progress_bar)
|
531
363
|
|
532
364
|
if vars_dict is not None:
|
@@ -535,239 +367,14 @@ def setup_button_section(horizontal_container, settings_type='mask', run=True, a
|
|
535
367
|
style = ttk.Style(horizontal_container)
|
536
368
|
_ = set_dark_style(style, containers=[button_frame], widgets=widgets)
|
537
369
|
|
538
|
-
return button_scrollable_frame
|
539
|
-
|
540
|
-
def setup_help_section(horizontal_container, settings_type='mask'):
|
541
|
-
from .settings import descriptions
|
542
|
-
|
543
|
-
description_frame = tk.Frame(horizontal_container)
|
544
|
-
horizontal_container.add(description_frame, stretch="always", sticky="nsew")
|
545
|
-
description_frame.grid_columnconfigure(0, weight=1)
|
546
|
-
description_frame.grid_rowconfigure(1, weight=1) # Ensure the text widget row is expandable
|
547
|
-
|
548
|
-
description_label = spacrLabel(description_frame, text=f"{settings_type} Module", anchor='center', justify='center', align="center")
|
549
|
-
description_label.grid(row=0, column=0, pady=10, padx=10, sticky='ew')
|
550
|
-
|
551
|
-
# Set background color directly
|
552
|
-
style_out = set_dark_style(ttk.Style())
|
553
|
-
bg_color = style_out['bg_color']
|
554
|
-
fg_color = style_out['fg_color']
|
555
|
-
|
556
|
-
description_text_widget = tk.Text(description_frame, wrap="word", bg=bg_color, fg=fg_color)
|
557
|
-
description_text_widget.grid(row=1, column=0, sticky="nsew")
|
558
|
-
|
559
|
-
description_text = descriptions.get(settings_type, "No description available for this module.")
|
560
|
-
description_text_widget.insert("1.0", description_text)
|
561
|
-
description_text_widget.config(state="disabled") # Make the text widget read-only
|
562
|
-
|
563
|
-
def update_wraplength(event):
|
564
|
-
new_width = event.width - 20 # Adjust as needed
|
565
|
-
description_text_widget.config(width=new_width)
|
566
|
-
|
567
|
-
description_text_widget.bind('<Configure>', update_wraplength)
|
568
|
-
|
569
|
-
style = ttk.Style(horizontal_container)
|
570
|
-
_ = set_dark_style(style, containers=[description_frame], widgets=[description_label, description_text_widget])
|
571
|
-
|
572
|
-
return description_frame
|
573
|
-
|
574
|
-
def hide_all_settings(vars_dict, categories):
|
575
|
-
"""
|
576
|
-
Function to initially hide all settings in the GUI.
|
577
|
-
|
578
|
-
Parameters:
|
579
|
-
- categories: dict, The categories of settings with their corresponding settings.
|
580
|
-
- vars_dict: dict, The dictionary containing the settings and their corresponding widgets.
|
581
|
-
"""
|
582
|
-
|
583
|
-
if categories is None:
|
584
|
-
from .settings import categories
|
585
|
-
|
586
|
-
for category, settings in categories.items():
|
587
|
-
if any(setting in vars_dict for setting in settings):
|
588
|
-
vars_dict[category] = (None, None, tk.IntVar(value=0))
|
589
|
-
|
590
|
-
# Initially hide all settings
|
591
|
-
for setting in settings:
|
592
|
-
if setting in vars_dict:
|
593
|
-
label, widget, _ = vars_dict[setting]
|
594
|
-
label.grid_remove()
|
595
|
-
widget.grid_remove()
|
596
|
-
return vars_dict
|
597
|
-
|
598
|
-
def toggle_settings(button_scrollable_frame):
|
599
|
-
global vars_dict
|
600
|
-
from .settings import categories
|
601
|
-
|
602
|
-
if vars_dict is None:
|
603
|
-
raise ValueError("vars_dict is not initialized.")
|
604
|
-
|
605
|
-
active_categories = set()
|
606
|
-
|
607
|
-
def toggle_category(settings):
|
608
|
-
for setting in settings:
|
609
|
-
if setting in vars_dict:
|
610
|
-
label, widget, _ = vars_dict[setting]
|
611
|
-
if widget.grid_info():
|
612
|
-
label.grid_remove()
|
613
|
-
widget.grid_remove()
|
614
|
-
else:
|
615
|
-
label.grid()
|
616
|
-
widget.grid()
|
617
|
-
|
618
|
-
def on_category_select(selected_category):
|
619
|
-
if selected_category == "Select Category":
|
620
|
-
return
|
621
|
-
if selected_category in categories:
|
622
|
-
toggle_category(categories[selected_category])
|
623
|
-
if selected_category in active_categories:
|
624
|
-
active_categories.remove(selected_category)
|
625
|
-
else:
|
626
|
-
active_categories.add(selected_category)
|
627
|
-
category_dropdown.update_styles(active_categories)
|
628
|
-
category_var.set("Select Category")
|
629
|
-
|
630
|
-
category_var = tk.StringVar()
|
631
|
-
non_empty_categories = [category for category, settings in categories.items() if any(setting in vars_dict for setting in settings)]
|
632
|
-
category_dropdown = spacrDropdownMenu(button_scrollable_frame.scrollable_frame, category_var, non_empty_categories, command=on_category_select)
|
633
|
-
category_dropdown.grid(row=7, column=0, sticky="ew", pady=2, padx=2)
|
634
|
-
vars_dict = hide_all_settings(vars_dict, categories)
|
635
|
-
|
636
|
-
def process_fig_queue():
|
637
|
-
global canvas, fig_queue, canvas_widget, parent_frame
|
638
|
-
|
639
|
-
def clear_canvas(canvas):
|
640
|
-
for ax in canvas.figure.get_axes():
|
641
|
-
ax.clear()
|
642
|
-
canvas.draw_idle()
|
643
|
-
|
644
|
-
try:
|
645
|
-
while not fig_queue.empty():
|
646
|
-
clear_canvas(canvas)
|
647
|
-
fig = fig_queue.get_nowait()
|
648
|
-
for ax in fig.get_axes():
|
649
|
-
ax.set_xticks([]) # Remove x-axis ticks
|
650
|
-
ax.set_yticks([]) # Remove y-axis ticks
|
651
|
-
ax.xaxis.set_visible(False) # Hide the x-axis
|
652
|
-
ax.yaxis.set_visible(False) # Hide the y-axis
|
653
|
-
fig.tight_layout()
|
654
|
-
fig.set_facecolor('black')
|
655
|
-
canvas.figure = fig
|
656
|
-
fig_width, fig_height = canvas_widget.winfo_width(), canvas_widget.winfo_height()
|
657
|
-
fig.set_size_inches(fig_width / fig.dpi, fig_height / fig.dpi, forward=True)
|
658
|
-
canvas.draw_idle()
|
659
|
-
except Exception as e:
|
660
|
-
traceback.print_exc()
|
661
|
-
finally:
|
662
|
-
after_id = canvas_widget.after(100, process_fig_queue)
|
663
|
-
parent_frame.after_tasks.append(after_id)
|
664
|
-
|
665
|
-
def process_console_queue():
|
666
|
-
global q, console_output, parent_frame, progress_bar
|
667
|
-
|
668
|
-
# Initialize function attribute if it doesn't exist
|
669
|
-
if not hasattr(process_console_queue, "completed_tasks"):
|
670
|
-
process_console_queue.completed_tasks = []
|
671
|
-
|
672
|
-
ansi_escape_pattern = re.compile(r'\x1B\[[0-?]*[ -/]*[@-~]')
|
673
|
-
|
674
|
-
while not q.empty():
|
675
|
-
message = q.get_nowait()
|
676
|
-
clean_message = ansi_escape_pattern.sub('', message)
|
677
|
-
console_output.insert(tk.END, clean_message + "\n")
|
678
|
-
console_output.see(tk.END)
|
679
|
-
|
680
|
-
# Check if the message contains progress information
|
681
|
-
if clean_message.startswith("Progress"):
|
682
|
-
try:
|
683
|
-
# Extract the progress information
|
684
|
-
match = re.search(r'(\d+)/(\d+)', clean_message)
|
685
|
-
if match:
|
686
|
-
current_progress = int(match.group(1))
|
687
|
-
total_progress = int(match.group(2))
|
688
|
-
|
689
|
-
# Add the task to the completed set
|
690
|
-
process_console_queue.completed_tasks.append(current_progress)
|
691
|
-
|
692
|
-
# Calculate the unique progress count
|
693
|
-
unique_progress_count = len(np.unique(process_console_queue.completed_tasks))
|
694
|
-
|
695
|
-
# Update the progress bar
|
696
|
-
if progress_bar:
|
697
|
-
progress_bar['maximum'] = total_progress
|
698
|
-
progress_bar['value'] = unique_progress_count
|
699
|
-
|
700
|
-
# Extract and update additional information
|
701
|
-
operation_match = re.search(r'operation_type: ([\w\s]+)', clean_message)
|
702
|
-
if operation_match:
|
703
|
-
progress_bar.operation_type = operation_match.group(1)
|
704
|
-
|
705
|
-
time_image_match = re.search(r'Time/image: ([\d.]+) sec', clean_message)
|
706
|
-
if time_image_match:
|
707
|
-
progress_bar.time_image = float(time_image_match.group(1))
|
708
|
-
|
709
|
-
time_batch_match = re.search(r'Time/batch: ([\d.]+) sec', clean_message)
|
710
|
-
if time_batch_match:
|
711
|
-
progress_bar.time_batch = float(time_batch_match.group(1))
|
712
|
-
|
713
|
-
time_left_match = re.search(r'Time_left: ([\d.]+) min', clean_message)
|
714
|
-
if time_left_match:
|
715
|
-
progress_bar.time_left = float(time_left_match.group(1))
|
716
|
-
|
717
|
-
# Update the progress label
|
718
|
-
if progress_bar.progress_label:
|
719
|
-
progress_bar.update_label()
|
720
|
-
|
721
|
-
# Clear completed tasks when progress is complete
|
722
|
-
if unique_progress_count >= total_progress:
|
723
|
-
process_console_queue.completed_tasks.clear()
|
724
|
-
except Exception as e:
|
725
|
-
print(f"Error parsing progress message: {e}")
|
726
|
-
|
727
|
-
after_id = console_output.after(100, process_console_queue)
|
728
|
-
parent_frame.after_tasks.append(after_id)
|
729
|
-
|
730
|
-
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, usage_bars_var):
|
731
|
-
global q, console_output, parent_frame, vars_dict, canvas, canvas_widget, scrollable_frame, fig_queue, progress_bar, usage_bars
|
732
|
-
q = q_var
|
733
|
-
console_output = console_output_var
|
734
|
-
parent_frame = parent_frame_var
|
735
|
-
vars_dict = vars_dict_var
|
736
|
-
canvas = canvas_var
|
737
|
-
canvas_widget = canvas_widget_var
|
738
|
-
scrollable_frame = scrollable_frame_var
|
739
|
-
fig_queue = fig_queue_var
|
740
|
-
progress_bar = progress_bar_var
|
741
|
-
usage_bars = usage_bars_var
|
742
|
-
|
743
|
-
def create_containers(parent_frame):
|
744
|
-
vertical_container = tk.PanedWindow(parent_frame, orient=tk.VERTICAL)
|
745
|
-
horizontal_container = tk.PanedWindow(vertical_container, orient=tk.HORIZONTAL)
|
746
|
-
settings_frame = tk.Frame(horizontal_container)
|
747
|
-
return vertical_container, horizontal_container, settings_frame
|
748
|
-
|
749
|
-
def setup_frame(parent_frame):
|
750
|
-
style = ttk.Style(parent_frame)
|
751
|
-
vertical_container, horizontal_container, settings_frame = create_containers(parent_frame)
|
752
|
-
containers = [vertical_container, horizontal_container, settings_frame]
|
753
|
-
|
754
|
-
set_dark_style(style, parent_frame, containers)
|
755
|
-
set_default_font(parent_frame, font_name="Helvetica", size=8)
|
756
|
-
|
757
|
-
vertical_container.grid(row=0, column=0, sticky=tk.NSEW)
|
758
|
-
vertical_container.add(horizontal_container, stretch="always")
|
759
|
-
horizontal_container.grid_columnconfigure(0, weight=1)
|
760
|
-
horizontal_container.grid_columnconfigure(1, weight=1)
|
761
|
-
settings_frame.grid_rowconfigure(0, weight=0)
|
762
|
-
settings_frame.grid_rowconfigure(1, weight=1)
|
763
|
-
settings_frame.grid_columnconfigure(0, weight=1)
|
764
|
-
horizontal_container.add(settings_frame, stretch="always", sticky="nsew")
|
765
|
-
|
766
|
-
return parent_frame, vertical_container, horizontal_container
|
370
|
+
return button_scrollable_frame, btn_col
|
767
371
|
|
768
|
-
def setup_usage_panel(horizontal_container):
|
372
|
+
def setup_usage_panel(horizontal_container, btn_col):
|
769
373
|
global usage_bars
|
770
374
|
from .gui_utils import set_element_size
|
375
|
+
from .gui_elements import set_dark_style
|
376
|
+
|
377
|
+
usg_col = 1
|
771
378
|
|
772
379
|
def update_usage(ram_bar, vram_bar, gpu_bar, usage_bars, parent_frame):
|
773
380
|
# Update RAM usage
|
@@ -792,34 +399,39 @@ def setup_usage_panel(horizontal_container):
|
|
792
399
|
parent_frame.after(1000, update_usage, ram_bar, vram_bar, gpu_bar, usage_bars, parent_frame)
|
793
400
|
|
794
401
|
size_dict = set_element_size(horizontal_container)
|
795
|
-
|
402
|
+
usage_panel_height = size_dict['panel_height']
|
403
|
+
usage_frame = tk.Frame(horizontal_container, height=usage_panel_height)
|
404
|
+
horizontal_container.add(usage_frame)
|
796
405
|
|
797
|
-
usage_frame = tk.Frame(horizontal_container)
|
798
|
-
horizontal_container.add(usage_frame, stretch="always", sticky="nsew")
|
799
406
|
usage_frame.grid_rowconfigure(0, weight=0)
|
800
407
|
usage_frame.grid_rowconfigure(1, weight=1)
|
801
408
|
usage_frame.grid_columnconfigure(0, weight=1)
|
802
409
|
usage_frame.grid_columnconfigure(1, weight=1)
|
803
410
|
|
804
|
-
|
805
|
-
usage_label.grid(row=0, column=0, pady=10, padx=10, columnspan=2)
|
806
|
-
|
807
|
-
usage_scrollable_frame = spacrFrame(usage_frame)
|
411
|
+
usage_scrollable_frame = spacrFrame(usage_frame, scrollbar=False)
|
808
412
|
usage_scrollable_frame.grid(row=1, column=0, sticky="nsew", columnspan=2)
|
809
|
-
widgets = [
|
413
|
+
widgets = [usage_scrollable_frame.scrollable_frame]
|
414
|
+
|
810
415
|
usage_bars = []
|
811
|
-
max_elements_per_column =
|
416
|
+
max_elements_per_column = 6
|
812
417
|
row = 0
|
813
418
|
col = 0
|
814
419
|
|
815
420
|
# Initialize RAM, VRAM, and GPU bars as None
|
816
421
|
ram_bar, vram_bar, gpu_bar = None, None, None
|
422
|
+
|
423
|
+
# Configure the style for the label
|
424
|
+
style = ttk.Style()
|
425
|
+
style_out = set_dark_style(style)
|
426
|
+
size = style_out['font_size'] - 2
|
427
|
+
usage_font = tkFont.Font(family=style_out['font_family'], size=size)
|
428
|
+
style.configure("usage.TLabel", font=usage_font, foreground=style_out['fg_color'])
|
817
429
|
|
818
430
|
# Try adding RAM bar
|
819
431
|
try:
|
820
432
|
ram_info = psutil.virtual_memory()
|
821
433
|
ram_label_text = f"RAM"
|
822
|
-
label = ttk.Label(usage_scrollable_frame.scrollable_frame, text=ram_label_text, anchor='w')
|
434
|
+
label = ttk.Label(usage_scrollable_frame.scrollable_frame, text=ram_label_text, anchor='w', style="usage.TLabel")
|
823
435
|
label.grid(row=row, column=2 * col, pady=5, padx=5, sticky='w')
|
824
436
|
ram_bar = spacrProgressBar(usage_scrollable_frame.scrollable_frame, orient='horizontal', mode='determinate', length=size_dict['bar_size'], label=False)
|
825
437
|
ram_bar.grid(row=row, column=2 * col + 1, pady=5, padx=5, sticky='ew')
|
@@ -836,7 +448,7 @@ def setup_usage_panel(horizontal_container):
|
|
836
448
|
if gpus:
|
837
449
|
gpu = gpus[0]
|
838
450
|
vram_label_text = f"VRAM"
|
839
|
-
label = ttk.Label(usage_scrollable_frame.scrollable_frame, text=vram_label_text, anchor='w')
|
451
|
+
label = ttk.Label(usage_scrollable_frame.scrollable_frame, text=vram_label_text, anchor='w', style="usage.TLabel")
|
840
452
|
label.grid(row=row, column=2 * col, pady=5, padx=5, sticky='w')
|
841
453
|
vram_bar = spacrProgressBar(usage_scrollable_frame.scrollable_frame, orient='horizontal', mode='determinate', length=size_dict['bar_size'], label=False)
|
842
454
|
vram_bar.grid(row=row, column=2 * col + 1, pady=5, padx=5, sticky='ew')
|
@@ -846,7 +458,7 @@ def setup_usage_panel(horizontal_container):
|
|
846
458
|
row += 1
|
847
459
|
|
848
460
|
gpu_label_text = f"GPU"
|
849
|
-
label = ttk.Label(usage_scrollable_frame.scrollable_frame, text=gpu_label_text, anchor='w')
|
461
|
+
label = ttk.Label(usage_scrollable_frame.scrollable_frame, text=gpu_label_text, anchor='w', style="usage.TLabel")
|
850
462
|
label.grid(row=row, column=2 * col, pady=5, padx=5, sticky='w')
|
851
463
|
gpu_bar = spacrProgressBar(usage_scrollable_frame.scrollable_frame, orient='horizontal', mode='determinate', length=size_dict['bar_size'], label=False)
|
852
464
|
gpu_bar.grid(row=row, column=2 * col + 1, pady=5, padx=5, sticky='ew')
|
@@ -861,12 +473,12 @@ def setup_usage_panel(horizontal_container):
|
|
861
473
|
try:
|
862
474
|
cpu_cores = psutil.cpu_count(logical=True)
|
863
475
|
cpu_freq = psutil.cpu_freq()
|
864
|
-
|
476
|
+
|
865
477
|
for core in range(cpu_cores):
|
866
478
|
if row > 0 and row % max_elements_per_column == 0:
|
867
479
|
col += 1
|
868
480
|
row = 0
|
869
|
-
label = ttk.Label(usage_scrollable_frame.scrollable_frame, text=f"Core {core+1}", anchor='w')
|
481
|
+
label = ttk.Label(usage_scrollable_frame.scrollable_frame, text=f"Core {core+1}", anchor='w', style="usage.TLabel")
|
870
482
|
label.grid(row=row, column=2 * col, pady=2, padx=5, sticky='w')
|
871
483
|
bar = spacrProgressBar(usage_scrollable_frame.scrollable_frame, orient='horizontal', mode='determinate', length=size_dict['bar_size'], label=False)
|
872
484
|
bar.grid(row=row, column=2 * col + 1, pady=2, padx=5, sticky='ew')
|
@@ -877,42 +489,8 @@ def setup_usage_panel(horizontal_container):
|
|
877
489
|
except Exception as e:
|
878
490
|
print(f"Could not add CPU core usage bars: {e}")
|
879
491
|
|
880
|
-
# Adding the text box for hardware information
|
881
|
-
#hardware_frame = tk.Frame(horizontal_container)
|
882
|
-
#horizontal_container.add(hardware_frame, stretch="always", sticky="nsew")
|
883
|
-
#hardware_frame.grid_columnconfigure(0, weight=1)
|
884
|
-
|
885
|
-
#hardware_info = tk.Text(hardware_frame, height=1, wrap='none', bg='black', fg='white', bd=0)
|
886
|
-
#hardware_info.grid(row=0, column=0, pady=10, padx=5, sticky='ew')
|
887
|
-
|
888
|
-
#hardware_text = ""
|
889
|
-
#try:
|
890
|
-
# ram_info = psutil.virtual_memory()
|
891
|
-
# hardware_text += f"RAM: {ram_info.total / (1024 ** 3):.1f} GB "
|
892
|
-
#except Exception as e:
|
893
|
-
# hardware_text += f"RAM: Could not retrieve ({e}) "
|
894
|
-
|
895
|
-
#try:
|
896
|
-
# gpus = GPUtil.getGPUs()
|
897
|
-
# if gpus:
|
898
|
-
# gpu = gpus[0]
|
899
|
-
# hardware_text += f"VRAM: {gpu.memoryTotal / 1024:.1f} GB "
|
900
|
-
# hardware_text += f"GPU: {gpu.name} "
|
901
|
-
#except Exception as e:
|
902
|
-
# hardware_text += f"VRAM and GPU: Could not retrieve ({e}) "
|
903
|
-
|
904
|
-
#try:
|
905
|
-
# if cpu_freq:
|
906
|
-
# hardware_text += f"CPU Max Clock Speed: {cpu_freq.max / 1000:.0f} GHz"
|
907
|
-
#except Exception as e:
|
908
|
-
# hardware_text += f"CPU Max Clock Speed: Could not retrieve ({e})"
|
909
|
-
|
910
|
-
#hardware_info.insert(tk.END, hardware_text)
|
911
|
-
#hardware_info.configure(state='disabled')
|
912
|
-
#widgets.append(hardware_info)
|
913
|
-
|
914
492
|
style = ttk.Style(horizontal_container)
|
915
|
-
_ = set_dark_style(style, containers=[usage_frame], widgets=widgets)
|
493
|
+
_ = set_dark_style(style, containers=[usage_frame], widgets=widgets)
|
916
494
|
|
917
495
|
if ram_bar is None:
|
918
496
|
ram_bar = spacrProgressBar(usage_scrollable_frame.scrollable_frame, orient='horizontal', mode='determinate', length=size_dict['bar_size'], label=False)
|
@@ -920,37 +498,158 @@ def setup_usage_panel(horizontal_container):
|
|
920
498
|
vram_bar = spacrProgressBar(usage_scrollable_frame.scrollable_frame, orient='horizontal', mode='determinate', length=size_dict['bar_size'], label=False)
|
921
499
|
if gpu_bar is None:
|
922
500
|
gpu_bar = spacrProgressBar(usage_scrollable_frame.scrollable_frame, orient='horizontal', mode='determinate', length=size_dict['bar_size'], label=False)
|
501
|
+
|
502
|
+
update_usage(ram_bar, vram_bar, gpu_bar, usage_bars, usage_frame)
|
503
|
+
return usage_scrollable_frame, usage_bars, usg_col
|
504
|
+
|
505
|
+
def initiate_abort():
|
506
|
+
global thread_control, q, parent_frame
|
507
|
+
if thread_control.get("run_thread") is not None:
|
508
|
+
try:
|
509
|
+
q.put("Aborting processes...")
|
510
|
+
thread_control.get("run_thread").terminate()
|
511
|
+
thread_control["run_thread"] = None
|
512
|
+
q.put("Processes aborted.")
|
513
|
+
except Exception as e:
|
514
|
+
q.put(f"Error aborting process: {e}")
|
515
|
+
|
516
|
+
thread_control = {"run_thread": None, "stop_requested": False}
|
517
|
+
|
518
|
+
|
519
|
+
def start_process(q=None, fig_queue=None, settings_type='mask'):
|
520
|
+
global thread_control, vars_dict, parent_frame
|
521
|
+
from .settings import check_settings, expected_types
|
522
|
+
from .gui_utils import run_function_gui
|
523
|
+
|
524
|
+
if q is None:
|
525
|
+
q = Queue()
|
526
|
+
if fig_queue is None:
|
527
|
+
fig_queue = Queue()
|
528
|
+
|
529
|
+
try:
|
530
|
+
settings = check_settings(vars_dict, expected_types, q)
|
531
|
+
except ValueError as e:
|
532
|
+
q.put(f"Error: {e}")
|
533
|
+
return
|
534
|
+
|
535
|
+
if thread_control.get("run_thread") is not None:
|
536
|
+
initiate_abort()
|
537
|
+
|
538
|
+
stop_requested = Value('i', 0)
|
539
|
+
thread_control["stop_requested"] = stop_requested
|
540
|
+
|
541
|
+
process_args = (settings_type, settings, q, fig_queue, stop_requested)
|
542
|
+
if settings_type in [
|
543
|
+
'mask', 'measure', 'simulation', 'sequencing', 'classify', 'cellpose_dataset',
|
544
|
+
'train_cellpose', 'ml_analyze', 'cellpose_masks', 'cellpose_all', 'map_barcodes',
|
545
|
+
'regression', 'recruitment', 'plaques', 'cellpose_compare', 'vision_scores',
|
546
|
+
'vision_dataset'
|
547
|
+
]:
|
548
|
+
thread_control["run_thread"] = Process(target=run_function_gui, args=process_args)
|
549
|
+
else:
|
550
|
+
q.put(f"Error: Unknown settings type '{settings_type}'")
|
551
|
+
return
|
552
|
+
thread_control["run_thread"].start()
|
553
|
+
|
554
|
+
def process_console_queue():
|
555
|
+
global q, console_output, parent_frame, progress_bar
|
556
|
+
|
557
|
+
# Initialize function attribute if it doesn't exist
|
558
|
+
if not hasattr(process_console_queue, "completed_tasks"):
|
559
|
+
process_console_queue.completed_tasks = []
|
560
|
+
if not hasattr(process_console_queue, "current_maximum"):
|
561
|
+
process_console_queue.current_maximum = None
|
923
562
|
|
924
|
-
|
925
|
-
|
563
|
+
ansi_escape_pattern = re.compile(r'\x1B\[[0-?]*[ -/]*[@-~]')
|
564
|
+
|
565
|
+
while not q.empty():
|
566
|
+
message = q.get_nowait()
|
567
|
+
clean_message = ansi_escape_pattern.sub('', message)
|
568
|
+
console_output.insert(tk.END, clean_message + "\n")
|
569
|
+
console_output.see(tk.END)
|
570
|
+
|
571
|
+
# Check if the message contains progress information
|
572
|
+
if clean_message.startswith("Progress:"):
|
573
|
+
try:
|
574
|
+
# Extract the progress information
|
575
|
+
match = re.search(r'Progress: (\d+)/(\d+), operation_type: ([\w\s]*)(.*)', clean_message)
|
576
|
+
print('match', match)
|
577
|
+
if match:
|
578
|
+
current_progress = int(match.group(1))
|
579
|
+
total_progress = int(match.group(2))
|
580
|
+
operation_type = match.group(3).strip()
|
581
|
+
time_info = match.group(4).strip()
|
582
|
+
|
583
|
+
# Check if the maximum value has changed
|
584
|
+
if process_console_queue.current_maximum != total_progress:
|
585
|
+
process_console_queue.current_maximum = total_progress
|
586
|
+
process_console_queue.completed_tasks = []
|
587
|
+
|
588
|
+
# Add the task to the completed set
|
589
|
+
process_console_queue.completed_tasks.append(current_progress)
|
590
|
+
|
591
|
+
# Calculate the unique progress count
|
592
|
+
unique_progress_count = len(np.unique(process_console_queue.completed_tasks))
|
593
|
+
|
594
|
+
# Update the progress bar
|
595
|
+
if progress_bar:
|
596
|
+
progress_bar['maximum'] = total_progress
|
597
|
+
progress_bar['value'] = unique_progress_count
|
598
|
+
|
599
|
+
# Extract and update additional information
|
600
|
+
if operation_type:
|
601
|
+
progress_bar.operation_type = operation_type
|
602
|
+
|
603
|
+
time_image_match = re.search(r'Time/image: ([\d.]+) sec', time_info)
|
604
|
+
if time_image_match:
|
605
|
+
progress_bar.time_image = float(time_image_match.group(1))
|
606
|
+
|
607
|
+
time_batch_match = re.search(r'Time/batch: ([\d.]+) sec', time_info)
|
608
|
+
if time_batch_match:
|
609
|
+
progress_bar.time_batch = float(time_batch_match.group(1))
|
610
|
+
|
611
|
+
time_left_match = re.search(r'Time_left: ([\d.]+) min', time_info)
|
612
|
+
if time_left_match:
|
613
|
+
progress_bar.time_left = float(time_left_match.group(1))
|
614
|
+
|
615
|
+
# Update the progress label
|
616
|
+
if progress_bar.progress_label:
|
617
|
+
progress_bar.update_label()
|
618
|
+
|
619
|
+
# Clear completed tasks when progress is complete
|
620
|
+
if unique_progress_count >= total_progress:
|
621
|
+
process_console_queue.completed_tasks.clear()
|
622
|
+
|
623
|
+
except Exception as e:
|
624
|
+
print(f"Error parsing progress message: {e}")
|
625
|
+
|
626
|
+
after_id = console_output.after(10, process_console_queue)
|
627
|
+
parent_frame.after_tasks.append(after_id)
|
926
628
|
|
927
629
|
def initiate_root(parent, settings_type='mask'):
|
928
|
-
global q, fig_queue, parent_frame, scrollable_frame, button_frame, vars_dict, canvas, canvas_widget, button_scrollable_frame, progress_bar
|
929
|
-
from .gui_utils import main_thread_update_function
|
630
|
+
global q, fig_queue, thread_control, parent_frame, scrollable_frame, button_frame, vars_dict, canvas, canvas_widget, button_scrollable_frame, progress_bar
|
631
|
+
from .gui_utils import main_thread_update_function, setup_frame, set_element_size
|
930
632
|
from .gui import gui_app
|
633
|
+
from .settings import descriptions
|
634
|
+
|
931
635
|
set_start_method('spawn', force=True)
|
932
636
|
print("Initializing root with settings_type:", settings_type)
|
933
637
|
|
934
638
|
parent_frame = parent
|
935
|
-
parent_frame.update_idletasks()
|
936
|
-
frame_width = int(parent_frame.winfo_width())
|
937
|
-
frame_height = int(parent_frame.winfo_height())
|
938
|
-
print(frame_width, frame_height)
|
939
|
-
dims = [frame_width, frame_height]
|
940
639
|
|
941
|
-
if not
|
942
|
-
|
640
|
+
if not isinstance(parent_frame, (tk.Tk, tk.Toplevel)):
|
641
|
+
parent_window = parent_frame.winfo_toplevel()
|
642
|
+
else:
|
643
|
+
parent_window = parent_frame
|
943
644
|
|
944
|
-
|
945
|
-
|
946
|
-
|
947
|
-
|
948
|
-
except tk.TclError as e:
|
949
|
-
print(f"Error destroying widget: {e}")
|
645
|
+
parent_window.update_idletasks()
|
646
|
+
|
647
|
+
if not hasattr(parent_window, 'after_tasks'):
|
648
|
+
parent_window.after_tasks = []
|
950
649
|
|
951
650
|
q = Queue()
|
952
651
|
fig_queue = Queue()
|
953
|
-
parent_frame, vertical_container, horizontal_container = setup_frame(parent_frame)
|
652
|
+
parent_frame, vertical_container, horizontal_container, settings_container = setup_frame(parent_frame)
|
954
653
|
|
955
654
|
if settings_type == 'annotate':
|
956
655
|
from .app_annotate import initiate_annotation_app
|
@@ -959,21 +658,24 @@ def initiate_root(parent, settings_type='mask'):
|
|
959
658
|
from .app_make_masks import initiate_make_mask_app
|
960
659
|
initiate_make_mask_app(horizontal_container)
|
961
660
|
else:
|
962
|
-
scrollable_frame, vars_dict = setup_settings_panel(
|
963
|
-
|
964
|
-
|
965
|
-
_, usage_bars = setup_usage_panel(horizontal_container)
|
966
|
-
_ = setup_help_section(horizontal_container, settings_type)
|
967
|
-
|
661
|
+
scrollable_frame, vars_dict = setup_settings_panel(settings_container, settings_type)
|
662
|
+
print('setup_settings_panel')
|
968
663
|
canvas, canvas_widget = setup_plot_section(vertical_container)
|
969
|
-
console_output = setup_console(vertical_container)
|
664
|
+
console_output, _ = setup_console(vertical_container)
|
665
|
+
button_scrollable_frame, btn_col = setup_button_section(horizontal_container, settings_type)
|
666
|
+
_, usage_bars, btn_col = setup_usage_panel(horizontal_container, btn_col)
|
970
667
|
|
971
|
-
set_globals(q, console_output, parent_frame, vars_dict, canvas, canvas_widget, scrollable_frame, fig_queue, progress_bar, usage_bars)
|
668
|
+
set_globals(thread_control, q, console_output, parent_frame, vars_dict, canvas, canvas_widget, scrollable_frame, fig_queue, progress_bar, usage_bars)
|
669
|
+
description_text = descriptions.get(settings_type, "No description available for this module.")
|
670
|
+
|
671
|
+
q.put(f"Console")
|
672
|
+
q.put(f" ")
|
673
|
+
q.put(description_text)
|
674
|
+
|
972
675
|
process_console_queue()
|
973
676
|
process_fig_queue()
|
974
|
-
after_id =
|
975
|
-
|
677
|
+
after_id = parent_window.after(100, lambda: main_thread_update_function(parent_window, q, fig_queue, canvas_widget))
|
678
|
+
parent_window.after_tasks.append(after_id)
|
976
679
|
|
977
680
|
print("Root initialization complete")
|
978
681
|
return parent_frame, vars_dict
|
979
|
-
|