spacr 0.0.18__py3-none-any.whl → 0.0.20__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_measure_app.py CHANGED
@@ -7,7 +7,7 @@ import matplotlib.pyplot as plt
7
7
  matplotlib.use('Agg') # Use the non-GUI Agg backend
8
8
  from multiprocessing import Process, Queue, Value
9
9
  from ttkthemes import ThemedTk
10
- from tkinter import filedialog
10
+ from tkinter import filedialog, StringVar, BooleanVar, IntVar, DoubleVar, Tk
11
11
 
12
12
  try:
13
13
  ctypes.windll.shcore.SetProcessDpiAwareness(True)
@@ -16,7 +16,7 @@ except AttributeError:
16
16
 
17
17
  from .logger import log_function_call
18
18
  from .gui_utils import ScrollableFrame, StdoutRedirector, process_stdout_stderr, set_dark_style, set_default_font, generate_fields, create_dark_mode, main_thread_update_function
19
- from .gui_utils import measure_variables, measure_crop_wrapper, clear_canvas, safe_literal_eval, check_measure_gui_settings, add_measure_gui_defaults
19
+ from .gui_utils import measure_variables, measure_crop_wrapper, clear_canvas, safe_literal_eval, check_measure_gui_settings, read_settings_from_csv, update_settings_from_csv
20
20
 
21
21
  thread_control = {"run_thread": None, "stop_requested": False}
22
22
 
@@ -25,8 +25,8 @@ def run_measure_gui(q, fig_queue, stop_requested):
25
25
  global vars_dict
26
26
  process_stdout_stderr(q)
27
27
  try:
28
+ print('hello')
28
29
  settings = check_measure_gui_settings(vars_dict)
29
- settings = add_measure_gui_defaults(settings)
30
30
  #for key in settings:
31
31
  # value = settings[key]
32
32
  # print(key, value, type(value))
@@ -60,29 +60,15 @@ def initiate_abort():
60
60
  thread_control["run_thread"].terminate()
61
61
  thread_control["run_thread"] = None
62
62
 
63
+ @log_function_call
63
64
  def import_settings(scrollable_frame):
64
- global vars_dict, original_variables_structure
65
+ global vars_dict
65
66
 
66
67
  csv_file_path = filedialog.askopenfilename(filetypes=[("CSV files", "*.csv")])
67
-
68
- if not csv_file_path:
69
- return
70
-
71
- imported_variables = {}
72
-
73
- with open(csv_file_path, newline='') as csvfile:
74
- reader = csv.DictReader(csvfile)
75
- for row in reader:
76
- key = row['Key']
77
- value = row['Value']
78
- # Evaluate the value safely using safe_literal_eval
79
- imported_variables[key] = safe_literal_eval(value)
80
-
81
- # Track changed variables and apply the imported ones, printing changes as we go
82
- for key, var in vars_dict.items():
83
- if key in imported_variables and var.get() != imported_variables[key]:
84
- print(f"Updating '{key}' from '{var.get()}' to '{imported_variables[key]}'")
85
- var.set(imported_variables[key])
68
+ csv_settings = read_settings_from_csv(csv_file_path)
69
+ variables = measure_variables()
70
+ new_settings = update_settings_from_csv(variables, csv_settings)
71
+ vars_dict = generate_fields(new_settings, scrollable_frame)
86
72
 
87
73
  @log_function_call
88
74
  def initiate_measure_root(width, height):
@@ -201,7 +187,7 @@ def initiate_measure_root(width, height):
201
187
  _process_fig_queue()
202
188
  create_dark_mode(root, style, console_output)
203
189
 
204
- root.after(100, lambda: main_thread_update_function(root, q, fig_queue, canvas_widget, progress_label))
190
+ #root.after(100, lambda: main_thread_update_function(root, q, fig_queue, canvas_widget, progress_label))
205
191
 
206
192
  return root, vars_dict
207
193
 
spacr/gui_utils.py CHANGED
@@ -1,6 +1,6 @@
1
- import spacr, inspect, traceback, io, sys, ast, ctypes, matplotlib, re
2
- matplotlib.use('Agg')
1
+ import spacr, inspect, traceback, io, sys, ast, ctypes, matplotlib, re, csv
3
2
  import matplotlib.pyplot as plt
3
+ matplotlib.use('Agg')
4
4
  import numpy as np
5
5
  import tkinter as tk
6
6
  from tkinter import ttk, messagebox
@@ -14,6 +14,26 @@ except AttributeError:
14
14
 
15
15
  from .logger import log_function_call
16
16
 
17
+ def read_settings_from_csv(csv_file_path):
18
+ settings = {}
19
+ with open(csv_file_path, newline='') as csvfile:
20
+ reader = csv.DictReader(csvfile)
21
+ for row in reader:
22
+ key = row['Key']
23
+ value = row['Value']
24
+ settings[key] = value
25
+ return settings
26
+
27
+ def update_settings_from_csv(variables, csv_settings):
28
+ new_settings = variables.copy() # Start with a copy of the original settings
29
+ for key, value in csv_settings.items():
30
+ if key in new_settings:
31
+ # Get the variable type and options from the original settings
32
+ var_type, options, _ = new_settings[key]
33
+ # Update the default value with the CSV value, keeping the type and options unchanged
34
+ new_settings[key] = (var_type, options, value)
35
+ return new_settings
36
+
17
37
  def safe_literal_eval(value):
18
38
  try:
19
39
  # First, try to evaluate as a literal
@@ -103,62 +123,52 @@ def check_mask_gui_settings(vars_dict):
103
123
  def check_measure_gui_settings(vars_dict):
104
124
  settings = {}
105
125
  for key, var in vars_dict.items():
106
- value = var.get() # This retrieves the string representation for entries or the actual value for checkboxes and combos
126
+ value = var.get() # Retrieves the string representation for entries or the actual value for checkboxes and combos.
107
127
 
108
128
  try:
109
- if key == 'channels' or key == 'png_dims':
110
- # Converts string representation to a list of integers
129
+ if key in ['channels', 'png_dims']:
111
130
  settings[key] = [int(chan) for chan in ast.literal_eval(value)] if value else []
112
131
 
113
132
  elif key in ['cell_loc', 'pathogen_loc', 'treatment_loc']:
114
- settings[key] = ast.literal_eval(value) if value else None
115
-
133
+ # Convert to a list of lists of strings, ensuring all structures are lists.
134
+ settings[key] = [list(map(str, sublist)) for sublist in ast.literal_eval(value)] if value else []
135
+
116
136
  elif key == 'dialate_png_ratios':
117
- settings[key] = [float(num) for num in eval(value)] if value else None
137
+ settings[key] = [float(num) for num in ast.literal_eval(value)] if value else []
118
138
 
119
139
  elif key == 'normalize':
120
- # Converts 'normalize' into a list of two integers
121
- settings[key] = [int(num) for num in ast.literal_eval(value)] if value else None
140
+ settings[key] = [int(num) for num in ast.literal_eval(value)] if value else []
122
141
 
123
- elif key == 'normalize_by':
124
- # 'normalize_by' is a string, so directly assign the value
142
+ # Directly assign string values for these specific keys
143
+ elif key in ['normalize_by', 'experiment', 'measurement', 'input_folder']:
125
144
  settings[key] = value
126
145
 
127
146
  elif key == 'png_size':
128
- # Converts string representation into a list of lists of integers
129
- temp_val = ast.literal_eval(value) if value else []
130
- settings[key] = [list(map(int, dim)) for dim in temp_val] if temp_val else None
131
-
132
- # Handling for other keys as in your original function...
147
+ settings[key] = [list(map(int, dim)) for dim in ast.literal_eval(value)] if value else []
133
148
 
134
- elif key in ['pathogens', 'treatments', 'cells', 'crop_mode', 'timelapse_objects']:
135
- # Ensuring these are evaluated correctly as lists or other structures
136
- settings[key] = ast.literal_eval(value) if value else None
137
-
138
- elif key == 'timelapse_objects':
139
- # Ensure it's a list of strings
140
- settings[key] = eval(value) if value else []
141
-
142
- # Handling for keys that should be treated as strings directly
143
- elif key in ['normalize_by', 'experiment', 'measurement', 'input_folder']:
144
- settings[key] = str(value) if value else None
149
+ # Ensure these are lists of strings, converting from tuples if necessary
150
+ elif key in ['timelapse_objects', 'crop_mode', 'cells', 'pathogens', 'treatments']:
151
+ eval_value = ast.literal_eval(value) if value else []
152
+ settings[key] = list(map(str, eval_value)) if isinstance(eval_value, (list, tuple)) else [str(eval_value)]
145
153
 
146
- # Handling for single values that are not strings (int, float, bool)
154
+ # Handling for single non-string values (int, float, bool)
147
155
  elif key in ['cell_mask_dim', 'cell_min_size', 'nucleus_mask_dim', 'nucleus_min_size', 'pathogen_mask_dim', 'pathogen_min_size', 'cytoplasm_min_size', 'max_workers', 'channel_of_interest', 'nr_imgs']:
148
156
  settings[key] = int(value) if value else None
149
157
 
150
158
  elif key == 'um_per_pixel':
151
159
  settings[key] = float(value) if value else None
152
160
 
153
- # Direct handling of boolean values based on checkboxes
161
+ # Handling boolean values based on checkboxes
154
162
  elif key in ['save_png', 'use_bounding_box', 'save_measurements', 'plot', 'plot_filtration', 'include_uninfected', 'dialate_pngs', 'timelapse', 'representative_images']:
155
- settings[key] = bool(value)
163
+ settings[key] = var.get()
156
164
 
157
165
  except SyntaxError as e:
158
- messagebox.showerror("Error", f"Syntax error processing {key}: {str(e)}")
166
+ print(f"Syntax error processing {key}: {str(e)}")
167
+ #messagebox.showerror("Error", f"Syntax error processing {key}: {str(e)}")
159
168
  return None
160
169
  except Exception as e:
161
- messagebox.showerror("Error", f"Error processing {key}: {str(e)}")
170
+ print(f"Error processing {key}: {str(e)}")
171
+ #messagebox.showerror("Error", f"Error processing {key}: {str(e)}")
162
172
  return None
163
173
 
164
174
  return settings
@@ -268,6 +278,10 @@ def sim_variables():
268
278
  }
269
279
  return variables
270
280
 
281
+ def add_measure_gui_defaults(settings):
282
+ settings['compartments'] = ['pathogen', 'cytoplasm']
283
+ return settings
284
+
271
285
  def measure_variables():
272
286
  variables = {
273
287
  'input_folder':('entry', None, '/mnt/data/CellVoyager/40x/einar/mitotrackerHeLaToxoDsRed_20240224_123156/test_gui/merged'),
@@ -304,6 +318,7 @@ def measure_variables():
304
318
  'treatments': ('entry', None, '["cm","lovastatin_20uM"]'),
305
319
  'treatment_loc': ('entry', None, '[["c1","c2"], ["c3","c4"]]'),
306
320
  'channel_of_interest':('entry', None, 3),
321
+ 'compartments':('entry', None, '["pathogen","cytoplasm"]'),
307
322
  'measurement':('entry', None, 'mean_intensity'),
308
323
  'nr_imgs':('entry', None, 32),
309
324
  'um_per_pixel':('entry', None, 0.1)
@@ -352,7 +367,7 @@ def classify_variables():
352
367
  return variables
353
368
 
354
369
 
355
- @log_function_call
370
+ #@log_function_call
356
371
  def create_input_field(frame, label_text, row, var_type='entry', options=None, default_value=None):
357
372
  label = ttk.Label(frame, text=label_text, style='TLabel') # Assuming you have a dark mode style for labels too
358
373
  label.grid(column=0, row=row, sticky=tk.W, padx=5, pady=5)
@@ -376,10 +391,6 @@ def create_input_field(frame, label_text, row, var_type='entry', options=None, d
376
391
  var = None # Placeholder in case of an undefined var_type
377
392
 
378
393
  return var
379
-
380
- def add_measure_gui_defaults(settings):
381
- settings['compartments'] = ['pathogen', 'cytoplasm']
382
- return settings
383
394
 
384
395
  def mask_variables():
385
396
  variables = {
@@ -401,10 +412,10 @@ def mask_variables():
401
412
  'pathogen_background': ('entry', None, 100),
402
413
  'pathogen_Signal_to_noise': ('entry', None, 3),
403
414
  'pathogen_CP_prob': ('entry', None, 0),
404
- #'preprocess': ('check', None, True),
405
- #'masks': ('check', None, True),
406
- #'examples_to_plot': ('entry', None, 1),
407
- #'randomize': ('check', None, True),
415
+ 'preprocess': ('check', None, True),
416
+ 'masks': ('check', None, True),
417
+ 'examples_to_plot': ('entry', None, 1),
418
+ 'randomize': ('check', None, True),
408
419
  'batch_size': ('entry', None, 50),
409
420
  'timelapse': ('check', None, False),
410
421
  'timelapse_displacement': ('entry', None, None),
@@ -413,14 +424,14 @@ def mask_variables():
413
424
  'timelapse_remove_transient': ('check', None, True),
414
425
  'timelapse_mode': ('combo', ['trackpy', 'btrack'], 'trackpy'),
415
426
  'timelapse_objects': ('combo', ['cell','nucleus','pathogen','cytoplasm', None], None),
416
- #'fps': ('entry', None, 2),
417
- #'remove_background': ('check', None, True),
427
+ 'fps': ('entry', None, 2),
428
+ 'remove_background': ('check', None, True),
418
429
  'lower_quantile': ('entry', None, 0.01),
419
- #'merge': ('check', None, False),
420
- #'normalize_plots': ('check', None, True),
421
- #'all_to_mip': ('check', None, False),
422
- #'pick_slice': ('check', None, False),
423
- #'skip_mode': ('entry', None, None),
430
+ 'merge': ('check', None, False),
431
+ 'normalize_plots': ('check', None, True),
432
+ 'all_to_mip': ('check', None, False),
433
+ 'pick_slice': ('check', None, False),
434
+ 'skip_mode': ('entry', None, None),
424
435
  'save': ('check', None, True),
425
436
  'plot': ('check', None, True),
426
437
  'workers': ('entry', None, 30),
@@ -488,7 +499,7 @@ def set_dark_style(style):
488
499
  style.configure('TEntry', background='#333333', foreground='white')
489
500
  style.configure('TCheckbutton', background='#333333', foreground='white')
490
501
 
491
- @log_function_call
502
+ #@log_function_call
492
503
  def main_thread_update_function(root, q, fig_queue, canvas_widget, progress_label):
493
504
  try:
494
505
  ansi_escape_pattern = re.compile(r'\x1B\[[0-?]*[ -/]*[@-~]')
@@ -497,6 +508,8 @@ def main_thread_update_function(root, q, fig_queue, canvas_widget, progress_labe
497
508
  clean_message = ansi_escape_pattern.sub('', message)
498
509
  if clean_message.startswith("Progress"):
499
510
  progress_label.config(text=clean_message)
511
+ if clean_message.startswith("\rProgress"):
512
+ progress_label.config(text=clean_message)
500
513
  elif clean_message.startswith("Successfully"):
501
514
  progress_label.config(text=clean_message)
502
515
  elif clean_message.startswith("Processing"):
@@ -543,7 +556,6 @@ def clear_canvas(canvas):
543
556
  # Redraw the now empty canvas without changing its size
544
557
  canvas.draw_idle()
545
558
 
546
- @log_function_call
547
559
  def measure_crop_wrapper(settings, q, fig_queue):
548
560
  """
549
561
  Wraps the measure_crop function to integrate with GUI processes.
@@ -567,6 +579,7 @@ def measure_crop_wrapper(settings, q, fig_queue):
567
579
  plt.show = my_show
568
580
 
569
581
  try:
582
+ print('start')
570
583
  spacr.measure.measure_crop(settings=settings, annotation_settings={}, advanced_settings={})
571
584
  except Exception as e:
572
585
  errorMessage = f"Error during processing: {e}"
@@ -599,7 +612,7 @@ def preprocess_generate_masks_wrapper(settings, q, fig_queue):
599
612
  plt.show = my_show
600
613
 
601
614
  try:
602
- spacr.core.preprocess_generate_masks(settings['src'], settings=settings, advanced_settings={})
615
+ spacr.core.preprocess_generate_masks(settings['src'], settings=settings)
603
616
  except Exception as e:
604
617
  errorMessage = f"Error during processing: {e}"
605
618
  q.put(errorMessage) # Send the error message to the GUI via the queue