spacr 0.2.41__py3-none-any.whl → 0.2.46__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,14 +7,10 @@ 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
14
-
15
- import psutil, gpustat
11
+ import psutil
16
12
  import GPUtil
17
- from threading import Thread
18
- from time import sleep
19
-
13
+ import tkinter.font as tkFont
20
14
 
21
15
  try:
22
16
  ctypes.windll.shcore.SetProcessDpiAwareness(True)
@@ -24,7 +18,7 @@ except AttributeError:
24
18
  pass
25
19
 
26
20
  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
27
- from .gui_elements import spacrProgressBar, spacrButton, spacrLabel, spacrFrame, spacrDropdownMenu ,set_dark_style, set_default_font
21
+ from .gui_elements import spacrProgressBar, spacrButton, spacrLabel, spacrFrame, spacrDropdownMenu ,set_dark_style
28
22
 
29
23
  # Define global variables
30
24
  q = None
@@ -39,143 +33,92 @@ fig_queue = None
39
33
 
40
34
  thread_control = {"run_thread": None, "stop_requested": False}
41
35
 
42
- def initiate_abort():
43
- global thread_control
44
- if isinstance(thread_control.get("stop_requested"), Synchronized):
45
- thread_control["stop_requested"].value = 1
46
- if thread_control.get("run_thread") is not None:
47
- thread_control["run_thread"].terminate()
48
- thread_control["run_thread"].join()
49
- thread_control["run_thread"] = None
50
-
51
- def spacrFigShow(fig_queue=None):
52
- """
53
- Replacement for plt.show() that queues figures instead of displaying them.
54
- """
55
- fig = plt.gcf()
56
- if fig_queue:
57
- fig_queue.put(fig)
58
- else:
59
- fig.show()
60
- plt.close(fig)
36
+ def toggle_settings(button_scrollable_frame):
37
+ global vars_dict
38
+ from .settings import categories
39
+ from .gui_utils import hide_all_settings
40
+ if vars_dict is None:
41
+ raise ValueError("vars_dict is not initialized.")
61
42
 
62
- def function_gui_wrapper(function=None, settings={}, q=None, fig_queue=None, imports=1):
43
+ active_categories = set()
63
44
 
64
- """
65
- Wraps the run_multiple_simulations function to integrate with GUI processes.
66
-
67
- Parameters:
68
- - settings: dict, The settings for the run_multiple_simulations function.
69
- - q: multiprocessing.Queue, Queue for logging messages to the GUI.
70
- - fig_queue: multiprocessing.Queue, Queue for sending figures to the GUI.
71
- """
45
+ def toggle_category(settings):
46
+ for setting in settings:
47
+ if setting in vars_dict:
48
+ label, widget, _ = vars_dict[setting]
49
+ if widget.grid_info():
50
+ label.grid_remove()
51
+ widget.grid_remove()
52
+ else:
53
+ label.grid()
54
+ widget.grid()
72
55
 
73
- # Temporarily override plt.show
74
- original_show = plt.show
75
- plt.show = lambda: spacrFigShow(fig_queue)
56
+ def on_category_select(selected_category):
57
+ if selected_category == "Select Category":
58
+ return
59
+ if selected_category in categories:
60
+ toggle_category(categories[selected_category])
61
+ if selected_category in active_categories:
62
+ active_categories.remove(selected_category)
63
+ else:
64
+ active_categories.add(selected_category)
65
+ category_dropdown.update_styles(active_categories)
66
+ category_var.set("Select Category")
67
+
68
+ category_var = tk.StringVar()
69
+ non_empty_categories = [category for category, settings in categories.items() if any(setting in vars_dict for setting in settings)]
70
+ category_dropdown = spacrDropdownMenu(button_scrollable_frame.scrollable_frame, category_var, non_empty_categories, command=on_category_select)
71
+ category_dropdown.grid(row=0, column=4, sticky="ew", pady=2, padx=2)
72
+ vars_dict = hide_all_settings(vars_dict, categories)
73
+
74
+ def process_fig_queue():
75
+ global canvas, fig_queue, canvas_widget, parent_frame
76
+
77
+ def clear_canvas(canvas):
78
+ for ax in canvas.figure.get_axes():
79
+ ax.clear()
80
+ canvas.draw_idle()
76
81
 
77
82
  try:
78
- if imports == 1:
79
- function(settings=settings)
80
- elif imports == 2:
81
- function(src=settings['src'], settings=settings)
82
- except Exception as e:
83
- # Send the error message to the GUI via the queue
84
- errorMessage = f"Error during processing: {e}"
85
- q.put(errorMessage)
86
- traceback.print_exc()
87
- finally:
88
- # Restore the original plt.show function
89
- plt.show = original_show
90
-
91
- def run_function_gui(settings_type, settings, q, fig_queue, stop_requested):
92
- from .gui_utils import process_stdout_stderr
93
- 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
94
- from .io import generate_cellpose_train_test
95
- from .measure import measure_crop
96
- from .sim import run_multiple_simulations
97
- from .deep_spacr import train_test_model
98
- from .sequencing import analyze_reads, map_barcodes_folder, perform_regression
99
- process_stdout_stderr(q)
100
-
101
- print(f'run_function_gui settings_type: {settings_type}')
102
-
103
- if settings_type == 'mask':
104
- function = preprocess_generate_masks
105
- imports = 2
106
- elif settings_type == 'measure':
107
- function = measure_crop
108
- imports = 1
109
- elif settings_type == 'simulation':
110
- function = run_multiple_simulations
111
- imports = 1
112
- elif settings_type == 'sequencing':
113
- function = analyze_reads
114
- imports = 1
115
- elif settings_type == 'classify':
116
- function = train_test_model
117
- imports = 2
118
- elif settings_type == 'train_cellpose':
119
- function = train_cellpose
120
- imports = 1
121
- elif settings_type == 'ml_analyze':
122
- function = generate_ml_scores
123
- imports = 2
124
- elif settings_type == 'cellpose_masks':
125
- function = identify_masks_finetune
126
- imports = 1
127
- elif settings_type == 'cellpose_all':
128
- function = check_cellpose_models
129
- imports = 1
130
- elif settings_type == 'map_barcodes':
131
- function = map_barcodes_folder
132
- imports = 2
133
- elif settings_type == 'regression':
134
- function = perform_regression
135
- imports = 2
136
- elif settings_type == 'recruitment':
137
- function = analyze_recruitment
138
- imports = 2
139
- else:
140
- raise ValueError(f"Invalid settings type: {settings_type}")
141
- try:
142
- function_gui_wrapper(function, settings, q, fig_queue, imports)
83
+ while not fig_queue.empty():
84
+ clear_canvas(canvas)
85
+ fig = fig_queue.get_nowait()
86
+ for ax in fig.get_axes():
87
+ ax.set_xticks([]) # Remove x-axis ticks
88
+ ax.set_yticks([]) # Remove y-axis ticks
89
+ ax.xaxis.set_visible(False) # Hide the x-axis
90
+ ax.yaxis.set_visible(False) # Hide the y-axis
91
+ fig.tight_layout()
92
+ fig.set_facecolor('black')
93
+ canvas.figure = fig
94
+ fig_width, fig_height = canvas_widget.winfo_width(), canvas_widget.winfo_height()
95
+ fig.set_size_inches(fig_width / fig.dpi, fig_height / fig.dpi, forward=True)
96
+ canvas.draw_idle()
143
97
  except Exception as e:
144
- q.put(f"Error during processing: {e}")
145
98
  traceback.print_exc()
146
99
  finally:
147
- stop_requested.value = 1
100
+ after_id = canvas_widget.after(100, process_fig_queue)
101
+ parent_frame.after_tasks.append(after_id)
148
102
 
149
- def start_process(q=None, fig_queue=None, settings_type='mask'):
150
- global thread_control, vars_dict
151
- from .settings import check_settings, expected_types
152
103
 
153
- if q is None:
154
- q = Queue()
155
- if fig_queue is None:
156
- fig_queue = Queue()
157
-
158
- try:
159
- settings = check_settings(vars_dict, expected_types, q)
160
- except ValueError as e:
161
- q.put(f"Error: {e}")
162
- return
163
104
 
164
- if thread_control.get("run_thread") is not None:
165
- initiate_abort()
166
-
167
- stop_requested = Value('i', 0)
168
- thread_control["stop_requested"] = stop_requested
169
105
 
170
- process_args = (settings_type, settings, q, fig_queue, stop_requested)
171
- 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']:
172
- thread_control["run_thread"] = Process(target=run_function_gui, args=process_args)
173
- else:
174
- q.put(f"Error: Unknown settings type '{settings_type}'")
175
- return
176
- thread_control["run_thread"].start()
106
+ 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):
107
+ global thread_control, q, console_output, parent_frame, vars_dict, canvas, canvas_widget, scrollable_frame, fig_queue, progress_bar, usage_bars
108
+ thread_control = thread_control_var
109
+ q = q_var
110
+ console_output = console_output_var
111
+ parent_frame = parent_frame_var
112
+ vars_dict = vars_dict_var
113
+ canvas = canvas_var
114
+ canvas_widget = canvas_widget_var
115
+ scrollable_frame = scrollable_frame_var
116
+ fig_queue = fig_queue_var
117
+ progress_bar = progress_bar_var
118
+ usage_bars = usage_bars_var
177
119
 
178
120
  def import_settings(settings_type='mask'):
121
+ from .gui_utils import convert_settings_dict_for_gui, hide_all_settings
179
122
  global vars_dict, scrollable_frame, button_scrollable_frame
180
123
  from .settings import generate_fields
181
124
 
@@ -224,74 +167,23 @@ def import_settings(settings_type='mask'):
224
167
  vars_dict = generate_fields(new_settings, scrollable_frame)
225
168
  vars_dict = hide_all_settings(vars_dict, categories=None)
226
169
 
227
- def convert_settings_dict_for_gui(settings):
228
- from torchvision import models as torch_models
229
- torchvision_models = [name for name, obj in torch_models.__dict__.items() if callable(obj)]
230
- chans = ['0', '1', '2', '3', '4', '5', '6', '7', '8', None]
231
- chans_v2 = [0, 1, 2, 3, None]
232
- variables = {}
233
- special_cases = {
234
- 'metadata_type': ('combo', ['cellvoyager', 'cq1', 'nikon', 'zeis', 'custom'], 'cellvoyager'),
235
- 'channels': ('combo', ['[0,1,2,3]', '[0,1,2]', '[0,1]', '[0]'], '[0,1,2,3]'),
236
- 'channel_dims': ('combo', ['[0,1,2,3]', '[0,1,2]', '[0,1]', '[0]'], '[0,1,2,3]'),
237
- 'cell_mask_dim': ('combo', chans, None),
238
- 'cell_chann_dim': ('combo', chans, None),
239
- 'nucleus_mask_dim': ('combo', chans, None),
240
- 'nucleus_chann_dim': ('combo', chans, None),
241
- 'pathogen_mask_dim': ('combo', chans, None),
242
- 'pathogen_chann_dim': ('combo', chans, None),
243
- 'crop_mode': ('combo', ['cell', 'nucleus', 'pathogen', '[cell, nucleus, pathogen]', '[cell,nucleus, pathogen]'], ['cell']),
244
- 'magnification': ('combo', [20, 40, 60], 20),
245
- 'nucleus_channel': ('combo', chans_v2, None),
246
- 'cell_channel': ('combo', chans_v2, None),
247
- 'channel_of_interest': ('combo', chans_v2, None),
248
- 'pathogen_channel': ('combo', chans_v2, None),
249
- 'timelapse_mode': ('combo', ['trackpy', 'btrack'], 'trackpy'),
250
- 'train_mode': ('combo', ['erm', 'irm'], 'erm'),
251
- 'clustering': ('combo', ['dbscan', 'kmean'], 'dbscan'),
252
- 'reduction_method': ('combo', ['umap', 'tsne'], 'umap'),
253
- 'model_name': ('combo', ['cyto', 'cyto_2', 'cyto_3', 'nuclei'], 'cyto'),
254
- 'regression_type': ('combo', ['ols','gls','wls','rlm','glm','mixed','quantile','logit','probit','poisson','lasso','ridge'], 'ols'),
255
- 'timelapse_objects': ('combo', ['cell', 'nucleus', 'pathogen', 'cytoplasm', None], None),
256
- 'model_type': ('combo', torchvision_models, 'resnet50'),
257
- 'optimizer_type': ('combo', ['adamw', 'adam'], 'adamw'),
258
- 'schedule': ('combo', ['reduce_lr_on_plateau', 'step_lr'], 'reduce_lr_on_plateau'),
259
- 'loss_type': ('combo', ['focal_loss', 'binary_cross_entropy_with_logits'], 'focal_loss'),
260
- 'normalize_by': ('combo', ['fov', 'png'], 'png'),
261
- 'agg_type': ('combo', ['mean', 'median'], 'mean'),
262
- 'grouping': ('combo', ['mean', 'median'], 'mean'),
263
- 'min_max': ('combo', ['allq', 'all'], 'allq'),
264
- 'transform': ('combo', ['log', 'sqrt', 'square', None], None)
265
- }
266
-
267
- for key, value in settings.items():
268
- if key in special_cases:
269
- variables[key] = special_cases[key]
270
- elif isinstance(value, bool):
271
- variables[key] = ('check', None, value)
272
- elif isinstance(value, int) or isinstance(value, float):
273
- variables[key] = ('entry', None, value)
274
- elif isinstance(value, str):
275
- variables[key] = ('entry', None, value)
276
- elif value is None:
277
- variables[key] = ('entry', None, value)
278
- elif isinstance(value, list):
279
- variables[key] = ('entry', None, str(value))
280
- else:
281
- variables[key] = ('entry', None, str(value))
282
- return variables
283
-
284
- def setup_settings_panel(vertical_container, settings_type='mask', window_dimensions=[500, 1000]):
170
+ def setup_settings_panel(vertical_container, settings_type='mask'):
285
171
  global vars_dict, scrollable_frame
286
172
  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
173
+ from .gui_utils import convert_settings_dict_for_gui, set_element_size
174
+
175
+ size_dict = set_element_size(vertical_container)
176
+ settings_width = size_dict['settings_width']
287
177
 
288
- width = (window_dimensions[0]) // 6
289
- height = window_dimensions[1]
178
+ # Create a PanedWindow for the settings panel
179
+ settings_paned_window = tk.PanedWindow(vertical_container, orient=tk.HORIZONTAL)
180
+ vertical_container.add(settings_paned_window, stretch="always")
181
+
182
+ settings_frame = tk.Frame(settings_paned_window, width=settings_width)
183
+ settings_frame.pack_propagate(False) # Prevent the frame from resizing based on its children
184
+
185
+ settings_paned_window.add(settings_frame)
290
186
 
291
- settings_frame = tk.Frame(vertical_container)
292
- vertical_container.add(settings_frame, stretch="always")
293
- settings_label = spacrLabel(settings_frame, text="Settings", anchor='center', justify='center', align="center")
294
- settings_label.grid(row=0, column=0, pady=10, padx=10)
295
187
  scrollable_frame = spacrFrame(settings_frame)
296
188
  scrollable_frame.grid(row=1, column=0, sticky="nsew")
297
189
  settings_frame.grid_rowconfigure(1, weight=1)
@@ -328,7 +220,7 @@ def setup_settings_panel(vertical_container, settings_type='mask', window_dimens
328
220
  vars_dict = generate_fields(variables, scrollable_frame)
329
221
 
330
222
  containers = [settings_frame]
331
- widgets = [settings_label, scrollable_frame]
223
+ widgets = [scrollable_frame]
332
224
 
333
225
  style = ttk.Style(vertical_container)
334
226
  _ = set_dark_style(style, containers=containers, widgets=widgets)
@@ -362,19 +254,44 @@ def setup_plot_section(vertical_container):
362
254
 
363
255
  def setup_console(vertical_container):
364
256
  global console_output
365
- console_frame = tk.Frame(vertical_container)
366
- vertical_container.add(console_frame, stretch="always")
367
- console_label = spacrLabel(console_frame, text="Console", anchor='center', justify='center', align="center")
368
- console_label.grid(row=0, column=0, pady=10, padx=10)
369
- console_output = scrolledtext.ScrolledText(console_frame, height=10)
370
- console_output.grid(row=1, column=0, sticky="nsew")
371
- console_frame.grid_rowconfigure(1, weight=1)
257
+ from .gui_elements import set_dark_style
258
+
259
+ # Apply dark style and get style output
260
+ style = ttk.Style()
261
+ style_out = set_dark_style(style)
262
+
263
+ # Create a PanedWindow to hold the main content and console sections
264
+ main_paned_window = tk.PanedWindow(vertical_container, orient=tk.VERTICAL, bg=style_out['bg_color'])
265
+ vertical_container.add(main_paned_window, stretch="always")
266
+
267
+ # Create the main content frame
268
+ main_content_frame = tk.Frame(main_paned_window, bg=style_out['bg_color'])
269
+ main_paned_window.add(main_content_frame, stretch="always")
270
+
271
+ # Create a frame to hold the console and button sections
272
+ console_button_frame = tk.Frame(main_paned_window, bg=style_out['bg_color'])
273
+ main_paned_window.add(console_button_frame, stretch="always")
274
+
275
+ # Create the main console frame
276
+ console_frame = tk.Frame(console_button_frame, bg=style_out['bg_color'])
277
+ console_frame.pack(fill=tk.BOTH, expand=True)
278
+
279
+ # Create the scrollable frame (which is a Text widget) with white text
280
+ family = style_out['font_family']
281
+ size = style_out['font_size'] -2
282
+ font = tkFont.Font(family=family, size=size)
283
+ console_output = tk.Text(console_frame, bg=style_out['bg_color'], fg=style_out['fg_color'], font=font)
284
+ console_output.pack(fill=tk.BOTH, expand=True)
285
+
286
+ # Configure the grid to allow expansion
287
+ console_frame.grid_rowconfigure(0, weight=1)
372
288
  console_frame.grid_columnconfigure(0, weight=1)
373
- containers = [console_frame]
374
- widgets = [console_label, console_output]
375
- style = ttk.Style(vertical_container)
376
- _ = set_dark_style(style, containers=containers, widgets=widgets)
377
- return console_output
289
+
290
+ # Create a lower frame to act as the anchor point
291
+ lower_frame = tk.Frame(console_button_frame, bg=style_out['bg_color'])
292
+ lower_frame.pack(fill=tk.X, expand=False)
293
+
294
+ return console_output, console_frame
378
295
 
379
296
  def setup_progress_frame(vertical_container):
380
297
  global progress_output
@@ -394,139 +311,54 @@ def setup_progress_frame(vertical_container):
394
311
  _ = set_dark_style(style, containers=containers, widgets=widgets)
395
312
  return progress_output
396
313
 
397
- def download_hug_dataset():
398
- global vars_dict, q
399
- dataset_repo_id = "einarolafsson/toxo_mito"
400
- settings_repo_id = "einarolafsson/spacr_settings"
401
- dataset_subfolder = "plate1"
402
- local_dir = os.path.join(os.path.expanduser("~"), "datasets") # Set to the home directory
403
-
404
- # Download the dataset
405
- try:
406
- dataset_path = download_dataset(dataset_repo_id, dataset_subfolder, local_dir)
407
- if 'src' in vars_dict:
408
- vars_dict['src'][2].set(dataset_path)
409
- q.put(f"Set source path to: {vars_dict['src'][2].get()}\n")
410
- q.put(f"Dataset downloaded to: {dataset_path}\n")
411
- except Exception as e:
412
- q.put(f"Failed to download dataset: {e}\n")
413
-
414
- # Download the settings files
415
- try:
416
- settings_path = download_dataset(settings_repo_id, "", local_dir)
417
- q.put(f"Settings downloaded to: {settings_path}\n")
418
- except Exception as e:
419
- q.put(f"Failed to download settings: {e}\n")
420
-
421
- def download_dataset(repo_id, subfolder, local_dir=None, retries=5, delay=5):
422
- global q
423
- """
424
- Downloads a dataset or settings files from Hugging Face and returns the local path.
425
-
426
- Args:
427
- repo_id (str): The repository ID (e.g., 'einarolafsson/toxo_mito' or 'einarolafsson/spacr_settings').
428
- subfolder (str): The subfolder path within the repository (e.g., 'plate1' or the settings subfolder).
429
- local_dir (str): The local directory where the files will be saved. Defaults to the user's home directory.
430
- retries (int): Number of retry attempts in case of failure.
431
- delay (int): Delay in seconds between retries.
432
-
433
- Returns:
434
- str: The local path to the downloaded files.
435
- """
436
- if local_dir is None:
437
- local_dir = os.path.join(os.path.expanduser("~"), "datasets")
438
-
439
- local_subfolder_dir = os.path.join(local_dir, subfolder if subfolder else "settings")
440
- if not os.path.exists(local_subfolder_dir):
441
- os.makedirs(local_subfolder_dir)
442
- elif len(os.listdir(local_subfolder_dir)) > 0:
443
- q.put(f"Files already downloaded to: {local_subfolder_dir}")
444
- return local_subfolder_dir
445
-
446
- attempt = 0
447
- while attempt < retries:
448
- try:
449
- files = list_repo_files(repo_id, repo_type="dataset")
450
- subfolder_files = [file for file in files if file.startswith(subfolder) or (subfolder == "" and file.endswith('.csv'))]
451
-
452
- for file_name in subfolder_files:
453
- for download_attempt in range(retries):
454
- try:
455
- url = f"https://huggingface.co/datasets/{repo_id}/resolve/main/{file_name}?download=true"
456
- response = requests.get(url, stream=True)
457
- response.raise_for_status()
458
-
459
- local_file_path = os.path.join(local_subfolder_dir, os.path.basename(file_name))
460
- with open(local_file_path, 'wb') as file:
461
- for chunk in response.iter_content(chunk_size=8192):
462
- file.write(chunk)
463
- q.put(f"Downloaded file: {file_name}")
464
- break
465
- except (requests.HTTPError, requests.Timeout) as e:
466
- q.put(f"Error downloading {file_name}: {e}. Retrying in {delay} seconds...")
467
- time.sleep(delay)
468
- else:
469
- raise Exception(f"Failed to download {file_name} after multiple attempts.")
470
-
471
- return local_subfolder_dir
472
-
473
- except (requests.HTTPError, requests.Timeout) as e:
474
- q.put(f"Error downloading files: {e}. Retrying in {delay} seconds...")
475
- attempt += 1
476
- time.sleep(delay)
477
-
478
- raise Exception("Failed to download files after multiple attempts.")
479
-
480
-
481
-
482
314
  def setup_button_section(horizontal_container, settings_type='mask', run=True, abort=True, download=True, import_btn=True):
483
- global button_frame, button_scrollable_frame, run_button, abort_button, download_dataset_button, import_button, q, fig_queue, vars_dict, progress_bar
484
- from .gui_utils import set_element_size
315
+ 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
316
+ from .gui_utils import set_element_size, download_hug_dataset
317
+ from .settings import categories
318
+
485
319
  size_dict = set_element_size(horizontal_container)
486
- button_frame = tk.Frame(horizontal_container)
487
- horizontal_container.add(button_frame, stretch="always", sticky="nsew")
488
- button_frame.grid_rowconfigure(0, weight=0)
489
- button_frame.grid_rowconfigure(1, weight=1)
490
- button_frame.grid_columnconfigure(0, weight=1)
320
+ button_section_height = size_dict['panel_height']
321
+ button_frame = tk.Frame(horizontal_container, height=button_section_height)
322
+
323
+ # Prevent the frame from resizing based on the child widget
324
+ #button_frame.pack_propagate(False)
491
325
 
492
- categories_label = spacrLabel(button_frame, text="Categories", anchor='center', justify='center', align="center")
493
- categories_label.grid(row=0, column=0, pady=10, padx=10)
494
- button_scrollable_frame = spacrFrame(button_frame)
326
+ horizontal_container.add(button_frame, stretch="always", sticky="nsew")
327
+ button_scrollable_frame = spacrFrame(button_frame, scrollbar=False)
495
328
  button_scrollable_frame.grid(row=1, column=0, sticky="nsew")
496
-
497
- widgets = [categories_label, button_scrollable_frame.scrollable_frame]
329
+ widgets = [button_scrollable_frame.scrollable_frame]
498
330
 
499
331
  btn_col = 0
500
- btn_row = 3
332
+ btn_row = 0
501
333
 
502
334
  if run:
503
- print(f'settings_type: {settings_type}')
504
- 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'])
335
+ 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)
505
336
  run_button.grid(row=btn_row, column=btn_col, pady=5, padx=5, sticky='ew')
506
337
  widgets.append(run_button)
507
- btn_row += 1
338
+ btn_col += 1
508
339
 
509
340
  if abort and settings_type in ['mask', 'measure', 'classify', 'sequencing', 'umap']:
510
- abort_button = spacrButton(button_scrollable_frame.scrollable_frame, text="abort", command=initiate_abort, show_text=False, size=size_dict['btn_size'])
341
+ abort_button = spacrButton(button_scrollable_frame.scrollable_frame, text="abort", command=lambda: initiate_abort(), show_text=False, size=size_dict['btn_size'], animation=False)
511
342
  abort_button.grid(row=btn_row, column=btn_col, pady=5, padx=5, sticky='ew')
512
343
  widgets.append(abort_button)
513
- btn_row += 1
344
+ btn_col += 1
514
345
 
515
346
  if download and settings_type in ['mask']:
516
- download_dataset_button = spacrButton(button_scrollable_frame.scrollable_frame, text="download", command=download_hug_dataset, show_text=False, size=size_dict['btn_size'])
347
+ 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)
517
348
  download_dataset_button.grid(row=btn_row, column=btn_col, pady=5, padx=5, sticky='ew')
518
349
  widgets.append(download_dataset_button)
519
- btn_row += 1
350
+ btn_col += 1
520
351
 
521
352
  if import_btn:
522
- import_button = spacrButton(button_scrollable_frame.scrollable_frame, text="settings", command=lambda: import_settings(settings_type),show_text=False, size=size_dict['btn_size'])
353
+ 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)
523
354
  import_button.grid(row=btn_row, column=btn_col, pady=5, padx=5, sticky='ew')
524
355
  widgets.append(import_button)
525
356
  btn_row += 1
526
357
 
527
358
  # Add the progress bar under the settings category menu
528
359
  progress_bar = spacrProgressBar(button_scrollable_frame.scrollable_frame, orient='horizontal', mode='determinate')
529
- progress_bar.grid(row=0, column=0, columnspan=2, pady=5, padx=5, sticky='ew')
360
+ progress_bar.grid(row=btn_row, column=0, columnspan=6, pady=5, padx=5, sticky='ew')
361
+ progress_bar.set_label_position() # Set the label position after grid placement
530
362
  widgets.append(progress_bar)
531
363
 
532
364
  if vars_dict is not None:
@@ -535,239 +367,14 @@ def setup_button_section(horizontal_container, settings_type='mask', run=True, a
535
367
  style = ttk.Style(horizontal_container)
536
368
  _ = set_dark_style(style, containers=[button_frame], widgets=widgets)
537
369
 
538
- return button_scrollable_frame
539
-
540
- def setup_help_section(horizontal_container, settings_type='mask'):
541
- from .settings import descriptions
542
-
543
- description_frame = tk.Frame(horizontal_container)
544
- horizontal_container.add(description_frame, stretch="always", sticky="nsew")
545
- description_frame.grid_columnconfigure(0, weight=1)
546
- description_frame.grid_rowconfigure(1, weight=1) # Ensure the text widget row is expandable
547
-
548
- description_label = spacrLabel(description_frame, text=f"{settings_type} Module", anchor='center', justify='center', align="center")
549
- description_label.grid(row=0, column=0, pady=10, padx=10, sticky='ew')
550
-
551
- # Set background color directly
552
- style_out = set_dark_style(ttk.Style())
553
- bg_color = style_out['bg_color']
554
- fg_color = style_out['fg_color']
555
-
556
- description_text_widget = tk.Text(description_frame, wrap="word", bg=bg_color, fg=fg_color)
557
- description_text_widget.grid(row=1, column=0, sticky="nsew")
558
-
559
- description_text = descriptions.get(settings_type, "No description available for this module.")
560
- description_text_widget.insert("1.0", description_text)
561
- description_text_widget.config(state="disabled") # Make the text widget read-only
562
-
563
- def update_wraplength(event):
564
- new_width = event.width - 20 # Adjust as needed
565
- description_text_widget.config(width=new_width)
566
-
567
- description_text_widget.bind('<Configure>', update_wraplength)
568
-
569
- style = ttk.Style(horizontal_container)
570
- _ = set_dark_style(style, containers=[description_frame], widgets=[description_label, description_text_widget])
571
-
572
- return description_frame
573
-
574
- def hide_all_settings(vars_dict, categories):
575
- """
576
- Function to initially hide all settings in the GUI.
577
-
578
- Parameters:
579
- - categories: dict, The categories of settings with their corresponding settings.
580
- - vars_dict: dict, The dictionary containing the settings and their corresponding widgets.
581
- """
582
-
583
- if categories is None:
584
- from .settings import categories
585
-
586
- for category, settings in categories.items():
587
- if any(setting in vars_dict for setting in settings):
588
- vars_dict[category] = (None, None, tk.IntVar(value=0))
589
-
590
- # Initially hide all settings
591
- for setting in settings:
592
- if setting in vars_dict:
593
- label, widget, _ = vars_dict[setting]
594
- label.grid_remove()
595
- widget.grid_remove()
596
- return vars_dict
597
-
598
- def toggle_settings(button_scrollable_frame):
599
- global vars_dict
600
- from .settings import categories
601
-
602
- if vars_dict is None:
603
- raise ValueError("vars_dict is not initialized.")
604
-
605
- active_categories = set()
606
-
607
- def toggle_category(settings):
608
- for setting in settings:
609
- if setting in vars_dict:
610
- label, widget, _ = vars_dict[setting]
611
- if widget.grid_info():
612
- label.grid_remove()
613
- widget.grid_remove()
614
- else:
615
- label.grid()
616
- widget.grid()
617
-
618
- def on_category_select(selected_category):
619
- if selected_category == "Select Category":
620
- return
621
- if selected_category in categories:
622
- toggle_category(categories[selected_category])
623
- if selected_category in active_categories:
624
- active_categories.remove(selected_category)
625
- else:
626
- active_categories.add(selected_category)
627
- category_dropdown.update_styles(active_categories)
628
- category_var.set("Select Category")
629
-
630
- category_var = tk.StringVar()
631
- non_empty_categories = [category for category, settings in categories.items() if any(setting in vars_dict for setting in settings)]
632
- category_dropdown = spacrDropdownMenu(button_scrollable_frame.scrollable_frame, category_var, non_empty_categories, command=on_category_select)
633
- category_dropdown.grid(row=7, column=0, sticky="ew", pady=2, padx=2)
634
- vars_dict = hide_all_settings(vars_dict, categories)
635
-
636
- def process_fig_queue():
637
- global canvas, fig_queue, canvas_widget, parent_frame
638
-
639
- def clear_canvas(canvas):
640
- for ax in canvas.figure.get_axes():
641
- ax.clear()
642
- canvas.draw_idle()
643
-
644
- try:
645
- while not fig_queue.empty():
646
- clear_canvas(canvas)
647
- fig = fig_queue.get_nowait()
648
- for ax in fig.get_axes():
649
- ax.set_xticks([]) # Remove x-axis ticks
650
- ax.set_yticks([]) # Remove y-axis ticks
651
- ax.xaxis.set_visible(False) # Hide the x-axis
652
- ax.yaxis.set_visible(False) # Hide the y-axis
653
- fig.tight_layout()
654
- fig.set_facecolor('black')
655
- canvas.figure = fig
656
- fig_width, fig_height = canvas_widget.winfo_width(), canvas_widget.winfo_height()
657
- fig.set_size_inches(fig_width / fig.dpi, fig_height / fig.dpi, forward=True)
658
- canvas.draw_idle()
659
- except Exception as e:
660
- traceback.print_exc()
661
- finally:
662
- after_id = canvas_widget.after(100, process_fig_queue)
663
- parent_frame.after_tasks.append(after_id)
664
-
665
- def process_console_queue():
666
- global q, console_output, parent_frame, progress_bar
667
-
668
- # Initialize function attribute if it doesn't exist
669
- if not hasattr(process_console_queue, "completed_tasks"):
670
- process_console_queue.completed_tasks = []
671
-
672
- ansi_escape_pattern = re.compile(r'\x1B\[[0-?]*[ -/]*[@-~]')
673
-
674
- while not q.empty():
675
- message = q.get_nowait()
676
- clean_message = ansi_escape_pattern.sub('', message)
677
- console_output.insert(tk.END, clean_message + "\n")
678
- console_output.see(tk.END)
679
-
680
- # Check if the message contains progress information
681
- if clean_message.startswith("Progress"):
682
- try:
683
- # Extract the progress information
684
- match = re.search(r'(\d+)/(\d+)', clean_message)
685
- if match:
686
- current_progress = int(match.group(1))
687
- total_progress = int(match.group(2))
688
-
689
- # Add the task to the completed set
690
- process_console_queue.completed_tasks.append(current_progress)
691
-
692
- # Calculate the unique progress count
693
- unique_progress_count = len(np.unique(process_console_queue.completed_tasks))
694
-
695
- # Update the progress bar
696
- if progress_bar:
697
- progress_bar['maximum'] = total_progress
698
- progress_bar['value'] = unique_progress_count
699
-
700
- # Extract and update additional information
701
- operation_match = re.search(r'operation_type: ([\w\s]+)', clean_message)
702
- if operation_match:
703
- progress_bar.operation_type = operation_match.group(1)
704
-
705
- time_image_match = re.search(r'Time/image: ([\d.]+) sec', clean_message)
706
- if time_image_match:
707
- progress_bar.time_image = float(time_image_match.group(1))
708
-
709
- time_batch_match = re.search(r'Time/batch: ([\d.]+) sec', clean_message)
710
- if time_batch_match:
711
- progress_bar.time_batch = float(time_batch_match.group(1))
712
-
713
- time_left_match = re.search(r'Time_left: ([\d.]+) min', clean_message)
714
- if time_left_match:
715
- progress_bar.time_left = float(time_left_match.group(1))
716
-
717
- # Update the progress label
718
- if progress_bar.progress_label:
719
- progress_bar.update_label()
720
-
721
- # Clear completed tasks when progress is complete
722
- if unique_progress_count >= total_progress:
723
- process_console_queue.completed_tasks.clear()
724
- except Exception as e:
725
- print(f"Error parsing progress message: {e}")
726
-
727
- after_id = console_output.after(100, process_console_queue)
728
- parent_frame.after_tasks.append(after_id)
729
-
730
- 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, usage_bars_var):
731
- global q, console_output, parent_frame, vars_dict, canvas, canvas_widget, scrollable_frame, fig_queue, progress_bar, usage_bars
732
- q = q_var
733
- console_output = console_output_var
734
- parent_frame = parent_frame_var
735
- vars_dict = vars_dict_var
736
- canvas = canvas_var
737
- canvas_widget = canvas_widget_var
738
- scrollable_frame = scrollable_frame_var
739
- fig_queue = fig_queue_var
740
- progress_bar = progress_bar_var
741
- usage_bars = usage_bars_var
742
-
743
- def create_containers(parent_frame):
744
- vertical_container = tk.PanedWindow(parent_frame, orient=tk.VERTICAL)
745
- horizontal_container = tk.PanedWindow(vertical_container, orient=tk.HORIZONTAL)
746
- settings_frame = tk.Frame(horizontal_container)
747
- return vertical_container, horizontal_container, settings_frame
748
-
749
- def setup_frame(parent_frame):
750
- style = ttk.Style(parent_frame)
751
- vertical_container, horizontal_container, settings_frame = create_containers(parent_frame)
752
- containers = [vertical_container, horizontal_container, settings_frame]
753
-
754
- set_dark_style(style, parent_frame, containers)
755
- set_default_font(parent_frame, font_name="Helvetica", size=8)
756
-
757
- vertical_container.grid(row=0, column=0, sticky=tk.NSEW)
758
- vertical_container.add(horizontal_container, stretch="always")
759
- horizontal_container.grid_columnconfigure(0, weight=1)
760
- horizontal_container.grid_columnconfigure(1, weight=1)
761
- settings_frame.grid_rowconfigure(0, weight=0)
762
- settings_frame.grid_rowconfigure(1, weight=1)
763
- settings_frame.grid_columnconfigure(0, weight=1)
764
- horizontal_container.add(settings_frame, stretch="always", sticky="nsew")
765
-
766
- return parent_frame, vertical_container, horizontal_container
370
+ return button_scrollable_frame, btn_col
767
371
 
768
- def setup_usage_panel(horizontal_container):
372
+ def setup_usage_panel(horizontal_container, btn_col):
769
373
  global usage_bars
770
374
  from .gui_utils import set_element_size
375
+ from .gui_elements import set_dark_style
376
+
377
+ usg_col = 1
771
378
 
772
379
  def update_usage(ram_bar, vram_bar, gpu_bar, usage_bars, parent_frame):
773
380
  # Update RAM usage
@@ -792,34 +399,39 @@ def setup_usage_panel(horizontal_container):
792
399
  parent_frame.after(1000, update_usage, ram_bar, vram_bar, gpu_bar, usage_bars, parent_frame)
793
400
 
794
401
  size_dict = set_element_size(horizontal_container)
795
- print(size_dict)
402
+ usage_panel_height = size_dict['panel_height']
403
+ usage_frame = tk.Frame(horizontal_container, height=usage_panel_height)
404
+ horizontal_container.add(usage_frame)
796
405
 
797
- usage_frame = tk.Frame(horizontal_container)
798
- horizontal_container.add(usage_frame, stretch="always", sticky="nsew")
799
406
  usage_frame.grid_rowconfigure(0, weight=0)
800
407
  usage_frame.grid_rowconfigure(1, weight=1)
801
408
  usage_frame.grid_columnconfigure(0, weight=1)
802
409
  usage_frame.grid_columnconfigure(1, weight=1)
803
410
 
804
- usage_label = spacrLabel(usage_frame, text="Hardware Stats", anchor='center', justify='center', align="center")
805
- usage_label.grid(row=0, column=0, pady=10, padx=10, columnspan=2)
806
-
807
- usage_scrollable_frame = spacrFrame(usage_frame)
411
+ usage_scrollable_frame = spacrFrame(usage_frame, scrollbar=False)
808
412
  usage_scrollable_frame.grid(row=1, column=0, sticky="nsew", columnspan=2)
809
- widgets = [usage_label, usage_scrollable_frame.scrollable_frame]
413
+ widgets = [usage_scrollable_frame.scrollable_frame]
414
+
810
415
  usage_bars = []
811
- max_elements_per_column = 12
416
+ max_elements_per_column = 6
812
417
  row = 0
813
418
  col = 0
814
419
 
815
420
  # Initialize RAM, VRAM, and GPU bars as None
816
421
  ram_bar, vram_bar, gpu_bar = None, None, None
422
+
423
+ # Configure the style for the label
424
+ style = ttk.Style()
425
+ style_out = set_dark_style(style)
426
+ size = style_out['font_size'] - 2
427
+ usage_font = tkFont.Font(family=style_out['font_family'], size=size)
428
+ style.configure("usage.TLabel", font=usage_font, foreground=style_out['fg_color'])
817
429
 
818
430
  # Try adding RAM bar
819
431
  try:
820
432
  ram_info = psutil.virtual_memory()
821
433
  ram_label_text = f"RAM"
822
- label = ttk.Label(usage_scrollable_frame.scrollable_frame, text=ram_label_text, anchor='w')
434
+ label = ttk.Label(usage_scrollable_frame.scrollable_frame, text=ram_label_text, anchor='w', style="usage.TLabel")
823
435
  label.grid(row=row, column=2 * col, pady=5, padx=5, sticky='w')
824
436
  ram_bar = spacrProgressBar(usage_scrollable_frame.scrollable_frame, orient='horizontal', mode='determinate', length=size_dict['bar_size'], label=False)
825
437
  ram_bar.grid(row=row, column=2 * col + 1, pady=5, padx=5, sticky='ew')
@@ -836,7 +448,7 @@ def setup_usage_panel(horizontal_container):
836
448
  if gpus:
837
449
  gpu = gpus[0]
838
450
  vram_label_text = f"VRAM"
839
- label = ttk.Label(usage_scrollable_frame.scrollable_frame, text=vram_label_text, anchor='w')
451
+ label = ttk.Label(usage_scrollable_frame.scrollable_frame, text=vram_label_text, anchor='w', style="usage.TLabel")
840
452
  label.grid(row=row, column=2 * col, pady=5, padx=5, sticky='w')
841
453
  vram_bar = spacrProgressBar(usage_scrollable_frame.scrollable_frame, orient='horizontal', mode='determinate', length=size_dict['bar_size'], label=False)
842
454
  vram_bar.grid(row=row, column=2 * col + 1, pady=5, padx=5, sticky='ew')
@@ -846,7 +458,7 @@ def setup_usage_panel(horizontal_container):
846
458
  row += 1
847
459
 
848
460
  gpu_label_text = f"GPU"
849
- label = ttk.Label(usage_scrollable_frame.scrollable_frame, text=gpu_label_text, anchor='w')
461
+ label = ttk.Label(usage_scrollable_frame.scrollable_frame, text=gpu_label_text, anchor='w', style="usage.TLabel")
850
462
  label.grid(row=row, column=2 * col, pady=5, padx=5, sticky='w')
851
463
  gpu_bar = spacrProgressBar(usage_scrollable_frame.scrollable_frame, orient='horizontal', mode='determinate', length=size_dict['bar_size'], label=False)
852
464
  gpu_bar.grid(row=row, column=2 * col + 1, pady=5, padx=5, sticky='ew')
@@ -861,12 +473,12 @@ def setup_usage_panel(horizontal_container):
861
473
  try:
862
474
  cpu_cores = psutil.cpu_count(logical=True)
863
475
  cpu_freq = psutil.cpu_freq()
864
-
476
+
865
477
  for core in range(cpu_cores):
866
478
  if row > 0 and row % max_elements_per_column == 0:
867
479
  col += 1
868
480
  row = 0
869
- label = ttk.Label(usage_scrollable_frame.scrollable_frame, text=f"Core {core+1}", anchor='w')
481
+ label = ttk.Label(usage_scrollable_frame.scrollable_frame, text=f"Core {core+1}", anchor='w', style="usage.TLabel")
870
482
  label.grid(row=row, column=2 * col, pady=2, padx=5, sticky='w')
871
483
  bar = spacrProgressBar(usage_scrollable_frame.scrollable_frame, orient='horizontal', mode='determinate', length=size_dict['bar_size'], label=False)
872
484
  bar.grid(row=row, column=2 * col + 1, pady=2, padx=5, sticky='ew')
@@ -877,42 +489,8 @@ def setup_usage_panel(horizontal_container):
877
489
  except Exception as e:
878
490
  print(f"Could not add CPU core usage bars: {e}")
879
491
 
880
- # Adding the text box for hardware information
881
- #hardware_frame = tk.Frame(horizontal_container)
882
- #horizontal_container.add(hardware_frame, stretch="always", sticky="nsew")
883
- #hardware_frame.grid_columnconfigure(0, weight=1)
884
-
885
- #hardware_info = tk.Text(hardware_frame, height=1, wrap='none', bg='black', fg='white', bd=0)
886
- #hardware_info.grid(row=0, column=0, pady=10, padx=5, sticky='ew')
887
-
888
- #hardware_text = ""
889
- #try:
890
- # ram_info = psutil.virtual_memory()
891
- # hardware_text += f"RAM: {ram_info.total / (1024 ** 3):.1f} GB "
892
- #except Exception as e:
893
- # hardware_text += f"RAM: Could not retrieve ({e}) "
894
-
895
- #try:
896
- # gpus = GPUtil.getGPUs()
897
- # if gpus:
898
- # gpu = gpus[0]
899
- # hardware_text += f"VRAM: {gpu.memoryTotal / 1024:.1f} GB "
900
- # hardware_text += f"GPU: {gpu.name} "
901
- #except Exception as e:
902
- # hardware_text += f"VRAM and GPU: Could not retrieve ({e}) "
903
-
904
- #try:
905
- # if cpu_freq:
906
- # hardware_text += f"CPU Max Clock Speed: {cpu_freq.max / 1000:.0f} GHz"
907
- #except Exception as e:
908
- # hardware_text += f"CPU Max Clock Speed: Could not retrieve ({e})"
909
-
910
- #hardware_info.insert(tk.END, hardware_text)
911
- #hardware_info.configure(state='disabled')
912
- #widgets.append(hardware_info)
913
-
914
492
  style = ttk.Style(horizontal_container)
915
- _ = set_dark_style(style, containers=[usage_frame], widgets=widgets) # hardware_frame
493
+ _ = set_dark_style(style, containers=[usage_frame], widgets=widgets)
916
494
 
917
495
  if ram_bar is None:
918
496
  ram_bar = spacrProgressBar(usage_scrollable_frame.scrollable_frame, orient='horizontal', mode='determinate', length=size_dict['bar_size'], label=False)
@@ -920,37 +498,158 @@ def setup_usage_panel(horizontal_container):
920
498
  vram_bar = spacrProgressBar(usage_scrollable_frame.scrollable_frame, orient='horizontal', mode='determinate', length=size_dict['bar_size'], label=False)
921
499
  if gpu_bar is None:
922
500
  gpu_bar = spacrProgressBar(usage_scrollable_frame.scrollable_frame, orient='horizontal', mode='determinate', length=size_dict['bar_size'], label=False)
501
+
502
+ update_usage(ram_bar, vram_bar, gpu_bar, usage_bars, usage_frame)
503
+ return usage_scrollable_frame, usage_bars, usg_col
504
+
505
+ def initiate_abort():
506
+ global thread_control, q, parent_frame
507
+ if thread_control.get("run_thread") is not None:
508
+ try:
509
+ q.put("Aborting processes...")
510
+ thread_control.get("run_thread").terminate()
511
+ thread_control["run_thread"] = None
512
+ q.put("Processes aborted.")
513
+ except Exception as e:
514
+ q.put(f"Error aborting process: {e}")
515
+
516
+ thread_control = {"run_thread": None, "stop_requested": False}
517
+
518
+
519
+ def start_process(q=None, fig_queue=None, settings_type='mask'):
520
+ global thread_control, vars_dict, parent_frame
521
+ from .settings import check_settings, expected_types
522
+ from .gui_utils import run_function_gui
523
+
524
+ if q is None:
525
+ q = Queue()
526
+ if fig_queue is None:
527
+ fig_queue = Queue()
528
+
529
+ try:
530
+ settings = check_settings(vars_dict, expected_types, q)
531
+ except ValueError as e:
532
+ q.put(f"Error: {e}")
533
+ return
534
+
535
+ if thread_control.get("run_thread") is not None:
536
+ initiate_abort()
537
+
538
+ stop_requested = Value('i', 0)
539
+ thread_control["stop_requested"] = stop_requested
540
+
541
+ process_args = (settings_type, settings, q, fig_queue, stop_requested)
542
+ if settings_type in [
543
+ 'mask', 'measure', 'simulation', 'sequencing', 'classify', 'cellpose_dataset',
544
+ 'train_cellpose', 'ml_analyze', 'cellpose_masks', 'cellpose_all', 'map_barcodes',
545
+ 'regression', 'recruitment', 'plaques', 'cellpose_compare', 'vision_scores',
546
+ 'vision_dataset'
547
+ ]:
548
+ thread_control["run_thread"] = Process(target=run_function_gui, args=process_args)
549
+ else:
550
+ q.put(f"Error: Unknown settings type '{settings_type}'")
551
+ return
552
+ thread_control["run_thread"].start()
553
+
554
+ def process_console_queue():
555
+ global q, console_output, parent_frame, progress_bar
556
+
557
+ # Initialize function attribute if it doesn't exist
558
+ if not hasattr(process_console_queue, "completed_tasks"):
559
+ process_console_queue.completed_tasks = []
560
+ if not hasattr(process_console_queue, "current_maximum"):
561
+ process_console_queue.current_maximum = None
923
562
 
924
- update_usage(ram_bar, vram_bar, gpu_bar, usage_bars, usage_frame)
925
- return usage_scrollable_frame, usage_bars
563
+ ansi_escape_pattern = re.compile(r'\x1B\[[0-?]*[ -/]*[@-~]')
564
+
565
+ while not q.empty():
566
+ message = q.get_nowait()
567
+ clean_message = ansi_escape_pattern.sub('', message)
568
+ console_output.insert(tk.END, clean_message + "\n")
569
+ console_output.see(tk.END)
570
+
571
+ # Check if the message contains progress information
572
+ if clean_message.startswith("Progress:"):
573
+ try:
574
+ # Extract the progress information
575
+ match = re.search(r'Progress: (\d+)/(\d+), operation_type: ([\w\s]*)(.*)', clean_message)
576
+ print('match', match)
577
+ if match:
578
+ current_progress = int(match.group(1))
579
+ total_progress = int(match.group(2))
580
+ operation_type = match.group(3).strip()
581
+ time_info = match.group(4).strip()
582
+
583
+ # Check if the maximum value has changed
584
+ if process_console_queue.current_maximum != total_progress:
585
+ process_console_queue.current_maximum = total_progress
586
+ process_console_queue.completed_tasks = []
587
+
588
+ # Add the task to the completed set
589
+ process_console_queue.completed_tasks.append(current_progress)
590
+
591
+ # Calculate the unique progress count
592
+ unique_progress_count = len(np.unique(process_console_queue.completed_tasks))
593
+
594
+ # Update the progress bar
595
+ if progress_bar:
596
+ progress_bar['maximum'] = total_progress
597
+ progress_bar['value'] = unique_progress_count
598
+
599
+ # Extract and update additional information
600
+ if operation_type:
601
+ progress_bar.operation_type = operation_type
602
+
603
+ time_image_match = re.search(r'Time/image: ([\d.]+) sec', time_info)
604
+ if time_image_match:
605
+ progress_bar.time_image = float(time_image_match.group(1))
606
+
607
+ time_batch_match = re.search(r'Time/batch: ([\d.]+) sec', time_info)
608
+ if time_batch_match:
609
+ progress_bar.time_batch = float(time_batch_match.group(1))
610
+
611
+ time_left_match = re.search(r'Time_left: ([\d.]+) min', time_info)
612
+ if time_left_match:
613
+ progress_bar.time_left = float(time_left_match.group(1))
614
+
615
+ # Update the progress label
616
+ if progress_bar.progress_label:
617
+ progress_bar.update_label()
618
+
619
+ # Clear completed tasks when progress is complete
620
+ if unique_progress_count >= total_progress:
621
+ process_console_queue.completed_tasks.clear()
622
+
623
+ except Exception as e:
624
+ print(f"Error parsing progress message: {e}")
625
+
626
+ after_id = console_output.after(10, process_console_queue)
627
+ parent_frame.after_tasks.append(after_id)
926
628
 
927
629
  def initiate_root(parent, settings_type='mask'):
928
- global q, fig_queue, parent_frame, scrollable_frame, button_frame, vars_dict, canvas, canvas_widget, button_scrollable_frame, progress_bar
929
- from .gui_utils import main_thread_update_function
630
+ global q, fig_queue, thread_control, parent_frame, scrollable_frame, button_frame, vars_dict, canvas, canvas_widget, button_scrollable_frame, progress_bar
631
+ from .gui_utils import main_thread_update_function, setup_frame, set_element_size
930
632
  from .gui import gui_app
633
+ from .settings import descriptions
634
+
931
635
  set_start_method('spawn', force=True)
932
636
  print("Initializing root with settings_type:", settings_type)
933
637
 
934
638
  parent_frame = parent
935
- parent_frame.update_idletasks()
936
- frame_width = int(parent_frame.winfo_width())
937
- frame_height = int(parent_frame.winfo_height())
938
- print(frame_width, frame_height)
939
- dims = [frame_width, frame_height]
940
639
 
941
- if not hasattr(parent_frame, 'after_tasks'):
942
- parent_frame.after_tasks = []
640
+ if not isinstance(parent_frame, (tk.Tk, tk.Toplevel)):
641
+ parent_window = parent_frame.winfo_toplevel()
642
+ else:
643
+ parent_window = parent_frame
943
644
 
944
- # Clear previous content instead of destroying the root
945
- for widget in parent_frame.winfo_children():
946
- try:
947
- widget.destroy()
948
- except tk.TclError as e:
949
- print(f"Error destroying widget: {e}")
645
+ parent_window.update_idletasks()
646
+
647
+ if not hasattr(parent_window, 'after_tasks'):
648
+ parent_window.after_tasks = []
950
649
 
951
650
  q = Queue()
952
651
  fig_queue = Queue()
953
- parent_frame, vertical_container, horizontal_container = setup_frame(parent_frame)
652
+ parent_frame, vertical_container, horizontal_container, settings_container = setup_frame(parent_frame)
954
653
 
955
654
  if settings_type == 'annotate':
956
655
  from .app_annotate import initiate_annotation_app
@@ -959,21 +658,24 @@ def initiate_root(parent, settings_type='mask'):
959
658
  from .app_make_masks import initiate_make_mask_app
960
659
  initiate_make_mask_app(horizontal_container)
961
660
  else:
962
- scrollable_frame, vars_dict = setup_settings_panel(horizontal_container, settings_type, window_dimensions=dims)
963
- button_scrollable_frame = setup_button_section(horizontal_container, settings_type)
964
-
965
- _, usage_bars = setup_usage_panel(horizontal_container)
966
- _ = setup_help_section(horizontal_container, settings_type)
967
-
661
+ scrollable_frame, vars_dict = setup_settings_panel(settings_container, settings_type)
662
+ print('setup_settings_panel')
968
663
  canvas, canvas_widget = setup_plot_section(vertical_container)
969
- console_output = setup_console(vertical_container)
664
+ console_output, _ = setup_console(vertical_container)
665
+ button_scrollable_frame, btn_col = setup_button_section(horizontal_container, settings_type)
666
+ _, usage_bars, btn_col = setup_usage_panel(horizontal_container, btn_col)
970
667
 
971
- set_globals(q, console_output, parent_frame, vars_dict, canvas, canvas_widget, scrollable_frame, fig_queue, progress_bar, usage_bars)
668
+ set_globals(thread_control, q, console_output, parent_frame, vars_dict, canvas, canvas_widget, scrollable_frame, fig_queue, progress_bar, usage_bars)
669
+ description_text = descriptions.get(settings_type, "No description available for this module.")
670
+
671
+ q.put(f"Console")
672
+ q.put(f" ")
673
+ q.put(description_text)
674
+
972
675
  process_console_queue()
973
676
  process_fig_queue()
974
- after_id = parent_frame.after(100, lambda: main_thread_update_function(parent_frame, q, fig_queue, canvas_widget))
975
- parent_frame.after_tasks.append(after_id)
677
+ after_id = parent_window.after(100, lambda: main_thread_update_function(parent_window, q, fig_queue, canvas_widget))
678
+ parent_window.after_tasks.append(after_id)
976
679
 
977
680
  print("Root initialization complete")
978
681
  return parent_frame, vars_dict
979
-