spacr 0.2.3__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/app_annotate.py +3 -4
- spacr/core.py +100 -282
- spacr/gui.py +20 -38
- spacr/gui_core.py +406 -499
- spacr/gui_elements.py +395 -60
- spacr/gui_utils.py +393 -73
- spacr/io.py +130 -50
- spacr/measure.py +199 -154
- 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 +12 -87
- spacr/utils.py +45 -10
- {spacr-0.2.3.dist-info → spacr-0.2.5.dist-info}/METADATA +5 -1
- spacr-0.2.5.dist-info/RECORD +100 -0
- spacr-0.2.3.dist-info/RECORD +0 -58
- {spacr-0.2.3.dist-info → spacr-0.2.5.dist-info}/LICENSE +0 -0
- {spacr-0.2.3.dist-info → spacr-0.2.5.dist-info}/WHEEL +0 -0
- {spacr-0.2.3.dist-info → spacr-0.2.5.dist-info}/entry_points.txt +0 -0
- {spacr-0.2.3.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
|
177
|
+
|
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")
|
280
184
|
|
281
|
-
|
282
|
-
|
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,278 +320,257 @@ 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
|
|
367
|
+
# Add the progress bar under the settings category menu
|
521
368
|
progress_bar = spacrProgressBar(button_scrollable_frame.scrollable_frame, orient='horizontal', mode='determinate')
|
522
|
-
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
|
523
371
|
widgets.append(progress_bar)
|
524
372
|
|
525
373
|
if vars_dict is not None:
|
526
374
|
toggle_settings(button_scrollable_frame)
|
527
375
|
|
528
|
-
description_frame = tk.Frame(horizontal_container)
|
529
|
-
horizontal_container.add(description_frame, stretch="always", sticky="nsew")
|
530
|
-
description_frame.grid_columnconfigure(0, weight=1)
|
531
|
-
description_frame.grid_rowconfigure(0, weight=1)
|
532
|
-
|
533
|
-
description_label = tk.Label(description_frame, text="Module Description", anchor='nw', justify='left', wraplength=width - 50)
|
534
|
-
description_label.grid(row=0, column=0, pady=50, padx=20, sticky='nsew')
|
535
|
-
description_text = descriptions.get(settings_type, "No description available for this module.")
|
536
|
-
description_label.config(text=description_text)
|
537
|
-
|
538
|
-
def update_wraplength(event):
|
539
|
-
new_width = event.width - 40 # Adjust as needed
|
540
|
-
description_label.config(wraplength=new_width)
|
541
|
-
|
542
|
-
description_label.bind('<Configure>', update_wraplength)
|
543
|
-
|
544
|
-
containers = [button_frame, description_frame]
|
545
|
-
widgets.extend([description_label])
|
546
|
-
|
547
376
|
style = ttk.Style(horizontal_container)
|
548
|
-
_ = set_dark_style(style, containers=
|
549
|
-
|
550
|
-
return button_scrollable_frame
|
551
|
-
|
552
|
-
|
553
|
-
|
554
|
-
|
555
|
-
|
556
|
-
|
557
|
-
|
558
|
-
|
559
|
-
|
560
|
-
|
561
|
-
|
562
|
-
|
563
|
-
|
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
|
564
428
|
|
565
|
-
|
566
|
-
|
567
|
-
|
568
|
-
|
569
|
-
|
570
|
-
|
571
|
-
|
572
|
-
|
573
|
-
|
574
|
-
|
575
|
-
|
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}")
|
576
453
|
|
577
|
-
|
578
|
-
|
579
|
-
|
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}")
|
580
480
|
|
581
|
-
|
582
|
-
|
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}")
|
583
500
|
|
584
|
-
|
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
|
585
513
|
|
586
|
-
|
587
|
-
|
588
|
-
|
589
|
-
|
590
|
-
|
591
|
-
|
592
|
-
|
593
|
-
|
594
|
-
|
595
|
-
|
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}")
|
596
524
|
|
597
|
-
|
598
|
-
if selected_category == "Select Category":
|
599
|
-
return
|
600
|
-
if selected_category in categories:
|
601
|
-
toggle_category(categories[selected_category])
|
602
|
-
if selected_category in active_categories:
|
603
|
-
active_categories.remove(selected_category)
|
604
|
-
else:
|
605
|
-
active_categories.add(selected_category)
|
606
|
-
category_dropdown.update_styles(active_categories)
|
607
|
-
category_var.set("Select Category")
|
525
|
+
thread_control = {"run_thread": None, "stop_requested": False}
|
608
526
|
|
609
|
-
category_var = tk.StringVar()
|
610
|
-
non_empty_categories = [category for category, settings in categories.items() if any(setting in vars_dict for setting in settings)]
|
611
|
-
category_dropdown = spacrDropdownMenu(button_scrollable_frame.scrollable_frame, category_var, non_empty_categories, command=on_category_select)
|
612
|
-
category_dropdown.grid(row=5, column=0, sticky="ew", pady=2, padx=2)
|
613
|
-
vars_dict = hide_all_settings(vars_dict, categories)
|
614
527
|
|
615
|
-
def
|
616
|
-
global
|
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
|
617
532
|
|
618
|
-
|
619
|
-
|
620
|
-
|
621
|
-
|
533
|
+
if q is None:
|
534
|
+
q = Queue()
|
535
|
+
if fig_queue is None:
|
536
|
+
fig_queue = Queue()
|
622
537
|
|
623
538
|
try:
|
624
|
-
|
625
|
-
|
626
|
-
|
627
|
-
|
628
|
-
ax.set_xticks([]) # Remove x-axis ticks
|
629
|
-
ax.set_yticks([]) # Remove y-axis ticks
|
630
|
-
ax.xaxis.set_visible(False) # Hide the x-axis
|
631
|
-
ax.yaxis.set_visible(False) # Hide the y-axis
|
632
|
-
fig.tight_layout()
|
633
|
-
fig.set_facecolor('black')
|
634
|
-
canvas.figure = fig
|
635
|
-
fig_width, fig_height = canvas_widget.winfo_width(), canvas_widget.winfo_height()
|
636
|
-
fig.set_size_inches(fig_width / fig.dpi, fig_height / fig.dpi, forward=True)
|
637
|
-
canvas.draw_idle()
|
638
|
-
except Exception as e:
|
639
|
-
traceback.print_exc()
|
640
|
-
finally:
|
641
|
-
after_id = canvas_widget.after(100, process_fig_queue)
|
642
|
-
parent_frame.after_tasks.append(after_id)
|
539
|
+
settings = check_settings(vars_dict, expected_types, q)
|
540
|
+
except ValueError as e:
|
541
|
+
q.put(f"Error: {e}")
|
542
|
+
return
|
643
543
|
|
644
|
-
|
645
|
-
|
646
|
-
|
647
|
-
|
648
|
-
|
649
|
-
|
650
|
-
|
651
|
-
|
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
|
549
|
+
|
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()
|
652
562
|
|
653
563
|
def process_console_queue():
|
654
|
-
global q, console_output, parent_frame, progress_bar
|
564
|
+
global q, console_output, parent_frame, progress_bar
|
655
565
|
|
656
566
|
# Initialize function attribute if it doesn't exist
|
657
567
|
if not hasattr(process_console_queue, "completed_tasks"):
|
658
568
|
process_console_queue.completed_tasks = []
|
569
|
+
if not hasattr(process_console_queue, "current_maximum"):
|
570
|
+
process_console_queue.current_maximum = None
|
659
571
|
|
660
572
|
ansi_escape_pattern = re.compile(r'\x1B\[[0-?]*[ -/]*[@-~]')
|
661
|
-
|
573
|
+
|
662
574
|
while not q.empty():
|
663
575
|
message = q.get_nowait()
|
664
576
|
clean_message = ansi_escape_pattern.sub('', message)
|
@@ -666,17 +578,24 @@ def process_console_queue():
|
|
666
578
|
console_output.see(tk.END)
|
667
579
|
|
668
580
|
# Check if the message contains progress information
|
669
|
-
if clean_message.startswith("Progress"):
|
581
|
+
if clean_message.startswith("Progress:"):
|
670
582
|
try:
|
671
583
|
# Extract the progress information
|
672
|
-
match = re.search(r'(\d+)/(\d+)', clean_message)
|
584
|
+
match = re.search(r'Progress: (\d+)/(\d+), operation_type: ([\w\s]*)(.*)', clean_message)
|
585
|
+
|
673
586
|
if match:
|
674
587
|
current_progress = int(match.group(1))
|
675
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 = []
|
676
596
|
|
677
597
|
# Add the task to the completed set
|
678
598
|
process_console_queue.completed_tasks.append(current_progress)
|
679
|
-
#print('completed_tasks', process_console_queue.completed_tasks)
|
680
599
|
|
681
600
|
# Calculate the unique progress count
|
682
601
|
unique_progress_count = len(np.unique(process_console_queue.completed_tasks))
|
@@ -686,85 +605,64 @@ def process_console_queue():
|
|
686
605
|
progress_bar['maximum'] = total_progress
|
687
606
|
progress_bar['value'] = unique_progress_count
|
688
607
|
|
608
|
+
# Extract and update additional information
|
609
|
+
if operation_type:
|
610
|
+
progress_bar.operation_type = operation_type
|
611
|
+
|
612
|
+
time_image_match = re.search(r'Time/image: ([\d.]+) sec', time_info)
|
613
|
+
if time_image_match:
|
614
|
+
progress_bar.time_image = float(time_image_match.group(1))
|
615
|
+
|
616
|
+
time_batch_match = re.search(r'Time/batch: ([\d.]+) sec', time_info)
|
617
|
+
if time_batch_match:
|
618
|
+
progress_bar.time_batch = float(time_batch_match.group(1))
|
619
|
+
|
620
|
+
time_left_match = re.search(r'Time_left: ([\d.]+) min', time_info)
|
621
|
+
if time_left_match:
|
622
|
+
progress_bar.time_left = float(time_left_match.group(1))
|
623
|
+
|
689
624
|
# Update the progress label
|
690
|
-
if progress_label:
|
691
|
-
|
692
|
-
progress_label.config(text=progress_label_text)
|
625
|
+
if progress_bar.progress_label:
|
626
|
+
progress_bar.update_label()
|
693
627
|
|
694
628
|
# Clear completed tasks when progress is complete
|
695
|
-
if unique_progress_count
|
629
|
+
if unique_progress_count >= total_progress:
|
696
630
|
process_console_queue.completed_tasks.clear()
|
631
|
+
|
697
632
|
except Exception as e:
|
698
633
|
print(f"Error parsing progress message: {e}")
|
699
|
-
|
700
|
-
|
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)
|
701
640
|
parent_frame.after_tasks.append(after_id)
|
702
641
|
|
703
|
-
|
704
|
-
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):
|
705
|
-
global q, console_output, parent_frame, vars_dict, canvas, canvas_widget, scrollable_frame, progress_label, fig_queue, progress_bar
|
706
|
-
q = q_var
|
707
|
-
console_output = console_output_var
|
708
|
-
parent_frame = parent_frame_var
|
709
|
-
vars_dict = vars_dict_var
|
710
|
-
canvas = canvas_var
|
711
|
-
canvas_widget = canvas_widget_var
|
712
|
-
scrollable_frame = scrollable_frame_var
|
713
|
-
fig_queue = fig_queue_var
|
714
|
-
progress_bar = progress_bar_var
|
715
|
-
|
716
|
-
def create_containers(parent_frame):
|
717
|
-
vertical_container = tk.PanedWindow(parent_frame, orient=tk.VERTICAL)
|
718
|
-
horizontal_container = tk.PanedWindow(vertical_container, orient=tk.HORIZONTAL)
|
719
|
-
settings_frame = tk.Frame(horizontal_container)
|
720
|
-
return vertical_container, horizontal_container, settings_frame
|
721
|
-
|
722
|
-
def setup_frame(parent_frame):
|
723
|
-
style = ttk.Style(parent_frame)
|
724
|
-
vertical_container, horizontal_container, settings_frame = create_containers(parent_frame)
|
725
|
-
containers = [vertical_container, horizontal_container, settings_frame]
|
726
|
-
|
727
|
-
set_dark_style(style, parent_frame, containers)
|
728
|
-
set_default_font(parent_frame, font_name="Helvetica", size=8)
|
729
|
-
|
730
|
-
vertical_container.grid(row=0, column=0, sticky=tk.NSEW)
|
731
|
-
vertical_container.add(horizontal_container, stretch="always")
|
732
|
-
horizontal_container.grid_columnconfigure(0, weight=1)
|
733
|
-
horizontal_container.grid_columnconfigure(1, weight=1)
|
734
|
-
settings_frame.grid_rowconfigure(0, weight=0)
|
735
|
-
settings_frame.grid_rowconfigure(1, weight=1)
|
736
|
-
settings_frame.grid_columnconfigure(0, weight=1)
|
737
|
-
horizontal_container.add(settings_frame, stretch="always", sticky="nsew")
|
738
|
-
|
739
|
-
return parent_frame, vertical_container, horizontal_container
|
740
|
-
|
741
642
|
def initiate_root(parent, settings_type='mask'):
|
742
|
-
global q, fig_queue, parent_frame, scrollable_frame, button_frame, vars_dict, canvas, canvas_widget, button_scrollable_frame, progress_bar
|
743
|
-
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
|
744
645
|
from .gui import gui_app
|
646
|
+
from .settings import descriptions
|
647
|
+
|
745
648
|
set_start_method('spawn', force=True)
|
746
649
|
print("Initializing root with settings_type:", settings_type)
|
747
650
|
|
748
651
|
parent_frame = parent
|
749
|
-
parent_frame.update_idletasks()
|
750
|
-
frame_width = int(parent_frame.winfo_width())
|
751
|
-
frame_height = int(parent_frame.winfo_height())
|
752
|
-
print(frame_width, frame_height)
|
753
|
-
dims = [frame_width, frame_height]
|
754
652
|
|
755
|
-
if not
|
756
|
-
|
653
|
+
if not isinstance(parent_frame, (tk.Tk, tk.Toplevel)):
|
654
|
+
parent_window = parent_frame.winfo_toplevel()
|
655
|
+
else:
|
656
|
+
parent_window = parent_frame
|
757
657
|
|
758
|
-
|
759
|
-
|
760
|
-
|
761
|
-
|
762
|
-
except tk.TclError as e:
|
763
|
-
print(f"Error destroying widget: {e}")
|
658
|
+
parent_window.update_idletasks()
|
659
|
+
|
660
|
+
if not hasattr(parent_window, 'after_tasks'):
|
661
|
+
parent_window.after_tasks = []
|
764
662
|
|
765
663
|
q = Queue()
|
766
664
|
fig_queue = Queue()
|
767
|
-
parent_frame, vertical_container, horizontal_container = setup_frame(parent_frame)
|
665
|
+
parent_frame, vertical_container, horizontal_container, settings_container = setup_frame(parent_frame)
|
768
666
|
|
769
667
|
if settings_type == 'annotate':
|
770
668
|
from .app_annotate import initiate_annotation_app
|
@@ -773,17 +671,26 @@ def initiate_root(parent, settings_type='mask'):
|
|
773
671
|
from .app_make_masks import initiate_make_mask_app
|
774
672
|
initiate_make_mask_app(horizontal_container)
|
775
673
|
else:
|
776
|
-
scrollable_frame, vars_dict = setup_settings_panel(
|
777
|
-
|
674
|
+
scrollable_frame, vars_dict = setup_settings_panel(settings_container, settings_type)
|
675
|
+
print('setup_settings_panel')
|
778
676
|
canvas, canvas_widget = setup_plot_section(vertical_container)
|
779
|
-
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)
|
780
680
|
|
781
|
-
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
|
+
|
782
688
|
process_console_queue()
|
783
689
|
process_fig_queue()
|
784
|
-
after_id =
|
785
|
-
|
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)
|
786
692
|
|
787
693
|
print("Root initialization complete")
|
788
694
|
return parent_frame, vars_dict
|
789
695
|
|
696
|
+
|