spacr 0.2.32__py3-none-any.whl → 0.2.45__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/gui_core.py CHANGED
@@ -1,6 +1,4 @@
1
- import os, traceback, ctypes, matplotlib, requests, csv, matplotlib, time, requests, re
2
- import matplotlib.pyplot as plt
3
- matplotlib.use('Agg')
1
+ import os, traceback, ctypes, requests, csv, time, requests, re
4
2
  import tkinter as tk
5
3
  from tkinter import ttk
6
4
  from tkinter import filedialog
@@ -9,15 +7,17 @@ from multiprocessing.sharedctypes import Synchronized
9
7
  from tkinter import ttk, scrolledtext
10
8
  from matplotlib.figure import Figure
11
9
  from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
12
- from huggingface_hub import list_repo_files
13
10
  import numpy as np
11
+ import psutil
12
+ import GPUtil
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, set_default_font
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,92 @@ fig_queue = None
32
32
 
33
33
  thread_control = {"run_thread": None, "stop_requested": False}
34
34
 
35
- def initiate_abort():
36
- global thread_control
37
- if isinstance(thread_control.get("stop_requested"), Synchronized):
38
- thread_control["stop_requested"].value = 1
39
- if thread_control.get("run_thread") is not None:
40
- thread_control["run_thread"].terminate()
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
- def function_gui_wrapper(function=None, settings={}, q=None, fig_queue=None, imports=1):
42
+ active_categories = set()
56
43
 
57
- """
58
- Wraps the run_multiple_simulations function to integrate with GUI processes.
59
-
60
- Parameters:
61
- - settings: dict, The settings for the run_multiple_simulations function.
62
- - q: multiprocessing.Queue, Queue for logging messages to the GUI.
63
- - fig_queue: multiprocessing.Queue, Queue for sending figures to the GUI.
64
- """
44
+ def toggle_category(settings):
45
+ for setting in settings:
46
+ if setting in vars_dict:
47
+ label, widget, _ = vars_dict[setting]
48
+ if widget.grid_info():
49
+ label.grid_remove()
50
+ widget.grid_remove()
51
+ else:
52
+ label.grid()
53
+ widget.grid()
65
54
 
66
- # Temporarily override plt.show
67
- original_show = plt.show
68
- plt.show = lambda: spacrFigShow(fig_queue)
55
+ def on_category_select(selected_category):
56
+ if selected_category == "Select Category":
57
+ return
58
+ if selected_category in categories:
59
+ toggle_category(categories[selected_category])
60
+ if selected_category in active_categories:
61
+ active_categories.remove(selected_category)
62
+ else:
63
+ active_categories.add(selected_category)
64
+ category_dropdown.update_styles(active_categories)
65
+ category_var.set("Select Category")
66
+
67
+ category_var = tk.StringVar()
68
+ non_empty_categories = [category for category, settings in categories.items() if any(setting in vars_dict for setting in settings)]
69
+ category_dropdown = spacrDropdownMenu(button_scrollable_frame.scrollable_frame, category_var, non_empty_categories, command=on_category_select)
70
+ category_dropdown.grid(row=0, column=4, sticky="ew", pady=2, padx=2)
71
+ vars_dict = hide_all_settings(vars_dict, categories)
72
+
73
+ def process_fig_queue():
74
+ global canvas, fig_queue, canvas_widget, parent_frame
75
+
76
+ def clear_canvas(canvas):
77
+ for ax in canvas.figure.get_axes():
78
+ ax.clear()
79
+ canvas.draw_idle()
69
80
 
70
81
  try:
71
- if imports == 1:
72
- function(settings=settings)
73
- elif imports == 2:
74
- function(src=settings['src'], settings=settings)
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)
82
+ while not fig_queue.empty():
83
+ clear_canvas(canvas)
84
+ fig = fig_queue.get_nowait()
85
+ for ax in fig.get_axes():
86
+ ax.set_xticks([]) # Remove x-axis ticks
87
+ ax.set_yticks([]) # Remove y-axis ticks
88
+ ax.xaxis.set_visible(False) # Hide the x-axis
89
+ ax.yaxis.set_visible(False) # Hide the y-axis
90
+ fig.tight_layout()
91
+ fig.set_facecolor('black')
92
+ canvas.figure = fig
93
+ fig_width, fig_height = canvas_widget.winfo_width(), canvas_widget.winfo_height()
94
+ fig.set_size_inches(fig_width / fig.dpi, fig_height / fig.dpi, forward=True)
95
+ canvas.draw_idle()
136
96
  except Exception as e:
137
- q.put(f"Error during processing: {e}")
138
97
  traceback.print_exc()
139
98
  finally:
140
- stop_requested.value = 1
141
-
142
- def start_process(q=None, fig_queue=None, settings_type='mask'):
143
- global thread_control, vars_dict
144
- from .settings import check_settings, expected_types
99
+ after_id = canvas_widget.after(100, process_fig_queue)
100
+ parent_frame.after_tasks.append(after_id)
145
101
 
146
- if q is None:
147
- q = Queue()
148
- if fig_queue is None:
149
- fig_queue = Queue()
150
102
 
151
- try:
152
- settings = check_settings(vars_dict, expected_types, q)
153
- except ValueError as e:
154
- q.put(f"Error: {e}")
155
- return
156
103
 
157
- if thread_control.get("run_thread") is not None:
158
- initiate_abort()
159
-
160
- stop_requested = Value('i', 0)
161
- thread_control["stop_requested"] = stop_requested
162
104
 
163
- process_args = (settings_type, settings, q, fig_queue, stop_requested)
164
- if settings_type in ['mask','measure','simulation','sequencing','classify','cellpose_dataset','train_cellpose','ml_analyze','cellpose_masks','cellpose_all','map_barcodes','regression','recruitment','plaques','cellpose_compare','vision_scores','vision_dataset']:
165
- thread_control["run_thread"] = Process(target=run_function_gui, args=process_args)
166
- else:
167
- q.put(f"Error: Unknown settings type '{settings_type}'")
168
- return
169
- thread_control["run_thread"].start()
105
+ 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):
106
+ global thread_control, q, console_output, parent_frame, vars_dict, canvas, canvas_widget, scrollable_frame, fig_queue, progress_bar, usage_bars
107
+ thread_control = thread_control_var
108
+ q = q_var
109
+ console_output = console_output_var
110
+ parent_frame = parent_frame_var
111
+ vars_dict = vars_dict_var
112
+ canvas = canvas_var
113
+ canvas_widget = canvas_widget_var
114
+ scrollable_frame = scrollable_frame_var
115
+ fig_queue = fig_queue_var
116
+ progress_bar = progress_bar_var
117
+ usage_bars = usage_bars_var
170
118
 
171
119
  def import_settings(settings_type='mask'):
120
+ from .gui_utils import convert_settings_dict_for_gui, hide_all_settings
172
121
  global vars_dict, scrollable_frame, button_scrollable_frame
173
122
  from .settings import generate_fields
174
123
 
@@ -217,74 +166,23 @@ def import_settings(settings_type='mask'):
217
166
  vars_dict = generate_fields(new_settings, scrollable_frame)
218
167
  vars_dict = hide_all_settings(vars_dict, categories=None)
219
168
 
220
- def convert_settings_dict_for_gui(settings):
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]):
169
+ def setup_settings_panel(vertical_container, settings_type='mask'):
278
170
  global vars_dict, scrollable_frame
279
171
  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
172
+ from .gui_utils import convert_settings_dict_for_gui, set_element_size
173
+
174
+ size_dict = set_element_size(vertical_container)
175
+ settings_width = size_dict['settings_width']
280
176
 
281
- width = (window_dimensions[0]) // 6
282
- height = window_dimensions[1]
177
+ # Create a PanedWindow for the settings panel
178
+ settings_paned_window = tk.PanedWindow(vertical_container, orient=tk.HORIZONTAL)
179
+ vertical_container.add(settings_paned_window, stretch="always")
180
+
181
+ settings_frame = tk.Frame(settings_paned_window, width=settings_width)
182
+ settings_frame.pack_propagate(False) # Prevent the frame from resizing based on its children
183
+
184
+ settings_paned_window.add(settings_frame)
283
185
 
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
186
  scrollable_frame = spacrFrame(settings_frame)
289
187
  scrollable_frame.grid(row=1, column=0, sticky="nsew")
290
188
  settings_frame.grid_rowconfigure(1, weight=1)
@@ -321,7 +219,7 @@ def setup_settings_panel(vertical_container, settings_type='mask', window_dimens
321
219
  vars_dict = generate_fields(variables, scrollable_frame)
322
220
 
323
221
  containers = [settings_frame]
324
- widgets = [settings_label, scrollable_frame]
222
+ widgets = [scrollable_frame]
325
223
 
326
224
  style = ttk.Style(vertical_container)
327
225
  _ = set_dark_style(style, containers=containers, widgets=widgets)
@@ -355,19 +253,50 @@ def setup_plot_section(vertical_container):
355
253
 
356
254
  def setup_console(vertical_container):
357
255
  global console_output
358
- console_frame = tk.Frame(vertical_container)
359
- vertical_container.add(console_frame, stretch="always")
360
- console_label = spacrLabel(console_frame, text="Console", anchor='center', justify='center', align="center")
361
- console_label.grid(row=0, column=0, pady=10, padx=10)
362
- console_output = scrolledtext.ScrolledText(console_frame, height=10)
363
- console_output.grid(row=1, column=0, sticky="nsew")
364
- console_frame.grid_rowconfigure(1, weight=1)
256
+
257
+ # Create a frame to hold the console and button sections
258
+ console_button_frame = tk.Frame(vertical_container)
259
+ vertical_container.add(console_button_frame, stretch="always")
260
+
261
+ # Create a PanedWindow for resizing height
262
+ console_paned_window = tk.PanedWindow(console_button_frame, orient=tk.VERTICAL, bg='black')
263
+ console_paned_window.pack(fill=tk.BOTH, expand=True)
264
+
265
+ # Create the main console frame using spacrFrame with textbox=True
266
+ console_frame = spacrFrame(console_paned_window, textbox=True)
267
+ console_paned_window.add(console_frame, stretch="always")
268
+
269
+ # Assign the scrollable frame (which is a Text widget) to console_output
270
+ console_output = console_frame.scrollable_frame
271
+
272
+ # Ensure the Text widget spans the entire console frame
273
+ console_output.grid(row=0, column=0, sticky="nsew")
274
+
275
+ # Configure the grid to allow expansion
276
+ console_frame.grid_rowconfigure(0, weight=1)
365
277
  console_frame.grid_columnconfigure(0, weight=1)
366
- containers = [console_frame]
367
- widgets = [console_label, console_output]
368
- style = ttk.Style(vertical_container)
369
- _ = set_dark_style(style, containers=containers, widgets=widgets)
370
- return console_output
278
+
279
+ # Create a lower frame to act as the anchor point
280
+ lower_frame = tk.Frame(console_paned_window)
281
+ console_paned_window.add(lower_frame, minsize=10) # Adjust minsize to ensure usability
282
+
283
+ # Dynamically adjust the height and width of the console_frame
284
+ def adjust_frame_size(event):
285
+ console_frame.update_idletasks()
286
+ new_width = console_paned_window.winfo_width()
287
+ new_height = console_paned_window.winfo_height()
288
+ console_frame.config(width=new_width, height=new_height)
289
+ console_output.config(width=new_width, height=new_height)
290
+
291
+ # Bind the configure event to dynamically adjust size
292
+ console_paned_window.bind("<Configure>", adjust_frame_size)
293
+
294
+ # Apply dark style to the PanedWindow and lower frame
295
+ style = ttk.Style()
296
+ set_dark_style(style, containers=[console_paned_window, lower_frame])
297
+
298
+ return console_output, console_frame
299
+
371
300
 
372
301
  def setup_progress_frame(vertical_container):
373
302
  global progress_output
@@ -387,260 +316,285 @@ def setup_progress_frame(vertical_container):
387
316
  _ = set_dark_style(style, containers=containers, widgets=widgets)
388
317
  return progress_output
389
318
 
390
- def download_hug_dataset():
391
- global vars_dict, q
392
- dataset_repo_id = "einarolafsson/toxo_mito"
393
- settings_repo_id = "einarolafsson/spacr_settings"
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.")
319
+ def setup_button_section(horizontal_container, settings_type='mask', run=True, abort=True, download=True, import_btn=True):
320
+ 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
321
+ from .gui_utils import set_element_size, download_hug_dataset
322
+ from .settings import categories
472
323
 
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
476
324
 
477
- width = (window_dimensions[0]) // 8
478
- height = window_dimensions[1]
325
+ size_dict = set_element_size(horizontal_container)
326
+ button_section_height = size_dict['panel_height']
327
+ button_frame = tk.Frame(horizontal_container, height=button_section_height)
328
+
329
+ # Prevent the frame from resizing based on the child widget
330
+ button_frame.pack_propagate(False)
479
331
 
480
- button_frame = tk.Frame(horizontal_container)
481
332
  horizontal_container.add(button_frame, stretch="always", sticky="nsew")
482
- button_frame.grid_rowconfigure(0, weight=0)
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)
333
+ button_scrollable_frame = spacrFrame(button_frame, scrollbar=False)
489
334
  button_scrollable_frame.grid(row=1, column=0, sticky="nsew")
490
-
491
- widgets = [categories_label, button_scrollable_frame.scrollable_frame]
335
+ widgets = [button_scrollable_frame.scrollable_frame]
492
336
 
493
337
  btn_col = 0
494
- btn_row = 3
338
+ btn_row = 0
495
339
 
496
340
  if run:
497
341
  print(f'settings_type: {settings_type}')
498
- run_button = spacrButton(button_scrollable_frame.scrollable_frame, text="run", command=lambda: start_process(q, fig_queue, settings_type))
342
+ 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
343
  run_button.grid(row=btn_row, column=btn_col, pady=5, padx=5, sticky='ew')
500
344
  widgets.append(run_button)
501
- btn_row += 1
345
+ btn_col += 1
502
346
 
503
347
  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)
348
+ 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
349
  abort_button.grid(row=btn_row, column=btn_col, pady=5, padx=5, sticky='ew')
506
350
  widgets.append(abort_button)
507
- btn_row += 1
351
+ btn_col += 1
508
352
 
509
353
  if download and settings_type in ['mask']:
510
- download_dataset_button = spacrButton(button_scrollable_frame.scrollable_frame, text="download", command=download_hug_dataset)
354
+ 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
355
  download_dataset_button.grid(row=btn_row, column=btn_col, pady=5, padx=5, sticky='ew')
512
356
  widgets.append(download_dataset_button)
513
- btn_row += 1
357
+ btn_col += 1
514
358
 
515
359
  if import_btn:
516
- import_button = spacrButton(button_scrollable_frame.scrollable_frame, text="settings", command=lambda: import_settings(settings_type))
360
+ 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
361
  import_button.grid(row=btn_row, column=btn_col, pady=5, padx=5, sticky='ew')
518
362
  widgets.append(import_button)
519
363
  btn_row += 1
520
364
 
521
365
  # Add the progress bar under the settings category menu
522
366
  progress_bar = spacrProgressBar(button_scrollable_frame.scrollable_frame, orient='horizontal', mode='determinate')
523
- progress_bar.grid(row=0, column=0, columnspan=2, pady=5, padx=5, sticky='ew')
367
+ progress_bar.grid(row=btn_row, column=0, columnspan=5, pady=5, padx=5, sticky='ew')
368
+ progress_bar.set_label_position() # Set the label position after grid placement
524
369
  widgets.append(progress_bar)
525
370
 
526
371
  if vars_dict is not None:
527
372
  toggle_settings(button_scrollable_frame)
528
373
 
529
- description_frame = tk.Frame(horizontal_container)
530
- horizontal_container.add(description_frame, stretch="always", sticky="nsew")
531
- description_frame.grid_columnconfigure(0, weight=1)
532
- description_frame.grid_rowconfigure(0, weight=1) # Add this line to make the row expandable
374
+ style = ttk.Style(horizontal_container)
375
+ _ = set_dark_style(style, containers=[button_frame], widgets=widgets)
533
376
 
534
- description_label = tk.Label(description_frame, text="Module Description", anchor='nw', justify='left', wraplength=width - 50)
535
- description_label.grid(row=0, column=0, pady=50, padx=20, sticky='nsew')
536
- description_text = descriptions.get(settings_type, "No description available for this module.")
537
- description_label.config(text=description_text)
377
+ return button_scrollable_frame
538
378
 
539
- def update_wraplength(event):
540
- new_width = event.width - 40 # Adjust as needed
541
- description_label.config(wraplength=new_width)
379
+ def setup_usage_panel(horizontal_container):
380
+ global usage_bars
381
+ from .gui_utils import set_element_size
382
+
383
+ def update_usage(ram_bar, vram_bar, gpu_bar, usage_bars, parent_frame):
384
+ # Update RAM usage
385
+ ram_usage = psutil.virtual_memory().percent
386
+ ram_bar['value'] = ram_usage
387
+
388
+ # Update GPU and VRAM usage
389
+ gpus = GPUtil.getGPUs()
390
+ if gpus:
391
+ gpu = gpus[0]
392
+ vram_usage = gpu.memoryUtil * 100
393
+ gpu_usage = gpu.load * 100
394
+ vram_bar['value'] = vram_usage
395
+ gpu_bar['value'] = gpu_usage
396
+
397
+ # Update CPU usage for each core
398
+ cpu_percentages = psutil.cpu_percent(percpu=True)
399
+ for bar, usage in zip(usage_bars[3:], cpu_percentages):
400
+ bar['value'] = usage
401
+
402
+ # Schedule the function to run again after 1000 ms (1 second)
403
+ parent_frame.after(1000, update_usage, ram_bar, vram_bar, gpu_bar, usage_bars, parent_frame)
404
+
405
+ size_dict = set_element_size(horizontal_container)
406
+ usage_panel_height = size_dict['panel_height']
407
+ usage_frame = tk.Frame(horizontal_container, height=usage_panel_height)
408
+ horizontal_container.add(usage_frame, stretch="always", sticky="nsew")
409
+
410
+ usage_frame.grid_rowconfigure(0, weight=0)
411
+ usage_frame.grid_rowconfigure(1, weight=1)
412
+ usage_frame.grid_columnconfigure(0, weight=1)
413
+ usage_frame.grid_columnconfigure(1, weight=1)
414
+
415
+ usage_scrollable_frame = spacrFrame(usage_frame, scrollbar=False)
416
+ usage_scrollable_frame.grid(row=1, column=0, sticky="nsew", columnspan=2)
417
+ widgets = [usage_scrollable_frame.scrollable_frame]
418
+ usage_bars = []
419
+ max_elements_per_column = 6
420
+ row = 0
421
+ col = 0
422
+
423
+ # Initialize RAM, VRAM, and GPU bars as None
424
+ ram_bar, vram_bar, gpu_bar = None, None, None
425
+
426
+ # Try adding RAM bar
427
+ try:
428
+ ram_info = psutil.virtual_memory()
429
+ ram_label_text = f"RAM"
430
+ label = ttk.Label(usage_scrollable_frame.scrollable_frame, text=ram_label_text, anchor='w')
431
+ label.grid(row=row, column=2 * col, pady=5, padx=5, sticky='w')
432
+ ram_bar = spacrProgressBar(usage_scrollable_frame.scrollable_frame, orient='horizontal', mode='determinate', length=size_dict['bar_size'], label=False)
433
+ ram_bar.grid(row=row, column=2 * col + 1, pady=5, padx=5, sticky='ew')
434
+ widgets.append(label)
435
+ widgets.append(ram_bar)
436
+ usage_bars.append(ram_bar)
437
+ row += 1
438
+ except Exception as e:
439
+ print(f"Could not add RAM usage bar: {e}")
542
440
 
543
- description_label.bind('<Configure>', update_wraplength)
441
+ # Try adding VRAM and GPU usage bars
442
+ try:
443
+ gpus = GPUtil.getGPUs()
444
+ if gpus:
445
+ gpu = gpus[0]
446
+ vram_label_text = f"VRAM"
447
+ label = ttk.Label(usage_scrollable_frame.scrollable_frame, text=vram_label_text, anchor='w')
448
+ label.grid(row=row, column=2 * col, pady=5, padx=5, sticky='w')
449
+ vram_bar = spacrProgressBar(usage_scrollable_frame.scrollable_frame, orient='horizontal', mode='determinate', length=size_dict['bar_size'], label=False)
450
+ vram_bar.grid(row=row, column=2 * col + 1, pady=5, padx=5, sticky='ew')
451
+ widgets.append(label)
452
+ widgets.append(vram_bar)
453
+ usage_bars.append(vram_bar)
454
+ row += 1
455
+
456
+ gpu_label_text = f"GPU"
457
+ label = ttk.Label(usage_scrollable_frame.scrollable_frame, text=gpu_label_text, anchor='w')
458
+ label.grid(row=row, column=2 * col, pady=5, padx=5, sticky='w')
459
+ gpu_bar = spacrProgressBar(usage_scrollable_frame.scrollable_frame, orient='horizontal', mode='determinate', length=size_dict['bar_size'], label=False)
460
+ gpu_bar.grid(row=row, column=2 * col + 1, pady=5, padx=5, sticky='ew')
461
+ widgets.append(label)
462
+ widgets.append(gpu_bar)
463
+ usage_bars.append(gpu_bar)
464
+ row += 1
465
+ except Exception as e:
466
+ print(f"Could not add VRAM or GPU usage bars: {e}")
544
467
 
545
- containers = [button_frame, description_frame]
546
- widgets.extend([description_label])
468
+ # Add CPU core usage bars
469
+ try:
470
+ cpu_cores = psutil.cpu_count(logical=True)
471
+ cpu_freq = psutil.cpu_freq()
472
+
473
+ for core in range(cpu_cores):
474
+ if row > 0 and row % max_elements_per_column == 0:
475
+ col += 1
476
+ row = 0
477
+ label = ttk.Label(usage_scrollable_frame.scrollable_frame, text=f"Core {core+1}", anchor='w')
478
+ label.grid(row=row, column=2 * col, pady=2, padx=5, sticky='w')
479
+ bar = spacrProgressBar(usage_scrollable_frame.scrollable_frame, orient='horizontal', mode='determinate', length=size_dict['bar_size'], label=False)
480
+ bar.grid(row=row, column=2 * col + 1, pady=2, padx=5, sticky='ew')
481
+ widgets.append(label)
482
+ widgets.append(bar)
483
+ usage_bars.append(bar)
484
+ row += 1
485
+ except Exception as e:
486
+ print(f"Could not add CPU core usage bars: {e}")
547
487
 
548
488
  style = ttk.Style(horizontal_container)
549
- _ = set_dark_style(style, containers=containers, widgets=widgets)
489
+ _ = set_dark_style(style, containers=[usage_frame], widgets=widgets)
550
490
 
551
- return button_scrollable_frame
491
+ if ram_bar is None:
492
+ ram_bar = spacrProgressBar(usage_scrollable_frame.scrollable_frame, orient='horizontal', mode='determinate', length=size_dict['bar_size'], label=False)
493
+ if vram_bar is None:
494
+ vram_bar = spacrProgressBar(usage_scrollable_frame.scrollable_frame, orient='horizontal', mode='determinate', length=size_dict['bar_size'], label=False)
495
+ if gpu_bar is None:
496
+ gpu_bar = spacrProgressBar(usage_scrollable_frame.scrollable_frame, orient='horizontal', mode='determinate', length=size_dict['bar_size'], label=False)
552
497
 
498
+ update_usage(ram_bar, vram_bar, gpu_bar, usage_bars, usage_frame)
499
+ return usage_scrollable_frame, usage_bars
553
500
 
554
- def hide_all_settings(vars_dict, categories):
555
- """
556
- Function to initially hide all settings in the GUI.
501
+ def setup_help_section(horizontal_container, settings_type='mask'):
502
+ from .settings import descriptions
503
+ from .gui_utils import set_element_size
557
504
 
558
- Parameters:
559
- - categories: dict, The categories of settings with their corresponding settings.
560
- - vars_dict: dict, The dictionary containing the settings and their corresponding widgets.
561
- """
505
+ size_dict = set_element_size(horizontal_container)
506
+ help_section_height = size_dict['panel_height']
562
507
 
563
- if categories is None:
564
- from .settings import categories
508
+ # Create the frame for the help section
509
+ description_frame = tk.Frame(horizontal_container, height=help_section_height)
510
+ description_frame.pack_propagate(False)
565
511
 
566
- for category, settings in categories.items():
567
- if any(setting in vars_dict for setting in settings):
568
- vars_dict[category] = (None, None, tk.IntVar(value=0))
569
-
570
- # Initially hide all settings
571
- for setting in settings:
572
- if setting in vars_dict:
573
- label, widget, _ = vars_dict[setting]
574
- label.grid_remove()
575
- widget.grid_remove()
576
- return vars_dict
512
+ # Add the description frame to the horizontal container
513
+ horizontal_container.add(description_frame, stretch="always", sticky="nsew")
514
+ description_frame.grid_columnconfigure(0, weight=1)
515
+ description_frame.grid_rowconfigure(0, weight=1) # Ensure the text widget row is expandable
577
516
 
578
- def toggle_settings(button_scrollable_frame):
579
- global vars_dict
580
- from .settings import categories
517
+ style_out = set_dark_style(ttk.Style())
518
+ bg_color = style_out['bg_color']
519
+ fg_color = style_out['fg_color']
581
520
 
582
- if vars_dict is None:
583
- raise ValueError("vars_dict is not initialized.")
521
+ # Insert the description text
522
+ description_text = descriptions.get(settings_type, "No description available for this module.")
523
+ first_line, *rest_of_description = description_text.split('\n', 1)
584
524
 
585
- active_categories = set()
525
+ # Create the first line label
526
+ first_line_label = ttk.Label(description_frame, text=first_line, anchor='w', background=bg_color, foreground=fg_color)
527
+ first_line_label.grid(row=0, column=0, sticky='ew')
586
528
 
587
- def toggle_category(settings):
588
- for setting in settings:
589
- if setting in vars_dict:
590
- label, widget, _ = vars_dict[setting]
591
- if widget.grid_info():
592
- label.grid_remove()
593
- widget.grid_remove()
594
- else:
595
- label.grid()
596
- widget.grid()
529
+ # Create the text widget with the appropriate background and foreground colors
530
+ description_text_widget = tk.Text(description_frame, wrap="word", bg=bg_color, fg=fg_color, bd=0, highlightthickness=0)
531
+ description_text_widget.grid(row=1, column=0, sticky="nsew")
597
532
 
598
- def on_category_select(selected_category):
599
- if selected_category == "Select Category":
600
- return
601
- if selected_category in categories:
602
- toggle_category(categories[selected_category])
603
- if selected_category in active_categories:
604
- active_categories.remove(selected_category)
605
- else:
606
- active_categories.add(selected_category)
607
- category_dropdown.update_styles(active_categories)
608
- category_var.set("Select Category")
533
+ # Insert the rest of the description text
534
+ if rest_of_description:
535
+ description_text_widget.insert("1.0", rest_of_description[0].lstrip())
536
+ description_text_widget.config(state="disabled") # Make the text widget read-only
609
537
 
610
- category_var = tk.StringVar()
611
- non_empty_categories = [category for category, settings in categories.items() if any(setting in vars_dict for setting in settings)]
612
- category_dropdown = spacrDropdownMenu(button_scrollable_frame.scrollable_frame, category_var, non_empty_categories, command=on_category_select)
613
- category_dropdown.grid(row=7, column=0, sticky="ew", pady=2, padx=2)
614
- vars_dict = hide_all_settings(vars_dict, categories)
538
+ def update_wraplength(event):
539
+ new_width = event.width - 20 # Adjust as needed
540
+ description_text_widget.config(width=new_width)
615
541
 
616
- def process_fig_queue():
617
- global canvas, fig_queue, canvas_widget, parent_frame
542
+ description_text_widget.bind('<Configure>', update_wraplength)
618
543
 
619
- def clear_canvas(canvas):
620
- for ax in canvas.figure.get_axes():
621
- ax.clear()
622
- canvas.draw_idle()
544
+ # Apply dark style
545
+ style = ttk.Style(horizontal_container)
546
+ _ = set_dark_style(style, containers=[description_frame], widgets=[description_text_widget, first_line_label])
547
+
548
+ return description_frame
549
+
550
+ def initiate_abort():
551
+ global thread_control, q, parent_frame
552
+ if thread_control.get("run_thread") is not None:
553
+ try:
554
+ q.put("Aborting processes...")
555
+ thread_control.get("run_thread").terminate()
556
+ thread_control["run_thread"] = None
557
+ q.put("Processes aborted.")
558
+ except Exception as e:
559
+ q.put(f"Error aborting process: {e}")
560
+
561
+ thread_control = {"run_thread": None, "stop_requested": False}
562
+
563
+
564
+ def start_process(q=None, fig_queue=None, settings_type='mask'):
565
+ global thread_control, vars_dict, parent_frame
566
+ from .settings import check_settings, expected_types
567
+ from .gui_utils import run_function_gui
568
+
569
+ if q is None:
570
+ q = Queue()
571
+ if fig_queue is None:
572
+ fig_queue = Queue()
623
573
 
624
574
  try:
625
- while not fig_queue.empty():
626
- clear_canvas(canvas)
627
- fig = fig_queue.get_nowait()
628
- for ax in fig.get_axes():
629
- ax.set_xticks([]) # Remove x-axis ticks
630
- ax.set_yticks([]) # Remove y-axis ticks
631
- ax.xaxis.set_visible(False) # Hide the x-axis
632
- ax.yaxis.set_visible(False) # Hide the y-axis
633
- fig.tight_layout()
634
- fig.set_facecolor('black')
635
- canvas.figure = fig
636
- fig_width, fig_height = canvas_widget.winfo_width(), canvas_widget.winfo_height()
637
- fig.set_size_inches(fig_width / fig.dpi, fig_height / fig.dpi, forward=True)
638
- canvas.draw_idle()
639
- except Exception as e:
640
- traceback.print_exc()
641
- finally:
642
- after_id = canvas_widget.after(100, process_fig_queue)
643
- parent_frame.after_tasks.append(after_id)
575
+ settings = check_settings(vars_dict, expected_types, q)
576
+ except ValueError as e:
577
+ q.put(f"Error: {e}")
578
+ return
579
+
580
+ if thread_control.get("run_thread") is not None:
581
+ initiate_abort()
582
+
583
+ stop_requested = Value('i', 0)
584
+ thread_control["stop_requested"] = stop_requested
585
+
586
+ process_args = (settings_type, settings, q, fig_queue, stop_requested)
587
+ if settings_type in [
588
+ 'mask', 'measure', 'simulation', 'sequencing', 'classify', 'cellpose_dataset',
589
+ 'train_cellpose', 'ml_analyze', 'cellpose_masks', 'cellpose_all', 'map_barcodes',
590
+ 'regression', 'recruitment', 'plaques', 'cellpose_compare', 'vision_scores',
591
+ 'vision_dataset'
592
+ ]:
593
+ thread_control["run_thread"] = Process(target=run_function_gui, args=process_args)
594
+ else:
595
+ q.put(f"Error: Unknown settings type '{settings_type}'")
596
+ return
597
+ thread_control["run_thread"].start()
644
598
 
645
599
  def process_console_queue():
646
600
  global q, console_output, parent_frame, progress_bar
@@ -658,13 +612,15 @@ def process_console_queue():
658
612
  console_output.see(tk.END)
659
613
 
660
614
  # Check if the message contains progress information
661
- if clean_message.startswith("Progress"):
615
+ if clean_message.startswith("Progress:"):
662
616
  try:
663
617
  # Extract the progress information
664
- match = re.search(r'(\d+)/(\d+)', clean_message)
618
+ match = re.search(r'Progress: (\d+)/(\d+), operation_type: ([\w\s]*)(.*)', clean_message)
665
619
  if match:
666
620
  current_progress = int(match.group(1))
667
621
  total_progress = int(match.group(2))
622
+ operation_type = match.group(3).strip()
623
+ time_info = match.group(4).strip()
668
624
 
669
625
  # Add the task to the completed set
670
626
  process_console_queue.completed_tasks.append(current_progress)
@@ -678,19 +634,18 @@ def process_console_queue():
678
634
  progress_bar['value'] = unique_progress_count
679
635
 
680
636
  # Extract and update additional information
681
- operation_match = re.search(r'operation_type: ([\w\s]+)', clean_message)
682
- if operation_match:
683
- progress_bar.operation_type = operation_match.group(1)
637
+ if operation_type:
638
+ progress_bar.operation_type = operation_type
684
639
 
685
- time_image_match = re.search(r'Time/image: ([\d.]+) sec', clean_message)
640
+ time_image_match = re.search(r'Time/image: ([\d.]+) sec', time_info)
686
641
  if time_image_match:
687
642
  progress_bar.time_image = float(time_image_match.group(1))
688
643
 
689
- time_batch_match = re.search(r'Time/batch: ([\d.]+) sec', clean_message)
644
+ time_batch_match = re.search(r'Time/batch: ([\d.]+) sec', time_info)
690
645
  if time_batch_match:
691
646
  progress_bar.time_batch = float(time_batch_match.group(1))
692
647
 
693
- time_left_match = re.search(r'Time_left: ([\d.]+) min', clean_message)
648
+ time_left_match = re.search(r'Time_left: ([\d.]+) min', time_info)
694
649
  if time_left_match:
695
650
  progress_bar.time_left = float(time_left_match.group(1))
696
651
 
@@ -703,74 +658,32 @@ def process_console_queue():
703
658
  process_console_queue.completed_tasks.clear()
704
659
  except Exception as e:
705
660
  print(f"Error parsing progress message: {e}")
706
-
661
+
707
662
  after_id = console_output.after(100, process_console_queue)
708
663
  parent_frame.after_tasks.append(after_id)
709
664
 
710
- def set_globals(q_var, console_output_var, parent_frame_var, vars_dict_var, canvas_var, canvas_widget_var, scrollable_frame_var, fig_queue_var, progress_bar_var):
711
- global q, console_output, parent_frame, vars_dict, canvas, canvas_widget, scrollable_frame, fig_queue, progress_bar
712
- q = q_var
713
- console_output = console_output_var
714
- parent_frame = parent_frame_var
715
- vars_dict = vars_dict_var
716
- canvas = canvas_var
717
- canvas_widget = canvas_widget_var
718
- scrollable_frame = scrollable_frame_var
719
- fig_queue = fig_queue_var
720
- progress_bar = progress_bar_var
721
-
722
- def create_containers(parent_frame):
723
- vertical_container = tk.PanedWindow(parent_frame, orient=tk.VERTICAL)
724
- horizontal_container = tk.PanedWindow(vertical_container, orient=tk.HORIZONTAL)
725
- settings_frame = tk.Frame(horizontal_container)
726
- return vertical_container, horizontal_container, settings_frame
727
-
728
- def setup_frame(parent_frame):
729
- style = ttk.Style(parent_frame)
730
- vertical_container, horizontal_container, settings_frame = create_containers(parent_frame)
731
- containers = [vertical_container, horizontal_container, settings_frame]
732
-
733
- set_dark_style(style, parent_frame, containers)
734
- set_default_font(parent_frame, font_name="Helvetica", size=8)
735
-
736
- vertical_container.grid(row=0, column=0, sticky=tk.NSEW)
737
- vertical_container.add(horizontal_container, stretch="always")
738
- horizontal_container.grid_columnconfigure(0, weight=1)
739
- horizontal_container.grid_columnconfigure(1, weight=1)
740
- settings_frame.grid_rowconfigure(0, weight=0)
741
- settings_frame.grid_rowconfigure(1, weight=1)
742
- settings_frame.grid_columnconfigure(0, weight=1)
743
- horizontal_container.add(settings_frame, stretch="always", sticky="nsew")
744
-
745
- return parent_frame, vertical_container, horizontal_container
746
-
747
665
  def initiate_root(parent, settings_type='mask'):
748
- global q, fig_queue, parent_frame, scrollable_frame, button_frame, vars_dict, canvas, canvas_widget, button_scrollable_frame, progress_bar
749
- from .gui_utils import main_thread_update_function
666
+ global q, fig_queue, thread_control, parent_frame, scrollable_frame, button_frame, vars_dict, canvas, canvas_widget, button_scrollable_frame, progress_bar
667
+ from .gui_utils import main_thread_update_function, setup_frame, set_element_size
750
668
  from .gui import gui_app
751
669
  set_start_method('spawn', force=True)
752
670
  print("Initializing root with settings_type:", settings_type)
753
671
 
754
672
  parent_frame = parent
755
- parent_frame.update_idletasks()
756
- frame_width = int(parent_frame.winfo_width())
757
- frame_height = int(parent_frame.winfo_height())
758
- print(frame_width, frame_height)
759
- dims = [frame_width, frame_height]
760
673
 
761
- if not hasattr(parent_frame, 'after_tasks'):
762
- parent_frame.after_tasks = []
674
+ if not isinstance(parent_frame, (tk.Tk, tk.Toplevel)):
675
+ parent_window = parent_frame.winfo_toplevel()
676
+ else:
677
+ parent_window = parent_frame
678
+
679
+ parent_window.update_idletasks()
763
680
 
764
- # Clear previous content instead of destroying the root
765
- for widget in parent_frame.winfo_children():
766
- try:
767
- widget.destroy()
768
- except tk.TclError as e:
769
- print(f"Error destroying widget: {e}")
681
+ if not hasattr(parent_window, 'after_tasks'):
682
+ parent_window.after_tasks = []
770
683
 
771
684
  q = Queue()
772
685
  fig_queue = Queue()
773
- parent_frame, vertical_container, horizontal_container = setup_frame(parent_frame)
686
+ parent_frame, vertical_container, horizontal_container, settings_container = setup_frame(parent_frame)
774
687
 
775
688
  if settings_type == 'annotate':
776
689
  from .app_annotate import initiate_annotation_app
@@ -779,17 +692,19 @@ def initiate_root(parent, settings_type='mask'):
779
692
  from .app_make_masks import initiate_make_mask_app
780
693
  initiate_make_mask_app(horizontal_container)
781
694
  else:
782
- scrollable_frame, vars_dict = setup_settings_panel(horizontal_container, settings_type, window_dimensions=dims)
783
- button_scrollable_frame = setup_button_section(horizontal_container, settings_type, window_dimensions=dims)
695
+ scrollable_frame, vars_dict = setup_settings_panel(settings_container, settings_type)
784
696
  canvas, canvas_widget = setup_plot_section(vertical_container)
785
- console_output = setup_console(vertical_container)
786
-
787
- set_globals(q, console_output, parent_frame, vars_dict, canvas, canvas_widget, scrollable_frame, fig_queue, progress_bar)
697
+ console_output, console_frame = setup_console(vertical_container)
698
+ button_scrollable_frame = setup_button_section(horizontal_container, settings_type)
699
+ _, usage_bars = setup_usage_panel(horizontal_container)
700
+ _ = setup_help_section(horizontal_container, settings_type)
701
+
702
+ set_globals(thread_control, q, console_output, parent_frame, vars_dict, canvas, canvas_widget, scrollable_frame, fig_queue, progress_bar, usage_bars)
703
+ q.put(f"Console")
788
704
  process_console_queue()
789
705
  process_fig_queue()
790
- after_id = parent_frame.after(100, lambda: main_thread_update_function(parent_frame, q, fig_queue, canvas_widget))
791
- parent_frame.after_tasks.append(after_id)
706
+ after_id = parent_window.after(100, lambda: main_thread_update_function(parent_window, q, fig_queue, canvas_widget))
707
+ parent_window.after_tasks.append(after_id)
792
708
 
793
709
  print("Root initialization complete")
794
710
  return parent_frame, vars_dict
795
-