spacr 0.2.5__py3-none-any.whl → 0.2.8__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/__init__.py +1 -11
- spacr/core.py +226 -287
- spacr/deep_spacr.py +248 -269
- spacr/gui.py +41 -19
- spacr/gui_core.py +404 -151
- spacr/gui_elements.py +778 -179
- spacr/gui_utils.py +163 -106
- spacr/io.py +116 -45
- spacr/measure.py +1 -0
- spacr/plot.py +51 -5
- spacr/sequencing.py +477 -587
- spacr/settings.py +211 -66
- spacr/utils.py +34 -14
- {spacr-0.2.5.dist-info → spacr-0.2.8.dist-info}/METADATA +46 -39
- {spacr-0.2.5.dist-info → spacr-0.2.8.dist-info}/RECORD +19 -19
- {spacr-0.2.5.dist-info → spacr-0.2.8.dist-info}/WHEEL +1 -1
- {spacr-0.2.5.dist-info → spacr-0.2.8.dist-info}/LICENSE +0 -0
- {spacr-0.2.5.dist-info → spacr-0.2.8.dist-info}/entry_points.txt +0 -0
- {spacr-0.2.5.dist-info → spacr-0.2.8.dist-info}/top_level.txt +0 -0
spacr/gui_utils.py
CHANGED
@@ -1,20 +1,48 @@
|
|
1
|
-
import os, io, sys, ast, ctypes, ast, sqlite3, requests, time, traceback,
|
2
|
-
import traceback
|
1
|
+
import os, io, sys, ast, ctypes, ast, sqlite3, requests, time, traceback, torch
|
3
2
|
import tkinter as tk
|
4
3
|
from tkinter import ttk
|
5
4
|
import matplotlib
|
6
5
|
import matplotlib.pyplot as plt
|
7
6
|
matplotlib.use('Agg')
|
8
7
|
from huggingface_hub import list_repo_files
|
9
|
-
import
|
8
|
+
import psutil
|
10
9
|
|
11
|
-
from .
|
12
|
-
from .gui_elements import AnnotateApp, spacrEntry, spacrCheck, spacrCombo, set_default_font
|
10
|
+
from .gui_elements import AnnotateApp, spacrEntry, spacrCheck, spacrCombo
|
13
11
|
|
14
12
|
try:
|
15
13
|
ctypes.windll.shcore.SetProcessDpiAwareness(True)
|
16
14
|
except AttributeError:
|
17
15
|
pass
|
16
|
+
|
17
|
+
def initialize_cuda():
|
18
|
+
"""
|
19
|
+
Initializes CUDA in the main process by performing a simple GPU operation.
|
20
|
+
"""
|
21
|
+
if torch.cuda.is_available():
|
22
|
+
# Allocate a small tensor on the GPU
|
23
|
+
_ = torch.tensor([0.0], device='cuda')
|
24
|
+
print("CUDA initialized in the main process.")
|
25
|
+
else:
|
26
|
+
print("CUDA is not available.")
|
27
|
+
|
28
|
+
def set_high_priority(process):
|
29
|
+
try:
|
30
|
+
p = psutil.Process(process.pid)
|
31
|
+
if os.name == 'nt': # Windows
|
32
|
+
p.nice(psutil.HIGH_PRIORITY_CLASS)
|
33
|
+
else: # Unix-like systems
|
34
|
+
p.nice(-10) # Adjusted priority level
|
35
|
+
print(f"Successfully set high priority for process: {process.pid}")
|
36
|
+
except psutil.AccessDenied as e:
|
37
|
+
print(f"Access denied when trying to set high priority for process {process.pid}: {e}")
|
38
|
+
except psutil.NoSuchProcess as e:
|
39
|
+
print(f"No such process {process.pid}: {e}")
|
40
|
+
except Exception as e:
|
41
|
+
print(f"Failed to set high priority for process {process.pid}: {e}")
|
42
|
+
|
43
|
+
def set_cpu_affinity(process):
|
44
|
+
p = psutil.Process(process.pid)
|
45
|
+
p.cpu_affinity(list(range(os.cpu_count())))
|
18
46
|
|
19
47
|
def proceed_with_app(root, app_name, app_func):
|
20
48
|
# Clear the current content frame
|
@@ -29,6 +57,10 @@ def proceed_with_app(root, app_name, app_func):
|
|
29
57
|
app_func(root.content_frame)
|
30
58
|
|
31
59
|
def load_app(root, app_name, app_func):
|
60
|
+
# Clear the canvas if it exists
|
61
|
+
if root.canvas is not None:
|
62
|
+
root.clear_frame(root.canvas)
|
63
|
+
|
32
64
|
# Cancel all scheduled after tasks
|
33
65
|
if hasattr(root, 'after_tasks'):
|
34
66
|
for task in root.after_tasks:
|
@@ -38,42 +70,72 @@ def load_app(root, app_name, app_func):
|
|
38
70
|
# Exit functionality only for the annotation and make_masks apps
|
39
71
|
if app_name not in ["Annotate", "make_masks"] and hasattr(root, 'current_app_exit_func'):
|
40
72
|
root.next_app_func = proceed_with_app
|
41
|
-
root.next_app_args = (app_name, app_func)
|
73
|
+
root.next_app_args = (app_name, app_func)
|
42
74
|
root.current_app_exit_func()
|
43
75
|
else:
|
44
76
|
proceed_with_app(root, app_name, app_func)
|
45
|
-
|
46
|
-
def parse_list_v1(value):
|
47
|
-
try:
|
48
|
-
parsed_value = ast.literal_eval(value)
|
49
|
-
if isinstance(parsed_value, list):
|
50
|
-
return parsed_value
|
51
|
-
else:
|
52
|
-
raise ValueError
|
53
|
-
except (ValueError, SyntaxError):
|
54
|
-
raise ValueError("Invalid format for list")
|
55
77
|
|
56
78
|
def parse_list(value):
|
79
|
+
"""
|
80
|
+
Parses a string representation of a list and returns the parsed list.
|
81
|
+
|
82
|
+
Args:
|
83
|
+
value (str): The string representation of the list.
|
84
|
+
|
85
|
+
Returns:
|
86
|
+
list: The parsed list.
|
87
|
+
|
88
|
+
Raises:
|
89
|
+
ValueError: If the input value is not a valid list format or contains mixed types or unsupported types.
|
90
|
+
"""
|
57
91
|
try:
|
58
92
|
parsed_value = ast.literal_eval(value)
|
59
93
|
if isinstance(parsed_value, list):
|
60
|
-
|
94
|
+
# Check if the list elements are homogeneous (all int or all str)
|
95
|
+
if all(isinstance(item, int) for item in parsed_value):
|
96
|
+
return parsed_value
|
97
|
+
elif all(isinstance(item, str) for item in parsed_value):
|
98
|
+
return parsed_value
|
99
|
+
else:
|
100
|
+
raise ValueError("List contains mixed types or unsupported types")
|
61
101
|
else:
|
62
102
|
raise ValueError(f"Expected a list but got {type(parsed_value).__name__}")
|
63
103
|
except (ValueError, SyntaxError) as e:
|
64
104
|
raise ValueError(f"Invalid format for list: {value}. Error: {e}")
|
65
|
-
|
105
|
+
|
66
106
|
# Usage example in your create_input_field function
|
67
107
|
def create_input_field(frame, label_text, row, var_type='entry', options=None, default_value=None):
|
68
|
-
|
108
|
+
"""
|
109
|
+
Create an input field in the specified frame.
|
110
|
+
|
111
|
+
Args:
|
112
|
+
frame (tk.Frame): The frame in which the input field will be created.
|
113
|
+
label_text (str): The text to be displayed as the label for the input field.
|
114
|
+
row (int): The row in which the input field will be placed.
|
115
|
+
var_type (str, optional): The type of input field to create. Defaults to 'entry'.
|
116
|
+
options (list, optional): The list of options for a combo box input field. Defaults to None.
|
117
|
+
default_value (str, optional): The default value for the input field. Defaults to None.
|
118
|
+
|
119
|
+
Returns:
|
120
|
+
tuple: A tuple containing the label, input widget, variable, and custom frame.
|
121
|
+
|
122
|
+
Raises:
|
123
|
+
Exception: If an error occurs while creating the input field.
|
124
|
+
|
125
|
+
"""
|
126
|
+
from .gui_elements import set_dark_style, set_element_size
|
127
|
+
|
69
128
|
label_column = 0
|
70
129
|
widget_column = 0 # Both label and widget will be in the same column
|
71
130
|
|
72
131
|
style_out = set_dark_style(ttk.Style())
|
132
|
+
font_loader = style_out['font_loader']
|
133
|
+
font_size = style_out['font_size']
|
73
134
|
size_dict = set_element_size()
|
74
135
|
size_dict['settings_width'] = size_dict['settings_width'] - int(size_dict['settings_width']*0.1)
|
75
136
|
|
76
137
|
# Replace underscores with spaces and capitalize the first letter
|
138
|
+
|
77
139
|
label_text = label_text.replace('_', ' ').capitalize()
|
78
140
|
|
79
141
|
# Configure the column widths
|
@@ -86,34 +148,37 @@ def create_input_field(frame, label_text, row, var_type='entry', options=None, d
|
|
86
148
|
# Apply styles to custom frame
|
87
149
|
custom_frame.update_idletasks()
|
88
150
|
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"))
|
91
151
|
|
92
152
|
# Create and configure the label
|
93
|
-
label =
|
153
|
+
label = tk.Label(custom_frame, text=label_text, bg=style_out['bg_color'], fg=style_out['fg_color'], font=font_loader.get_font(size=font_size), anchor='e', justify='right')
|
94
154
|
label.grid(column=label_column, row=0, sticky=tk.W, padx=(5, 2), pady=5) # Place the label in the first row
|
95
155
|
|
96
156
|
# Create and configure the input widget based on var_type
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
157
|
+
try:
|
158
|
+
if var_type == 'entry':
|
159
|
+
var = tk.StringVar(value=default_value)
|
160
|
+
entry = spacrEntry(custom_frame, textvariable=var, outline=False, width=size_dict['settings_width'])
|
161
|
+
entry.grid(column=widget_column, row=1, sticky=tk.W, padx=(2, 5), pady=5) # Place the entry in the second row
|
162
|
+
return (label, entry, var, custom_frame) # Return both the label and the entry, and the variable
|
163
|
+
elif var_type == 'check':
|
164
|
+
var = tk.BooleanVar(value=default_value) # Set default value (True/False)
|
165
|
+
check = spacrCheck(custom_frame, text="", variable=var)
|
166
|
+
check.grid(column=widget_column, row=1, sticky=tk.W, padx=(2, 5), pady=5) # Place the checkbutton in the second row
|
167
|
+
return (label, check, var, custom_frame) # Return both the label and the checkbutton, and the variable
|
168
|
+
elif var_type == 'combo':
|
169
|
+
var = tk.StringVar(value=default_value) # Set default value
|
170
|
+
combo = spacrCombo(custom_frame, textvariable=var, values=options, width=size_dict['settings_width']) # Apply TCombobox style
|
171
|
+
combo.grid(column=widget_column, row=1, sticky=tk.W, padx=(2, 5), pady=5) # Place the combobox in the second row
|
172
|
+
if default_value:
|
173
|
+
combo.set(default_value)
|
174
|
+
return (label, combo, var, custom_frame) # Return both the label and the combobox, and the variable
|
175
|
+
else:
|
176
|
+
var = None # Placeholder in case of an undefined var_type
|
177
|
+
return (label, None, var, custom_frame)
|
178
|
+
except Exception as e:
|
179
|
+
traceback.print_exc()
|
180
|
+
print(f"Error creating input field: {e}")
|
181
|
+
print(f"Wrong type for {label_text} Expected {var_type}")
|
117
182
|
|
118
183
|
def process_stdout_stderr(q):
|
119
184
|
"""
|
@@ -141,16 +206,6 @@ def cancel_after_tasks(frame):
|
|
141
206
|
frame.after_cancel(task)
|
142
207
|
frame.after_tasks.clear()
|
143
208
|
|
144
|
-
def main_thread_update_function(root, q, fig_queue, canvas_widget):
|
145
|
-
try:
|
146
|
-
#ansi_escape_pattern = re.compile(r'\x1B\[[0-?]*[ -/]*[@-~]')
|
147
|
-
while not q.empty():
|
148
|
-
message = q.get_nowait()
|
149
|
-
except Exception as e:
|
150
|
-
print(f"Error updating GUI canvas: {e}")
|
151
|
-
finally:
|
152
|
-
root.after(100, lambda: main_thread_update_function(root, q, fig_queue, canvas_widget))
|
153
|
-
|
154
209
|
def annotate(settings):
|
155
210
|
from .settings import set_annotate_default_settings
|
156
211
|
settings = set_annotate_default_settings(settings)
|
@@ -181,6 +236,12 @@ def annotate(settings):
|
|
181
236
|
|
182
237
|
def generate_annotate_fields(frame):
|
183
238
|
from .settings import set_annotate_default_settings
|
239
|
+
from .gui_elements import set_dark_style
|
240
|
+
|
241
|
+
style_out = set_dark_style(ttk.Style())
|
242
|
+
font_loader = style_out['font_loader']
|
243
|
+
font_size = style_out['font_size'] - 2
|
244
|
+
|
184
245
|
vars_dict = {}
|
185
246
|
settings = set_annotate_default_settings(settings={})
|
186
247
|
|
@@ -192,8 +253,8 @@ def generate_annotate_fields(frame):
|
|
192
253
|
|
193
254
|
# Arrange input fields and labels
|
194
255
|
for row, (name, data) in enumerate(vars_dict.items()):
|
195
|
-
|
196
|
-
|
256
|
+
tk.Label(frame, text=f"{name.replace('_', ' ').capitalize()}:", bg=style_out['bg_color'], fg=style_out['fg_color'], font=font_loader.get_font(size=font_size)).grid(row=row, column=0)
|
257
|
+
#ttk.Label(frame, text=f"{name.replace('_', ' ').capitalize()}:", background="black", foreground="white").grid(row=row, column=0)
|
197
258
|
if isinstance(data['value'], list):
|
198
259
|
# Convert lists to comma-separated strings
|
199
260
|
data['entry'].insert(0, ','.join(map(str, data['value'])))
|
@@ -321,27 +382,6 @@ def annotate_with_image_refs(settings, root, shutdown_callback):
|
|
321
382
|
# Call load_images after setting up the root window
|
322
383
|
app.load_images()
|
323
384
|
|
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
385
|
def convert_settings_dict_for_gui(settings):
|
346
386
|
from torchvision import models as torch_models
|
347
387
|
torchvision_models = [name for name, obj in torch_models.__dict__.items() if callable(obj)]
|
@@ -351,7 +391,9 @@ def convert_settings_dict_for_gui(settings):
|
|
351
391
|
special_cases = {
|
352
392
|
'metadata_type': ('combo', ['cellvoyager', 'cq1', 'nikon', 'zeis', 'custom'], 'cellvoyager'),
|
353
393
|
'channels': ('combo', ['[0,1,2,3]', '[0,1,2]', '[0,1]', '[0]'], '[0,1,2,3]'),
|
394
|
+
'train_channels': ('combo', ["['r','g','b']", "['r','g']", "['r','b']", "['g','b']", "['r']", "['g']", "['b']"], "['r','g','b']"),
|
354
395
|
'channel_dims': ('combo', ['[0,1,2,3]', '[0,1,2]', '[0,1]', '[0]'], '[0,1,2,3]'),
|
396
|
+
'dataset_mode': ('combo', ['annotation', 'metadata', 'recruitment'], 'metadata'),
|
355
397
|
'cell_mask_dim': ('combo', chans, None),
|
356
398
|
'cell_chann_dim': ('combo', chans, None),
|
357
399
|
'nucleus_mask_dim': ('combo', chans, None),
|
@@ -397,6 +439,7 @@ def convert_settings_dict_for_gui(settings):
|
|
397
439
|
variables[key] = ('entry', None, str(value))
|
398
440
|
else:
|
399
441
|
variables[key] = ('entry', None, str(value))
|
442
|
+
|
400
443
|
return variables
|
401
444
|
|
402
445
|
|
@@ -440,15 +483,15 @@ def function_gui_wrapper(function=None, settings={}, q=None, fig_queue=None, imp
|
|
440
483
|
# Restore the original plt.show function
|
441
484
|
plt.show = original_show
|
442
485
|
|
443
|
-
|
444
486
|
def run_function_gui(settings_type, settings, q, fig_queue, stop_requested):
|
487
|
+
|
445
488
|
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
|
489
|
+
from .core import generate_image_umap, 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
490
|
from .io import generate_cellpose_train_test
|
448
491
|
from .measure import measure_crop
|
449
492
|
from .sim import run_multiple_simulations
|
450
|
-
from .deep_spacr import
|
451
|
-
from .sequencing import
|
493
|
+
from .deep_spacr import deep_spacr
|
494
|
+
from .sequencing import generate_barecode_mapping, perform_regression
|
452
495
|
process_stdout_stderr(q)
|
453
496
|
|
454
497
|
print(f'run_function_gui settings_type: {settings_type}')
|
@@ -462,12 +505,9 @@ def run_function_gui(settings_type, settings, q, fig_queue, stop_requested):
|
|
462
505
|
elif settings_type == 'simulation':
|
463
506
|
function = run_multiple_simulations
|
464
507
|
imports = 1
|
465
|
-
elif settings_type == 'sequencing':
|
466
|
-
function = analyze_reads
|
467
|
-
imports = 1
|
468
508
|
elif settings_type == 'classify':
|
469
|
-
function =
|
470
|
-
imports =
|
509
|
+
function = deep_spacr
|
510
|
+
imports = 1
|
471
511
|
elif settings_type == 'train_cellpose':
|
472
512
|
function = train_cellpose
|
473
513
|
imports = 1
|
@@ -481,14 +521,17 @@ def run_function_gui(settings_type, settings, q, fig_queue, stop_requested):
|
|
481
521
|
function = check_cellpose_models
|
482
522
|
imports = 1
|
483
523
|
elif settings_type == 'map_barcodes':
|
484
|
-
function =
|
485
|
-
imports =
|
524
|
+
function = generate_barecode_mapping
|
525
|
+
imports = 1
|
486
526
|
elif settings_type == 'regression':
|
487
527
|
function = perform_regression
|
488
528
|
imports = 2
|
489
529
|
elif settings_type == 'recruitment':
|
490
530
|
function = analyze_recruitment
|
491
|
-
imports =
|
531
|
+
imports = 1
|
532
|
+
elif settings_type == 'umap':
|
533
|
+
function = generate_image_umap
|
534
|
+
imports = 1
|
492
535
|
else:
|
493
536
|
raise ValueError(f"Invalid settings type: {settings_type}")
|
494
537
|
try:
|
@@ -499,7 +542,6 @@ def run_function_gui(settings_type, settings, q, fig_queue, stop_requested):
|
|
499
542
|
finally:
|
500
543
|
stop_requested.value = 1
|
501
544
|
|
502
|
-
|
503
545
|
def hide_all_settings(vars_dict, categories):
|
504
546
|
"""
|
505
547
|
Function to initially hide all settings in the GUI.
|
@@ -526,38 +568,49 @@ def hide_all_settings(vars_dict, categories):
|
|
526
568
|
return vars_dict
|
527
569
|
|
528
570
|
def setup_frame(parent_frame):
|
529
|
-
from .gui_elements import set_dark_style,
|
571
|
+
from .gui_elements import set_dark_style, set_element_size
|
530
572
|
|
531
573
|
style = ttk.Style(parent_frame)
|
532
574
|
size_dict = set_element_size()
|
533
575
|
style_out = set_dark_style(style)
|
534
576
|
|
535
|
-
|
536
|
-
|
537
|
-
|
577
|
+
# Configure the main layout using PanedWindow
|
578
|
+
main_paned = tk.PanedWindow(parent_frame, orient=tk.HORIZONTAL, bg=style_out['bg_color'], bd=0, relief='flat')
|
579
|
+
main_paned.grid(row=0, column=0, sticky="nsew")
|
538
580
|
|
581
|
+
# Allow the main_paned to expand and fill the window
|
539
582
|
parent_frame.grid_rowconfigure(0, weight=1)
|
540
|
-
parent_frame.
|
541
|
-
parent_frame.grid_columnconfigure(0, weight=0)
|
542
|
-
parent_frame.grid_columnconfigure(1, weight=1)
|
583
|
+
parent_frame.grid_columnconfigure(0, weight=1)
|
543
584
|
|
544
|
-
|
545
|
-
|
546
|
-
|
585
|
+
# Create the settings container on the left
|
586
|
+
settings_container = tk.PanedWindow(main_paned, orient=tk.VERTICAL, width=size_dict['settings_width'], bg=style_out['bg_color'], bd=0, relief='flat')
|
587
|
+
main_paned.add(settings_container, minsize=100) # Allow resizing with a minimum size
|
547
588
|
|
548
|
-
#
|
549
|
-
|
550
|
-
|
589
|
+
# Create a right container frame to hold vertical and horizontal containers
|
590
|
+
right_frame = tk.Frame(main_paned, bg=style_out['bg_color'], bd=0, highlightthickness=0, relief='flat')
|
591
|
+
main_paned.add(right_frame, stretch="always")
|
551
592
|
|
552
|
-
|
593
|
+
# Configure the right_frame grid layout
|
594
|
+
right_frame.grid_rowconfigure(0, weight=1) # Vertical container expands
|
595
|
+
right_frame.grid_rowconfigure(1, weight=0) # Horizontal container at bottom
|
596
|
+
right_frame.grid_columnconfigure(0, weight=1)
|
553
597
|
|
554
|
-
|
555
|
-
|
556
|
-
|
557
|
-
|
598
|
+
# Inside right_frame, add vertical_container at the top
|
599
|
+
vertical_container = tk.PanedWindow(right_frame, orient=tk.VERTICAL, bg=style_out['bg_color'], bd=0, relief='flat')
|
600
|
+
vertical_container.grid(row=0, column=0, sticky="nsew")
|
601
|
+
|
602
|
+
# Add horizontal_container aligned with the bottom of settings_container
|
603
|
+
horizontal_container = tk.PanedWindow(right_frame, orient=tk.HORIZONTAL, height=size_dict['panel_height'], bg=style_out['bg_color'], bd=0, relief='flat')
|
604
|
+
horizontal_container.grid(row=1, column=0, sticky="ew")
|
605
|
+
|
606
|
+
# Example content for settings_container
|
607
|
+
tk.Label(settings_container, text="Settings Container", bg=style_out['bg_color']).pack(fill=tk.BOTH, expand=True)
|
608
|
+
|
609
|
+
set_dark_style(style, parent_frame, [settings_container, vertical_container, horizontal_container, main_paned])
|
558
610
|
|
559
611
|
return parent_frame, vertical_container, horizontal_container, settings_container
|
560
612
|
|
613
|
+
|
561
614
|
def download_hug_dataset(q, vars_dict):
|
562
615
|
dataset_repo_id = "einarolafsson/toxo_mito"
|
563
616
|
settings_repo_id = "einarolafsson/spacr_settings"
|
@@ -638,3 +691,7 @@ def download_dataset(q, repo_id, subfolder, local_dir=None, retries=5, delay=5):
|
|
638
691
|
time.sleep(delay)
|
639
692
|
|
640
693
|
raise Exception("Failed to download files after multiple attempts.")
|
694
|
+
|
695
|
+
def ensure_after_tasks(frame):
|
696
|
+
if not hasattr(frame, 'after_tasks'):
|
697
|
+
frame.after_tasks = []
|