spacr 0.1.1__py3-none-any.whl → 0.1.6__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/measure.py CHANGED
@@ -617,6 +617,13 @@ def _measure_crop_core(index, time_ls, file, settings):
617
617
  start = time.time()
618
618
  try:
619
619
  source_folder = os.path.dirname(settings['input_folder'])
620
+ #if not os.path.basename(source_folder).endswith('merged'):
621
+ # source_folder = os.path.join(source_folder, 'merged')
622
+ # print(f'changed source_folder to {source_folder}')
623
+
624
+ #if not os.path.exists(source_folder):
625
+ # return
626
+
620
627
  file_name = os.path.splitext(file)[0]
621
628
  data = np.load(os.path.join(settings['input_folder'], file))
622
629
 
@@ -750,6 +757,15 @@ def _measure_crop_core(index, time_ls, file, settings):
750
757
  if isinstance(settings['crop_mode'], list):
751
758
  crop_ls = settings['crop_mode']
752
759
  size_ls = settings['png_size']
760
+
761
+ if isinstance(size_ls[0], int):
762
+ size_ls = [size_ls]
763
+ if len(crop_ls) > 1 and len(size_ls) == 1:
764
+ size_ls = size_ls * len(crop_ls)
765
+
766
+ if len(crop_ls) != len(size_ls):
767
+ print(f"Setting: size_ls: {settings['png_size']} should be a list of integers, or a list of lists of integers if crop_ls: {settings['crop_mode']} has multiple elements")
768
+
753
769
  for crop_idx, crop_mode in enumerate(crop_ls):
754
770
  width, height = size_ls[crop_idx]
755
771
  if crop_mode == 'cell':
@@ -926,9 +942,14 @@ def measure_crop(settings):
926
942
  settings = get_measure_crop_settings(settings)
927
943
  settings = measure_test_mode(settings)
928
944
 
929
- if not os.path.exists(settings['input_folder']):
930
- print(f"Error: {settings['input_folder']} does not exist")
931
- return
945
+ #src_fldr = settings['input_folder']
946
+ #if not os.path.basename(src_fldr).endswith('merged'):
947
+ # settings['input_folder'] = os.path.join(src_fldr, 'merged')
948
+ # print(f"changed input_folder to {src_fldr}")
949
+
950
+ #if not os.path.exists(settings['input_folder']):
951
+ # print(f'input_folder: {settings["input_folder"]} does not exist')
952
+ # return
932
953
 
933
954
  if settings['cell_mask_dim'] is None:
934
955
  settings['include_uninfected'] = True
spacr/measure_app.py ADDED
@@ -0,0 +1,246 @@
1
+ import sys, traceback, matplotlib, ctypes
2
+ import tkinter as tk
3
+ from tkinter import ttk, scrolledtext
4
+ from matplotlib.figure import Figure
5
+ from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
6
+ matplotlib.use('Agg') # Use the non-GUI Agg backend
7
+ from multiprocessing import Process, Queue, Value
8
+ from tkinter import filedialog
9
+
10
+ try:
11
+ ctypes.windll.shcore.SetProcessDpiAwareness(True)
12
+ except AttributeError:
13
+ pass
14
+
15
+ from .logger import log_function_call
16
+ from .gui_utils import ScrollableFrame, StdoutRedirector, CustomButton, ToggleSwitch, ToolTip
17
+ from .gui_utils import process_stdout_stderr, set_dark_style, set_default_font, generate_fields, main_thread_update_function, create_menu_bar
18
+ from .gui_utils import measure_variables, measure_crop_wrapper, clear_canvas, check_measure_gui_settings, read_settings_from_csv, update_settings_from_csv, style_text_boxes
19
+
20
+ thread_control = {"run_thread": None, "stop_requested": False}
21
+
22
+ def import_settings(scrollable_frame):
23
+ global vars_dict
24
+
25
+ csv_file_path = filedialog.askopenfilename(filetypes=[("CSV files", "*.csv")])
26
+ csv_settings = read_settings_from_csv(csv_file_path)
27
+ variables = measure_variables()
28
+ new_settings = update_settings_from_csv(variables, csv_settings)
29
+ vars_dict = generate_fields(new_settings, scrollable_frame)
30
+
31
+ def toggle_test_mode():
32
+ global vars_dict
33
+ current_state = vars_dict['test_mode'][2].get()
34
+ new_state = not current_state
35
+ vars_dict['test_mode'][2].set(new_state)
36
+ if new_state:
37
+ test_mode_button.config(bg="blue")
38
+ else:
39
+ test_mode_button.config(bg="gray")
40
+
41
+ def toggle_advanced_settings():
42
+ global vars_dict
43
+
44
+ timelapse_settings = ['timelapse', 'timelapse_objects']
45
+ misc_settings = ['representative_images', 'plot', 'plot_filtration', 'include_uninfected', 'dialate_pngs', 'dialate_png_ratios']
46
+ opperational_settings = ['max_workers','experiment','cells','cell_loc','pathogens','pathogen_loc','treatments','treatment_loc','channel_of_interest','compartments','measurement','nr_imgs', 'um_per_pixel']
47
+
48
+ advanced_settings = timelapse_settings+misc_settings+opperational_settings
49
+
50
+ # Toggle visibility of advanced settings
51
+ for setting in advanced_settings:
52
+ label, widget, var = vars_dict[setting]
53
+ if advanced_var.get() is False:
54
+ label.grid_remove() # Hide the label
55
+ widget.grid_remove() # Hide the widget
56
+ else:
57
+ label.grid() # Show the label
58
+ widget.grid() # Show the widget
59
+
60
+ #@log_function_call
61
+ def run_measure_gui(q, fig_queue, stop_requested):
62
+ global vars_dict
63
+ process_stdout_stderr(q)
64
+ try:
65
+ print('hello')
66
+ settings = check_measure_gui_settings(vars_dict)
67
+ measure_crop_wrapper(settings=settings, q=q, fig_queue=fig_queue)
68
+ except Exception as e:
69
+ q.put(f"Error during processing: {e}")
70
+ traceback.print_exc()
71
+ finally:
72
+ stop_requested.value = 1
73
+
74
+ #@log_function_call
75
+ def start_process(q, fig_queue):
76
+ global thread_control
77
+ if thread_control.get("run_thread") is not None:
78
+ initiate_abort()
79
+
80
+ stop_requested = Value('i', 0) # multiprocessing shared value for inter-process communication
81
+ thread_control["stop_requested"] = stop_requested
82
+ thread_control["run_thread"] = Process(target=run_measure_gui, args=(q, fig_queue, stop_requested))
83
+ thread_control["run_thread"].start()
84
+
85
+ #@log_function_call
86
+ def initiate_abort():
87
+ global thread_control
88
+ if thread_control.get("stop_requested") is not None:
89
+ thread_control["stop_requested"].value = 1
90
+
91
+ if thread_control.get("run_thread") is not None:
92
+ thread_control["run_thread"].join(timeout=5)
93
+ if thread_control["run_thread"].is_alive():
94
+ thread_control["run_thread"].terminate()
95
+ thread_control["run_thread"] = None
96
+
97
+ #@log_function_call
98
+ def initiate_measure_root(parent_frame):
99
+ global vars_dict, q, canvas, fig_queue, canvas_widget, thread_control, variables, advanced_var, scrollable_frame
100
+
101
+ style = ttk.Style(parent_frame)
102
+ set_dark_style(style)
103
+ style_text_boxes(style)
104
+ set_default_font(parent_frame, font_name="Helvetica", size=8)
105
+
106
+ parent_frame.configure(bg='black')
107
+ parent_frame.grid_rowconfigure(0, weight=1)
108
+ parent_frame.grid_columnconfigure(0, weight=1)
109
+
110
+ fig_queue = Queue()
111
+
112
+ # Initialize after_tasks if not already done
113
+ if not hasattr(parent_frame, 'after_tasks'):
114
+ parent_frame.after_tasks = []
115
+
116
+ def _process_fig_queue():
117
+ global canvas
118
+ try:
119
+ while not fig_queue.empty():
120
+ clear_canvas(canvas)
121
+ fig = fig_queue.get_nowait()
122
+ for ax in fig.get_axes():
123
+ ax.set_xticks([]) # Remove x-axis ticks
124
+ ax.set_yticks([]) # Remove y-axis ticks
125
+ ax.xaxis.set_visible(False) # Hide the x-axis
126
+ ax.yaxis.set_visible(False) # Hide the y-axis
127
+ fig.tight_layout()
128
+ fig.set_facecolor('black')
129
+ canvas.figure = fig
130
+ fig_width, fig_height = canvas_widget.winfo_width(), canvas_widget.winfo_height()
131
+ fig.set_size_inches(fig_width / fig.dpi, fig_height / fig.dpi, forward=True)
132
+ canvas.draw_idle()
133
+ except Exception as e:
134
+ traceback.print_exc()
135
+ finally:
136
+ after_id = canvas_widget.after(100, _process_fig_queue)
137
+ parent_frame.after_tasks.append(after_id)
138
+
139
+ def _process_console_queue():
140
+ while not q.empty():
141
+ message = q.get_nowait()
142
+ console_output.insert(tk.END, message)
143
+ console_output.see(tk.END)
144
+ after_id = console_output.after(100, _process_console_queue)
145
+ parent_frame.after_tasks.append(after_id)
146
+
147
+ # Clear previous content if any
148
+ for widget in parent_frame.winfo_children():
149
+ widget.destroy()
150
+
151
+ vertical_container = tk.PanedWindow(parent_frame, orient=tk.HORIZONTAL)
152
+ vertical_container.grid(row=0, column=0, sticky=tk.NSEW)
153
+ parent_frame.grid_rowconfigure(0, weight=1)
154
+ parent_frame.grid_columnconfigure(0, weight=1)
155
+
156
+ # Settings Section
157
+ settings_frame = tk.Frame(vertical_container, bg='black')
158
+ vertical_container.add(settings_frame, stretch="always")
159
+ settings_label = ttk.Label(settings_frame, text="Settings", background="black", foreground="white")
160
+ settings_label.grid(row=0, column=0, pady=10, padx=10)
161
+ scrollable_frame = ScrollableFrame(settings_frame, width=500)
162
+ scrollable_frame.grid(row=1, column=0, sticky="nsew")
163
+ settings_frame.grid_rowconfigure(1, weight=1)
164
+ settings_frame.grid_columnconfigure(0, weight=1)
165
+
166
+ # Create advanced settings checkbox
167
+ advanced_var = tk.BooleanVar(value=False)
168
+ advanced_Toggle = ToggleSwitch(scrollable_frame.scrollable_frame, text="Advanced Settings", variable=advanced_var, command=toggle_advanced_settings)
169
+ advanced_Toggle.grid(row=48, column=0, pady=10, padx=10)
170
+ variables = measure_variables()
171
+ vars_dict = generate_fields(variables, scrollable_frame)
172
+ toggle_advanced_settings()
173
+ vars_dict['Test mode'] = (None, None, tk.BooleanVar(value=False))
174
+
175
+ # Button section
176
+ test_mode_button = CustomButton(scrollable_frame.scrollable_frame, text="Test Mode", command=toggle_test_mode)
177
+ test_mode_button.grid(row=47, column=1, pady=10, padx=10)
178
+ import_btn = CustomButton(scrollable_frame.scrollable_frame, text="Import", command=lambda: import_settings(scrollable_frame), font=('Helvetica', 10))
179
+ import_btn.grid(row=47, column=0, pady=20, padx=20)
180
+ run_button = CustomButton(scrollable_frame.scrollable_frame, text="Run", command=lambda: start_process(q, fig_queue), font=('Helvetica', 10))
181
+ run_button.grid(row=45, column=0, pady=20, padx=20)
182
+ abort_button = CustomButton(scrollable_frame.scrollable_frame, text="Abort", command=initiate_abort, font=('Helvetica', 10))
183
+ abort_button.grid(row=45, column=1, pady=20, padx=20)
184
+ progress_label = ttk.Label(scrollable_frame.scrollable_frame, text="Processing: 0%", background="black", foreground="white") # Create progress field
185
+ progress_label.grid(row=50, column=0, columnspan=2, sticky="ew", pady=(5, 0), padx=10)
186
+
187
+ # Plot Canvas Section
188
+ plot_frame = tk.PanedWindow(vertical_container, orient=tk.VERTICAL)
189
+ vertical_container.add(plot_frame, stretch="always")
190
+ figure = Figure(figsize=(30, 4), dpi=100, facecolor='black')
191
+ plot = figure.add_subplot(111)
192
+ plot.plot([], [])
193
+ plot.axis('off')
194
+ canvas = FigureCanvasTkAgg(figure, master=plot_frame)
195
+ canvas.get_tk_widget().configure(cursor='arrow', background='black', highlightthickness=0)
196
+ canvas_widget = canvas.get_tk_widget()
197
+ plot_frame.add(canvas_widget, stretch="always")
198
+ canvas.draw()
199
+ canvas.figure = figure
200
+
201
+ # Console Section
202
+ console_frame = tk.Frame(vertical_container, bg='black')
203
+ vertical_container.add(console_frame, stretch="always")
204
+ console_label = ttk.Label(console_frame, text="Console", background="black", foreground="white")
205
+ console_label.grid(row=0, column=0, pady=10, padx=10)
206
+ console_output = scrolledtext.ScrolledText(console_frame, height=10, bg='black', fg='white', insertbackground='white')
207
+ console_output.grid(row=1, column=0, sticky="nsew")
208
+ console_frame.grid_rowconfigure(1, weight=1)
209
+ console_frame.grid_columnconfigure(0, weight=1)
210
+
211
+ q = Queue()
212
+ sys.stdout = StdoutRedirector(console_output)
213
+ sys.stderr = StdoutRedirector(console_output)
214
+
215
+ _process_console_queue()
216
+ _process_fig_queue()
217
+
218
+ after_id = parent_frame.after(100, lambda: main_thread_update_function(parent_frame, q, fig_queue, canvas_widget, progress_label))
219
+ parent_frame.after_tasks.append(after_id)
220
+
221
+ return parent_frame, vars_dict
222
+
223
+ def gui_measure():
224
+ root = tk.Tk()
225
+ width = root.winfo_screenwidth()
226
+ height = root.winfo_screenheight()
227
+ root.geometry(f"{width}x{height}")
228
+ root.title("SpaCr: measure objects")
229
+
230
+ # Clear previous content if any
231
+ if hasattr(root, 'content_frame'):
232
+ for widget in root.content_frame.winfo_children():
233
+ widget.destroy()
234
+ root.content_frame.grid_forget()
235
+ else:
236
+ root.content_frame = tk.Frame(root)
237
+ root.content_frame.grid(row=1, column=0, sticky="nsew")
238
+ root.grid_rowconfigure(1, weight=1)
239
+ root.grid_columnconfigure(0, weight=1)
240
+
241
+ initiate_measure_root(root.content_frame)
242
+ create_menu_bar(root)
243
+ root.mainloop()
244
+
245
+ if __name__ == "__main__":
246
+ gui_measure()
spacr/sequencing.py CHANGED
@@ -37,22 +37,6 @@ def analyze_reads(settings):
37
37
  None
38
38
  """
39
39
 
40
- def save_chunk_to_hdf5_v1(output_file_path, data_chunk, chunk_counter):
41
- """
42
- Save a data chunk to an HDF5 file.
43
-
44
- Parameters:
45
- - output_file_path (str): The path to the output HDF5 file.
46
- - data_chunk (list): The data chunk to be saved.
47
- - chunk_counter (int): The counter for the current chunk.
48
-
49
- Returns:
50
- None
51
- """
52
- df = pd.DataFrame(data_chunk, columns=['combined_read', 'grna', 'plate_row', 'column', 'sample'])
53
- with pd.HDFStore(output_file_path, mode='a', complevel=5, complib='blosc') as store:
54
- store.put(f'reads/chunk_{chunk_counter}', df, format='table', append=True)
55
-
56
40
  def save_chunk_to_hdf5(output_file_path, data_chunk, chunk_counter):
57
41
  """
58
42
  Save a data chunk to an HDF5 file.
@@ -306,7 +290,7 @@ def analyze_reads(settings):
306
290
  qc_df = pd.DataFrame([qc])
307
291
  qc_df.to_csv(qc_file_path, index=False)
308
292
 
309
- from .utils import get_analyze_reads_default_settings
293
+ from .settings import get_analyze_reads_default_settings
310
294
 
311
295
  settings = get_analyze_reads_default_settings(settings)
312
296