spacr 0.1.11__tar.gz → 0.1.12__tar.gz

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.
Files changed (56) hide show
  1. {spacr-0.1.11/spacr.egg-info → spacr-0.1.12}/PKG-INFO +1 -1
  2. {spacr-0.1.11 → spacr-0.1.12}/setup.py +1 -1
  3. {spacr-0.1.11 → spacr-0.1.12}/spacr/__init__.py +12 -14
  4. spacr-0.1.11/spacr/annotate_app.py → spacr-0.1.12/spacr/app_annotate.py +25 -156
  5. spacr-0.1.11/spacr/classify_app.py → spacr-0.1.12/spacr/app_classify.py +9 -9
  6. spacr-0.1.11/spacr/make_masks_app.py → spacr-0.1.12/spacr/app_make_masks.py +4 -2
  7. spacr-0.1.11/spacr/make_masks_app_v2.py → spacr-0.1.12/spacr/app_make_masks_v2.py +1 -1
  8. spacr-0.1.11/spacr/mask_app.py → spacr-0.1.12/spacr/app_mask.py +2 -0
  9. spacr-0.1.11/spacr/measure_app.py → spacr-0.1.12/spacr/app_measure.py +2 -0
  10. {spacr-0.1.11 → spacr-0.1.12}/spacr/gui.py +9 -12
  11. {spacr-0.1.11 → spacr-0.1.12}/spacr/gui_utils.py +55 -29
  12. {spacr-0.1.11 → spacr-0.1.12/spacr.egg-info}/PKG-INFO +1 -1
  13. {spacr-0.1.11 → spacr-0.1.12}/spacr.egg-info/SOURCES.txt +6 -6
  14. {spacr-0.1.11 → spacr-0.1.12}/tests/test_annotate_app.py +1 -1
  15. {spacr-0.1.11 → spacr-0.1.12}/tests/test_gui_classify_app.py +1 -1
  16. {spacr-0.1.11 → spacr-0.1.12}/tests/test_gui_mask_app.py +1 -1
  17. {spacr-0.1.11 → spacr-0.1.12}/tests/test_gui_measure_app.py +1 -1
  18. {spacr-0.1.11 → spacr-0.1.12}/tests/test_gui_sim_app.py +1 -1
  19. {spacr-0.1.11 → spacr-0.1.12}/tests/test_mask_app.py +1 -1
  20. {spacr-0.1.11 → spacr-0.1.12}/LICENSE +0 -0
  21. {spacr-0.1.11 → spacr-0.1.12}/MANIFEST.in +0 -0
  22. {spacr-0.1.11 → spacr-0.1.12}/README.rst +0 -0
  23. {spacr-0.1.11 → spacr-0.1.12}/setup.cfg +0 -0
  24. {spacr-0.1.11 → spacr-0.1.12}/spacr/__main__.py +0 -0
  25. {spacr-0.1.11 → spacr-0.1.12}/spacr/chris.py +0 -0
  26. {spacr-0.1.11 → spacr-0.1.12}/spacr/core.py +0 -0
  27. {spacr-0.1.11 → spacr-0.1.12}/spacr/deep_spacr.py +0 -0
  28. {spacr-0.1.11 → spacr-0.1.12}/spacr/graph_learning.py +0 -0
  29. {spacr-0.1.11 → spacr-0.1.12}/spacr/io.py +0 -0
  30. {spacr-0.1.11 → spacr-0.1.12}/spacr/logger.py +0 -0
  31. {spacr-0.1.11 → spacr-0.1.12}/spacr/measure.py +0 -0
  32. {spacr-0.1.11 → spacr-0.1.12}/spacr/models/cp/toxo_plaque_cyto_e25000_X1120_Y1120.CP_model +0 -0
  33. {spacr-0.1.11 → spacr-0.1.12}/spacr/models/cp/toxo_plaque_cyto_e25000_X1120_Y1120.CP_model_settings.csv +0 -0
  34. {spacr-0.1.11 → spacr-0.1.12}/spacr/models/cp/toxo_pv_lumen.CP_model +0 -0
  35. {spacr-0.1.11 → spacr-0.1.12}/spacr/plot.py +0 -0
  36. {spacr-0.1.11 → spacr-0.1.12}/spacr/sequencing.py +0 -0
  37. {spacr-0.1.11 → spacr-0.1.12}/spacr/settings.py +0 -0
  38. {spacr-0.1.11 → spacr-0.1.12}/spacr/sim.py +0 -0
  39. {spacr-0.1.11 → spacr-0.1.12}/spacr/sim_app.py +0 -0
  40. {spacr-0.1.11 → spacr-0.1.12}/spacr/timelapse.py +0 -0
  41. {spacr-0.1.11 → spacr-0.1.12}/spacr/utils.py +0 -0
  42. {spacr-0.1.11 → spacr-0.1.12}/spacr/version.py +0 -0
  43. {spacr-0.1.11 → spacr-0.1.12}/spacr.egg-info/dependency_links.txt +0 -0
  44. {spacr-0.1.11 → spacr-0.1.12}/spacr.egg-info/entry_points.txt +0 -0
  45. {spacr-0.1.11 → spacr-0.1.12}/spacr.egg-info/requires.txt +0 -0
  46. {spacr-0.1.11 → spacr-0.1.12}/spacr.egg-info/top_level.txt +0 -0
  47. {spacr-0.1.11 → spacr-0.1.12}/tests/test_core.py +0 -0
  48. {spacr-0.1.11 → spacr-0.1.12}/tests/test_gui_utils.py +0 -0
  49. {spacr-0.1.11 → spacr-0.1.12}/tests/test_io.py +0 -0
  50. {spacr-0.1.11 → spacr-0.1.12}/tests/test_measure.py +0 -0
  51. {spacr-0.1.11 → spacr-0.1.12}/tests/test_plot.py +0 -0
  52. {spacr-0.1.11 → spacr-0.1.12}/tests/test_sim.py +0 -0
  53. {spacr-0.1.11 → spacr-0.1.12}/tests/test_timelapse.py +0 -0
  54. {spacr-0.1.11 → spacr-0.1.12}/tests/test_train.py +0 -0
  55. {spacr-0.1.11 → spacr-0.1.12}/tests/test_umap.py +0 -0
  56. {spacr-0.1.11 → spacr-0.1.12}/tests/test_utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: spacr
3
- Version: 0.1.11
3
+ Version: 0.1.12
4
4
  Summary: Spatial phenotype analysis of crisp screens (SpaCr)
5
5
  Home-page: https://github.com/EinarOlafsson/spacr
6
6
  Author: Einar Birnir Olafsson
@@ -61,7 +61,7 @@ dependencies = [
61
61
 
62
62
  setup(
63
63
  name="spacr",
64
- version="0.1.11",
64
+ version="0.1.12",
65
65
  author="Einar Birnir Olafsson",
66
66
  author_email="olafsson@med.umich.com",
67
67
  description="Spatial phenotype analysis of crisp screens (SpaCr)",
@@ -12,14 +12,13 @@ from . import sim
12
12
  from . import sequencing
13
13
  from . import timelapse
14
14
  from . import deep_spacr
15
- from . import annotate_app
16
- from . import annotate_app_v2
15
+ from . import app_annotate
17
16
  from . import gui_utils
18
- from . import gui_make_masks_app
19
- from . import gui_make_masks_app_v2
20
- from . import gui_mask_app
21
- from . import gui_measure_app
22
- from . import gui_classify_app
17
+ from . import app_make_masks
18
+ from . import app_make_masks_v2
19
+ from . import app_mask
20
+ from . import app_measure
21
+ from . import app_classify
23
22
  from . import logger
24
23
 
25
24
 
@@ -34,14 +33,13 @@ __all__ = [
34
33
  "sequencing"
35
34
  "timelapse",
36
35
  "deep_spacr",
37
- "annotate_app",
38
- "annotate_app_v2",
36
+ "app_annotate",
39
37
  "gui_utils",
40
- "gui_make_masks_app",
41
- "gui_make_masks_app_v2",
42
- "gui_mask_app",
43
- "gui_measure_app",
44
- "gui_classify_app",
38
+ "app_make_masks",
39
+ "app_make_masks_v2",
40
+ "app_mask",
41
+ "app_measure",
42
+ "app_classify",
45
43
  "logger"
46
44
  ]
47
45
 
@@ -11,29 +11,12 @@ from PIL import ImageTk
11
11
  from skimage.exposure import rescale_intensity
12
12
  from IPython.display import display, HTML
13
13
  from tkinter import font as tkFont
14
+ from tkinter import TclError
14
15
 
15
16
  from .gui_utils import ScrollableFrame, CustomButton, set_dark_style, set_default_font, style_text_boxes, create_menu_bar
16
17
 
17
18
  class ImageApp:
18
19
  def __init__(self, root, db_path, src, image_type=None, channels=None, grid_rows=None, grid_cols=None, image_size=(200, 200), annotation_column='annotate', normalize=False, percentiles=(1,99), measurement=None, threshold=None):
19
- """
20
- Initializes an instance of the ImageApp class.
21
-
22
- Parameters:
23
- - root (tkinter.Tk): The root window of the application.
24
- - db_path (str): The path to the SQLite database.
25
- - src (str): The source directory that should be upstream of 'data' in the paths.
26
- - image_type (str): The type of images to display.
27
- - channels (list): The channels to filter in the images.
28
- - grid_rows (int): The number of rows in the image grid.
29
- - grid_cols (int): The number of columns in the image grid.
30
- - image_size (tuple): The size of the displayed images.
31
- - annotation_column (str): The column name for image annotations in the database.
32
- - normalize (bool): Whether to normalize images to their 2nd and 98th percentiles. Defaults to False.
33
- - measurement (str): The measurement column to filter by.
34
- - threshold (float): The threshold value for filtering the measurement column.
35
- """
36
-
37
20
  self.root = root
38
21
  self.db_path = db_path
39
22
  self.src = src
@@ -69,9 +52,6 @@ class ImageApp:
69
52
  self.labels.append(label)
70
53
 
71
54
  def prefilter_paths_annotations(self):
72
- """
73
- Pre-filters the paths and annotations based on the specified measurement and threshold.
74
- """
75
55
  from .io import _read_and_join_tables
76
56
  from .utils import is_list_of_lists
77
57
 
@@ -159,21 +139,6 @@ class ImageApp:
159
139
  conn.close()
160
140
 
161
141
  def load_images(self):
162
- """
163
- Loads and displays images with annotations.
164
-
165
- This method retrieves image paths and annotations from a pre-filtered list,
166
- loads the images using a ThreadPoolExecutor for parallel processing,
167
- adds colored borders to images based on their annotations,
168
- and displays the images in the corresponding labels.
169
-
170
- Args:
171
- None
172
-
173
- Returns:
174
- None
175
- """
176
-
177
142
  for label in self.labels:
178
143
  label.config(image='')
179
144
 
@@ -213,16 +178,6 @@ class ImageApp:
213
178
  self.root.update()
214
179
 
215
180
  def load_single_image(self, path_annotation_tuple):
216
- """
217
- Loads a single image from the given path and annotation tuple.
218
-
219
- Args:
220
- path_annotation_tuple (tuple): A tuple containing the image path and its annotation.
221
-
222
- Returns:
223
- img (PIL.Image.Image): The loaded image.
224
- annotation: The annotation associated with the image.
225
- """
226
181
  path, annotation = path_annotation_tuple
227
182
  img = Image.open(path)
228
183
  img = self.normalize_image(img, self.normalize, self.percentiles)
@@ -233,18 +188,6 @@ class ImageApp:
233
188
 
234
189
  @staticmethod
235
190
  def normalize_image(img, normalize=False, percentiles=(1, 99)):
236
- """
237
- Normalize the pixel values of an image based on the 2nd and 98th percentiles or the image min and max values,
238
- and ensure the image is exported as 8-bit.
239
-
240
- Parameters:
241
- - img: PIL.Image.Image. The input image to be normalized.
242
- - normalize: bool. Whether to normalize based on the 2nd and 98th percentiles.
243
- - percentiles: tuple. The percentiles to use for normalization.
244
-
245
- Returns:
246
- - PIL.Image.Image. The normalized and 8-bit converted image.
247
- """
248
191
  img_array = np.array(img)
249
192
 
250
193
  if normalize:
@@ -261,17 +204,6 @@ class ImageApp:
261
204
  return Image.fromarray(img_array)
262
205
 
263
206
  def add_colored_border(self, img, border_width, border_color):
264
- """
265
- Adds a colored border to an image.
266
-
267
- Args:
268
- img (PIL.Image.Image): The input image.
269
- border_width (int): The width of the border in pixels.
270
- border_color (str): The color of the border in RGB format.
271
-
272
- Returns:
273
- PIL.Image.Image: The image with the colored border.
274
- """
275
207
  top_border = Image.new('RGB', (img.width, border_width), color=border_color)
276
208
  bottom_border = Image.new('RGB', (img.width, border_width), color=border_color)
277
209
  left_border = Image.new('RGB', (border_width, img.height), color=border_color)
@@ -287,15 +219,6 @@ class ImageApp:
287
219
  return bordered_img
288
220
 
289
221
  def filter_channels(self, img):
290
- """
291
- Filters the channels of an image based on the specified channels.
292
-
293
- Args:
294
- img (PIL.Image.Image): The input image.
295
-
296
- Returns:
297
- PIL.Image.Image: The filtered image.
298
- """
299
222
  r, g, b = img.split()
300
223
  if self.channels:
301
224
  if 'r' not in self.channels:
@@ -312,17 +235,6 @@ class ImageApp:
312
235
  return Image.merge("RGB", (r, g, b))
313
236
 
314
237
  def get_on_image_click(self, path, label, img):
315
- """
316
- Returns a callback function that handles the click event on an image.
317
-
318
- Parameters:
319
- path (str): The path of the image file.
320
- label (tkinter.Label): The label widget to update with the annotated image.
321
- img (PIL.Image.Image): The image object.
322
-
323
- Returns:
324
- function: The callback function for the image click event.
325
- """
326
238
  def on_image_click(event):
327
239
  new_annotation = 1 if event.num == 1 else (2 if event.num == 3 else None)
328
240
 
@@ -356,11 +268,6 @@ class ImageApp:
356
268
  """))
357
269
 
358
270
  def update_database_worker(self):
359
- """
360
- Worker function that continuously updates the database with pending updates from the update queue.
361
- It retrieves the pending updates from the queue, updates the corresponding records in the database,
362
- and resets the text in the HTML and status label.
363
- """
364
271
  conn = sqlite3.connect(self.db_path)
365
272
  c = conn.cursor()
366
273
 
@@ -383,51 +290,24 @@ class ImageApp:
383
290
  c.execute(f'UPDATE png_list SET {self.annotation_column} = ? WHERE png_path = ?', (new_annotation, path))
384
291
  conn.commit()
385
292
 
386
- # Reset the text
387
293
  ImageApp.update_html('')
388
294
  self.status_label.config(text='')
389
295
  self.root.update()
390
296
  time.sleep(0.1)
391
297
 
392
298
  def update_gui_text(self, text):
393
- """
394
- Update the text of the status label in the GUI.
395
-
396
- Args:
397
- text (str): The new text to be displayed in the status label.
398
-
399
- Returns:
400
- None
401
- """
402
299
  self.status_label.config(text=text)
403
300
  self.root.update()
404
301
 
405
302
  def next_page(self):
406
- """
407
- Moves to the next page of images in the grid.
408
-
409
- If there are pending updates in the dictionary, they are added to the update queue.
410
- The pending updates dictionary is then cleared.
411
- The index is incremented by the number of rows multiplied by the number of columns in the grid.
412
- Finally, the images are loaded for the new page.
413
- """
414
- if self.pending_updates: # Check if the dictionary is not empty
303
+ if self.pending_updates:
415
304
  self.update_queue.put(self.pending_updates.copy())
416
305
  self.pending_updates.clear()
417
306
  self.index += self.grid_rows * self.grid_cols
418
307
  self.load_images()
419
308
 
420
309
  def previous_page(self):
421
- """
422
- Move to the previous page in the grid.
423
-
424
- If there are pending updates in the dictionary, they are added to the update queue.
425
- The dictionary of pending updates is then cleared.
426
- The index is decremented by the number of rows multiplied by the number of columns in the grid.
427
- If the index becomes negative, it is set to 0.
428
- Finally, the images are loaded for the new page.
429
- """
430
- if self.pending_updates: # Check if the dictionary is not empty
310
+ if self.pending_updates:
431
311
  self.update_queue.put(self.pending_updates.copy())
432
312
  self.pending_updates.clear()
433
313
  self.index -= self.grid_rows * self.grid_cols
@@ -436,23 +316,15 @@ class ImageApp:
436
316
  self.load_images()
437
317
 
438
318
  def shutdown(self):
439
- """
440
- Shuts down the application.
441
-
442
- This method sets the terminate flag to True, clears the pending updates,
443
- updates the database, and quits the application.
444
-
445
- """
446
- self.terminate = True # Set terminate first
319
+ self.terminate = True
447
320
  self.update_queue.put(self.pending_updates.copy())
448
321
  self.pending_updates.clear()
449
- self.db_update_thread.join() # Join the thread to make sure database is updated
322
+ self.db_update_thread.join()
450
323
  self.root.quit()
451
324
  self.root.destroy()
452
325
  print(f'Quit application')
453
326
 
454
327
  def get_annotate_default_settings(settings):
455
-
456
328
  settings.setdefault('image_type', 'cell_png')
457
329
  settings.setdefault('channels', ['r', 'g', 'b'])
458
330
  settings.setdefault('geom', "3200x2000")
@@ -468,24 +340,6 @@ def get_annotate_default_settings(settings):
468
340
  return settings
469
341
 
470
342
  def annotate(settings):
471
- """
472
- Annotates images in a database using a graphical user interface.
473
-
474
- Args:
475
- db (str): The path to the SQLite database.
476
- src (str): The source directory that should be upstream of 'data' in the paths.
477
- image_type (str, optional): The type of images to load from the database. Defaults to None.
478
- channels (str, optional): The channels of the images to load from the database. Defaults to None.
479
- geom (str, optional): The geometry of the GUI window. Defaults to "1000x1100".
480
- img_size (tuple, optional): The size of the images to display in the GUI. Defaults to (200, 200).
481
- rows (int, optional): The number of rows in the image grid. Defaults to 5.
482
- columns (int, optional): The number of columns in the image grid. Defaults to 5.
483
- annotation_column (str, optional): The name of the annotation column in the database table. Defaults to 'annotate'.
484
- normalize (bool, optional): Whether to normalize images to their 2nd and 98th percentiles. Defaults to False.
485
- measurement (str, optional): The measurement column to filter by.
486
- threshold (float, optional): The threshold value for filtering the measurement column.
487
- """
488
-
489
343
  settings = get_annotate_default_settings(settings)
490
344
  src = settings['src']
491
345
 
@@ -604,6 +458,9 @@ def annotate_app(parent_frame, settings):
604
458
  annotate_with_image_refs(settings, root, lambda: load_next_app(root))
605
459
 
606
460
  def annotate_with_image_refs(settings, root, shutdown_callback):
461
+ from .gui_utils import proceed_with_app
462
+ from .gui import gui_app
463
+
607
464
  settings = get_annotate_default_settings(settings)
608
465
  src = settings['src']
609
466
 
@@ -632,9 +489,9 @@ def annotate_with_image_refs(settings, root, shutdown_callback):
632
489
  app.load_images()
633
490
 
634
491
  # Store the shutdown function and next app details in the root
635
- root.current_app_exit_func = app.shutdown
636
- root.next_app_func = None
637
- root.next_app_args = ()
492
+ root.current_app_exit_func = lambda: [app.shutdown(), shutdown_callback()]
493
+ root.next_app_func = proceed_with_app
494
+ root.next_app_args = ("Main App", gui_app) # Specify the main app function
638
495
 
639
496
  def load_next_app(root):
640
497
  # Get the next app function and arguments
@@ -642,7 +499,19 @@ def load_next_app(root):
642
499
  next_app_args = root.next_app_args
643
500
 
644
501
  if next_app_func:
645
- next_app_func(*next_app_args)
502
+ try:
503
+ if not root.winfo_exists():
504
+ raise tk.TclError
505
+ next_app_func(root, *next_app_args)
506
+ except tk.TclError:
507
+ # Reinitialize root if it has been destroyed
508
+ new_root = tk.Tk()
509
+ width = new_root.winfo_screenwidth()
510
+ height = new_root.winfo_screenheight()
511
+ new_root.geometry(f"{width}x{height}")
512
+ new_root.title("SpaCr Application")
513
+ next_app_func(new_root, *next_app_args)
514
+
646
515
 
647
516
  def gui_annotate():
648
517
  root = tk.Tk()
@@ -667,4 +536,4 @@ def gui_annotate():
667
536
  root.mainloop()
668
537
 
669
538
  if __name__ == "__main__":
670
- gui_annotate()
539
+ gui_annotate()
@@ -77,7 +77,7 @@ def initiate_classify_root(parent_frame):
77
77
  style_text_boxes(style)
78
78
  set_default_font(parent_frame, font_name="Helvetica", size=8)
79
79
 
80
- parent_frame.configure(bg='#333333')
80
+ parent_frame.configure(bg='black')
81
81
  parent_frame.grid_rowconfigure(0, weight=1)
82
82
  parent_frame.grid_columnconfigure(0, weight=1)
83
83
  fig_queue = Queue()
@@ -94,7 +94,7 @@ def initiate_classify_root(parent_frame):
94
94
  ax.xaxis.set_visible(False) # Hide the x-axis
95
95
  ax.yaxis.set_visible(False) # Hide the y-axis
96
96
  fig.tight_layout()
97
- fig.set_facecolor('#333333')
97
+ fig.set_facecolor('black')
98
98
  canvas.figure = fig
99
99
  fig_width, fig_height = canvas_widget.winfo_width(), canvas_widget.winfo_height()
100
100
  fig.set_size_inches(fig_width / fig.dpi, fig_height / fig.dpi, forward=True)
@@ -117,9 +117,9 @@ def initiate_classify_root(parent_frame):
117
117
  parent_frame.grid_columnconfigure(0, weight=1)
118
118
 
119
119
  # Settings Section
120
- settings_frame = tk.Frame(vertical_container, bg='#333333')
120
+ settings_frame = tk.Frame(vertical_container, bg='black')
121
121
  vertical_container.add(settings_frame, stretch="always")
122
- settings_label = ttk.Label(settings_frame, text="Settings", background="#333333", foreground="white")
122
+ settings_label = ttk.Label(settings_frame, text="Settings", background="black", foreground="white")
123
123
  settings_label.grid(row=0, column=0, pady=10, padx=10)
124
124
  scrollable_frame = ScrollableFrame(settings_frame, width=500)
125
125
  scrollable_frame.grid(row=1, column=0, sticky="nsew")
@@ -143,23 +143,23 @@ def initiate_classify_root(parent_frame):
143
143
  # Plot Canvas Section
144
144
  plot_frame = tk.PanedWindow(vertical_container, orient=tk.VERTICAL)
145
145
  vertical_container.add(plot_frame, stretch="always")
146
- figure = Figure(figsize=(30, 4), dpi=100, facecolor='#333333')
146
+ figure = Figure(figsize=(30, 4), dpi=100, facecolor='black')
147
147
  plot = figure.add_subplot(111)
148
148
  plot.plot([], [])
149
149
  plot.axis('off')
150
150
  canvas = FigureCanvasTkAgg(figure, master=plot_frame)
151
- canvas.get_tk_widget().configure(cursor='arrow', background='#333333', highlightthickness=0)
151
+ canvas.get_tk_widget().configure(cursor='arrow', background='black', highlightthickness=0)
152
152
  canvas_widget = canvas.get_tk_widget()
153
153
  plot_frame.add(canvas_widget, stretch="always")
154
154
  canvas.draw()
155
155
  canvas.figure = figure
156
156
 
157
157
  # Console Section
158
- console_frame = tk.Frame(vertical_container, bg='#333333')
158
+ console_frame = tk.Frame(vertical_container, bg='black')
159
159
  vertical_container.add(console_frame, stretch="always")
160
- console_label = ttk.Label(console_frame, text="Console", background="#333333", foreground="white")
160
+ console_label = ttk.Label(console_frame, text="Console", background="black", foreground="white")
161
161
  console_label.grid(row=0, column=0, pady=10, padx=10)
162
- console_output = scrolledtext.ScrolledText(console_frame, height=10, bg='#333333', fg='white', insertbackground='white')
162
+ console_output = scrolledtext.ScrolledText(console_frame, height=10, bg='black', fg='white', insertbackground='white')
163
163
  console_output.grid(row=1, column=0, sticky="nsew")
164
164
  console_frame.grid_rowconfigure(1, weight=1)
165
165
  console_frame.grid_columnconfigure(0, weight=1)
@@ -870,7 +870,7 @@ def initiate_mask_app_root(parent_frame):
870
870
  container = tk.PanedWindow(parent_frame, orient=tk.HORIZONTAL)
871
871
  container.pack(fill=tk.BOTH, expand=True)
872
872
 
873
- scrollable_frame = ScrollableFrame(container, bg='#333333')
873
+ scrollable_frame = ScrollableFrame(container, bg='black')
874
874
  container.add(scrollable_frame, stretch="always")
875
875
 
876
876
  # Setup input fields
@@ -905,7 +905,9 @@ def initiate_mask_app_root(parent_frame):
905
905
 
906
906
  def gui_make_masks():
907
907
  root = tk.Tk()
908
- root.geometry("1000x800")
908
+ width = root.winfo_screenwidth()
909
+ height = root.winfo_screenheight()
910
+ root.geometry(f"{width}x{height}")
909
911
  root.title("Mask Application")
910
912
 
911
913
  # Clear previous content if any
@@ -645,7 +645,7 @@ def initiate_mask_app_root(width, height):
645
645
  container = tk.PanedWindow(root, orient=tk.HORIZONTAL)
646
646
  container.pack(fill=tk.BOTH, expand=True)
647
647
 
648
- scrollable_frame = ScrollableFrame(container, bg='#333333')
648
+ scrollable_frame = ScrollableFrame(container, bg='black')
649
649
  container.add(scrollable_frame, stretch="always")
650
650
 
651
651
  vars_dict = {
@@ -22,6 +22,8 @@ from .gui_utils import mask_variables, check_mask_gui_settings, preprocess_gener
22
22
 
23
23
  thread_control = {"run_thread": None, "stop_requested": False}
24
24
 
25
+ test_mode_button = None
26
+
25
27
  def toggle_test_mode():
26
28
  global vars_dict
27
29
  current_state = vars_dict['test_mode'][2].get()
@@ -28,6 +28,8 @@ def import_settings(scrollable_frame):
28
28
  new_settings = update_settings_from_csv(variables, csv_settings)
29
29
  vars_dict = generate_fields(new_settings, scrollable_frame)
30
30
 
31
+ test_mode_button = None
32
+
31
33
  def toggle_test_mode():
32
34
  global vars_dict
33
35
  current_state = vars_dict['test_mode'][2].get()
@@ -6,21 +6,21 @@ import os
6
6
  import requests
7
7
 
8
8
  # Import your GUI apps
9
- from .gui_mask_app import initiate_mask_root
10
- from .gui_measure_app import initiate_measure_root
11
- from .annotate_app import initiate_annotation_app_root
12
- from .gui_make_masks_app import initiate_mask_app_root
13
- from .gui_classify_app import initiate_classify_root
9
+ from .app_mask import initiate_mask_root
10
+ from .app_measure import initiate_measure_root
11
+ from .app_annotate import initiate_annotation_app_root
12
+ from .app_make_masks import initiate_mask_app_root
13
+ from .app_classify import initiate_classify_root
14
14
  from .gui_utils import CustomButton, style_text_boxes, create_menu_bar
15
15
 
16
16
  class MainApp(tk.Tk):
17
17
  def __init__(self):
18
18
  super().__init__()
19
+ width = self.winfo_screenwidth()
20
+ height = self.winfo_screenheight()
21
+ self.geometry(f"{width}x{height}")
19
22
  self.title("SpaCr GUI Collection")
20
- self.geometry("1100x1500")
21
23
  self.configure(bg="black")
22
- #self.attributes('-fullscreen', True)
23
-
24
24
  style = ttk.Style()
25
25
  style_text_boxes(style)
26
26
 
@@ -37,7 +37,7 @@ class MainApp(tk.Tk):
37
37
 
38
38
  def create_widgets(self):
39
39
  # Create the menu bar
40
- self.gui_create_menu_bar()
40
+ create_menu_bar(self)
41
41
 
42
42
  # Create a canvas to hold the selected app and other elements
43
43
  self.canvas = tk.Canvas(self, bg="black", highlightthickness=0)
@@ -52,9 +52,6 @@ class MainApp(tk.Tk):
52
52
  # Create startup screen with buttons for each GUI app
53
53
  self.create_startup_screen()
54
54
 
55
- def gui_create_menu_bar(self):
56
- create_menu_bar(self)
57
-
58
55
  def create_startup_screen(self):
59
56
  self.clear_frame(self.content_frame)
60
57
 
@@ -65,11 +65,11 @@ def load_app(root, app_name, app_func):
65
65
  app_func(root.content_frame)
66
66
 
67
67
  def create_menu_bar(root):
68
- from .gui_mask_app import initiate_mask_root
69
- from .gui_measure_app import initiate_measure_root
70
- from .annotate_app import initiate_annotation_app_root
71
- from .gui_make_masks_app import initiate_mask_app_root
72
- from .gui_classify_app import initiate_classify_root
68
+ from .app_mask import initiate_mask_root
69
+ from .app_measure import initiate_measure_root
70
+ from .app_annotate import initiate_annotation_app_root
71
+ from .app_make_masks import initiate_mask_app_root
72
+ from .app_classify import initiate_classify_root
73
73
 
74
74
  gui_apps = {
75
75
  "Mask": initiate_mask_root,
@@ -96,6 +96,42 @@ def create_menu_bar(root):
96
96
  # Configure the menu for the root window
97
97
  root.config(menu=menu_bar)
98
98
 
99
+ def proceed_with_app(root, app_name, app_func):
100
+
101
+ from .app_mask import gui_mask
102
+ from .app_measure import gui_measure
103
+ from .app_annotate import gui_annotate
104
+ from .app_make_masks import gui_make_masks
105
+ from .app_classify import gui_classify
106
+ from .gui import gui_app
107
+
108
+ # Clear the current content frame
109
+ if hasattr(root, 'content_frame'):
110
+ for widget in root.content_frame.winfo_children():
111
+ widget.destroy()
112
+ else:
113
+ root.content_frame = tk.Frame(root)
114
+ root.content_frame.grid(row=1, column=0, sticky="nsew")
115
+ root.grid_rowconfigure(1, weight=1)
116
+ root.grid_columnconfigure(0, weight=1)
117
+
118
+ # Initialize the new app in the content frame
119
+ if app_name == "Main App":
120
+ root.destroy() # Close the current window
121
+ gui_app() # Open the main app window
122
+ elif app_name == "Mask":
123
+ gui_mask()
124
+ elif app_name == "Measure":
125
+ gui_measure()
126
+ elif app_name == "Annotate":
127
+ gui_annotate()
128
+ elif app_name == "Make Masks":
129
+ gui_make_masks()
130
+ elif app_name == "Classify":
131
+ gui_classify()
132
+ else:
133
+ raise ValueError(f"Invalid app name: {app_name}")
134
+
99
135
  def load_app(root, app_name, app_func):
100
136
  # Cancel all scheduled after tasks
101
137
  if hasattr(root, 'after_tasks'):
@@ -103,35 +139,25 @@ def load_app(root, app_name, app_func):
103
139
  root.after_cancel(task)
104
140
  root.after_tasks = []
105
141
 
106
- def proceed_with_app():
107
- # Clear the current content frame
108
- if hasattr(root, 'content_frame'):
109
- for widget in root.content_frame.winfo_children():
110
- widget.destroy()
111
- else:
112
- root.content_frame = tk.Frame(root)
113
- root.content_frame.grid(row=1, column=0, sticky="nsew")
114
- root.grid_rowconfigure(1, weight=1)
115
- root.grid_columnconfigure(0, weight=1)
116
-
117
- # Initialize the new app in the content frame
118
- app_func(root.content_frame)
119
-
120
142
  # Exit functionality only for the annotation app
121
143
  if app_name != "Annotate" and hasattr(root, 'current_app_exit_func'):
122
144
  root.next_app_func = proceed_with_app
145
+ root.next_app_args = (app_name, app_func) # Ensure correct arguments
123
146
  root.current_app_exit_func()
124
147
  else:
125
- proceed_with_app()
148
+ proceed_with_app(root, app_name, app_func)
149
+
126
150
 
127
151
  def create_menu_bar(root):
128
- from .gui_mask_app import initiate_mask_root
129
- from .gui_measure_app import initiate_measure_root
130
- from .annotate_app import initiate_annotation_app_root
131
- from .gui_make_masks_app import initiate_mask_app_root
132
- from .gui_classify_app import initiate_classify_root
152
+ from .app_mask import initiate_mask_root
153
+ from .app_measure import initiate_measure_root
154
+ from .app_annotate import initiate_annotation_app_root
155
+ from .app_make_masks import initiate_mask_app_root
156
+ from .app_classify import initiate_classify_root
157
+ from .gui import gui_app
133
158
 
134
159
  gui_apps = {
160
+ "Main App": gui_app,
135
161
  "Mask": initiate_mask_root,
136
162
  "Measure": initiate_measure_root,
137
163
  "Annotate": initiate_annotation_app_root,
@@ -381,19 +407,19 @@ def style_text_boxes_v1(style):
381
407
  background=[('active', '#66b2b2'), ('disabled', '#004d4d'), ('!disabled', '#008080')],
382
408
  foreground=[('active', '#ffffff'), ('disabled', '#888888')])
383
409
  style.configure('Custom.TLabel', padding='5 5 5 5', borderwidth=1, relief='flat', background='#000000', foreground='#ffffff', font=font_style)
384
- style.configure('TCheckbutton', background='#333333', foreground='#ffffff', indicatoron=False, relief='flat', font=font_style)
410
+ style.configure('TCheckbutton', background='black', foreground='#ffffff', indicatoron=False, relief='flat', font=font_style)
385
411
  style.map('TCheckbutton', background=[('selected', '#555555'), ('active', '#555555')])
386
412
 
387
413
  def style_text_boxes(style):
388
414
  font_style = tkFont.Font(family="Helvetica", size=10)
389
- style.configure('TEntry', padding='5 5 5 5', borderwidth=1, relief='solid', fieldbackground='#333333', foreground='#ffffff', font=font_style)
390
- style.configure('TCombobox', fieldbackground='#333333', background='#333333', foreground='#ffffff', font=font_style)
415
+ style.configure('TEntry', padding='5 5 5 5', borderwidth=1, relief='solid', fieldbackground='black', foreground='#ffffff', font=font_style)
416
+ style.configure('TCombobox', fieldbackground='black', background='black', foreground='#ffffff', font=font_style)
391
417
  style.configure('Custom.TButton', padding='10 10 10 10', borderwidth=1, relief='solid', background='#008080', foreground='#ffffff', font=font_style)
392
418
  style.map('Custom.TButton',
393
419
  background=[('active', '#66b2b2'), ('disabled', '#004d4d'), ('!disabled', '#008080')],
394
420
  foreground=[('active', '#ffffff'), ('disabled', '#888888')])
395
421
  style.configure('Custom.TLabel', padding='5 5 5 5', borderwidth=1, relief='flat', background='#000000', foreground='#ffffff', font=font_style)
396
- style.configure('TCheckbutton', background='#333333', foreground='#ffffff', indicatoron=False, relief='flat', font=font_style)
422
+ style.configure('TCheckbutton', background='black', foreground='#ffffff', indicatoron=False, relief='flat', font=font_style)
397
423
  style.map('TCheckbutton', background=[('selected', '#555555'), ('active', '#555555')])
398
424
 
399
425
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: spacr
3
- Version: 0.1.11
3
+ Version: 0.1.12
4
4
  Summary: Spatial phenotype analysis of crisp screens (SpaCr)
5
5
  Home-page: https://github.com/EinarOlafsson/spacr
6
6
  Author: Einar Birnir Olafsson
@@ -4,9 +4,13 @@ README.rst
4
4
  setup.py
5
5
  spacr/__init__.py
6
6
  spacr/__main__.py
7
- spacr/annotate_app.py
7
+ spacr/app_annotate.py
8
+ spacr/app_classify.py
9
+ spacr/app_make_masks.py
10
+ spacr/app_make_masks_v2.py
11
+ spacr/app_mask.py
12
+ spacr/app_measure.py
8
13
  spacr/chris.py
9
- spacr/classify_app.py
10
14
  spacr/core.py
11
15
  spacr/deep_spacr.py
12
16
  spacr/graph_learning.py
@@ -14,11 +18,7 @@ spacr/gui.py
14
18
  spacr/gui_utils.py
15
19
  spacr/io.py
16
20
  spacr/logger.py
17
- spacr/make_masks_app.py
18
- spacr/make_masks_app_v2.py
19
- spacr/mask_app.py
20
21
  spacr/measure.py
21
- spacr/measure_app.py
22
22
  spacr/plot.py
23
23
  spacr/sequencing.py
24
24
  spacr/settings.py
@@ -1,6 +1,6 @@
1
1
  import unittest
2
2
  from unittest.mock import MagicMock
3
- from spacr.annotate_app import ImageApp
3
+ from spacr.app_annotate import ImageApp
4
4
  from PIL import Image
5
5
 
6
6
  class TestImageApp(unittest.TestCase):
@@ -1,6 +1,6 @@
1
1
  import unittest
2
2
  from unittest.mock import MagicMock
3
- from spacr.gui_classify_app import initiate_classify_root
3
+ from spacr.app_classify import initiate_classify_root
4
4
 
5
5
  class TestInitiateClassifyRoot(unittest.TestCase):
6
6
  def setUp(self):
@@ -1,6 +1,6 @@
1
1
  import unittest
2
2
  from unittest.mock import MagicMock
3
- from spacr.gui_mask_app import initiate_mask_root
3
+ from spacr.app_mask import initiate_mask_root
4
4
 
5
5
  class TestInitiateMaskRoot(unittest.TestCase):
6
6
  def setUp(self):
@@ -1,6 +1,6 @@
1
1
  import unittest
2
2
  from unittest.mock import MagicMock
3
- from spacr.gui_measure_app import initiate_measure_root
3
+ from spacr.app_measure import initiate_measure_root
4
4
 
5
5
  class TestInitiateMeasureRoot(unittest.TestCase):
6
6
  def setUp(self):
@@ -1,6 +1,6 @@
1
1
  import unittest
2
2
  from unittest.mock import MagicMock
3
- from spacr.gui_sim_app import initiate_sim_root
3
+ from spacr.sim_app import initiate_sim_root
4
4
 
5
5
  class TestInitiateSimRoot(unittest.TestCase):
6
6
  def setUp(self):
@@ -1,6 +1,6 @@
1
1
  import unittest
2
2
  from unittest.mock import MagicMock
3
- from spacr.mask_app import modify_masks
3
+ from spacr.app_make_masks import modify_masks
4
4
 
5
5
  class TestModifyMasks(unittest.TestCase):
6
6
  def setUp(self):
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes