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/core.py +260 -54
- spacr/graph_learning.py +259 -65
- spacr/graph_learning_lap.py +73 -71
- spacr/gui_classify_app.py +5 -21
- spacr/gui_mask_app.py +9 -23
- spacr/gui_measure_app.py +10 -24
- spacr/gui_utils.py +65 -52
- spacr/io.py +248 -106
- spacr/measure.py +10 -16
- spacr/old_code.py +155 -1
- spacr/plot.py +92 -87
- spacr/timelapse.py +213 -52
- spacr/utils.py +219 -118
- {spacr-0.0.18.dist-info → spacr-0.0.20.dist-info}/METADATA +28 -26
- spacr-0.0.20.dist-info/RECORD +31 -0
- {spacr-0.0.18.dist-info → spacr-0.0.20.dist-info}/WHEEL +1 -1
- spacr/gui_temp.py +0 -212
- spacr/test_annotate_app.py +0 -58
- spacr/test_plot.py +0 -43
- spacr/test_train.py +0 -39
- spacr/test_utils.py +0 -33
- spacr-0.0.18.dist-info/RECORD +0 -36
- {spacr-0.0.18.dist-info → spacr-0.0.20.dist-info}/LICENSE +0 -0
- {spacr-0.0.18.dist-info → spacr-0.0.20.dist-info}/entry_points.txt +0 -0
- {spacr-0.0.18.dist-info → spacr-0.0.20.dist-info}/top_level.txt +0 -0
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,
|
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
|
65
|
+
global vars_dict
|
65
66
|
|
66
67
|
csv_file_path = filedialog.askopenfilename(filetypes=[("CSV files", "*.csv")])
|
67
|
-
|
68
|
-
|
69
|
-
|
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() #
|
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
|
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
|
-
|
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
|
137
|
+
settings[key] = [float(num) for num in ast.literal_eval(value)] if value else []
|
118
138
|
|
119
139
|
elif key == 'normalize':
|
120
|
-
|
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
|
-
|
124
|
-
|
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
|
-
|
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
|
-
|
135
|
-
|
136
|
-
|
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
|
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
|
-
#
|
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] =
|
163
|
+
settings[key] = var.get()
|
156
164
|
|
157
165
|
except SyntaxError as e:
|
158
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
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
|
-
|
417
|
-
|
427
|
+
'fps': ('entry', None, 2),
|
428
|
+
'remove_background': ('check', None, True),
|
418
429
|
'lower_quantile': ('entry', None, 0.01),
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
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
|
-
|
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
|
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
|