spacr 0.1.1__tar.gz → 0.1.11__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 (58) hide show
  1. {spacr-0.1.1/spacr.egg-info → spacr-0.1.11}/PKG-INFO +5 -1
  2. {spacr-0.1.1 → spacr-0.1.11}/setup.py +9 -5
  3. {spacr-0.1.1 → spacr-0.1.11}/spacr/__init__.py +4 -2
  4. spacr-0.1.1/spacr/annotate_app_v2.py → spacr-0.1.11/spacr/annotate_app.py +163 -4
  5. spacr-0.1.1/spacr/gui_classify_app.py → spacr-0.1.11/spacr/classify_app.py +20 -6
  6. {spacr-0.1.1 → spacr-0.1.11}/spacr/gui.py +19 -13
  7. {spacr-0.1.1 → spacr-0.1.11}/spacr/gui_utils.py +79 -15
  8. spacr-0.1.1/spacr/mask_app.py → spacr-0.1.11/spacr/make_masks_app.py +72 -70
  9. spacr-0.1.11/spacr/make_masks_app_v2.py +688 -0
  10. spacr-0.1.1/spacr/gui_mask_app.py → spacr-0.1.11/spacr/mask_app.py +8 -4
  11. spacr-0.1.1/spacr/gui_measure_app.py → spacr-0.1.11/spacr/measure_app.py +15 -5
  12. {spacr-0.1.1 → spacr-0.1.11/spacr.egg-info}/PKG-INFO +5 -1
  13. {spacr-0.1.1 → spacr-0.1.11}/spacr.egg-info/SOURCES.txt +5 -6
  14. {spacr-0.1.1 → spacr-0.1.11}/spacr.egg-info/entry_points.txt +3 -3
  15. {spacr-0.1.1 → spacr-0.1.11}/spacr.egg-info/requires.txt +4 -0
  16. spacr-0.1.1/spacr/annotate_app.py +0 -511
  17. spacr-0.1.1/spacr/gui_2.py +0 -160
  18. {spacr-0.1.1 → spacr-0.1.11}/LICENSE +0 -0
  19. {spacr-0.1.1 → spacr-0.1.11}/MANIFEST.in +0 -0
  20. {spacr-0.1.1 → spacr-0.1.11}/README.rst +0 -0
  21. {spacr-0.1.1 → spacr-0.1.11}/setup.cfg +0 -0
  22. {spacr-0.1.1 → spacr-0.1.11}/spacr/__main__.py +0 -0
  23. {spacr-0.1.1 → spacr-0.1.11}/spacr/chris.py +0 -0
  24. {spacr-0.1.1 → spacr-0.1.11}/spacr/core.py +0 -0
  25. {spacr-0.1.1 → spacr-0.1.11}/spacr/deep_spacr.py +0 -0
  26. {spacr-0.1.1 → spacr-0.1.11}/spacr/graph_learning.py +0 -0
  27. {spacr-0.1.1 → spacr-0.1.11}/spacr/io.py +0 -0
  28. {spacr-0.1.1 → spacr-0.1.11}/spacr/logger.py +0 -0
  29. {spacr-0.1.1 → spacr-0.1.11}/spacr/measure.py +0 -0
  30. {spacr-0.1.1 → spacr-0.1.11}/spacr/models/cp/toxo_plaque_cyto_e25000_X1120_Y1120.CP_model +0 -0
  31. {spacr-0.1.1 → spacr-0.1.11}/spacr/models/cp/toxo_plaque_cyto_e25000_X1120_Y1120.CP_model_settings.csv +0 -0
  32. {spacr-0.1.1 → spacr-0.1.11}/spacr/models/cp/toxo_pv_lumen.CP_model +0 -0
  33. {spacr-0.1.1 → spacr-0.1.11}/spacr/plot.py +0 -0
  34. {spacr-0.1.1 → spacr-0.1.11}/spacr/sequencing.py +0 -0
  35. {spacr-0.1.1 → spacr-0.1.11}/spacr/settings.py +0 -0
  36. {spacr-0.1.1 → spacr-0.1.11}/spacr/sim.py +0 -0
  37. /spacr-0.1.1/spacr/gui_sim_app.py → /spacr-0.1.11/spacr/sim_app.py +0 -0
  38. {spacr-0.1.1 → spacr-0.1.11}/spacr/timelapse.py +0 -0
  39. {spacr-0.1.1 → spacr-0.1.11}/spacr/utils.py +0 -0
  40. {spacr-0.1.1 → spacr-0.1.11}/spacr/version.py +0 -0
  41. {spacr-0.1.1 → spacr-0.1.11}/spacr.egg-info/dependency_links.txt +0 -0
  42. {spacr-0.1.1 → spacr-0.1.11}/spacr.egg-info/top_level.txt +0 -0
  43. {spacr-0.1.1 → spacr-0.1.11}/tests/test_annotate_app.py +0 -0
  44. {spacr-0.1.1 → spacr-0.1.11}/tests/test_core.py +0 -0
  45. {spacr-0.1.1 → spacr-0.1.11}/tests/test_gui_classify_app.py +0 -0
  46. {spacr-0.1.1 → spacr-0.1.11}/tests/test_gui_mask_app.py +0 -0
  47. {spacr-0.1.1 → spacr-0.1.11}/tests/test_gui_measure_app.py +0 -0
  48. {spacr-0.1.1 → spacr-0.1.11}/tests/test_gui_sim_app.py +0 -0
  49. {spacr-0.1.1 → spacr-0.1.11}/tests/test_gui_utils.py +0 -0
  50. {spacr-0.1.1 → spacr-0.1.11}/tests/test_io.py +0 -0
  51. {spacr-0.1.1 → spacr-0.1.11}/tests/test_mask_app.py +0 -0
  52. {spacr-0.1.1 → spacr-0.1.11}/tests/test_measure.py +0 -0
  53. {spacr-0.1.1 → spacr-0.1.11}/tests/test_plot.py +0 -0
  54. {spacr-0.1.1 → spacr-0.1.11}/tests/test_sim.py +0 -0
  55. {spacr-0.1.1 → spacr-0.1.11}/tests/test_timelapse.py +0 -0
  56. {spacr-0.1.1 → spacr-0.1.11}/tests/test_train.py +0 -0
  57. {spacr-0.1.1 → spacr-0.1.11}/tests/test_umap.py +0 -0
  58. {spacr-0.1.1 → spacr-0.1.11}/tests/test_utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: spacr
3
- Version: 0.1.1
3
+ Version: 0.1.11
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
@@ -40,6 +40,10 @@ Requires-Dist: ttf_opensans>=2020.10.30
40
40
  Requires-Dist: customtkinter<6.0,>=5.2.2
41
41
  Requires-Dist: biopython<2.0,>=1.80
42
42
  Requires-Dist: lxml<6.0,>=5.1.0
43
+ Requires-Dist: qtpy<2.5,>=2.4.1
44
+ Requires-Dist: superqt<0.7,>=0.6.7
45
+ Requires-Dist: pyqt6<6.8,>=6.7.1
46
+ Requires-Dist: pyqtgraph<0.14,>=0.13.7
43
47
  Provides-Extra: dev
44
48
  Requires-Dist: pytest>=3.9; extra == "dev"
45
49
  Provides-Extra: headless
@@ -52,12 +52,16 @@ dependencies = [
52
52
  'ttf_opensans>=2020.10.30',
53
53
  'customtkinter>=5.2.2,<6.0',
54
54
  'biopython>=1.80,<2.0',
55
- 'lxml>=5.1.0,<6.0'
55
+ 'lxml>=5.1.0,<6.0',
56
+ 'qtpy>=2.4.1,<2.5',
57
+ 'superqt>=0.6.7,<0.7',
58
+ 'pyqt6>=6.7.1,<6.8',
59
+ 'pyqtgraph>=0.13.7,<0.14'
56
60
  ]
57
61
 
58
62
  setup(
59
63
  name="spacr",
60
- version="0.1.01",
64
+ version="0.1.11",
61
65
  author="Einar Birnir Olafsson",
62
66
  author_email="olafsson@med.umich.com",
63
67
  description="Spatial phenotype analysis of crisp screens (SpaCr)",
@@ -71,12 +75,12 @@ setup(
71
75
  'console_scripts': [
72
76
  'mask=spacr.gui_mask_app:gui_mask',
73
77
  'measure=spacr.gui_measure_app:gui_measure',
74
- 'make_masks=spacr.mask_app:gui_make_masks',
75
- 'annotate=spacr.annotate_app:gui_annotation',
78
+ 'make_masks=spacr.gui_make_mask_app:gui_make_masks',
79
+ 'make_masks2=spacr.gui_make_mask_app_v2:gui_make_masks',
80
+ 'annotate=spacr.annotate_app_v2:gui_annotate',
76
81
  'classify=spacr.gui_classify_app:gui_classify',
77
82
  'sim=spacr.gui_sim_app:gui_sim',
78
83
  'gui=spacr.gui:gui_app',
79
- 'gui2=spacr.gui_2:gui_app',
80
84
  ],
81
85
  },
82
86
  extras_require={
@@ -15,7 +15,8 @@ from . import deep_spacr
15
15
  from . import annotate_app
16
16
  from . import annotate_app_v2
17
17
  from . import gui_utils
18
- from . import mask_app
18
+ from . import gui_make_masks_app
19
+ from . import gui_make_masks_app_v2
19
20
  from . import gui_mask_app
20
21
  from . import gui_measure_app
21
22
  from . import gui_classify_app
@@ -36,7 +37,8 @@ __all__ = [
36
37
  "annotate_app",
37
38
  "annotate_app_v2",
38
39
  "gui_utils",
39
- "mask_app",
40
+ "gui_make_masks_app",
41
+ "gui_make_masks_app_v2",
40
42
  "gui_mask_app",
41
43
  "gui_measure_app",
42
44
  "gui_classify_app",
@@ -1,16 +1,18 @@
1
+ import sqlite3
1
2
  from queue import Queue
2
- from tkinter import Label, Entry, Button
3
+ from tkinter import Label
3
4
  import tkinter as tk
5
+ from tkinter import ttk
4
6
  import os, threading, time, sqlite3
5
7
  import numpy as np
6
8
  from PIL import Image, ImageOps
7
9
  from concurrent.futures import ThreadPoolExecutor
8
10
  from PIL import ImageTk
9
- import pandas as pd
10
11
  from skimage.exposure import rescale_intensity
11
- import cv2
12
- import matplotlib.pyplot as plt
13
12
  from IPython.display import display, HTML
13
+ from tkinter import font as tkFont
14
+
15
+ from .gui_utils import ScrollableFrame, CustomButton, set_dark_style, set_default_font, style_text_boxes, create_menu_bar
14
16
 
15
17
  class ImageApp:
16
18
  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):
@@ -509,3 +511,160 @@ def annotate(settings):
509
511
 
510
512
  app.load_images()
511
513
  root.mainloop()
514
+
515
+ # Global list to keep references to PhotoImage objects
516
+ global_image_refs = []
517
+
518
+ def initiate_annotation_app_root(parent_frame):
519
+ style = ttk.Style(parent_frame)
520
+ set_dark_style(style)
521
+ style_text_boxes(style)
522
+ set_default_font(parent_frame, font_name="Arial", size=8)
523
+
524
+ parent_frame.configure(bg='black')
525
+
526
+ container = tk.PanedWindow(parent_frame, orient=tk.HORIZONTAL, bg='black')
527
+ container.pack(fill=tk.BOTH, expand=True)
528
+
529
+ scrollable_frame = ScrollableFrame(container, bg='black')
530
+ container.add(scrollable_frame, stretch="always")
531
+
532
+ # Setup input fields
533
+ vars_dict = {
534
+ 'src': ttk.Entry(scrollable_frame.scrollable_frame),
535
+ 'image_type': ttk.Entry(scrollable_frame.scrollable_frame),
536
+ 'channels': ttk.Entry(scrollable_frame.scrollable_frame),
537
+ 'geom': ttk.Entry(scrollable_frame.scrollable_frame),
538
+ 'img_size': ttk.Entry(scrollable_frame.scrollable_frame),
539
+ 'rows': ttk.Entry(scrollable_frame.scrollable_frame),
540
+ 'columns': ttk.Entry(scrollable_frame.scrollable_frame),
541
+ 'annotation_column': ttk.Entry(scrollable_frame.scrollable_frame),
542
+ 'normalize': ttk.Entry(scrollable_frame.scrollable_frame),
543
+ 'percentiles': ttk.Entry(scrollable_frame.scrollable_frame),
544
+ 'measurement': ttk.Entry(scrollable_frame.scrollable_frame),
545
+ 'threshold': ttk.Entry(scrollable_frame.scrollable_frame),
546
+ }
547
+
548
+ default_settings = {
549
+ 'src': 'path',
550
+ 'image_type': 'cell_png',
551
+ 'channels': 'r,g,b',
552
+ 'geom': "3200x2000",
553
+ 'img_size': '(200, 200)',
554
+ 'rows': '10',
555
+ 'columns': '18',
556
+ 'annotation_column': 'recruited_test',
557
+ 'normalize': 'False',
558
+ 'percentiles': '(2,98)',
559
+ 'measurement': 'None',
560
+ 'threshold': 'None'
561
+ }
562
+
563
+ # Arrange input fields and labels
564
+ row = 0
565
+ for name, entry in vars_dict.items():
566
+ ttk.Label(scrollable_frame.scrollable_frame, text=f"{name.replace('_', ' ').capitalize()}:",
567
+ background="black", foreground="white").grid(row=row, column=0)
568
+ entry.insert(0, default_settings[name])
569
+ entry.grid(row=row, column=1)
570
+ row += 1
571
+
572
+ # Function to be called when "Run" button is clicked
573
+ def run_app():
574
+ settings = {key: entry.get() for key, entry in vars_dict.items()}
575
+ settings['channels'] = settings['channels'].split(',')
576
+ settings['img_size'] = tuple(map(int, settings['img_size'][1:-1].split(',')))
577
+ settings['percentiles'] = tuple(map(int, settings['percentiles'][1:-1].split(',')))
578
+ settings['normalize'] = settings['normalize'].lower() == 'true'
579
+ settings['rows'] = int(settings['rows'])
580
+ settings['columns'] = int(settings['columns'])
581
+ if settings['measurement'].lower() == 'none':
582
+ settings['measurement'] = None
583
+ if settings['threshold'].lower() == 'none':
584
+ settings['threshold'] = None
585
+
586
+ # Clear previous content instead of destroying the root
587
+ if hasattr(parent_frame, 'winfo_children'):
588
+ for widget in parent_frame.winfo_children():
589
+ widget.destroy()
590
+
591
+ # Start the annotate application in the same root window
592
+ annotate_app(parent_frame, settings)
593
+
594
+ run_button = CustomButton(scrollable_frame.scrollable_frame, text="Run", command=run_app,
595
+ font=tkFont.Font(family="Arial", size=12, weight=tkFont.NORMAL))
596
+ run_button.grid(row=row, column=0, columnspan=2, pady=10, padx=10)
597
+
598
+ return parent_frame
599
+
600
+ def annotate_app(parent_frame, settings):
601
+ global global_image_refs
602
+ global_image_refs.clear()
603
+ root = parent_frame.winfo_toplevel()
604
+ annotate_with_image_refs(settings, root, lambda: load_next_app(root))
605
+
606
+ def annotate_with_image_refs(settings, root, shutdown_callback):
607
+ settings = get_annotate_default_settings(settings)
608
+ src = settings['src']
609
+
610
+ db = os.path.join(src, 'measurements/measurements.db')
611
+ conn = sqlite3.connect(db)
612
+ c = conn.cursor()
613
+ c.execute('PRAGMA table_info(png_list)')
614
+ cols = c.fetchall()
615
+ if settings['annotation_column'] not in [col[1] for col in cols]:
616
+ c.execute(f"ALTER TABLE png_list ADD COLUMN {settings['annotation_column']} integer")
617
+ conn.commit()
618
+ conn.close()
619
+
620
+ app = ImageApp(root, db, src, image_type=settings['image_type'], channels=settings['channels'], image_size=settings['img_size'], grid_rows=settings['rows'], grid_cols=settings['columns'], annotation_column=settings['annotation_column'], normalize=settings['normalize'], percentiles=settings['percentiles'], measurement=settings['measurement'], threshold=settings['threshold'])
621
+
622
+ # Set the canvas background to black
623
+ root.configure(bg='black')
624
+
625
+ next_button = tk.Button(root, text="Next", command=app.next_page, background='black', foreground='white')
626
+ next_button.grid(row=app.grid_rows, column=app.grid_cols - 1)
627
+ back_button = tk.Button(root, text="Back", command=app.previous_page, background='black', foreground='white')
628
+ back_button.grid(row=app.grid_rows, column=app.grid_cols - 2)
629
+ exit_button = tk.Button(root, text="Exit", command=lambda: [app.shutdown(), shutdown_callback()], background='black', foreground='white')
630
+ exit_button.grid(row=app.grid_rows, column=app.grid_cols - 3)
631
+
632
+ app.load_images()
633
+
634
+ # 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 = ()
638
+
639
+ def load_next_app(root):
640
+ # Get the next app function and arguments
641
+ next_app_func = root.next_app_func
642
+ next_app_args = root.next_app_args
643
+
644
+ if next_app_func:
645
+ next_app_func(*next_app_args)
646
+
647
+ def gui_annotate():
648
+ root = tk.Tk()
649
+ width = root.winfo_screenwidth()
650
+ height = root.winfo_screenheight()
651
+ root.geometry(f"{width}x{height}")
652
+ root.title("Annotate Application")
653
+
654
+ # Clear previous content if any
655
+ if hasattr(root, 'content_frame'):
656
+ for widget in root.content_frame.winfo_children():
657
+ widget.destroy()
658
+ root.content_frame.grid_forget()
659
+ else:
660
+ root.content_frame = tk.Frame(root)
661
+ root.content_frame.grid(row=1, column=0, sticky="nsew")
662
+ root.grid_rowconfigure(1, weight=1)
663
+ root.grid_columnconfigure(0, weight=1)
664
+
665
+ initiate_annotation_app_root(root.content_frame)
666
+ create_menu_bar(root)
667
+ root.mainloop()
668
+
669
+ if __name__ == "__main__":
670
+ gui_annotate()
@@ -131,11 +131,11 @@ def initiate_classify_root(parent_frame):
131
131
  vars_dict = generate_fields(variables, scrollable_frame)
132
132
 
133
133
  # Button section
134
- import_btn = CustomButton(scrollable_frame.scrollable_frame, text="Import Settings", command=lambda: import_settings(scrollable_frame))
134
+ import_btn = CustomButton(scrollable_frame.scrollable_frame, text="Import", command=lambda: import_settings(scrollable_frame), font=('Helvetica', 10))
135
135
  import_btn.grid(row=47, column=0, pady=20, padx=20)
136
- run_button = CustomButton(scrollable_frame.scrollable_frame, text="Run", command=lambda: start_process(q, fig_queue))
136
+ run_button = CustomButton(scrollable_frame.scrollable_frame, text="Run", command=lambda: start_process(q, fig_queue), font=('Helvetica', 10))
137
137
  run_button.grid(row=45, column=0, pady=20, padx=20)
138
- abort_button = CustomButton(scrollable_frame.scrollable_frame, text="Abort", command=initiate_abort)
138
+ abort_button = CustomButton(scrollable_frame.scrollable_frame, text="Abort", command=initiate_abort, font=('Helvetica', 10))
139
139
  abort_button.grid(row=45, column=1, pady=20, padx=20)
140
140
  progress_label = ttk.Label(scrollable_frame.scrollable_frame, text="Processing: 0%", background="black", foreground="white") # Create progress field
141
141
  progress_label.grid(row=50, column=0, columnspan=2, sticky="ew", pady=(5, 0), padx=10)
@@ -177,9 +177,23 @@ def initiate_classify_root(parent_frame):
177
177
 
178
178
  def gui_classify():
179
179
  root = tk.Tk()
180
- root.geometry("1000x800")
181
- root.title("SpaCer: generate masks")
182
- initiate_classify_root(root),
180
+ width = root.winfo_screenwidth()
181
+ height = root.winfo_screenheight()
182
+ root.geometry(f"{width}x{height}")
183
+ root.title("SpaCr: classify objects")
184
+
185
+ # Clear previous content if any
186
+ if hasattr(root, 'content_frame'):
187
+ for widget in root.content_frame.winfo_children():
188
+ widget.destroy()
189
+ root.content_frame.grid_forget()
190
+ else:
191
+ root.content_frame = tk.Frame(root)
192
+ root.content_frame.grid(row=1, column=0, sticky="nsew")
193
+ root.grid_rowconfigure(1, weight=1)
194
+ root.grid_columnconfigure(0, weight=1)
195
+
196
+ initiate_classify_root(root.content_frame)
183
197
  create_menu_bar(root)
184
198
  root.mainloop()
185
199
 
@@ -9,10 +9,9 @@ import requests
9
9
  from .gui_mask_app import initiate_mask_root
10
10
  from .gui_measure_app import initiate_measure_root
11
11
  from .annotate_app import initiate_annotation_app_root
12
- from .mask_app import initiate_mask_app_root
12
+ from .gui_make_masks_app import initiate_mask_app_root
13
13
  from .gui_classify_app import initiate_classify_root
14
-
15
- from .gui_utils import CustomButton, style_text_boxes
14
+ from .gui_utils import CustomButton, style_text_boxes, create_menu_bar
16
15
 
17
16
  class MainApp(tk.Tk):
18
17
  def __init__(self):
@@ -38,18 +37,24 @@ class MainApp(tk.Tk):
38
37
 
39
38
  def create_widgets(self):
40
39
  # Create the menu bar
41
- #create_menu_bar(self)
40
+ self.gui_create_menu_bar()
41
+
42
42
  # Create a canvas to hold the selected app and other elements
43
- self.canvas = tk.Canvas(self, bg="black", highlightthickness=0, width=4000, height=4000)
43
+ self.canvas = tk.Canvas(self, bg="black", highlightthickness=0)
44
44
  self.canvas.grid(row=0, column=0, sticky="nsew")
45
45
  self.grid_rowconfigure(0, weight=1)
46
46
  self.grid_columnconfigure(0, weight=1)
47
+
47
48
  # Create a frame inside the canvas to hold the main content
48
49
  self.content_frame = tk.Frame(self.canvas, bg="black")
49
50
  self.content_frame.pack(fill=tk.BOTH, expand=True)
51
+
50
52
  # Create startup screen with buttons for each GUI app
51
53
  self.create_startup_screen()
52
54
 
55
+ def gui_create_menu_bar(self):
56
+ create_menu_bar(self)
57
+
53
58
  def create_startup_screen(self):
54
59
  self.clear_frame(self.content_frame)
55
60
 
@@ -59,10 +64,10 @@ class MainApp(tk.Tk):
59
64
 
60
65
  # Load the logo image
61
66
  if not self.load_logo(logo_frame):
62
- tk.Label(logo_frame, text="Logo not found", bg="black", fg="white", font=('Helvetica', 24, tkFont.NORMAL)).pack(padx=10, pady=10)
67
+ tk.Label(logo_frame, text="Logo not found", bg="black", fg="white", font=('Helvetica', 24)).pack(padx=10, pady=10)
63
68
 
64
69
  # Add SpaCr text below the logo with padding for sharper text
65
- tk.Label(logo_frame, text="SpaCr", bg="black", fg="#008080", font=('Helvetica', 24, tkFont.NORMAL)).pack(padx=10, pady=10)
70
+ tk.Label(logo_frame, text="SpaCr", bg="black", fg="#008080", font=('Helvetica', 24)).pack(padx=10, pady=10)
66
71
 
67
72
  # Create a frame for the buttons and descriptions
68
73
  buttons_frame = tk.Frame(self.content_frame, bg="black")
@@ -72,10 +77,10 @@ class MainApp(tk.Tk):
72
77
  app_func, app_desc = app_data
73
78
 
74
79
  # Create custom button with text
75
- button = CustomButton(buttons_frame, text=app_name, command=lambda app_name=app_name: self.load_app(app_name), font=('Helvetica', 12))
80
+ button = CustomButton(buttons_frame, text=app_name, command=lambda app_name=app_name: self.load_app(app_name, app_func), font=('Helvetica', 12))
76
81
  button.grid(row=i, column=0, pady=10, padx=10, sticky="w")
77
82
 
78
- description_label = tk.Label(buttons_frame, text=app_desc, bg="black", fg="white", wraplength=800, justify="left", font=('Helvetica', 10, tkFont.NORMAL))
83
+ description_label = tk.Label(buttons_frame, text=app_desc, bg="black", fg="white", wraplength=800, justify="left", font=('Helvetica', 12))
79
84
  description_label.grid(row=i, column=1, pady=10, padx=10, sticky="w")
80
85
 
81
86
  # Ensure buttons have a fixed width
@@ -125,13 +130,14 @@ class MainApp(tk.Tk):
125
130
  print(f"An error occurred while processing the logo image: {e}")
126
131
  return False
127
132
 
128
- def load_app(self, app_name):
129
- selected_app_func, _ = self.gui_apps[app_name]
133
+ def load_app(self, app_name, app_func):
134
+ # Clear the current content frame
130
135
  self.clear_frame(self.content_frame)
131
136
 
137
+ # Initialize the selected app
132
138
  app_frame = tk.Frame(self.content_frame, bg="black")
133
139
  app_frame.pack(fill=tk.BOTH, expand=True)
134
- selected_app_func(app_frame)#, self.winfo_width(), self.winfo_height())
140
+ app_func(app_frame)
135
141
 
136
142
  def clear_frame(self, frame):
137
143
  for widget in frame.winfo_children():
@@ -142,4 +148,4 @@ def gui_app():
142
148
  app.mainloop()
143
149
 
144
150
  if __name__ == "__main__":
145
- gui_app()
151
+ gui_app()
@@ -47,6 +47,10 @@ def load_app(root, app_name, app_func):
47
47
  root.after_cancel(task)
48
48
  root.after_tasks = []
49
49
 
50
+ # Exit functionality only for the annotation app
51
+ if app_name == "Annotate" and hasattr(root, 'current_app_exit_func'):
52
+ root.current_app_exit_func()
53
+
50
54
  # Clear the current content frame
51
55
  if hasattr(root, 'content_frame'):
52
56
  for widget in root.content_frame.winfo_children():
@@ -64,7 +68,7 @@ def create_menu_bar(root):
64
68
  from .gui_mask_app import initiate_mask_root
65
69
  from .gui_measure_app import initiate_measure_root
66
70
  from .annotate_app import initiate_annotation_app_root
67
- from .mask_app import initiate_mask_app_root
71
+ from .gui_make_masks_app import initiate_mask_app_root
68
72
  from .gui_classify_app import initiate_classify_root
69
73
 
70
74
  gui_apps = {
@@ -92,6 +96,66 @@ def create_menu_bar(root):
92
96
  # Configure the menu for the root window
93
97
  root.config(menu=menu_bar)
94
98
 
99
+ def load_app(root, app_name, app_func):
100
+ # Cancel all scheduled after tasks
101
+ if hasattr(root, 'after_tasks'):
102
+ for task in root.after_tasks:
103
+ root.after_cancel(task)
104
+ root.after_tasks = []
105
+
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
+ # Exit functionality only for the annotation app
121
+ if app_name != "Annotate" and hasattr(root, 'current_app_exit_func'):
122
+ root.next_app_func = proceed_with_app
123
+ root.current_app_exit_func()
124
+ else:
125
+ proceed_with_app()
126
+
127
+ 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
133
+
134
+ gui_apps = {
135
+ "Mask": initiate_mask_root,
136
+ "Measure": initiate_measure_root,
137
+ "Annotate": initiate_annotation_app_root,
138
+ "Make Masks": initiate_mask_app_root,
139
+ "Classify": initiate_classify_root
140
+ }
141
+
142
+ def load_app_wrapper(app_name, app_func):
143
+ load_app(root, app_name, app_func)
144
+
145
+ # Create the menu bar
146
+ menu_bar = tk.Menu(root, bg="#008080", fg="white")
147
+ # Create a "SpaCr Applications" menu
148
+ app_menu = tk.Menu(menu_bar, tearoff=0, bg="#008080", fg="white")
149
+ menu_bar.add_cascade(label="SpaCr Applications", menu=app_menu)
150
+ # Add options to the "SpaCr Applications" menu
151
+ for app_name, app_func in gui_apps.items():
152
+ app_menu.add_command(label=app_name, command=lambda app_name=app_name, app_func=app_func: load_app_wrapper(app_name, app_func))
153
+ # Add a separator and an exit option
154
+ app_menu.add_separator()
155
+ app_menu.add_command(label="Exit", command=root.destroy) # Use root.destroy instead of root.quit
156
+ # Configure the menu for the root window
157
+ root.config(menu=menu_bar)
158
+
95
159
  class CustomButton(tk.Frame):
96
160
  def __init__(self, parent, text="", command=None, font=None, *args, **kwargs):
97
161
  super().__init__(parent, *args, **kwargs)
@@ -275,19 +339,6 @@ def check_and_download_font_v1():
275
339
  tkFont.nametofont("TkTextFont").configure(family=font_name, size=10)
276
340
  tkFont.nametofont("TkHeadingFont").configure(family=font_name, size=12)
277
341
 
278
- def style_text_boxes_v1(style):
279
- check_and_download_font()
280
- font_style = tkFont.Font(family="Helvetica", size=10) # Define the Helvetica font
281
- style.configure('TEntry', padding='5 5 5 5', borderwidth=1, relief='solid', fieldbackground='#000000', foreground='#ffffff', font=font_style)
282
- style.configure('TCombobox', fieldbackground='#000000', background='#000000', foreground='#ffffff', font=font_style)
283
- style.configure('Custom.TButton', padding='10 10 10 10', borderwidth=1, relief='solid', background='#008080', foreground='#ffffff', font=font_style)
284
- style.map('Custom.TButton',
285
- background=[('active', '#66b2b2'), ('disabled', '#004d4d'), ('!disabled', '#008080')],
286
- foreground=[('active', '#ffffff'), ('disabled', '#888888')])
287
- style.configure('Custom.TLabel', padding='5 5 5 5', borderwidth=1, relief='flat', background='#000000', foreground='#ffffff', font=font_style)
288
- style.configure('TCheckbutton', background='#333333', foreground='#ffffff', indicatoron=False, relief='flat', font=font_style)
289
- style.map('TCheckbutton', background=[('selected', '#555555'), ('active', '#555555')])
290
-
291
342
  def check_and_download_font():
292
343
  font_name = "Helvetica"
293
344
  font_dir = "fonts"
@@ -320,7 +371,7 @@ def check_and_download_font():
320
371
  tkFont.nametofont("TkTextFont").configure(family=font_name, size=10)
321
372
  tkFont.nametofont("TkHeadingFont").configure(family=font_name, size=12)
322
373
 
323
- def style_text_boxes(style):
374
+ def style_text_boxes_v1(style):
324
375
  check_and_download_font()
325
376
  font_style = tkFont.Font(family="Helvetica", size=10) # Define the Helvetica font
326
377
  style.configure('TEntry', padding='5 5 5 5', borderwidth=1, relief='solid', fieldbackground='#000000', foreground='#ffffff', font=font_style)
@@ -333,6 +384,19 @@ def style_text_boxes(style):
333
384
  style.configure('TCheckbutton', background='#333333', foreground='#ffffff', indicatoron=False, relief='flat', font=font_style)
334
385
  style.map('TCheckbutton', background=[('selected', '#555555'), ('active', '#555555')])
335
386
 
387
+ def style_text_boxes(style):
388
+ 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)
391
+ style.configure('Custom.TButton', padding='10 10 10 10', borderwidth=1, relief='solid', background='#008080', foreground='#ffffff', font=font_style)
392
+ style.map('Custom.TButton',
393
+ background=[('active', '#66b2b2'), ('disabled', '#004d4d'), ('!disabled', '#008080')],
394
+ foreground=[('active', '#ffffff'), ('disabled', '#888888')])
395
+ 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)
397
+ style.map('TCheckbutton', background=[('selected', '#555555'), ('active', '#555555')])
398
+
399
+
336
400
 
337
401
  def read_settings_from_csv(csv_file_path):
338
402
  settings = {}