spacr 0.0.21__tar.gz → 0.0.35__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 (59) hide show
  1. {spacr-0.0.21/spacr.egg-info → spacr-0.0.35}/PKG-INFO +1 -1
  2. {spacr-0.0.21 → spacr-0.0.35}/setup.py +8 -7
  3. {spacr-0.0.21 → spacr-0.0.35}/spacr/annotate_app.py +5 -3
  4. spacr-0.0.35/spacr/chris.py +50 -0
  5. spacr-0.0.35/spacr/gui.py +144 -0
  6. {spacr-0.0.21 → spacr-0.0.35}/spacr/gui_classify_app.py +65 -74
  7. spacr-0.0.35/spacr/gui_mask_app.py +222 -0
  8. spacr-0.0.35/spacr/gui_measure_app.py +223 -0
  9. {spacr-0.0.21 → spacr-0.0.35}/spacr/gui_utils.py +265 -35
  10. {spacr-0.0.21 → spacr-0.0.35}/spacr/io.py +1 -0
  11. {spacr-0.0.21 → spacr-0.0.35}/spacr/mask_app.py +6 -3
  12. {spacr-0.0.21 → spacr-0.0.35}/spacr/utils.py +0 -6
  13. {spacr-0.0.21 → spacr-0.0.35/spacr.egg-info}/PKG-INFO +1 -1
  14. {spacr-0.0.21 → spacr-0.0.35}/spacr.egg-info/SOURCES.txt +2 -0
  15. spacr-0.0.35/spacr.egg-info/entry_points.txt +8 -0
  16. spacr-0.0.21/spacr/gui_mask_app.py +0 -219
  17. spacr-0.0.21/spacr/gui_measure_app.py +0 -200
  18. spacr-0.0.21/spacr.egg-info/entry_points.txt +0 -7
  19. {spacr-0.0.21 → spacr-0.0.35}/LICENSE +0 -0
  20. {spacr-0.0.21 → spacr-0.0.35}/README.md +0 -0
  21. {spacr-0.0.21 → spacr-0.0.35}/setup.cfg +0 -0
  22. {spacr-0.0.21 → spacr-0.0.35}/spacr/__init__.py +0 -0
  23. {spacr-0.0.21 → spacr-0.0.35}/spacr/__main__.py +0 -0
  24. {spacr-0.0.21 → spacr-0.0.35}/spacr/alpha.py +0 -0
  25. {spacr-0.0.21 → spacr-0.0.35}/spacr/cli.py +0 -0
  26. {spacr-0.0.21 → spacr-0.0.35}/spacr/core.py +0 -0
  27. {spacr-0.0.21 → spacr-0.0.35}/spacr/foldseek.py +0 -0
  28. {spacr-0.0.21 → spacr-0.0.35}/spacr/get_alfafold_structures.py +0 -0
  29. {spacr-0.0.21 → spacr-0.0.35}/spacr/graph_learning.py +0 -0
  30. {spacr-0.0.21 → spacr-0.0.35}/spacr/graph_learning_lap.py +0 -0
  31. {spacr-0.0.21 → spacr-0.0.35}/spacr/gui_sim_app.py +0 -0
  32. {spacr-0.0.21 → spacr-0.0.35}/spacr/logger.py +0 -0
  33. {spacr-0.0.21 → spacr-0.0.35}/spacr/measure.py +0 -0
  34. {spacr-0.0.21 → spacr-0.0.35}/spacr/old_code.py +0 -0
  35. {spacr-0.0.21 → spacr-0.0.35}/spacr/plot.py +0 -0
  36. {spacr-0.0.21 → spacr-0.0.35}/spacr/sim.py +0 -0
  37. {spacr-0.0.21 → spacr-0.0.35}/spacr/timelapse.py +0 -0
  38. {spacr-0.0.21 → spacr-0.0.35}/spacr/train.py +0 -0
  39. {spacr-0.0.21 → spacr-0.0.35}/spacr/umap.py +0 -0
  40. {spacr-0.0.21 → spacr-0.0.35}/spacr/version.py +0 -0
  41. {spacr-0.0.21 → spacr-0.0.35}/spacr.egg-info/dependency_links.txt +0 -0
  42. {spacr-0.0.21 → spacr-0.0.35}/spacr.egg-info/requires.txt +0 -0
  43. {spacr-0.0.21 → spacr-0.0.35}/spacr.egg-info/top_level.txt +0 -0
  44. {spacr-0.0.21 → spacr-0.0.35}/tests/test_annotate_app.py +0 -0
  45. {spacr-0.0.21 → spacr-0.0.35}/tests/test_core.py +0 -0
  46. {spacr-0.0.21 → spacr-0.0.35}/tests/test_gui_classify_app.py +0 -0
  47. {spacr-0.0.21 → spacr-0.0.35}/tests/test_gui_mask_app.py +0 -0
  48. {spacr-0.0.21 → spacr-0.0.35}/tests/test_gui_measure_app.py +0 -0
  49. {spacr-0.0.21 → spacr-0.0.35}/tests/test_gui_sim_app.py +0 -0
  50. {spacr-0.0.21 → spacr-0.0.35}/tests/test_gui_utils.py +0 -0
  51. {spacr-0.0.21 → spacr-0.0.35}/tests/test_io.py +0 -0
  52. {spacr-0.0.21 → spacr-0.0.35}/tests/test_mask_app.py +0 -0
  53. {spacr-0.0.21 → spacr-0.0.35}/tests/test_measure.py +0 -0
  54. {spacr-0.0.21 → spacr-0.0.35}/tests/test_plot.py +0 -0
  55. {spacr-0.0.21 → spacr-0.0.35}/tests/test_sim.py +0 -0
  56. {spacr-0.0.21 → spacr-0.0.35}/tests/test_timelapse.py +0 -0
  57. {spacr-0.0.21 → spacr-0.0.35}/tests/test_train.py +0 -0
  58. {spacr-0.0.21 → spacr-0.0.35}/tests/test_umap.py +0 -0
  59. {spacr-0.0.21 → spacr-0.0.35}/tests/test_utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: spacr
3
- Version: 0.0.21
3
+ Version: 0.0.35
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
@@ -35,7 +35,7 @@ dependencies = [
35
35
 
36
36
  setup(
37
37
  name="spacr",
38
- version="0.0.21",
38
+ version="0.0.35",
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
- 'gui_mask=spacr.gui_mask_app:gui_mask',
49
- 'gui_measure=spacr.gui_measure_app:gui_measure',
50
- 'gui_make_masks=spacr.mask_app:gui_make_masks',
51
- 'gui_annotation=spacr.annotate_app:gui_annotation',
52
- 'gui_classify=spacr.gui_classify_app:gui_classify',
53
- 'gui_sim=spacr.gui_sim_app:gui_sim',
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
  """
@@ -425,7 +425,8 @@ def initiate_annotation_app_root(width, height):
425
425
  root = ThemedTk(theme=theme)
426
426
  style = ttk.Style(root)
427
427
  set_dark_style(style)
428
- set_default_font(root, font_name="Arial", size=10)
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,8 +16,8 @@ except AttributeError:
16
16
  pass
17
17
 
18
18
  from .logger import log_function_call
19
- from .gui_utils import ScrollableFrame, StdoutRedirector, create_dark_mode, set_dark_style, set_default_font, generate_fields, process_stdout_stderr, safe_literal_eval, 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
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
 
@@ -70,128 +70,119 @@ def import_settings(scrollable_frame):
70
70
  vars_dict = generate_fields(new_settings, scrollable_frame)
71
71
 
72
72
  @log_function_call
73
- def initiate_classify_root(width, height):
74
- global root, vars_dict, q, canvas, fig_queue, canvas_widget, thread_control
75
-
76
- theme = 'breeze'
73
+ def initiate_classify_root(parent_frame, width, height):
74
+ global vars_dict, q, canvas, fig_queue, canvas_widget, thread_control
77
75
 
78
- if theme in ['clam']:
79
- root = tk.Tk()
80
- style = ttk.Style(root)
81
- style.theme_use(theme) #plastik, clearlooks, elegance, default was clam #alt, breeze, arc
82
- set_dark_style(style)
83
- elif theme in ['breeze']:
84
- root = ThemedTk(theme="breeze")
85
- style = ttk.Style(root)
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
- # Scrollable Frame for user settings
134
- scrollable_frame = ScrollableFrame(vertical_container, bg='#333333')
135
- vertical_container.add(scrollable_frame, stretch="always")
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
- # Matplotlib figure setup
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([], []) # This creates an empty 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
- horizontal_container.add(canvas_widget, stretch="always")
154
+ plot_frame.add(canvas_widget, stretch="always")
157
155
  canvas.draw()
158
156
  canvas.figure = figure
159
157
 
160
- # Console output setup below the settings
161
- console_output = scrolledtext.ScrolledText(vertical_container, height=10)
162
- vertical_container.add(console_output, stretch="always")
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
- create_dark_mode(root, style, console_output)
186
-
187
- root.after(100, lambda: main_thread_update_function(root, q, fig_queue, canvas_widget, progress_label))
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
- global vars_dict, root
193
- root, vars_dict = initiate_classify_root(1000, 1500)
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()