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_utils.py
CHANGED
@@ -1,48 +1,20 @@
|
|
1
|
-
import os, io, sys, ast, ctypes,
|
1
|
+
import os, io, sys, ast, ctypes, ast, sqlite3, requests, time, traceback, pyautogui
|
2
|
+
import traceback
|
2
3
|
import tkinter as tk
|
3
4
|
from tkinter import ttk
|
5
|
+
import matplotlib
|
6
|
+
import matplotlib.pyplot as plt
|
7
|
+
matplotlib.use('Agg')
|
8
|
+
from huggingface_hub import list_repo_files
|
9
|
+
import traceback
|
4
10
|
|
5
11
|
from . gui_core import initiate_root
|
6
|
-
from .gui_elements import
|
12
|
+
from .gui_elements import AnnotateApp, spacrEntry, spacrCheck, spacrCombo, set_default_font
|
7
13
|
|
8
14
|
try:
|
9
15
|
ctypes.windll.shcore.SetProcessDpiAwareness(True)
|
10
16
|
except AttributeError:
|
11
17
|
pass
|
12
|
-
|
13
|
-
def proceed_with_app_v1(root, app_name, app_func):
|
14
|
-
from .gui import gui_app
|
15
|
-
|
16
|
-
# Clear the current content frame
|
17
|
-
if hasattr(root, 'content_frame'):
|
18
|
-
for widget in root.content_frame.winfo_children():
|
19
|
-
try:
|
20
|
-
widget.destroy()
|
21
|
-
except tk.TclError as e:
|
22
|
-
print(f"Error destroying widget: {e}")
|
23
|
-
else:
|
24
|
-
root.content_frame = tk.Frame(root)
|
25
|
-
root.content_frame.grid(row=1, column=0, sticky="nsew")
|
26
|
-
root.grid_rowconfigure(1, weight=1)
|
27
|
-
root.grid_columnconfigure(0, weight=1)
|
28
|
-
|
29
|
-
# Initialize the new app in the content frame
|
30
|
-
if app_name == "Mask":
|
31
|
-
initiate_root(root.content_frame, 'mask')
|
32
|
-
elif app_name == "Measure":
|
33
|
-
initiate_root(root.content_frame, 'measure')
|
34
|
-
elif app_name == "Classify":
|
35
|
-
initiate_root(root.content_frame, 'classify')
|
36
|
-
elif app_name == "Sequencing":
|
37
|
-
initiate_root(root.content_frame, 'sequencing')
|
38
|
-
elif app_name == "Umap":
|
39
|
-
initiate_root(root.content_frame, 'umap')
|
40
|
-
elif app_name == "Annotate":
|
41
|
-
initiate_root(root.content_frame, 'annotate')
|
42
|
-
elif app_name == "Make Masks":
|
43
|
-
initiate_root(root.content_frame, 'make_masks')
|
44
|
-
else:
|
45
|
-
raise ValueError(f"Invalid app name: {app_name}")
|
46
18
|
|
47
19
|
def proceed_with_app(root, app_name, app_func):
|
48
20
|
# Clear the current content frame
|
@@ -93,37 +65,55 @@ def parse_list(value):
|
|
93
65
|
|
94
66
|
# Usage example in your create_input_field function
|
95
67
|
def create_input_field(frame, label_text, row, var_type='entry', options=None, default_value=None):
|
68
|
+
from .gui_elements import set_dark_style
|
96
69
|
label_column = 0
|
97
|
-
widget_column =
|
70
|
+
widget_column = 0 # Both label and widget will be in the same column
|
71
|
+
|
72
|
+
style_out = set_dark_style(ttk.Style())
|
73
|
+
size_dict = set_element_size()
|
74
|
+
size_dict['settings_width'] = size_dict['settings_width'] - int(size_dict['settings_width']*0.1)
|
75
|
+
|
76
|
+
# Replace underscores with spaces and capitalize the first letter
|
77
|
+
label_text = label_text.replace('_', ' ').capitalize()
|
98
78
|
|
99
79
|
# Configure the column widths
|
100
|
-
frame.grid_columnconfigure(label_column, weight=
|
101
|
-
|
80
|
+
frame.grid_columnconfigure(label_column, weight=1) # Allow the column to expand
|
81
|
+
|
82
|
+
# Create a custom frame with a translucent background and rounded edges
|
83
|
+
custom_frame = tk.Frame(frame, bg=style_out['bg_color'], bd=2, relief='solid', width=size_dict['settings_width'])
|
84
|
+
custom_frame.grid(column=label_column, row=row, sticky=tk.EW, padx=(5, 5), pady=5)
|
85
|
+
|
86
|
+
# Apply styles to custom frame
|
87
|
+
custom_frame.update_idletasks()
|
88
|
+
custom_frame.config(highlightbackground=style_out['bg_color'], highlightthickness=1, bd=2)
|
89
|
+
#custom_frame.bind("<Enter>", lambda e: custom_frame.config(highlightbackground=style_out['active_color']))
|
90
|
+
#custom_frame.bind("<Leave>", lambda e: custom_frame.config(highlightbackground="white"))
|
102
91
|
|
103
|
-
#
|
104
|
-
label = ttk.Label(
|
105
|
-
label.grid(column=label_column, row=
|
92
|
+
# Create and configure the label
|
93
|
+
label = ttk.Label(custom_frame, text=label_text, background=style_out['bg_color'], foreground=style_out['fg_color'], font=(style_out['font_family'], style_out['font_size']), anchor='e', justify='right')
|
94
|
+
label.grid(column=label_column, row=0, sticky=tk.W, padx=(5, 2), pady=5) # Place the label in the first row
|
106
95
|
|
96
|
+
# Create and configure the input widget based on var_type
|
107
97
|
if var_type == 'entry':
|
108
|
-
var = tk.StringVar(value=default_value)
|
109
|
-
entry = spacrEntry(
|
110
|
-
entry.grid(column=widget_column, row=
|
111
|
-
return (label, entry, var) # Return both the label and the entry, and the variable
|
98
|
+
var = tk.StringVar(value=default_value)
|
99
|
+
entry = spacrEntry(custom_frame, textvariable=var, outline=False, width=size_dict['settings_width'])
|
100
|
+
entry.grid(column=widget_column, row=1, sticky=tk.W, padx=(2, 5), pady=5) # Place the entry in the second row
|
101
|
+
return (label, entry, var, custom_frame) # Return both the label and the entry, and the variable
|
112
102
|
elif var_type == 'check':
|
113
103
|
var = tk.BooleanVar(value=default_value) # Set default value (True/False)
|
114
|
-
check = spacrCheck(
|
115
|
-
check.grid(column=widget_column, row=
|
116
|
-
return (label, check, var) # Return both the label and the checkbutton, and the variable
|
104
|
+
check = spacrCheck(custom_frame, text="", variable=var)
|
105
|
+
check.grid(column=widget_column, row=1, sticky=tk.W, padx=(2, 5), pady=5) # Place the checkbutton in the second row
|
106
|
+
return (label, check, var, custom_frame) # Return both the label and the checkbutton, and the variable
|
117
107
|
elif var_type == 'combo':
|
118
108
|
var = tk.StringVar(value=default_value) # Set default value
|
119
|
-
combo = spacrCombo(
|
120
|
-
combo.grid(column=widget_column, row=
|
109
|
+
combo = spacrCombo(custom_frame, textvariable=var, values=options, width=size_dict['settings_width']) # Apply TCombobox style
|
110
|
+
combo.grid(column=widget_column, row=1, sticky=tk.W, padx=(2, 5), pady=5) # Place the combobox in the second row
|
121
111
|
if default_value:
|
122
112
|
combo.set(default_value)
|
123
|
-
return (label, combo, var) # Return both the label and the combobox, and the variable
|
113
|
+
return (label, combo, var, custom_frame) # Return both the label and the combobox, and the variable
|
124
114
|
else:
|
125
115
|
var = None # Placeholder in case of an undefined var_type
|
126
|
-
return (label, None, var)
|
116
|
+
return (label, None, var, custom_frame)
|
127
117
|
|
128
118
|
def process_stdout_stderr(q):
|
129
119
|
"""
|
@@ -139,10 +129,9 @@ class WriteToQueue(io.TextIOBase):
|
|
139
129
|
"""
|
140
130
|
def __init__(self, q):
|
141
131
|
self.q = q
|
142
|
-
|
143
132
|
def write(self, msg):
|
144
|
-
|
145
|
-
|
133
|
+
if msg.strip(): # Avoid empty messages
|
134
|
+
self.q.put(msg)
|
146
135
|
def flush(self):
|
147
136
|
pass
|
148
137
|
|
@@ -157,23 +146,6 @@ def main_thread_update_function(root, q, fig_queue, canvas_widget):
|
|
157
146
|
#ansi_escape_pattern = re.compile(r'\x1B\[[0-?]*[ -/]*[@-~]')
|
158
147
|
while not q.empty():
|
159
148
|
message = q.get_nowait()
|
160
|
-
#clean_message = ansi_escape_pattern.sub('', message)
|
161
|
-
#if clean_message.startswith("Progress"):
|
162
|
-
# progress_label.config(text=clean_message)
|
163
|
-
#if clean_message.startswith("\rProgress"):
|
164
|
-
# progress_label.config(text=clean_message)
|
165
|
-
#elif clean_message.startswith("Successfully"):
|
166
|
-
# progress_label.config(text=clean_message)
|
167
|
-
#elif clean_message.startswith("Processing"):
|
168
|
-
# progress_label.config(text=clean_message)
|
169
|
-
#elif clean_message.startswith("scale"):
|
170
|
-
# pass
|
171
|
-
#elif clean_message.startswith("plot_cropped_arrays"):
|
172
|
-
# pass
|
173
|
-
#elif clean_message == "" or clean_message == "\r" or clean_message.strip() == "":
|
174
|
-
# pass
|
175
|
-
#else:
|
176
|
-
# print(clean_message)
|
177
149
|
except Exception as e:
|
178
150
|
print(f"Error updating GUI canvas: {e}")
|
179
151
|
finally:
|
@@ -349,4 +321,320 @@ def annotate_with_image_refs(settings, root, shutdown_callback):
|
|
349
321
|
# Call load_images after setting up the root window
|
350
322
|
app.load_images()
|
351
323
|
|
324
|
+
def set_element_size():
|
325
|
+
|
326
|
+
screen_width, screen_height = pyautogui.size()
|
327
|
+
screen_area = screen_width * screen_height
|
328
|
+
|
329
|
+
# Calculate sizes based on screen dimensions
|
330
|
+
btn_size = int((screen_area * 0.002) ** 0.5) # Button size as a fraction of screen area
|
331
|
+
bar_size = screen_height // 30 # Bar size based on screen height
|
332
|
+
settings_width = screen_width // 4 # Settings panel width as a fraction of screen width
|
333
|
+
panel_width = screen_width - settings_width # Panel width as a fraction of screen width
|
334
|
+
panel_height = screen_height // 10 # Panel height as a fraction of screen height
|
335
|
+
|
336
|
+
size_dict = {
|
337
|
+
'btn_size': btn_size,
|
338
|
+
'bar_size': bar_size,
|
339
|
+
'settings_width': settings_width,
|
340
|
+
'panel_width': panel_width,
|
341
|
+
'panel_height': panel_height
|
342
|
+
}
|
343
|
+
return size_dict
|
344
|
+
|
345
|
+
def convert_settings_dict_for_gui(settings):
|
346
|
+
from torchvision import models as torch_models
|
347
|
+
torchvision_models = [name for name, obj in torch_models.__dict__.items() if callable(obj)]
|
348
|
+
chans = ['0', '1', '2', '3', '4', '5', '6', '7', '8', None]
|
349
|
+
chans_v2 = [0, 1, 2, 3, None]
|
350
|
+
variables = {}
|
351
|
+
special_cases = {
|
352
|
+
'metadata_type': ('combo', ['cellvoyager', 'cq1', 'nikon', 'zeis', 'custom'], 'cellvoyager'),
|
353
|
+
'channels': ('combo', ['[0,1,2,3]', '[0,1,2]', '[0,1]', '[0]'], '[0,1,2,3]'),
|
354
|
+
'channel_dims': ('combo', ['[0,1,2,3]', '[0,1,2]', '[0,1]', '[0]'], '[0,1,2,3]'),
|
355
|
+
'cell_mask_dim': ('combo', chans, None),
|
356
|
+
'cell_chann_dim': ('combo', chans, None),
|
357
|
+
'nucleus_mask_dim': ('combo', chans, None),
|
358
|
+
'nucleus_chann_dim': ('combo', chans, None),
|
359
|
+
'pathogen_mask_dim': ('combo', chans, None),
|
360
|
+
'pathogen_chann_dim': ('combo', chans, None),
|
361
|
+
'crop_mode': ('combo', ['cell', 'nucleus', 'pathogen', '[cell, nucleus, pathogen]', '[cell,nucleus, pathogen]'], ['cell']),
|
362
|
+
'magnification': ('combo', [20, 40, 60], 20),
|
363
|
+
'nucleus_channel': ('combo', chans_v2, None),
|
364
|
+
'cell_channel': ('combo', chans_v2, None),
|
365
|
+
'channel_of_interest': ('combo', chans_v2, None),
|
366
|
+
'pathogen_channel': ('combo', chans_v2, None),
|
367
|
+
'timelapse_mode': ('combo', ['trackpy', 'btrack'], 'trackpy'),
|
368
|
+
'train_mode': ('combo', ['erm', 'irm'], 'erm'),
|
369
|
+
'clustering': ('combo', ['dbscan', 'kmean'], 'dbscan'),
|
370
|
+
'reduction_method': ('combo', ['umap', 'tsne'], 'umap'),
|
371
|
+
'model_name': ('combo', ['cyto', 'cyto_2', 'cyto_3', 'nuclei'], 'cyto'),
|
372
|
+
'regression_type': ('combo', ['ols','gls','wls','rlm','glm','mixed','quantile','logit','probit','poisson','lasso','ridge'], 'ols'),
|
373
|
+
'timelapse_objects': ('combo', ['cell', 'nucleus', 'pathogen', 'cytoplasm', None], None),
|
374
|
+
'model_type': ('combo', torchvision_models, 'resnet50'),
|
375
|
+
'optimizer_type': ('combo', ['adamw', 'adam'], 'adamw'),
|
376
|
+
'schedule': ('combo', ['reduce_lr_on_plateau', 'step_lr'], 'reduce_lr_on_plateau'),
|
377
|
+
'loss_type': ('combo', ['focal_loss', 'binary_cross_entropy_with_logits'], 'focal_loss'),
|
378
|
+
'normalize_by': ('combo', ['fov', 'png'], 'png'),
|
379
|
+
'agg_type': ('combo', ['mean', 'median'], 'mean'),
|
380
|
+
'grouping': ('combo', ['mean', 'median'], 'mean'),
|
381
|
+
'min_max': ('combo', ['allq', 'all'], 'allq'),
|
382
|
+
'transform': ('combo', ['log', 'sqrt', 'square', None], None)
|
383
|
+
}
|
384
|
+
|
385
|
+
for key, value in settings.items():
|
386
|
+
if key in special_cases:
|
387
|
+
variables[key] = special_cases[key]
|
388
|
+
elif isinstance(value, bool):
|
389
|
+
variables[key] = ('check', None, value)
|
390
|
+
elif isinstance(value, int) or isinstance(value, float):
|
391
|
+
variables[key] = ('entry', None, value)
|
392
|
+
elif isinstance(value, str):
|
393
|
+
variables[key] = ('entry', None, value)
|
394
|
+
elif value is None:
|
395
|
+
variables[key] = ('entry', None, value)
|
396
|
+
elif isinstance(value, list):
|
397
|
+
variables[key] = ('entry', None, str(value))
|
398
|
+
else:
|
399
|
+
variables[key] = ('entry', None, str(value))
|
400
|
+
return variables
|
401
|
+
|
402
|
+
|
403
|
+
def spacrFigShow(fig_queue=None):
|
404
|
+
"""
|
405
|
+
Replacement for plt.show() that queues figures instead of displaying them.
|
406
|
+
"""
|
407
|
+
fig = plt.gcf()
|
408
|
+
if fig_queue:
|
409
|
+
fig_queue.put(fig)
|
410
|
+
else:
|
411
|
+
fig.show()
|
412
|
+
plt.close(fig)
|
352
413
|
|
414
|
+
def function_gui_wrapper(function=None, settings={}, q=None, fig_queue=None, imports=1):
|
415
|
+
|
416
|
+
"""
|
417
|
+
Wraps the run_multiple_simulations function to integrate with GUI processes.
|
418
|
+
|
419
|
+
Parameters:
|
420
|
+
- settings: dict, The settings for the run_multiple_simulations function.
|
421
|
+
- q: multiprocessing.Queue, Queue for logging messages to the GUI.
|
422
|
+
- fig_queue: multiprocessing.Queue, Queue for sending figures to the GUI.
|
423
|
+
"""
|
424
|
+
|
425
|
+
# Temporarily override plt.show
|
426
|
+
original_show = plt.show
|
427
|
+
plt.show = lambda: spacrFigShow(fig_queue)
|
428
|
+
|
429
|
+
try:
|
430
|
+
if imports == 1:
|
431
|
+
function(settings=settings)
|
432
|
+
elif imports == 2:
|
433
|
+
function(src=settings['src'], settings=settings)
|
434
|
+
except Exception as e:
|
435
|
+
# Send the error message to the GUI via the queue
|
436
|
+
errorMessage = f"Error during processing: {e}"
|
437
|
+
q.put(errorMessage)
|
438
|
+
traceback.print_exc()
|
439
|
+
finally:
|
440
|
+
# Restore the original plt.show function
|
441
|
+
plt.show = original_show
|
442
|
+
|
443
|
+
|
444
|
+
def run_function_gui(settings_type, settings, q, fig_queue, stop_requested):
|
445
|
+
from .gui_utils import process_stdout_stderr
|
446
|
+
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
|
447
|
+
from .io import generate_cellpose_train_test
|
448
|
+
from .measure import measure_crop
|
449
|
+
from .sim import run_multiple_simulations
|
450
|
+
from .deep_spacr import train_test_model
|
451
|
+
from .sequencing import analyze_reads, map_barcodes_folder, perform_regression
|
452
|
+
process_stdout_stderr(q)
|
453
|
+
|
454
|
+
print(f'run_function_gui settings_type: {settings_type}')
|
455
|
+
|
456
|
+
if settings_type == 'mask':
|
457
|
+
function = preprocess_generate_masks
|
458
|
+
imports = 2
|
459
|
+
elif settings_type == 'measure':
|
460
|
+
function = measure_crop
|
461
|
+
imports = 1
|
462
|
+
elif settings_type == 'simulation':
|
463
|
+
function = run_multiple_simulations
|
464
|
+
imports = 1
|
465
|
+
elif settings_type == 'sequencing':
|
466
|
+
function = analyze_reads
|
467
|
+
imports = 1
|
468
|
+
elif settings_type == 'classify':
|
469
|
+
function = train_test_model
|
470
|
+
imports = 2
|
471
|
+
elif settings_type == 'train_cellpose':
|
472
|
+
function = train_cellpose
|
473
|
+
imports = 1
|
474
|
+
elif settings_type == 'ml_analyze':
|
475
|
+
function = generate_ml_scores
|
476
|
+
imports = 2
|
477
|
+
elif settings_type == 'cellpose_masks':
|
478
|
+
function = identify_masks_finetune
|
479
|
+
imports = 1
|
480
|
+
elif settings_type == 'cellpose_all':
|
481
|
+
function = check_cellpose_models
|
482
|
+
imports = 1
|
483
|
+
elif settings_type == 'map_barcodes':
|
484
|
+
function = map_barcodes_folder
|
485
|
+
imports = 2
|
486
|
+
elif settings_type == 'regression':
|
487
|
+
function = perform_regression
|
488
|
+
imports = 2
|
489
|
+
elif settings_type == 'recruitment':
|
490
|
+
function = analyze_recruitment
|
491
|
+
imports = 2
|
492
|
+
else:
|
493
|
+
raise ValueError(f"Invalid settings type: {settings_type}")
|
494
|
+
try:
|
495
|
+
function_gui_wrapper(function, settings, q, fig_queue, imports)
|
496
|
+
except Exception as e:
|
497
|
+
q.put(f"Error during processing: {e}")
|
498
|
+
traceback.print_exc()
|
499
|
+
finally:
|
500
|
+
stop_requested.value = 1
|
501
|
+
|
502
|
+
|
503
|
+
def hide_all_settings(vars_dict, categories):
|
504
|
+
"""
|
505
|
+
Function to initially hide all settings in the GUI.
|
506
|
+
|
507
|
+
Parameters:
|
508
|
+
- categories: dict, The categories of settings with their corresponding settings.
|
509
|
+
- vars_dict: dict, The dictionary containing the settings and their corresponding widgets.
|
510
|
+
"""
|
511
|
+
|
512
|
+
if categories is None:
|
513
|
+
from .settings import categories
|
514
|
+
|
515
|
+
for category, settings in categories.items():
|
516
|
+
if any(setting in vars_dict for setting in settings):
|
517
|
+
vars_dict[category] = (None, None, tk.IntVar(value=0), None)
|
518
|
+
|
519
|
+
# Initially hide all settings
|
520
|
+
for setting in settings:
|
521
|
+
if setting in vars_dict:
|
522
|
+
label, widget, _, frame = vars_dict[setting]
|
523
|
+
label.grid_remove()
|
524
|
+
widget.grid_remove()
|
525
|
+
frame.grid_remove()
|
526
|
+
return vars_dict
|
527
|
+
|
528
|
+
def setup_frame(parent_frame):
|
529
|
+
from .gui_elements import set_dark_style, set_default_font
|
530
|
+
|
531
|
+
style = ttk.Style(parent_frame)
|
532
|
+
size_dict = set_element_size()
|
533
|
+
style_out = set_dark_style(style)
|
534
|
+
|
535
|
+
settings_container = tk.PanedWindow(parent_frame, orient=tk.VERTICAL, width=size_dict['settings_width'], bg=style_out['bg_color'])
|
536
|
+
vertical_container = tk.PanedWindow(parent_frame, orient=tk.VERTICAL, width=size_dict['panel_width'], bg=style_out['bg_color'])
|
537
|
+
horizontal_container = tk.PanedWindow(parent_frame, orient=tk.HORIZONTAL, height=size_dict['panel_height'], width=size_dict['panel_width'], bg=style_out['bg_color'])
|
538
|
+
|
539
|
+
parent_frame.grid_rowconfigure(0, weight=1)
|
540
|
+
parent_frame.grid_rowconfigure(1, weight=0)
|
541
|
+
parent_frame.grid_columnconfigure(0, weight=0)
|
542
|
+
parent_frame.grid_columnconfigure(1, weight=1)
|
543
|
+
|
544
|
+
settings_container.grid(row=0, column=0, rowspan=2, sticky="nsew")
|
545
|
+
vertical_container.grid(row=0, column=1, sticky="nsew")
|
546
|
+
horizontal_container.grid(row=1, column=1, sticky="ew")
|
547
|
+
|
548
|
+
# Ensure settings_container maintains its width
|
549
|
+
settings_container.grid_propagate(False)
|
550
|
+
settings_container.update_idletasks()
|
551
|
+
|
552
|
+
tk.Label(settings_container, text="Settings Container", bg=style_out['bg_color']).grid(row=0, column=0, sticky="ew")
|
553
|
+
|
554
|
+
set_dark_style(style, parent_frame, [settings_container, vertical_container, horizontal_container])
|
555
|
+
|
556
|
+
size = style_out['font_size'] - 2
|
557
|
+
set_default_font(parent_frame, font_name=style_out['font_family'], size=size)
|
558
|
+
|
559
|
+
return parent_frame, vertical_container, horizontal_container, settings_container
|
560
|
+
|
561
|
+
def download_hug_dataset(q, vars_dict):
|
562
|
+
dataset_repo_id = "einarolafsson/toxo_mito"
|
563
|
+
settings_repo_id = "einarolafsson/spacr_settings"
|
564
|
+
dataset_subfolder = "plate1"
|
565
|
+
local_dir = os.path.join(os.path.expanduser("~"), "datasets")
|
566
|
+
|
567
|
+
# Download the dataset
|
568
|
+
try:
|
569
|
+
dataset_path = download_dataset(q, dataset_repo_id, dataset_subfolder, local_dir)
|
570
|
+
if 'src' in vars_dict:
|
571
|
+
vars_dict['src'][2].set(dataset_path)
|
572
|
+
q.put(f"Set source path to: {vars_dict['src'][2].get()}\n")
|
573
|
+
q.put(f"Dataset downloaded to: {dataset_path}\n")
|
574
|
+
except Exception as e:
|
575
|
+
q.put(f"Failed to download dataset: {e}\n")
|
576
|
+
|
577
|
+
# Download the settings files
|
578
|
+
try:
|
579
|
+
settings_path = download_dataset(q, settings_repo_id, "", local_dir)
|
580
|
+
q.put(f"Settings downloaded to: {settings_path}\n")
|
581
|
+
except Exception as e:
|
582
|
+
q.put(f"Failed to download settings: {e}\n")
|
583
|
+
|
584
|
+
def download_dataset(q, repo_id, subfolder, local_dir=None, retries=5, delay=5):
|
585
|
+
"""
|
586
|
+
Downloads a dataset or settings files from Hugging Face and returns the local path.
|
587
|
+
|
588
|
+
Args:
|
589
|
+
repo_id (str): The repository ID (e.g., 'einarolafsson/toxo_mito' or 'einarolafsson/spacr_settings').
|
590
|
+
subfolder (str): The subfolder path within the repository (e.g., 'plate1' or the settings subfolder).
|
591
|
+
local_dir (str): The local directory where the files will be saved. Defaults to the user's home directory.
|
592
|
+
retries (int): Number of retry attempts in case of failure.
|
593
|
+
delay (int): Delay in seconds between retries.
|
594
|
+
|
595
|
+
Returns:
|
596
|
+
str: The local path to the downloaded files.
|
597
|
+
"""
|
598
|
+
if local_dir is None:
|
599
|
+
local_dir = os.path.join(os.path.expanduser("~"), "datasets")
|
600
|
+
|
601
|
+
local_subfolder_dir = os.path.join(local_dir, subfolder if subfolder else "settings")
|
602
|
+
if not os.path.exists(local_subfolder_dir):
|
603
|
+
os.makedirs(local_subfolder_dir)
|
604
|
+
elif len(os.listdir(local_subfolder_dir)) > 0:
|
605
|
+
q.put(f"Files already downloaded to: {local_subfolder_dir}")
|
606
|
+
return local_subfolder_dir
|
607
|
+
|
608
|
+
attempt = 0
|
609
|
+
while attempt < retries:
|
610
|
+
try:
|
611
|
+
files = list_repo_files(repo_id, repo_type="dataset")
|
612
|
+
subfolder_files = [file for file in files if file.startswith(subfolder) or (subfolder == "" and file.endswith('.csv'))]
|
613
|
+
|
614
|
+
for file_name in subfolder_files:
|
615
|
+
for download_attempt in range(retries):
|
616
|
+
try:
|
617
|
+
url = f"https://huggingface.co/datasets/{repo_id}/resolve/main/{file_name}?download=true"
|
618
|
+
response = requests.get(url, stream=True)
|
619
|
+
response.raise_for_status()
|
620
|
+
|
621
|
+
local_file_path = os.path.join(local_subfolder_dir, os.path.basename(file_name))
|
622
|
+
with open(local_file_path, 'wb') as file:
|
623
|
+
for chunk in response.iter_content(chunk_size=8192):
|
624
|
+
file.write(chunk)
|
625
|
+
q.put(f"Downloaded file: {file_name}")
|
626
|
+
break
|
627
|
+
except (requests.HTTPError, requests.Timeout) as e:
|
628
|
+
q.put(f"Error downloading {file_name}: {e}. Retrying in {delay} seconds...")
|
629
|
+
time.sleep(delay)
|
630
|
+
else:
|
631
|
+
raise Exception(f"Failed to download {file_name} after multiple attempts.")
|
632
|
+
|
633
|
+
return local_subfolder_dir
|
634
|
+
|
635
|
+
except (requests.HTTPError, requests.Timeout) as e:
|
636
|
+
q.put(f"Error downloading files: {e}. Retrying in {delay} seconds...")
|
637
|
+
attempt += 1
|
638
|
+
time.sleep(delay)
|
639
|
+
|
640
|
+
raise Exception("Failed to download files after multiple attempts.")
|