spacr 0.0.21__tar.gz → 0.0.36__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.0.21/spacr.egg-info → spacr-0.0.36}/PKG-INFO +1 -1
- {spacr-0.0.21 → spacr-0.0.36}/setup.py +8 -7
- {spacr-0.0.21 → spacr-0.0.36}/spacr/annotate_app.py +6 -4
- spacr-0.0.36/spacr/chris.py +50 -0
- spacr-0.0.36/spacr/gui.py +144 -0
- {spacr-0.0.21 → spacr-0.0.36}/spacr/gui_classify_app.py +69 -78
- spacr-0.0.36/spacr/gui_mask_app.py +222 -0
- spacr-0.0.36/spacr/gui_measure_app.py +223 -0
- {spacr-0.0.21 → spacr-0.0.36}/spacr/gui_utils.py +267 -37
- {spacr-0.0.21 → spacr-0.0.36}/spacr/io.py +4 -3
- {spacr-0.0.21 → spacr-0.0.36}/spacr/mask_app.py +7 -4
- {spacr-0.0.21 → spacr-0.0.36}/spacr/measure.py +4 -4
- {spacr-0.0.21 → spacr-0.0.36}/spacr/old_code.py +1 -1
- {spacr-0.0.21 → spacr-0.0.36}/spacr/utils.py +0 -6
- {spacr-0.0.21 → spacr-0.0.36/spacr.egg-info}/PKG-INFO +1 -1
- {spacr-0.0.21 → spacr-0.0.36}/spacr.egg-info/SOURCES.txt +2 -0
- spacr-0.0.36/spacr.egg-info/entry_points.txt +8 -0
- spacr-0.0.21/spacr/gui_mask_app.py +0 -219
- spacr-0.0.21/spacr/gui_measure_app.py +0 -200
- spacr-0.0.21/spacr.egg-info/entry_points.txt +0 -7
- {spacr-0.0.21 → spacr-0.0.36}/LICENSE +0 -0
- {spacr-0.0.21 → spacr-0.0.36}/README.md +0 -0
- {spacr-0.0.21 → spacr-0.0.36}/setup.cfg +0 -0
- {spacr-0.0.21 → spacr-0.0.36}/spacr/__init__.py +0 -0
- {spacr-0.0.21 → spacr-0.0.36}/spacr/__main__.py +0 -0
- {spacr-0.0.21 → spacr-0.0.36}/spacr/alpha.py +0 -0
- {spacr-0.0.21 → spacr-0.0.36}/spacr/cli.py +0 -0
- {spacr-0.0.21 → spacr-0.0.36}/spacr/core.py +0 -0
- {spacr-0.0.21 → spacr-0.0.36}/spacr/foldseek.py +0 -0
- {spacr-0.0.21 → spacr-0.0.36}/spacr/get_alfafold_structures.py +0 -0
- {spacr-0.0.21 → spacr-0.0.36}/spacr/graph_learning.py +0 -0
- {spacr-0.0.21 → spacr-0.0.36}/spacr/graph_learning_lap.py +0 -0
- {spacr-0.0.21 → spacr-0.0.36}/spacr/gui_sim_app.py +0 -0
- {spacr-0.0.21 → spacr-0.0.36}/spacr/logger.py +0 -0
- {spacr-0.0.21 → spacr-0.0.36}/spacr/plot.py +0 -0
- {spacr-0.0.21 → spacr-0.0.36}/spacr/sim.py +0 -0
- {spacr-0.0.21 → spacr-0.0.36}/spacr/timelapse.py +0 -0
- {spacr-0.0.21 → spacr-0.0.36}/spacr/train.py +0 -0
- {spacr-0.0.21 → spacr-0.0.36}/spacr/umap.py +0 -0
- {spacr-0.0.21 → spacr-0.0.36}/spacr/version.py +0 -0
- {spacr-0.0.21 → spacr-0.0.36}/spacr.egg-info/dependency_links.txt +0 -0
- {spacr-0.0.21 → spacr-0.0.36}/spacr.egg-info/requires.txt +0 -0
- {spacr-0.0.21 → spacr-0.0.36}/spacr.egg-info/top_level.txt +0 -0
- {spacr-0.0.21 → spacr-0.0.36}/tests/test_annotate_app.py +0 -0
- {spacr-0.0.21 → spacr-0.0.36}/tests/test_core.py +0 -0
- {spacr-0.0.21 → spacr-0.0.36}/tests/test_gui_classify_app.py +0 -0
- {spacr-0.0.21 → spacr-0.0.36}/tests/test_gui_mask_app.py +0 -0
- {spacr-0.0.21 → spacr-0.0.36}/tests/test_gui_measure_app.py +0 -0
- {spacr-0.0.21 → spacr-0.0.36}/tests/test_gui_sim_app.py +0 -0
- {spacr-0.0.21 → spacr-0.0.36}/tests/test_gui_utils.py +0 -0
- {spacr-0.0.21 → spacr-0.0.36}/tests/test_io.py +0 -0
- {spacr-0.0.21 → spacr-0.0.36}/tests/test_mask_app.py +0 -0
- {spacr-0.0.21 → spacr-0.0.36}/tests/test_measure.py +0 -0
- {spacr-0.0.21 → spacr-0.0.36}/tests/test_plot.py +0 -0
- {spacr-0.0.21 → spacr-0.0.36}/tests/test_sim.py +0 -0
- {spacr-0.0.21 → spacr-0.0.36}/tests/test_timelapse.py +0 -0
- {spacr-0.0.21 → spacr-0.0.36}/tests/test_train.py +0 -0
- {spacr-0.0.21 → spacr-0.0.36}/tests/test_umap.py +0 -0
- {spacr-0.0.21 → spacr-0.0.36}/tests/test_utils.py +0 -0
@@ -35,7 +35,7 @@ dependencies = [
|
|
35
35
|
|
36
36
|
setup(
|
37
37
|
name="spacr",
|
38
|
-
version="0.0.
|
38
|
+
version="0.0.36",
|
39
39
|
author="Einar Birnir Olafsson",
|
40
40
|
author_email="olafsson@med.umich.com",
|
41
41
|
description="Spatial phenotype analysis of crisp screens (SpaCr)",
|
@@ -45,12 +45,13 @@ setup(
|
|
45
45
|
install_requires=dependencies,
|
46
46
|
entry_points={
|
47
47
|
'console_scripts': [
|
48
|
-
'
|
49
|
-
'
|
50
|
-
'
|
51
|
-
'
|
52
|
-
'
|
53
|
-
'
|
48
|
+
'mask=spacr.gui_mask_app:gui_mask',
|
49
|
+
'measure=spacr.gui_measure_app:gui_measure',
|
50
|
+
'make_masks=spacr.mask_app:gui_make_masks',
|
51
|
+
'annotate=spacr.annotate_app:gui_annotation',
|
52
|
+
'classify=spacr.gui_classify_app:gui_classify',
|
53
|
+
'sim=spacr.gui_sim_app:gui_sim',
|
54
|
+
'gui=spacr.gui:gui_app',
|
54
55
|
],
|
55
56
|
},
|
56
57
|
extras_require={
|
@@ -13,7 +13,7 @@ from ttkthemes import ThemedTk
|
|
13
13
|
|
14
14
|
from .logger import log_function_call
|
15
15
|
|
16
|
-
from .gui_utils import ScrollableFrame, set_default_font, set_dark_style, create_dark_mode
|
16
|
+
from .gui_utils import ScrollableFrame, set_default_font, set_dark_style, create_dark_mode, style_text_boxes, create_menu_bar
|
17
17
|
|
18
18
|
class ImageApp:
|
19
19
|
"""
|
@@ -419,13 +419,14 @@ def check_for_duplicates(db):
|
|
419
419
|
conn.commit()
|
420
420
|
conn.close()
|
421
421
|
|
422
|
-
|
422
|
+
#@log_function_call
|
423
423
|
def initiate_annotation_app_root(width, height):
|
424
424
|
theme = 'breeze'
|
425
425
|
root = ThemedTk(theme=theme)
|
426
426
|
style = ttk.Style(root)
|
427
427
|
set_dark_style(style)
|
428
|
-
|
428
|
+
style_text_boxes(style)
|
429
|
+
set_default_font(root, font_name="Arial", size=8)
|
429
430
|
root.geometry(f"{width}x{height}")
|
430
431
|
root.title("Annotation App")
|
431
432
|
|
@@ -473,6 +474,7 @@ def initiate_annotation_app_root(width, height):
|
|
473
474
|
new_root = tk.Tk()
|
474
475
|
new_root.geometry(f"{width}x{height}")
|
475
476
|
new_root.title("Mask Application")
|
477
|
+
|
476
478
|
|
477
479
|
# Start the annotation application in the new root window
|
478
480
|
app_instance = annotate(db, image_type, channels, annotation_column, geom, img_size, rows, columns)
|
@@ -482,7 +484,7 @@ def initiate_annotation_app_root(width, height):
|
|
482
484
|
create_dark_mode(root, style, console_output=None)
|
483
485
|
|
484
486
|
run_button = ttk.Button(scrollable_frame.scrollable_frame, text="Run", command=run_app)
|
485
|
-
run_button.grid(row=row, column=0, columnspan=2, pady=10)
|
487
|
+
run_button.grid(row=row, column=0, columnspan=2, pady=10, padx=10)
|
486
488
|
|
487
489
|
return root
|
488
490
|
|
@@ -0,0 +1,50 @@
|
|
1
|
+
import pandas as pd
|
2
|
+
import numpy as np
|
3
|
+
from .core import _permutation_importance, _shap_analysis
|
4
|
+
|
5
|
+
def join_measurments_and_annotation(src, tables = ['cell', 'nucleus', 'pathogen','cytoplasm']):
|
6
|
+
|
7
|
+
from .io import _read_and_merge_data, _read_db
|
8
|
+
|
9
|
+
db_loc = [src+'/measurements/measurements.db']
|
10
|
+
loc = src+'/measurements/measurements.db'
|
11
|
+
df, _ = _read_and_merge_data(db_loc,
|
12
|
+
tables,
|
13
|
+
verbose=True,
|
14
|
+
include_multinucleated=True,
|
15
|
+
include_multiinfected=True,
|
16
|
+
include_noninfected=True)
|
17
|
+
|
18
|
+
paths_df = _read_db(loc, tables=['png_list'])
|
19
|
+
|
20
|
+
merged_df = pd.merge(df, paths_df[0], on='prcfo', how='left')
|
21
|
+
|
22
|
+
return merged_df
|
23
|
+
|
24
|
+
def plate_heatmap(src, model_type='xgboost', variable='predictions', grouping='mean', min_max='allq', cmap='viridis', channel_of_interest=3, min_count=25, n_estimators=100, col_to_compare='col', pos='c1', neg='c2', exclude=None, n_repeats=10, clean=True, nr_to_plot=20, verbose=False, n_jobs=-1):
|
25
|
+
from .io import _read_and_merge_data
|
26
|
+
from .plot import _plot_plates
|
27
|
+
|
28
|
+
db_loc = [src+'/measurements/measurements.db']
|
29
|
+
tables = ['cell', 'nucleus', 'pathogen','cytoplasm']
|
30
|
+
include_multinucleated, include_multiinfected, include_noninfected = True, 2.0, True
|
31
|
+
|
32
|
+
df = join_measurments_and_annotation(src, tables=['cell', 'nucleus', 'pathogen', 'cytoplasm'])
|
33
|
+
|
34
|
+
if not channel_of_interest is None:
|
35
|
+
df['recruitment'] = df[f'pathogen_channel_{channel_of_interest}_mean_intensity']/df[f'cytoplasm_channel_{channel_of_interest}_mean_intensity']
|
36
|
+
feature_string = f'channel_{channel_of_interest}'
|
37
|
+
else:
|
38
|
+
feature_string = None
|
39
|
+
|
40
|
+
output = _permutation_importance(df, feature_string, col_to_compare, pos, neg, exclude, n_repeats, clean, nr_to_plot, n_estimators=n_estimators, random_state=42, model_type=model_type, n_jobs=n_jobs)
|
41
|
+
|
42
|
+
_shap_analysis(output[3], output[4], output[5])
|
43
|
+
|
44
|
+
features = output[0].select_dtypes(include=[np.number]).columns.tolist()
|
45
|
+
|
46
|
+
if not variable in features:
|
47
|
+
raise ValueError(f"Variable {variable} not found in the dataframe. Please choose one of the following: {features}")
|
48
|
+
|
49
|
+
plate_heatmap = _plot_plates(output[0], variable, grouping, min_max, cmap, min_count)
|
50
|
+
return [output, plate_heatmap]
|
@@ -0,0 +1,144 @@
|
|
1
|
+
import tkinter as tk
|
2
|
+
from tkinter import ttk
|
3
|
+
from PIL import Image, ImageTk
|
4
|
+
import os
|
5
|
+
import requests
|
6
|
+
|
7
|
+
# Import your GUI apps
|
8
|
+
from .gui_mask_app import initiate_mask_root
|
9
|
+
from .gui_measure_app import initiate_measure_root
|
10
|
+
from .annotate_app import initiate_annotation_app_root
|
11
|
+
from .mask_app import initiate_mask_app_root
|
12
|
+
from .gui_classify_app import initiate_classify_root
|
13
|
+
|
14
|
+
from .gui_utils import CustomButton, style_text_boxes
|
15
|
+
|
16
|
+
class MainApp(tk.Tk):
|
17
|
+
def __init__(self):
|
18
|
+
super().__init__()
|
19
|
+
self.title("SpaCr GUI Collection")
|
20
|
+
self.geometry("1200x350")
|
21
|
+
self.configure(bg="black")
|
22
|
+
#self.attributes('-fullscreen', True)
|
23
|
+
|
24
|
+
style = ttk.Style()
|
25
|
+
style_text_boxes(style)
|
26
|
+
|
27
|
+
self.gui_apps = {
|
28
|
+
"Mask": (initiate_mask_root, "Generate cellpose masks for cells, nuclei and pathogen images."),
|
29
|
+
"Measure": (initiate_measure_root, "Measure single object intensity and morphological feature. Crop and save single object image"),
|
30
|
+
"Annotate": (initiate_annotation_app_root, "Annotation single object images on a grid. Annotations are saved to database."),
|
31
|
+
"Make Masks": (initiate_mask_app_root, "Adjust pre-existing Cellpose models to your specific dataset for improved performance"),
|
32
|
+
"Classify": (initiate_classify_root, "Train Torch Convolutional Neural Networks (CNNs) or Transformers to classify single object images.")
|
33
|
+
}
|
34
|
+
|
35
|
+
self.selected_app = tk.StringVar()
|
36
|
+
self.create_widgets()
|
37
|
+
|
38
|
+
def create_widgets(self):
|
39
|
+
# Create the menu bar
|
40
|
+
#create_menu_bar(self)
|
41
|
+
# Create a canvas to hold the selected app and other elements
|
42
|
+
self.canvas = tk.Canvas(self, bg="black", highlightthickness=0, width=4000, height=4000)
|
43
|
+
self.canvas.grid(row=0, column=0, sticky="nsew")
|
44
|
+
self.grid_rowconfigure(0, weight=1)
|
45
|
+
self.grid_columnconfigure(0, weight=1)
|
46
|
+
# Create a frame inside the canvas to hold the main content
|
47
|
+
self.content_frame = tk.Frame(self.canvas, bg="black")
|
48
|
+
self.content_frame.pack(fill=tk.BOTH, expand=True)
|
49
|
+
# Create startup screen with buttons for each GUI app
|
50
|
+
self.create_startup_screen()
|
51
|
+
|
52
|
+
def create_startup_screen(self):
|
53
|
+
self.clear_frame(self.content_frame)
|
54
|
+
|
55
|
+
# Create a frame for the logo and description
|
56
|
+
logo_frame = tk.Frame(self.content_frame, bg="black")
|
57
|
+
logo_frame.pack(pady=20, expand=True)
|
58
|
+
|
59
|
+
# Load the logo image
|
60
|
+
if not self.load_logo(logo_frame):
|
61
|
+
tk.Label(logo_frame, text="Logo not found", bg="black", fg="white", font=('Open Sans', 24)).pack(padx=20, pady=20)
|
62
|
+
|
63
|
+
# Add SpaCr text below the logo with padding for sharper text
|
64
|
+
tk.Label(logo_frame, text="SpaCr", bg="black", fg="#008080", font=('Open Sans', 24)).pack(padx=20, pady=20)
|
65
|
+
|
66
|
+
# Create a frame for the buttons and descriptions
|
67
|
+
buttons_frame = tk.Frame(self.content_frame, bg="black")
|
68
|
+
buttons_frame.pack(pady=20, expand=True, padx=20)
|
69
|
+
|
70
|
+
for i, (app_name, app_data) in enumerate(self.gui_apps.items()):
|
71
|
+
app_func, app_desc = app_data
|
72
|
+
|
73
|
+
# Create custom button with text
|
74
|
+
button = CustomButton(buttons_frame, text=app_name, command=lambda app_name=app_name: self.load_app(app_name))
|
75
|
+
button.grid(row=i, column=0, pady=20, padx=20, sticky="w")
|
76
|
+
|
77
|
+
description_label = tk.Label(buttons_frame, text=app_desc, bg="black", fg="white", wraplength=800, justify="left", font=('Open Sans', 12))
|
78
|
+
description_label.grid(row=i, column=1, pady=20, padx=20, sticky="w")
|
79
|
+
|
80
|
+
# Ensure buttons have a fixed width
|
81
|
+
buttons_frame.grid_columnconfigure(0, minsize=150)
|
82
|
+
# Ensure descriptions expand as needed
|
83
|
+
buttons_frame.grid_columnconfigure(1, weight=1)
|
84
|
+
|
85
|
+
def load_logo(self, frame):
|
86
|
+
def download_image(url, save_path):
|
87
|
+
try:
|
88
|
+
response = requests.get(url, stream=True)
|
89
|
+
response.raise_for_status() # Raise an HTTPError for bad responses
|
90
|
+
with open(save_path, 'wb') as f:
|
91
|
+
for chunk in response.iter_content(chunk_size=8192):
|
92
|
+
f.write(chunk)
|
93
|
+
return True
|
94
|
+
except requests.exceptions.RequestException as e:
|
95
|
+
print(f"Failed to download image from {url}: {e}")
|
96
|
+
return False
|
97
|
+
|
98
|
+
try:
|
99
|
+
img_path = os.path.join(os.path.dirname(__file__), 'logo_spacr.png')
|
100
|
+
print(f"Trying to load logo from {img_path}")
|
101
|
+
logo_image = Image.open(img_path)
|
102
|
+
except (FileNotFoundError, Image.UnidentifiedImageError):
|
103
|
+
print(f"File {img_path} not found or is not a valid image. Attempting to download from GitHub.")
|
104
|
+
if download_image('https://raw.githubusercontent.com/EinarOlafsson/spacr/main/spacr/logo_spacr.png', img_path):
|
105
|
+
try:
|
106
|
+
print(f"Downloaded file size: {os.path.getsize(img_path)} bytes")
|
107
|
+
logo_image = Image.open(img_path)
|
108
|
+
except Image.UnidentifiedImageError as e:
|
109
|
+
print(f"Downloaded file is not a valid image: {e}")
|
110
|
+
return False
|
111
|
+
else:
|
112
|
+
return False
|
113
|
+
except Exception as e:
|
114
|
+
print(f"An error occurred while loading the logo: {e}")
|
115
|
+
return False
|
116
|
+
try:
|
117
|
+
logo_image = logo_image.resize((800, 800), Image.Resampling.LANCZOS)
|
118
|
+
logo_photo = ImageTk.PhotoImage(logo_image)
|
119
|
+
logo_label = tk.Label(frame, image=logo_photo, bg="black")
|
120
|
+
logo_label.image = logo_photo # Keep a reference to avoid garbage collection
|
121
|
+
logo_label.pack()
|
122
|
+
return True
|
123
|
+
except Exception as e:
|
124
|
+
print(f"An error occurred while processing the logo image: {e}")
|
125
|
+
return False
|
126
|
+
|
127
|
+
def load_app(self, app_name):
|
128
|
+
selected_app_func, _ = self.gui_apps[app_name]
|
129
|
+
self.clear_frame(self.content_frame)
|
130
|
+
|
131
|
+
app_frame = tk.Frame(self.content_frame, bg="black")
|
132
|
+
app_frame.pack(fill=tk.BOTH, expand=True)
|
133
|
+
selected_app_func(app_frame, self.winfo_width(), self.winfo_height())
|
134
|
+
|
135
|
+
def clear_frame(self, frame):
|
136
|
+
for widget in frame.winfo_children():
|
137
|
+
widget.destroy()
|
138
|
+
|
139
|
+
def gui_app():
|
140
|
+
app = MainApp()
|
141
|
+
app.mainloop()
|
142
|
+
|
143
|
+
if __name__ == "__main__":
|
144
|
+
gui_app()
|
@@ -16,12 +16,12 @@ except AttributeError:
|
|
16
16
|
pass
|
17
17
|
|
18
18
|
from .logger import log_function_call
|
19
|
-
from .gui_utils import ScrollableFrame, StdoutRedirector,
|
20
|
-
from .gui_utils import classify_variables, check_classify_gui_settings, train_test_model_wrapper, read_settings_from_csv, update_settings_from_csv
|
19
|
+
from .gui_utils import ScrollableFrame, StdoutRedirector, CustomButton, set_dark_style, set_default_font, generate_fields, process_stdout_stderr, clear_canvas, main_thread_update_function
|
20
|
+
from .gui_utils import classify_variables, check_classify_gui_settings, train_test_model_wrapper, read_settings_from_csv, update_settings_from_csv, style_text_boxes, create_menu_bar
|
21
21
|
|
22
22
|
thread_control = {"run_thread": None, "stop_requested": False}
|
23
23
|
|
24
|
-
|
24
|
+
#@log_function_call
|
25
25
|
def initiate_abort():
|
26
26
|
global thread_control
|
27
27
|
if thread_control.get("stop_requested") is not None:
|
@@ -33,7 +33,7 @@ def initiate_abort():
|
|
33
33
|
thread_control["run_thread"].terminate()
|
34
34
|
thread_control["run_thread"] = None
|
35
35
|
|
36
|
-
|
36
|
+
#@log_function_call
|
37
37
|
def run_classify_gui(q, fig_queue, stop_requested):
|
38
38
|
global vars_dict
|
39
39
|
process_stdout_stderr(q)
|
@@ -49,7 +49,7 @@ def run_classify_gui(q, fig_queue, stop_requested):
|
|
49
49
|
finally:
|
50
50
|
stop_requested.value = 1
|
51
51
|
|
52
|
-
|
52
|
+
#@log_function_call
|
53
53
|
def start_process(q, fig_queue):
|
54
54
|
global thread_control
|
55
55
|
if thread_control.get("run_thread") is not None:
|
@@ -69,129 +69,120 @@ def import_settings(scrollable_frame):
|
|
69
69
|
new_settings = update_settings_from_csv(variables, csv_settings)
|
70
70
|
vars_dict = generate_fields(new_settings, scrollable_frame)
|
71
71
|
|
72
|
-
|
73
|
-
def initiate_classify_root(width, height):
|
74
|
-
global
|
75
|
-
|
76
|
-
theme = 'breeze'
|
72
|
+
#@log_function_call
|
73
|
+
def initiate_classify_root(parent_frame, width, height):
|
74
|
+
global vars_dict, q, canvas, fig_queue, canvas_widget, thread_control
|
77
75
|
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
set_dark_style(style)
|
87
|
-
|
88
|
-
set_default_font(root, font_name="Arial", size=10)
|
89
|
-
#root.state('zoomed') # For Windows to maximize the window
|
90
|
-
root.attributes('-fullscreen', True)
|
91
|
-
root.geometry(f"{width}x{height}")
|
92
|
-
root.title("SpaCer: generate masks")
|
76
|
+
style = ttk.Style(parent_frame)
|
77
|
+
set_dark_style(style)
|
78
|
+
style_text_boxes(style)
|
79
|
+
set_default_font(parent_frame, font_name="Open Sans", size=8)
|
80
|
+
|
81
|
+
parent_frame.configure(bg='#333333')
|
82
|
+
parent_frame.grid_rowconfigure(0, weight=1)
|
83
|
+
parent_frame.grid_columnconfigure(0, weight=1)
|
93
84
|
fig_queue = Queue()
|
94
|
-
|
85
|
+
|
95
86
|
def _process_fig_queue():
|
96
87
|
global canvas
|
97
88
|
try:
|
98
89
|
while not fig_queue.empty():
|
99
90
|
clear_canvas(canvas)
|
100
91
|
fig = fig_queue.get_nowait()
|
101
|
-
#set_fig_text_properties(fig, font_size=8)
|
102
92
|
for ax in fig.get_axes():
|
103
93
|
ax.set_xticks([]) # Remove x-axis ticks
|
104
94
|
ax.set_yticks([]) # Remove y-axis ticks
|
105
95
|
ax.xaxis.set_visible(False) # Hide the x-axis
|
106
96
|
ax.yaxis.set_visible(False) # Hide the y-axis
|
107
|
-
#ax.title.set_fontsize(14)
|
108
|
-
#disable_interactivity(fig)
|
109
97
|
fig.tight_layout()
|
110
98
|
fig.set_facecolor('#333333')
|
111
99
|
canvas.figure = fig
|
112
100
|
fig_width, fig_height = canvas_widget.winfo_width(), canvas_widget.winfo_height()
|
113
101
|
fig.set_size_inches(fig_width / fig.dpi, fig_height / fig.dpi, forward=True)
|
114
|
-
canvas.draw_idle()
|
102
|
+
canvas.draw_idle()
|
115
103
|
except Exception as e:
|
116
104
|
traceback.print_exc()
|
117
|
-
#pass
|
118
105
|
finally:
|
119
106
|
canvas_widget.after(100, _process_fig_queue)
|
120
|
-
|
121
|
-
# Process queue for console output
|
107
|
+
|
122
108
|
def _process_console_queue():
|
123
109
|
while not q.empty():
|
124
110
|
message = q.get_nowait()
|
125
111
|
console_output.insert(tk.END, message)
|
126
112
|
console_output.see(tk.END)
|
127
113
|
console_output.after(100, _process_console_queue)
|
128
|
-
|
129
|
-
# Vertical container for settings and console
|
130
|
-
vertical_container = tk.PanedWindow(root, orient=tk.HORIZONTAL) #VERTICAL
|
131
|
-
vertical_container.pack(fill=tk.BOTH, expand=True)
|
132
114
|
|
133
|
-
|
134
|
-
|
135
|
-
|
115
|
+
vertical_container = tk.PanedWindow(parent_frame, orient=tk.HORIZONTAL)
|
116
|
+
vertical_container.grid(row=0, column=0, sticky=tk.NSEW)
|
117
|
+
parent_frame.grid_rowconfigure(0, weight=1)
|
118
|
+
parent_frame.grid_columnconfigure(0, weight=1)
|
119
|
+
|
120
|
+
# Settings Section
|
121
|
+
settings_frame = tk.Frame(vertical_container, bg='#333333')
|
122
|
+
vertical_container.add(settings_frame, stretch="always")
|
123
|
+
settings_label = ttk.Label(settings_frame, text="Settings", background="#333333", foreground="white")
|
124
|
+
settings_label.grid(row=0, column=0, pady=10, padx=10)
|
125
|
+
scrollable_frame = ScrollableFrame(settings_frame, width=500)
|
126
|
+
scrollable_frame.grid(row=1, column=0, sticky="nsew")
|
127
|
+
settings_frame.grid_rowconfigure(1, weight=1)
|
128
|
+
settings_frame.grid_columnconfigure(0, weight=1)
|
136
129
|
|
137
130
|
# Setup for user input fields (variables)
|
138
131
|
variables = classify_variables()
|
139
132
|
vars_dict = generate_fields(variables, scrollable_frame)
|
140
|
-
|
141
|
-
# Horizontal container for Matplotlib figure and the vertical pane (for settings and console)
|
142
|
-
horizontal_container = tk.PanedWindow(vertical_container, orient=tk.VERTICAL) #HORIZONTAL
|
143
|
-
vertical_container.add(horizontal_container, stretch="always")
|
144
133
|
|
145
|
-
#
|
134
|
+
# Button section
|
135
|
+
import_btn = CustomButton(scrollable_frame.scrollable_frame, text="Import Settings", command=lambda: import_settings(scrollable_frame))
|
136
|
+
import_btn.grid(row=47, column=0, pady=20, padx=20)
|
137
|
+
run_button = CustomButton(scrollable_frame.scrollable_frame, text="Run", command=lambda: start_process(q, fig_queue))
|
138
|
+
run_button.grid(row=45, column=0, pady=20, padx=20)
|
139
|
+
abort_button = CustomButton(scrollable_frame.scrollable_frame, text="Abort", command=initiate_abort)
|
140
|
+
abort_button.grid(row=45, column=1, pady=20, padx=20)
|
141
|
+
progress_label = ttk.Label(scrollable_frame.scrollable_frame, text="Processing: 0%", background="black", foreground="white") # Create progress field
|
142
|
+
progress_label.grid(row=50, column=0, columnspan=2, sticky="ew", pady=(5, 0), padx=10)
|
143
|
+
|
144
|
+
# Plot Canvas Section
|
145
|
+
plot_frame = tk.PanedWindow(vertical_container, orient=tk.VERTICAL)
|
146
|
+
vertical_container.add(plot_frame, stretch="always")
|
146
147
|
figure = Figure(figsize=(30, 4), dpi=100, facecolor='#333333')
|
147
148
|
plot = figure.add_subplot(111)
|
148
|
-
plot.plot([], [])
|
149
|
+
plot.plot([], [])
|
149
150
|
plot.axis('off')
|
150
|
-
|
151
|
-
# Embedding the Matplotlib figure in the Tkinter window
|
152
|
-
canvas = FigureCanvasTkAgg(figure, master=horizontal_container)
|
151
|
+
canvas = FigureCanvasTkAgg(figure, master=plot_frame)
|
153
152
|
canvas.get_tk_widget().configure(cursor='arrow', background='#333333', highlightthickness=0)
|
154
|
-
#canvas.get_tk_widget().configure(cursor='arrow')
|
155
153
|
canvas_widget = canvas.get_tk_widget()
|
156
|
-
|
154
|
+
plot_frame.add(canvas_widget, stretch="always")
|
157
155
|
canvas.draw()
|
158
156
|
canvas.figure = figure
|
159
157
|
|
160
|
-
# Console
|
161
|
-
|
162
|
-
vertical_container.add(
|
158
|
+
# Console Section
|
159
|
+
console_frame = tk.Frame(vertical_container, bg='#333333')
|
160
|
+
vertical_container.add(console_frame, stretch="always")
|
161
|
+
console_label = ttk.Label(console_frame, text="Console", background="#333333", foreground="white")
|
162
|
+
console_label.grid(row=0, column=0, pady=10, padx=10)
|
163
|
+
console_output = scrolledtext.ScrolledText(console_frame, height=10, bg='#333333', fg='white', insertbackground='white')
|
164
|
+
console_output.grid(row=1, column=0, sticky="nsew")
|
165
|
+
console_frame.grid_rowconfigure(1, weight=1)
|
166
|
+
console_frame.grid_columnconfigure(0, weight=1)
|
163
167
|
|
164
|
-
# Queue and redirection setup for updating console output safely
|
165
168
|
q = Queue()
|
166
169
|
sys.stdout = StdoutRedirector(console_output)
|
167
170
|
sys.stderr = StdoutRedirector(console_output)
|
168
|
-
|
169
|
-
# This is your GUI setup where you create the Run button
|
170
|
-
run_button = ttk.Button(scrollable_frame.scrollable_frame, text="Run",command=lambda: start_process(q, fig_queue))
|
171
|
-
run_button.grid(row=40, column=0, pady=10)
|
172
|
-
|
173
|
-
abort_button = ttk.Button(scrollable_frame.scrollable_frame, text="Abort", command=initiate_abort)
|
174
|
-
abort_button.grid(row=40, column=1, pady=10)
|
175
|
-
|
176
|
-
progress_label = ttk.Label(scrollable_frame.scrollable_frame, text="Processing: 0%", background="#333333", foreground="white")
|
177
|
-
progress_label.grid(row=41, column=0, columnspan=2, sticky="ew", pady=(5, 0))
|
178
|
-
|
179
|
-
# Create the Import Settings button
|
180
|
-
import_btn = tk.Button(root, text="Import Settings", command=lambda: import_settings(scrollable_frame))
|
181
|
-
import_btn.pack(pady=20)
|
182
|
-
|
171
|
+
|
183
172
|
_process_console_queue()
|
184
173
|
_process_fig_queue()
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
return root, vars_dict
|
174
|
+
|
175
|
+
parent_frame.after(100, lambda: main_thread_update_function(parent_frame, q, fig_queue, canvas_widget, progress_label))
|
176
|
+
|
177
|
+
return parent_frame, vars_dict
|
190
178
|
|
191
179
|
def gui_classify():
|
192
|
-
|
193
|
-
root
|
180
|
+
root = tk.Tk()
|
181
|
+
root.geometry("1000x800")
|
182
|
+
root.title("SpaCer: generate masks")
|
183
|
+
initiate_classify_root(root, 1000, 800)
|
184
|
+
create_menu_bar(root)
|
194
185
|
root.mainloop()
|
195
186
|
|
196
187
|
if __name__ == "__main__":
|
197
|
-
gui_classify()
|
188
|
+
gui_classify()
|