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.
- {spacr-0.1.1/spacr.egg-info → spacr-0.1.11}/PKG-INFO +5 -1
- {spacr-0.1.1 → spacr-0.1.11}/setup.py +9 -5
- {spacr-0.1.1 → spacr-0.1.11}/spacr/__init__.py +4 -2
- spacr-0.1.1/spacr/annotate_app_v2.py → spacr-0.1.11/spacr/annotate_app.py +163 -4
- spacr-0.1.1/spacr/gui_classify_app.py → spacr-0.1.11/spacr/classify_app.py +20 -6
- {spacr-0.1.1 → spacr-0.1.11}/spacr/gui.py +19 -13
- {spacr-0.1.1 → spacr-0.1.11}/spacr/gui_utils.py +79 -15
- spacr-0.1.1/spacr/mask_app.py → spacr-0.1.11/spacr/make_masks_app.py +72 -70
- spacr-0.1.11/spacr/make_masks_app_v2.py +688 -0
- spacr-0.1.1/spacr/gui_mask_app.py → spacr-0.1.11/spacr/mask_app.py +8 -4
- spacr-0.1.1/spacr/gui_measure_app.py → spacr-0.1.11/spacr/measure_app.py +15 -5
- {spacr-0.1.1 → spacr-0.1.11/spacr.egg-info}/PKG-INFO +5 -1
- {spacr-0.1.1 → spacr-0.1.11}/spacr.egg-info/SOURCES.txt +5 -6
- {spacr-0.1.1 → spacr-0.1.11}/spacr.egg-info/entry_points.txt +3 -3
- {spacr-0.1.1 → spacr-0.1.11}/spacr.egg-info/requires.txt +4 -0
- spacr-0.1.1/spacr/annotate_app.py +0 -511
- spacr-0.1.1/spacr/gui_2.py +0 -160
- {spacr-0.1.1 → spacr-0.1.11}/LICENSE +0 -0
- {spacr-0.1.1 → spacr-0.1.11}/MANIFEST.in +0 -0
- {spacr-0.1.1 → spacr-0.1.11}/README.rst +0 -0
- {spacr-0.1.1 → spacr-0.1.11}/setup.cfg +0 -0
- {spacr-0.1.1 → spacr-0.1.11}/spacr/__main__.py +0 -0
- {spacr-0.1.1 → spacr-0.1.11}/spacr/chris.py +0 -0
- {spacr-0.1.1 → spacr-0.1.11}/spacr/core.py +0 -0
- {spacr-0.1.1 → spacr-0.1.11}/spacr/deep_spacr.py +0 -0
- {spacr-0.1.1 → spacr-0.1.11}/spacr/graph_learning.py +0 -0
- {spacr-0.1.1 → spacr-0.1.11}/spacr/io.py +0 -0
- {spacr-0.1.1 → spacr-0.1.11}/spacr/logger.py +0 -0
- {spacr-0.1.1 → spacr-0.1.11}/spacr/measure.py +0 -0
- {spacr-0.1.1 → spacr-0.1.11}/spacr/models/cp/toxo_plaque_cyto_e25000_X1120_Y1120.CP_model +0 -0
- {spacr-0.1.1 → spacr-0.1.11}/spacr/models/cp/toxo_plaque_cyto_e25000_X1120_Y1120.CP_model_settings.csv +0 -0
- {spacr-0.1.1 → spacr-0.1.11}/spacr/models/cp/toxo_pv_lumen.CP_model +0 -0
- {spacr-0.1.1 → spacr-0.1.11}/spacr/plot.py +0 -0
- {spacr-0.1.1 → spacr-0.1.11}/spacr/sequencing.py +0 -0
- {spacr-0.1.1 → spacr-0.1.11}/spacr/settings.py +0 -0
- {spacr-0.1.1 → spacr-0.1.11}/spacr/sim.py +0 -0
- /spacr-0.1.1/spacr/gui_sim_app.py → /spacr-0.1.11/spacr/sim_app.py +0 -0
- {spacr-0.1.1 → spacr-0.1.11}/spacr/timelapse.py +0 -0
- {spacr-0.1.1 → spacr-0.1.11}/spacr/utils.py +0 -0
- {spacr-0.1.1 → spacr-0.1.11}/spacr/version.py +0 -0
- {spacr-0.1.1 → spacr-0.1.11}/spacr.egg-info/dependency_links.txt +0 -0
- {spacr-0.1.1 → spacr-0.1.11}/spacr.egg-info/top_level.txt +0 -0
- {spacr-0.1.1 → spacr-0.1.11}/tests/test_annotate_app.py +0 -0
- {spacr-0.1.1 → spacr-0.1.11}/tests/test_core.py +0 -0
- {spacr-0.1.1 → spacr-0.1.11}/tests/test_gui_classify_app.py +0 -0
- {spacr-0.1.1 → spacr-0.1.11}/tests/test_gui_mask_app.py +0 -0
- {spacr-0.1.1 → spacr-0.1.11}/tests/test_gui_measure_app.py +0 -0
- {spacr-0.1.1 → spacr-0.1.11}/tests/test_gui_sim_app.py +0 -0
- {spacr-0.1.1 → spacr-0.1.11}/tests/test_gui_utils.py +0 -0
- {spacr-0.1.1 → spacr-0.1.11}/tests/test_io.py +0 -0
- {spacr-0.1.1 → spacr-0.1.11}/tests/test_mask_app.py +0 -0
- {spacr-0.1.1 → spacr-0.1.11}/tests/test_measure.py +0 -0
- {spacr-0.1.1 → spacr-0.1.11}/tests/test_plot.py +0 -0
- {spacr-0.1.1 → spacr-0.1.11}/tests/test_sim.py +0 -0
- {spacr-0.1.1 → spacr-0.1.11}/tests/test_timelapse.py +0 -0
- {spacr-0.1.1 → spacr-0.1.11}/tests/test_train.py +0 -0
- {spacr-0.1.1 → spacr-0.1.11}/tests/test_umap.py +0 -0
- {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.
|
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.
|
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.
|
75
|
-
'
|
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
|
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
|
-
"
|
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
|
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
|
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.
|
181
|
-
root.
|
182
|
-
|
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 .
|
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
|
-
|
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
|
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
|
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
|
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',
|
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
|
-
|
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
|
-
|
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 .
|
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
|
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 = {}
|