spacr 0.2.4__py3-none-any.whl → 0.2.5__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 +56 -67
- spacr/gui.py +20 -38
- spacr/gui_core.py +390 -489
- spacr/gui_elements.py +309 -59
- spacr/gui_utils.py +361 -73
- spacr/io.py +42 -46
- spacr/measure.py +198 -151
- spacr/plot.py +108 -42
- spacr/resources/font/open_sans/OFL.txt +93 -0
- spacr/resources/font/open_sans/OpenSans-Italic-VariableFont_wdth,wght.ttf +0 -0
- spacr/resources/font/open_sans/OpenSans-VariableFont_wdth,wght.ttf +0 -0
- spacr/resources/font/open_sans/README.txt +100 -0
- spacr/resources/font/open_sans/static/OpenSans-Bold.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans-BoldItalic.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans-ExtraBold.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans-ExtraBoldItalic.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans-Italic.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans-Light.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans-LightItalic.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans-Medium.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans-MediumItalic.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans-Regular.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans-SemiBold.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans-SemiBoldItalic.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans_Condensed-Bold.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans_Condensed-BoldItalic.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans_Condensed-ExtraBold.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans_Condensed-ExtraBoldItalic.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans_Condensed-Italic.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans_Condensed-Light.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans_Condensed-LightItalic.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans_Condensed-Medium.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans_Condensed-MediumItalic.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans_Condensed-Regular.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans_Condensed-SemiBold.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans_Condensed-SemiBoldItalic.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-Bold.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-BoldItalic.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-ExtraBold.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-ExtraBoldItalic.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-Italic.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-Light.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-LightItalic.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-Medium.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-MediumItalic.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-Regular.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-SemiBold.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-SemiBoldItalic.ttf +0 -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 +11 -83
- spacr/utils.py +13 -33
- {spacr-0.2.4.dist-info → spacr-0.2.5.dist-info}/METADATA +5 -1
- spacr-0.2.5.dist-info/RECORD +100 -0
- spacr-0.2.4.dist-info/RECORD +0 -58
- {spacr-0.2.4.dist-info → spacr-0.2.5.dist-info}/LICENSE +0 -0
- {spacr-0.2.4.dist-info → spacr-0.2.5.dist-info}/WHEEL +0 -0
- {spacr-0.2.4.dist-info → spacr-0.2.5.dist-info}/entry_points.txt +0 -0
- {spacr-0.2.4.dist-info → spacr-0.2.5.dist-info}/top_level.txt +0 -0
spacr/gui_core.py
CHANGED
@@ -1,23 +1,23 @@
|
|
1
|
-
import
|
2
|
-
import matplotlib.pyplot as plt
|
3
|
-
matplotlib.use('Agg')
|
1
|
+
import traceback, ctypes, csv, re, time
|
4
2
|
import tkinter as tk
|
5
3
|
from tkinter import ttk
|
6
4
|
from tkinter import filedialog
|
7
|
-
from multiprocessing import Process, Value, Queue, set_start_method
|
8
|
-
from multiprocessing.sharedctypes import Synchronized
|
5
|
+
from multiprocessing import Process, Value, Queue, set_start_method, Manager
|
9
6
|
from tkinter import ttk, scrolledtext
|
10
7
|
from matplotlib.figure import Figure
|
11
8
|
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
|
12
|
-
from huggingface_hub import list_repo_files
|
13
9
|
import numpy as np
|
10
|
+
import psutil
|
11
|
+
import GPUtil
|
12
|
+
import tkinter.font as tkFont
|
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,96 @@ 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, _, frame = vars_dict[setting]
|
48
|
+
if widget.grid_info():
|
49
|
+
label.grid_remove()
|
50
|
+
widget.grid_remove()
|
51
|
+
frame.grid_remove()
|
52
|
+
else:
|
53
|
+
label.grid()
|
54
|
+
widget.grid()
|
55
|
+
frame.grid()
|
65
56
|
|
66
|
-
|
67
|
-
|
68
|
-
|
57
|
+
def on_category_select(selected_category):
|
58
|
+
if selected_category == "Select Category":
|
59
|
+
return
|
60
|
+
if selected_category in categories:
|
61
|
+
toggle_category(categories[selected_category])
|
62
|
+
if selected_category in active_categories:
|
63
|
+
active_categories.remove(selected_category)
|
64
|
+
else:
|
65
|
+
active_categories.add(selected_category)
|
66
|
+
category_dropdown.update_styles(active_categories)
|
67
|
+
category_var.set("Select Category")
|
69
68
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
except Exception as e:
|
76
|
-
# Send the error message to the GUI via the queue
|
77
|
-
errorMessage = f"Error during processing: {e}"
|
78
|
-
q.put(errorMessage)
|
79
|
-
traceback.print_exc()
|
80
|
-
finally:
|
81
|
-
# Restore the original plt.show function
|
82
|
-
plt.show = original_show
|
83
|
-
|
84
|
-
def run_function_gui(settings_type, settings, q, fig_queue, stop_requested):
|
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)
|
136
|
-
except Exception as e:
|
137
|
-
q.put(f"Error during processing: {e}")
|
138
|
-
traceback.print_exc()
|
139
|
-
finally:
|
140
|
-
stop_requested.value = 1
|
69
|
+
category_var = tk.StringVar()
|
70
|
+
non_empty_categories = [category for category, settings in categories.items() if any(setting in vars_dict for setting in settings)]
|
71
|
+
category_dropdown = spacrDropdownMenu(button_scrollable_frame.scrollable_frame, category_var, non_empty_categories, command=on_category_select)
|
72
|
+
category_dropdown.grid(row=0, column=4, sticky="ew", pady=2, padx=2)
|
73
|
+
vars_dict = hide_all_settings(vars_dict, categories)
|
141
74
|
|
142
|
-
def
|
143
|
-
global
|
144
|
-
from .settings import check_settings, expected_types
|
75
|
+
def process_fig_queue():
|
76
|
+
global canvas, fig_queue, canvas_widget, parent_frame
|
145
77
|
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
78
|
+
def clear_canvas(canvas):
|
79
|
+
for ax in canvas.figure.get_axes():
|
80
|
+
ax.clear()
|
81
|
+
canvas.draw_idle()
|
150
82
|
|
151
83
|
try:
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
84
|
+
while not fig_queue.empty():
|
85
|
+
time.sleep(1)
|
86
|
+
clear_canvas(canvas)
|
87
|
+
fig = fig_queue.get_nowait()
|
156
88
|
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
89
|
+
for ax in fig.get_axes():
|
90
|
+
ax.set_xticks([]) # Remove x-axis ticks
|
91
|
+
ax.set_yticks([]) # Remove y-axis ticks
|
92
|
+
ax.xaxis.set_visible(False) # Hide the x-axis
|
93
|
+
ax.yaxis.set_visible(False) # Hide the y-axis
|
162
94
|
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
95
|
+
# Adjust layout to minimize spacing between axes
|
96
|
+
fig.subplots_adjust(left=0, right=1, top=1, bottom=0, wspace=0.01, hspace=0.01)
|
97
|
+
fig.set_facecolor('black')
|
98
|
+
|
99
|
+
canvas.figure = fig
|
100
|
+
fig_width, fig_height = canvas_widget.winfo_width(), canvas_widget.winfo_height()
|
101
|
+
fig.set_size_inches(fig_width / fig.dpi, fig_height / fig.dpi, forward=True)
|
102
|
+
canvas.draw_idle()
|
103
|
+
except Exception as e:
|
104
|
+
traceback.print_exc()
|
105
|
+
finally:
|
106
|
+
after_id = canvas_widget.after(1, process_fig_queue)
|
107
|
+
parent_frame.after_tasks.append(after_id)
|
108
|
+
|
109
|
+
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):
|
110
|
+
global thread_control, q, console_output, parent_frame, vars_dict, canvas, canvas_widget, scrollable_frame, fig_queue, progress_bar, usage_bars
|
111
|
+
thread_control = thread_control_var
|
112
|
+
q = q_var
|
113
|
+
console_output = console_output_var
|
114
|
+
parent_frame = parent_frame_var
|
115
|
+
vars_dict = vars_dict_var
|
116
|
+
canvas = canvas_var
|
117
|
+
canvas_widget = canvas_widget_var
|
118
|
+
scrollable_frame = scrollable_frame_var
|
119
|
+
fig_queue = fig_queue_var
|
120
|
+
progress_bar = progress_bar_var
|
121
|
+
usage_bars = usage_bars_var
|
170
122
|
|
171
123
|
def import_settings(settings_type='mask'):
|
124
|
+
from .gui_utils import convert_settings_dict_for_gui, hide_all_settings
|
172
125
|
global vars_dict, scrollable_frame, button_scrollable_frame
|
173
126
|
from .settings import generate_fields
|
174
127
|
|
@@ -217,74 +170,23 @@ def import_settings(settings_type='mask'):
|
|
217
170
|
vars_dict = generate_fields(new_settings, scrollable_frame)
|
218
171
|
vars_dict = hide_all_settings(vars_dict, categories=None)
|
219
172
|
|
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]):
|
173
|
+
def setup_settings_panel(vertical_container, settings_type='mask'):
|
278
174
|
global vars_dict, scrollable_frame
|
279
175
|
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
|
176
|
+
from .gui_utils import convert_settings_dict_for_gui, set_element_size
|
280
177
|
|
281
|
-
|
282
|
-
|
178
|
+
size_dict = set_element_size()
|
179
|
+
settings_width = size_dict['settings_width']
|
180
|
+
|
181
|
+
# Create a PanedWindow for the settings panel
|
182
|
+
settings_paned_window = tk.PanedWindow(vertical_container, orient=tk.HORIZONTAL, width=size_dict['settings_width'])
|
183
|
+
vertical_container.add(settings_paned_window, stretch="always")
|
184
|
+
|
185
|
+
settings_frame = tk.Frame(settings_paned_window, width=settings_width)
|
186
|
+
settings_frame.pack_propagate(False) # Prevent the frame from resizing based on its children
|
187
|
+
|
188
|
+
settings_paned_window.add(settings_frame)
|
283
189
|
|
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
190
|
scrollable_frame = spacrFrame(settings_frame)
|
289
191
|
scrollable_frame.grid(row=1, column=0, sticky="nsew")
|
290
192
|
settings_frame.grid_rowconfigure(1, weight=1)
|
@@ -321,7 +223,7 @@ def setup_settings_panel(vertical_container, settings_type='mask', window_dimens
|
|
321
223
|
vars_dict = generate_fields(variables, scrollable_frame)
|
322
224
|
|
323
225
|
containers = [settings_frame]
|
324
|
-
widgets = [
|
226
|
+
widgets = [scrollable_frame]
|
325
227
|
|
326
228
|
style = ttk.Style(vertical_container)
|
327
229
|
_ = set_dark_style(style, containers=containers, widgets=widgets)
|
@@ -331,8 +233,12 @@ def setup_settings_panel(vertical_container, settings_type='mask', window_dimens
|
|
331
233
|
|
332
234
|
def setup_plot_section(vertical_container):
|
333
235
|
global canvas, canvas_widget
|
334
|
-
|
236
|
+
|
237
|
+
# Create a frame for the plot section
|
238
|
+
plot_frame = tk.Frame(vertical_container, bg='lightgrey')
|
335
239
|
vertical_container.add(plot_frame, stretch="always")
|
240
|
+
|
241
|
+
# Set up the plot
|
336
242
|
figure = Figure(figsize=(30, 4), dpi=100)
|
337
243
|
plot = figure.add_subplot(111)
|
338
244
|
plot.plot([], []) # This creates an empty plot.
|
@@ -340,7 +246,11 @@ def setup_plot_section(vertical_container):
|
|
340
246
|
canvas = FigureCanvasTkAgg(figure, master=plot_frame)
|
341
247
|
canvas.get_tk_widget().configure(cursor='arrow', highlightthickness=0)
|
342
248
|
canvas_widget = canvas.get_tk_widget()
|
343
|
-
|
249
|
+
canvas_widget.grid(row=0, column=0, sticky="nsew") # Use grid for the canvas widget
|
250
|
+
|
251
|
+
plot_frame.grid_rowconfigure(0, weight=1)
|
252
|
+
plot_frame.grid_columnconfigure(0, weight=1)
|
253
|
+
|
344
254
|
canvas.draw()
|
345
255
|
canvas.figure = figure
|
346
256
|
style_out = set_dark_style(ttk.Style())
|
@@ -351,23 +261,46 @@ def setup_plot_section(vertical_container):
|
|
351
261
|
widgets = [canvas_widget]
|
352
262
|
style = ttk.Style(vertical_container)
|
353
263
|
_ = set_dark_style(style, containers=containers, widgets=widgets)
|
264
|
+
|
354
265
|
return canvas, canvas_widget
|
355
266
|
|
356
267
|
def setup_console(vertical_container):
|
357
268
|
global console_output
|
358
|
-
|
269
|
+
from .gui_elements import set_dark_style
|
270
|
+
|
271
|
+
# Apply dark style and get style output
|
272
|
+
style = ttk.Style()
|
273
|
+
style_out = set_dark_style(style)
|
274
|
+
|
275
|
+
# Create a frame for the console section
|
276
|
+
console_frame = tk.Frame(vertical_container, bg=style_out['bg_color'])
|
359
277
|
vertical_container.add(console_frame, stretch="always")
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
278
|
+
|
279
|
+
# Create a thicker frame at the top for the hover effect
|
280
|
+
top_border = tk.Frame(console_frame, height=5, bg=style_out['bg_color'])
|
281
|
+
top_border.grid(row=0, column=0, sticky="ew", pady=(0, 2))
|
282
|
+
|
283
|
+
# Create the scrollable frame (which is a Text widget) with white text
|
284
|
+
family = style_out['font_family']
|
285
|
+
size = style_out['font_size'] - 2
|
286
|
+
font = tkFont.Font(family=family, size=size)
|
287
|
+
console_output = tk.Text(console_frame, bg=style_out['bg_color'], fg=style_out['fg_color'], font=font, bd=0, highlightthickness=0)
|
288
|
+
console_output.grid(row=1, column=0, sticky="nsew") # Use grid for console_output
|
289
|
+
|
290
|
+
# Configure the grid to allow expansion
|
364
291
|
console_frame.grid_rowconfigure(1, weight=1)
|
365
292
|
console_frame.grid_columnconfigure(0, weight=1)
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
293
|
+
|
294
|
+
def on_enter(event):
|
295
|
+
top_border.config(bg=style_out['active_color'])
|
296
|
+
|
297
|
+
def on_leave(event):
|
298
|
+
top_border.config(bg=style_out['bg_color'])
|
299
|
+
|
300
|
+
console_output.bind("<Enter>", on_enter)
|
301
|
+
console_output.bind("<Leave>", on_leave)
|
302
|
+
|
303
|
+
return console_output, console_frame
|
371
304
|
|
372
305
|
def setup_progress_frame(vertical_container):
|
373
306
|
global progress_output
|
@@ -387,260 +320,245 @@ def setup_progress_frame(vertical_container):
|
|
387
320
|
_ = set_dark_style(style, containers=containers, widgets=widgets)
|
388
321
|
return progress_output
|
389
322
|
|
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.")
|
472
|
-
|
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
|
323
|
+
def setup_button_section(horizontal_container, settings_type='mask', run=True, abort=True, download=True, import_btn=True):
|
324
|
+
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
|
325
|
+
from .gui_utils import set_element_size, download_hug_dataset
|
326
|
+
from .settings import categories
|
476
327
|
|
477
|
-
|
478
|
-
|
328
|
+
size_dict = set_element_size()
|
329
|
+
button_section_height = size_dict['panel_height']
|
330
|
+
button_frame = tk.Frame(horizontal_container, height=button_section_height)
|
331
|
+
|
332
|
+
# Prevent the frame from resizing based on the child widget
|
333
|
+
#button_frame.pack_propagate(False)
|
479
334
|
|
480
|
-
button_frame = tk.Frame(horizontal_container)
|
481
335
|
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)
|
336
|
+
button_scrollable_frame = spacrFrame(button_frame, scrollbar=False)
|
489
337
|
button_scrollable_frame.grid(row=1, column=0, sticky="nsew")
|
490
|
-
|
491
|
-
widgets = [categories_label, button_scrollable_frame.scrollable_frame]
|
338
|
+
widgets = [button_scrollable_frame.scrollable_frame]
|
492
339
|
|
493
340
|
btn_col = 0
|
494
|
-
btn_row =
|
341
|
+
btn_row = 0
|
495
342
|
|
496
343
|
if run:
|
497
|
-
|
498
|
-
run_button = spacrButton(button_scrollable_frame.scrollable_frame, text="run", command=lambda: start_process(q, fig_queue, settings_type))
|
344
|
+
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
345
|
run_button.grid(row=btn_row, column=btn_col, pady=5, padx=5, sticky='ew')
|
500
346
|
widgets.append(run_button)
|
501
|
-
|
347
|
+
btn_col += 1
|
502
348
|
|
503
349
|
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)
|
350
|
+
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
351
|
abort_button.grid(row=btn_row, column=btn_col, pady=5, padx=5, sticky='ew')
|
506
352
|
widgets.append(abort_button)
|
507
|
-
|
353
|
+
btn_col += 1
|
508
354
|
|
509
355
|
if download and settings_type in ['mask']:
|
510
|
-
download_dataset_button = spacrButton(button_scrollable_frame.scrollable_frame, text="download", command=download_hug_dataset)
|
356
|
+
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
357
|
download_dataset_button.grid(row=btn_row, column=btn_col, pady=5, padx=5, sticky='ew')
|
512
358
|
widgets.append(download_dataset_button)
|
513
|
-
|
359
|
+
btn_col += 1
|
514
360
|
|
515
361
|
if import_btn:
|
516
|
-
import_button = spacrButton(button_scrollable_frame.scrollable_frame, text="settings", command=lambda: import_settings(settings_type))
|
362
|
+
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
363
|
import_button.grid(row=btn_row, column=btn_col, pady=5, padx=5, sticky='ew')
|
518
364
|
widgets.append(import_button)
|
519
365
|
btn_row += 1
|
520
366
|
|
521
367
|
# Add the progress bar under the settings category menu
|
522
368
|
progress_bar = spacrProgressBar(button_scrollable_frame.scrollable_frame, orient='horizontal', mode='determinate')
|
523
|
-
progress_bar.grid(row=
|
369
|
+
progress_bar.grid(row=btn_row, column=0, columnspan=6, pady=5, padx=5, sticky='ew')
|
370
|
+
progress_bar.set_label_position() # Set the label position after grid placement
|
524
371
|
widgets.append(progress_bar)
|
525
372
|
|
526
373
|
if vars_dict is not None:
|
527
374
|
toggle_settings(button_scrollable_frame)
|
528
375
|
|
529
|
-
description_frame = tk.Frame(horizontal_container)
|
530
|
-
horizontal_container.add(description_frame, stretch="always", sticky="nsew")
|
531
|
-
description_frame.grid_columnconfigure(0, weight=1)
|
532
|
-
description_frame.grid_rowconfigure(0, weight=1) # Add this line to make the row expandable
|
533
|
-
|
534
|
-
description_label = tk.Label(description_frame, text="Module Description", anchor='nw', justify='left', wraplength=width - 50)
|
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)
|
538
|
-
|
539
|
-
def update_wraplength(event):
|
540
|
-
new_width = event.width - 40 # Adjust as needed
|
541
|
-
description_label.config(wraplength=new_width)
|
542
|
-
|
543
|
-
description_label.bind('<Configure>', update_wraplength)
|
544
|
-
|
545
|
-
containers = [button_frame, description_frame]
|
546
|
-
widgets.extend([description_label])
|
547
|
-
|
548
376
|
style = ttk.Style(horizontal_container)
|
549
|
-
_ = set_dark_style(style, containers=
|
550
|
-
|
551
|
-
return button_scrollable_frame
|
552
|
-
|
553
|
-
|
554
|
-
|
555
|
-
|
556
|
-
|
377
|
+
_ = set_dark_style(style, containers=[button_frame], widgets=widgets)
|
378
|
+
|
379
|
+
return button_scrollable_frame, btn_col
|
380
|
+
|
381
|
+
def setup_usage_panel(horizontal_container, btn_col):
|
382
|
+
global usage_bars
|
383
|
+
from .gui_utils import set_element_size
|
384
|
+
from .gui_elements import set_dark_style
|
385
|
+
|
386
|
+
usg_col = 1
|
387
|
+
|
388
|
+
def update_usage(ram_bar, vram_bar, gpu_bar, usage_bars, parent_frame):
|
389
|
+
# Update RAM usage
|
390
|
+
ram_usage = psutil.virtual_memory().percent
|
391
|
+
ram_bar['value'] = ram_usage
|
392
|
+
|
393
|
+
# Update GPU and VRAM usage
|
394
|
+
gpus = GPUtil.getGPUs()
|
395
|
+
if gpus:
|
396
|
+
gpu = gpus[0]
|
397
|
+
vram_usage = gpu.memoryUtil * 100
|
398
|
+
gpu_usage = gpu.load * 100
|
399
|
+
vram_bar['value'] = vram_usage
|
400
|
+
gpu_bar['value'] = gpu_usage
|
401
|
+
|
402
|
+
# Update CPU usage for each core
|
403
|
+
cpu_percentages = psutil.cpu_percent(percpu=True)
|
404
|
+
for bar, usage in zip(usage_bars[3:], cpu_percentages):
|
405
|
+
bar['value'] = usage
|
406
|
+
|
407
|
+
# Schedule the function to run again after 1000 ms (1 second)
|
408
|
+
parent_frame.after(1000, update_usage, ram_bar, vram_bar, gpu_bar, usage_bars, parent_frame)
|
409
|
+
|
410
|
+
size_dict = set_element_size()
|
411
|
+
usage_panel_height = size_dict['panel_height']
|
412
|
+
usage_frame = tk.Frame(horizontal_container, height=usage_panel_height)
|
413
|
+
horizontal_container.add(usage_frame)
|
414
|
+
|
415
|
+
usage_frame.grid_rowconfigure(0, weight=0)
|
416
|
+
usage_frame.grid_rowconfigure(1, weight=1)
|
417
|
+
usage_frame.grid_columnconfigure(0, weight=1)
|
418
|
+
usage_frame.grid_columnconfigure(1, weight=1)
|
419
|
+
|
420
|
+
usage_scrollable_frame = spacrFrame(usage_frame, scrollbar=False)
|
421
|
+
usage_scrollable_frame.grid(row=1, column=0, sticky="nsew", columnspan=2)
|
422
|
+
widgets = [usage_scrollable_frame.scrollable_frame]
|
423
|
+
|
424
|
+
usage_bars = []
|
425
|
+
max_elements_per_column = 4
|
426
|
+
row = 0
|
427
|
+
col = 0
|
557
428
|
|
558
|
-
|
559
|
-
|
560
|
-
|
561
|
-
|
429
|
+
# Initialize RAM, VRAM, and GPU bars as None
|
430
|
+
ram_bar, vram_bar, gpu_bar = None, None, None
|
431
|
+
|
432
|
+
# Configure the style for the label
|
433
|
+
style = ttk.Style()
|
434
|
+
style_out = set_dark_style(style)
|
435
|
+
size = style_out['font_size'] - 2
|
436
|
+
usage_font = tkFont.Font(family=style_out['font_family'], size=size)
|
437
|
+
style.configure("usage.TLabel", font=usage_font, foreground=style_out['fg_color'])
|
438
|
+
|
439
|
+
# Try adding RAM bar
|
440
|
+
try:
|
441
|
+
ram_info = psutil.virtual_memory()
|
442
|
+
ram_label_text = f"RAM"
|
443
|
+
label = ttk.Label(usage_scrollable_frame.scrollable_frame, text=ram_label_text, anchor='w', style="usage.TLabel")
|
444
|
+
label.grid(row=row, column=2 * col, pady=5, padx=5, sticky='w')
|
445
|
+
ram_bar = spacrProgressBar(usage_scrollable_frame.scrollable_frame, orient='horizontal', mode='determinate', length=size_dict['bar_size'], label=False)
|
446
|
+
ram_bar.grid(row=row, column=2 * col + 1, pady=5, padx=5, sticky='ew')
|
447
|
+
widgets.append(label)
|
448
|
+
widgets.append(ram_bar)
|
449
|
+
usage_bars.append(ram_bar)
|
450
|
+
row += 1
|
451
|
+
except Exception as e:
|
452
|
+
print(f"Could not add RAM usage bar: {e}")
|
562
453
|
|
563
|
-
|
564
|
-
|
454
|
+
# Try adding VRAM and GPU usage bars
|
455
|
+
try:
|
456
|
+
gpus = GPUtil.getGPUs()
|
457
|
+
if gpus:
|
458
|
+
gpu = gpus[0]
|
459
|
+
vram_label_text = f"VRAM"
|
460
|
+
label = ttk.Label(usage_scrollable_frame.scrollable_frame, text=vram_label_text, anchor='w', style="usage.TLabel")
|
461
|
+
label.grid(row=row, column=2 * col, pady=5, padx=5, sticky='w')
|
462
|
+
vram_bar = spacrProgressBar(usage_scrollable_frame.scrollable_frame, orient='horizontal', mode='determinate', length=size_dict['bar_size'], label=False)
|
463
|
+
vram_bar.grid(row=row, column=2 * col + 1, pady=5, padx=5, sticky='ew')
|
464
|
+
widgets.append(label)
|
465
|
+
widgets.append(vram_bar)
|
466
|
+
usage_bars.append(vram_bar)
|
467
|
+
row += 1
|
468
|
+
|
469
|
+
gpu_label_text = f"GPU"
|
470
|
+
label = ttk.Label(usage_scrollable_frame.scrollable_frame, text=gpu_label_text, anchor='w', style="usage.TLabel")
|
471
|
+
label.grid(row=row, column=2 * col, pady=5, padx=5, sticky='w')
|
472
|
+
gpu_bar = spacrProgressBar(usage_scrollable_frame.scrollable_frame, orient='horizontal', mode='determinate', length=size_dict['bar_size'], label=False)
|
473
|
+
gpu_bar.grid(row=row, column=2 * col + 1, pady=5, padx=5, sticky='ew')
|
474
|
+
widgets.append(label)
|
475
|
+
widgets.append(gpu_bar)
|
476
|
+
usage_bars.append(gpu_bar)
|
477
|
+
row += 1
|
478
|
+
except Exception as e:
|
479
|
+
print(f"Could not add VRAM or GPU usage bars: {e}")
|
565
480
|
|
566
|
-
|
567
|
-
|
568
|
-
|
569
|
-
|
570
|
-
|
571
|
-
|
572
|
-
|
573
|
-
|
574
|
-
|
575
|
-
|
576
|
-
|
481
|
+
# Add CPU core usage bars
|
482
|
+
try:
|
483
|
+
cpu_cores = psutil.cpu_count(logical=True)
|
484
|
+
cpu_freq = psutil.cpu_freq()
|
485
|
+
|
486
|
+
for core in range(cpu_cores):
|
487
|
+
if row > 0 and row % max_elements_per_column == 0:
|
488
|
+
col += 1
|
489
|
+
row = 0
|
490
|
+
label = ttk.Label(usage_scrollable_frame.scrollable_frame, text=f"Core {core+1}", anchor='w', style="usage.TLabel")
|
491
|
+
label.grid(row=row, column=2 * col, pady=2, padx=5, sticky='w')
|
492
|
+
bar = spacrProgressBar(usage_scrollable_frame.scrollable_frame, orient='horizontal', mode='determinate', length=size_dict['bar_size'], label=False)
|
493
|
+
bar.grid(row=row, column=2 * col + 1, pady=2, padx=5, sticky='ew')
|
494
|
+
widgets.append(label)
|
495
|
+
widgets.append(bar)
|
496
|
+
usage_bars.append(bar)
|
497
|
+
row += 1
|
498
|
+
except Exception as e:
|
499
|
+
print(f"Could not add CPU core usage bars: {e}")
|
577
500
|
|
578
|
-
|
579
|
-
|
580
|
-
|
501
|
+
style = ttk.Style(horizontal_container)
|
502
|
+
_ = set_dark_style(style, containers=[usage_frame], widgets=widgets)
|
503
|
+
|
504
|
+
if ram_bar is None:
|
505
|
+
ram_bar = spacrProgressBar(usage_scrollable_frame.scrollable_frame, orient='horizontal', mode='determinate', length=size_dict['bar_size'], label=False)
|
506
|
+
if vram_bar is None:
|
507
|
+
vram_bar = spacrProgressBar(usage_scrollable_frame.scrollable_frame, orient='horizontal', mode='determinate', length=size_dict['bar_size'], label=False)
|
508
|
+
if gpu_bar is None:
|
509
|
+
gpu_bar = spacrProgressBar(usage_scrollable_frame.scrollable_frame, orient='horizontal', mode='determinate', length=size_dict['bar_size'], label=False)
|
510
|
+
|
511
|
+
update_usage(ram_bar, vram_bar, gpu_bar, usage_bars, usage_frame)
|
512
|
+
return usage_scrollable_frame, usage_bars, usg_col
|
581
513
|
|
582
|
-
|
583
|
-
|
514
|
+
def initiate_abort():
|
515
|
+
global thread_control, q, parent_frame
|
516
|
+
if thread_control.get("run_thread") is not None:
|
517
|
+
try:
|
518
|
+
q.put("Aborting processes...")
|
519
|
+
thread_control.get("run_thread").terminate()
|
520
|
+
thread_control["run_thread"] = None
|
521
|
+
q.put("Processes aborted.")
|
522
|
+
except Exception as e:
|
523
|
+
q.put(f"Error aborting process: {e}")
|
584
524
|
|
585
|
-
|
525
|
+
thread_control = {"run_thread": None, "stop_requested": False}
|
586
526
|
|
587
|
-
def toggle_category(settings):
|
588
|
-
for setting in settings:
|
589
|
-
if setting in vars_dict:
|
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()
|
597
527
|
|
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")
|
528
|
+
def start_process(q=None, fig_queue=None, settings_type='mask'):
|
529
|
+
global thread_control, vars_dict, parent_frame
|
530
|
+
from .settings import check_settings, expected_types
|
531
|
+
from .gui_utils import run_function_gui
|
609
532
|
|
610
|
-
|
611
|
-
|
612
|
-
|
613
|
-
|
614
|
-
vars_dict = hide_all_settings(vars_dict, categories)
|
533
|
+
if q is None:
|
534
|
+
q = Queue()
|
535
|
+
if fig_queue is None:
|
536
|
+
fig_queue = Queue()
|
615
537
|
|
616
|
-
|
617
|
-
|
538
|
+
try:
|
539
|
+
settings = check_settings(vars_dict, expected_types, q)
|
540
|
+
except ValueError as e:
|
541
|
+
q.put(f"Error: {e}")
|
542
|
+
return
|
618
543
|
|
619
|
-
|
620
|
-
|
621
|
-
|
622
|
-
|
544
|
+
if thread_control.get("run_thread") is not None:
|
545
|
+
initiate_abort()
|
546
|
+
|
547
|
+
stop_requested = Value('i', 0)
|
548
|
+
thread_control["stop_requested"] = stop_requested
|
623
549
|
|
624
|
-
|
625
|
-
|
626
|
-
|
627
|
-
|
628
|
-
|
629
|
-
|
630
|
-
|
631
|
-
|
632
|
-
|
633
|
-
|
634
|
-
|
635
|
-
|
636
|
-
fig_width, fig_height = canvas_widget.winfo_width(), canvas_widget.winfo_height()
|
637
|
-
fig.set_size_inches(fig_width / fig.dpi, fig_height / fig.dpi, forward=True)
|
638
|
-
canvas.draw_idle()
|
639
|
-
except Exception as e:
|
640
|
-
traceback.print_exc()
|
641
|
-
finally:
|
642
|
-
after_id = canvas_widget.after(100, process_fig_queue)
|
643
|
-
parent_frame.after_tasks.append(after_id)
|
550
|
+
process_args = (settings_type, settings, q, fig_queue, stop_requested)
|
551
|
+
if settings_type in [
|
552
|
+
'mask', 'measure', 'simulation', 'sequencing', 'classify', 'cellpose_dataset',
|
553
|
+
'train_cellpose', 'ml_analyze', 'cellpose_masks', 'cellpose_all', 'map_barcodes',
|
554
|
+
'regression', 'recruitment', 'plaques', 'cellpose_compare', 'vision_scores',
|
555
|
+
'vision_dataset'
|
556
|
+
]:
|
557
|
+
thread_control["run_thread"] = Process(target=run_function_gui, args=process_args)
|
558
|
+
else:
|
559
|
+
q.put(f"Error: Unknown settings type '{settings_type}'")
|
560
|
+
return
|
561
|
+
thread_control["run_thread"].start()
|
644
562
|
|
645
563
|
def process_console_queue():
|
646
564
|
global q, console_output, parent_frame, progress_bar
|
@@ -648,9 +566,11 @@ def process_console_queue():
|
|
648
566
|
# Initialize function attribute if it doesn't exist
|
649
567
|
if not hasattr(process_console_queue, "completed_tasks"):
|
650
568
|
process_console_queue.completed_tasks = []
|
569
|
+
if not hasattr(process_console_queue, "current_maximum"):
|
570
|
+
process_console_queue.current_maximum = None
|
651
571
|
|
652
572
|
ansi_escape_pattern = re.compile(r'\x1B\[[0-?]*[ -/]*[@-~]')
|
653
|
-
|
573
|
+
|
654
574
|
while not q.empty():
|
655
575
|
message = q.get_nowait()
|
656
576
|
clean_message = ansi_escape_pattern.sub('', message)
|
@@ -658,13 +578,21 @@ def process_console_queue():
|
|
658
578
|
console_output.see(tk.END)
|
659
579
|
|
660
580
|
# Check if the message contains progress information
|
661
|
-
if clean_message.startswith("Progress"):
|
581
|
+
if clean_message.startswith("Progress:"):
|
662
582
|
try:
|
663
583
|
# Extract the progress information
|
664
|
-
match = re.search(r'(\d+)/(\d+)', clean_message)
|
584
|
+
match = re.search(r'Progress: (\d+)/(\d+), operation_type: ([\w\s]*)(.*)', clean_message)
|
585
|
+
|
665
586
|
if match:
|
666
587
|
current_progress = int(match.group(1))
|
667
588
|
total_progress = int(match.group(2))
|
589
|
+
operation_type = match.group(3).strip()
|
590
|
+
time_info = match.group(4).strip()
|
591
|
+
|
592
|
+
# Check if the maximum value has changed
|
593
|
+
if process_console_queue.current_maximum != total_progress:
|
594
|
+
process_console_queue.current_maximum = total_progress
|
595
|
+
process_console_queue.completed_tasks = []
|
668
596
|
|
669
597
|
# Add the task to the completed set
|
670
598
|
process_console_queue.completed_tasks.append(current_progress)
|
@@ -678,19 +606,18 @@ def process_console_queue():
|
|
678
606
|
progress_bar['value'] = unique_progress_count
|
679
607
|
|
680
608
|
# Extract and update additional information
|
681
|
-
|
682
|
-
|
683
|
-
progress_bar.operation_type = operation_match.group(1)
|
609
|
+
if operation_type:
|
610
|
+
progress_bar.operation_type = operation_type
|
684
611
|
|
685
|
-
time_image_match = re.search(r'Time/image: ([\d.]+) sec',
|
612
|
+
time_image_match = re.search(r'Time/image: ([\d.]+) sec', time_info)
|
686
613
|
if time_image_match:
|
687
614
|
progress_bar.time_image = float(time_image_match.group(1))
|
688
615
|
|
689
|
-
time_batch_match = re.search(r'Time/batch: ([\d.]+) sec',
|
616
|
+
time_batch_match = re.search(r'Time/batch: ([\d.]+) sec', time_info)
|
690
617
|
if time_batch_match:
|
691
618
|
progress_bar.time_batch = float(time_batch_match.group(1))
|
692
619
|
|
693
|
-
time_left_match = re.search(r'Time_left: ([\d.]+) min',
|
620
|
+
time_left_match = re.search(r'Time_left: ([\d.]+) min', time_info)
|
694
621
|
if time_left_match:
|
695
622
|
progress_bar.time_left = float(time_left_match.group(1))
|
696
623
|
|
@@ -701,76 +628,41 @@ def process_console_queue():
|
|
701
628
|
# Clear completed tasks when progress is complete
|
702
629
|
if unique_progress_count >= total_progress:
|
703
630
|
process_console_queue.completed_tasks.clear()
|
631
|
+
|
704
632
|
except Exception as e:
|
705
633
|
print(f"Error parsing progress message: {e}")
|
706
|
-
|
707
|
-
|
634
|
+
#else:
|
635
|
+
# # Only insert messages that do not start with "Progress:"
|
636
|
+
# console_output.insert(tk.END, clean_message + "\n")
|
637
|
+
# console_output.see(tk.END)
|
638
|
+
|
639
|
+
after_id = console_output.after(1, process_console_queue)
|
708
640
|
parent_frame.after_tasks.append(after_id)
|
709
641
|
|
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
642
|
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
|
643
|
+
global q, fig_queue, thread_control, parent_frame, scrollable_frame, button_frame, vars_dict, canvas, canvas_widget, button_scrollable_frame, progress_bar
|
644
|
+
from .gui_utils import main_thread_update_function, setup_frame, set_element_size
|
750
645
|
from .gui import gui_app
|
646
|
+
from .settings import descriptions
|
647
|
+
|
751
648
|
set_start_method('spawn', force=True)
|
752
649
|
print("Initializing root with settings_type:", settings_type)
|
753
650
|
|
754
651
|
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
652
|
|
761
|
-
if not
|
762
|
-
|
653
|
+
if not isinstance(parent_frame, (tk.Tk, tk.Toplevel)):
|
654
|
+
parent_window = parent_frame.winfo_toplevel()
|
655
|
+
else:
|
656
|
+
parent_window = parent_frame
|
657
|
+
|
658
|
+
parent_window.update_idletasks()
|
763
659
|
|
764
|
-
|
765
|
-
|
766
|
-
try:
|
767
|
-
widget.destroy()
|
768
|
-
except tk.TclError as e:
|
769
|
-
print(f"Error destroying widget: {e}")
|
660
|
+
if not hasattr(parent_window, 'after_tasks'):
|
661
|
+
parent_window.after_tasks = []
|
770
662
|
|
771
663
|
q = Queue()
|
772
664
|
fig_queue = Queue()
|
773
|
-
parent_frame, vertical_container, horizontal_container = setup_frame(parent_frame)
|
665
|
+
parent_frame, vertical_container, horizontal_container, settings_container = setup_frame(parent_frame)
|
774
666
|
|
775
667
|
if settings_type == 'annotate':
|
776
668
|
from .app_annotate import initiate_annotation_app
|
@@ -779,17 +671,26 @@ def initiate_root(parent, settings_type='mask'):
|
|
779
671
|
from .app_make_masks import initiate_make_mask_app
|
780
672
|
initiate_make_mask_app(horizontal_container)
|
781
673
|
else:
|
782
|
-
scrollable_frame, vars_dict = setup_settings_panel(
|
783
|
-
|
674
|
+
scrollable_frame, vars_dict = setup_settings_panel(settings_container, settings_type)
|
675
|
+
print('setup_settings_panel')
|
784
676
|
canvas, canvas_widget = setup_plot_section(vertical_container)
|
785
|
-
console_output = setup_console(vertical_container)
|
677
|
+
console_output, _ = setup_console(vertical_container)
|
678
|
+
button_scrollable_frame, btn_col = setup_button_section(horizontal_container, settings_type)
|
679
|
+
_, usage_bars, btn_col = setup_usage_panel(horizontal_container, btn_col)
|
786
680
|
|
787
|
-
set_globals(q, console_output, parent_frame, vars_dict, canvas, canvas_widget, scrollable_frame, fig_queue, progress_bar)
|
681
|
+
set_globals(thread_control, q, console_output, parent_frame, vars_dict, canvas, canvas_widget, scrollable_frame, fig_queue, progress_bar, usage_bars)
|
682
|
+
description_text = descriptions.get(settings_type, "No description available for this module.")
|
683
|
+
|
684
|
+
q.put(f"Console")
|
685
|
+
q.put(f" ")
|
686
|
+
q.put(description_text)
|
687
|
+
|
788
688
|
process_console_queue()
|
789
689
|
process_fig_queue()
|
790
|
-
after_id =
|
791
|
-
|
690
|
+
after_id = parent_window.after(100, lambda: main_thread_update_function(parent_window, q, fig_queue, canvas_widget))
|
691
|
+
parent_window.after_tasks.append(after_id)
|
792
692
|
|
793
693
|
print("Root initialization complete")
|
794
694
|
return parent_frame, vars_dict
|
795
695
|
|
696
|
+
|