spacr 0.2.41__tar.gz → 0.2.46__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 (83) hide show
  1. {spacr-0.2.41/spacr.egg-info → spacr-0.2.46}/PKG-INFO +1 -1
  2. {spacr-0.2.41 → spacr-0.2.46}/setup.py +1 -1
  3. {spacr-0.2.41 → spacr-0.2.46}/spacr/core.py +46 -66
  4. {spacr-0.2.41 → spacr-0.2.46}/spacr/gui.py +20 -38
  5. {spacr-0.2.41 → spacr-0.2.46}/spacr/gui_core.py +327 -625
  6. {spacr-0.2.41 → spacr-0.2.46}/spacr/gui_elements.py +175 -24
  7. spacr-0.2.46/spacr/gui_utils.py +624 -0
  8. {spacr-0.2.41 → spacr-0.2.46}/spacr/io.py +43 -46
  9. {spacr-0.2.41 → spacr-0.2.46}/spacr/plot.py +106 -0
  10. spacr-0.2.46/spacr/resources/icons/logo.pdf +2786 -6
  11. spacr-0.2.46/spacr/resources/icons/logo_spacr.png +0 -0
  12. {spacr-0.2.41 → spacr-0.2.46}/spacr/settings.py +0 -2
  13. {spacr-0.2.41 → spacr-0.2.46}/spacr/utils.py +4 -24
  14. {spacr-0.2.41 → spacr-0.2.46/spacr.egg-info}/PKG-INFO +1 -1
  15. {spacr-0.2.41 → spacr-0.2.46}/spacr.egg-info/SOURCES.txt +2 -0
  16. spacr-0.2.41/spacr/gui_utils.py +0 -362
  17. {spacr-0.2.41 → spacr-0.2.46}/LICENSE +0 -0
  18. {spacr-0.2.41 → spacr-0.2.46}/MANIFEST.in +0 -0
  19. {spacr-0.2.41 → spacr-0.2.46}/README.rst +0 -0
  20. {spacr-0.2.41 → spacr-0.2.46}/setup.cfg +0 -0
  21. {spacr-0.2.41 → spacr-0.2.46}/spacr/__init__.py +0 -0
  22. {spacr-0.2.41 → spacr-0.2.46}/spacr/__main__.py +0 -0
  23. {spacr-0.2.41 → spacr-0.2.46}/spacr/app_annotate.py +0 -0
  24. {spacr-0.2.41 → spacr-0.2.46}/spacr/app_classify.py +0 -0
  25. {spacr-0.2.41 → spacr-0.2.46}/spacr/app_make_masks.py +0 -0
  26. {spacr-0.2.41 → spacr-0.2.46}/spacr/app_mask.py +0 -0
  27. {spacr-0.2.41 → spacr-0.2.46}/spacr/app_measure.py +0 -0
  28. {spacr-0.2.41 → spacr-0.2.46}/spacr/app_sequencing.py +0 -0
  29. {spacr-0.2.41 → spacr-0.2.46}/spacr/app_umap.py +0 -0
  30. {spacr-0.2.41 → spacr-0.2.46}/spacr/chris.py +0 -0
  31. {spacr-0.2.41 → spacr-0.2.46}/spacr/deep_spacr.py +0 -0
  32. {spacr-0.2.41 → spacr-0.2.46}/spacr/graph_learning.py +0 -0
  33. {spacr-0.2.41 → spacr-0.2.46}/spacr/logger.py +0 -0
  34. {spacr-0.2.41 → spacr-0.2.46}/spacr/measure.py +0 -0
  35. {spacr-0.2.41 → spacr-0.2.46}/spacr/resources/icons/abort.png +0 -0
  36. {spacr-0.2.41 → spacr-0.2.46}/spacr/resources/icons/annotate.png +0 -0
  37. {spacr-0.2.41 → spacr-0.2.46}/spacr/resources/icons/cellpose_all.png +0 -0
  38. {spacr-0.2.41 → spacr-0.2.46}/spacr/resources/icons/cellpose_masks.png +0 -0
  39. {spacr-0.2.41 → spacr-0.2.46}/spacr/resources/icons/classify.png +0 -0
  40. {spacr-0.2.41 → spacr-0.2.46}/spacr/resources/icons/default.png +0 -0
  41. {spacr-0.2.41 → spacr-0.2.46}/spacr/resources/icons/download.png +0 -0
  42. /spacr-0.2.41/spacr/resources/icons/logo_spacr.png → /spacr-0.2.46/spacr/resources/icons/logo_spacr_1.png +0 -0
  43. {spacr-0.2.41 → spacr-0.2.46}/spacr/resources/icons/make_masks.png +0 -0
  44. {spacr-0.2.41 → spacr-0.2.46}/spacr/resources/icons/map_barcodes.png +0 -0
  45. {spacr-0.2.41 → spacr-0.2.46}/spacr/resources/icons/mask.png +0 -0
  46. {spacr-0.2.41 → spacr-0.2.46}/spacr/resources/icons/measure.png +0 -0
  47. {spacr-0.2.41 → spacr-0.2.46}/spacr/resources/icons/ml_analyze.png +0 -0
  48. {spacr-0.2.41 → spacr-0.2.46}/spacr/resources/icons/recruitment.png +0 -0
  49. {spacr-0.2.41 → spacr-0.2.46}/spacr/resources/icons/regression.png +0 -0
  50. {spacr-0.2.41 → spacr-0.2.46}/spacr/resources/icons/run.png +0 -0
  51. {spacr-0.2.41 → spacr-0.2.46}/spacr/resources/icons/sequencing.png +0 -0
  52. {spacr-0.2.41 → spacr-0.2.46}/spacr/resources/icons/settings.png +0 -0
  53. {spacr-0.2.41 → spacr-0.2.46}/spacr/resources/icons/spacr_logo_rotation.gif +0 -0
  54. {spacr-0.2.41 → spacr-0.2.46}/spacr/resources/icons/train_cellpose.png +0 -0
  55. {spacr-0.2.41 → spacr-0.2.46}/spacr/resources/icons/umap.png +0 -0
  56. {spacr-0.2.41 → spacr-0.2.46}/spacr/resources/models/cp/toxo_plaque_cyto_e25000_X1120_Y1120.CP_model +0 -0
  57. {spacr-0.2.41 → spacr-0.2.46}/spacr/resources/models/cp/toxo_plaque_cyto_e25000_X1120_Y1120.CP_model_settings.csv +0 -0
  58. {spacr-0.2.41 → spacr-0.2.46}/spacr/resources/models/cp/toxo_pv_lumen.CP_model +0 -0
  59. {spacr-0.2.41 → spacr-0.2.46}/spacr/sequencing.py +0 -0
  60. {spacr-0.2.41 → spacr-0.2.46}/spacr/sim.py +0 -0
  61. {spacr-0.2.41 → spacr-0.2.46}/spacr/sim_app.py +0 -0
  62. {spacr-0.2.41 → spacr-0.2.46}/spacr/timelapse.py +0 -0
  63. {spacr-0.2.41 → spacr-0.2.46}/spacr/version.py +0 -0
  64. {spacr-0.2.41 → spacr-0.2.46}/spacr.egg-info/dependency_links.txt +0 -0
  65. {spacr-0.2.41 → spacr-0.2.46}/spacr.egg-info/entry_points.txt +0 -0
  66. {spacr-0.2.41 → spacr-0.2.46}/spacr.egg-info/requires.txt +0 -0
  67. {spacr-0.2.41 → spacr-0.2.46}/spacr.egg-info/top_level.txt +0 -0
  68. {spacr-0.2.41 → spacr-0.2.46}/tests/test_annotate_app.py +0 -0
  69. {spacr-0.2.41 → spacr-0.2.46}/tests/test_core.py +0 -0
  70. {spacr-0.2.41 → spacr-0.2.46}/tests/test_gui_classify_app.py +0 -0
  71. {spacr-0.2.41 → spacr-0.2.46}/tests/test_gui_mask_app.py +0 -0
  72. {spacr-0.2.41 → spacr-0.2.46}/tests/test_gui_measure_app.py +0 -0
  73. {spacr-0.2.41 → spacr-0.2.46}/tests/test_gui_sim_app.py +0 -0
  74. {spacr-0.2.41 → spacr-0.2.46}/tests/test_gui_utils.py +0 -0
  75. {spacr-0.2.41 → spacr-0.2.46}/tests/test_io.py +0 -0
  76. {spacr-0.2.41 → spacr-0.2.46}/tests/test_mask_app.py +0 -0
  77. {spacr-0.2.41 → spacr-0.2.46}/tests/test_measure.py +0 -0
  78. {spacr-0.2.41 → spacr-0.2.46}/tests/test_plot.py +0 -0
  79. {spacr-0.2.41 → spacr-0.2.46}/tests/test_sim.py +0 -0
  80. {spacr-0.2.41 → spacr-0.2.46}/tests/test_timelapse.py +0 -0
  81. {spacr-0.2.41 → spacr-0.2.46}/tests/test_train.py +0 -0
  82. {spacr-0.2.41 → spacr-0.2.46}/tests/test_umap.py +0 -0
  83. {spacr-0.2.41 → spacr-0.2.46}/tests/test_utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: spacr
3
- Version: 0.2.41
3
+ Version: 0.2.46
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
@@ -53,7 +53,7 @@ dependencies = [
53
53
 
54
54
  setup(
55
55
  name="spacr",
56
- version="0.2.41",
56
+ version="0.2.46",
57
57
  author="Einar Birnir Olafsson",
58
58
  author_email="olafsson@med.umich.com",
59
59
  description="Spatial phenotype analysis of crisp screens (SpaCr)",
@@ -39,6 +39,10 @@ matplotlib.use('Agg')
39
39
 
40
40
  from .logger import log_function_call
41
41
 
42
+ import warnings
43
+ warnings.filterwarnings("ignore", message="3D stack used, but stitch_threshold=0 and do_3D=False, so masks are made per plane only")
44
+
45
+
42
46
  def analyze_plaques(folder):
43
47
  summary_data = []
44
48
  details_data = []
@@ -80,7 +84,6 @@ def analyze_plaques(folder):
80
84
 
81
85
  print(f"Analysis completed and saved to database '{db_name}'.")
82
86
 
83
-
84
87
  def train_cellpose(settings):
85
88
 
86
89
  from .io import _load_normalized_images_and_labels, _load_images_and_labels
@@ -1032,10 +1035,6 @@ def apply_model_to_tar(tar_path, model_path, file_type='cell_png', image_size=22
1032
1035
  files_to_process = len(data_loader)
1033
1036
  print_progress(files_processed, files_to_process, n_jobs=n_jobs, time_ls=time_ls, batch_size=batch_size, operation_type="Tar dataset")
1034
1037
 
1035
-
1036
-
1037
-
1038
-
1039
1038
  data = {'path':filenames_list, 'pred':prediction_pos_probs}
1040
1039
  df = pd.DataFrame(data, index=None)
1041
1040
  df = process_vision_results(df, threshold)
@@ -1695,9 +1694,9 @@ def analyze_recruitment(src, metadata_settings={}, advanced_settings={}):
1695
1694
  def preprocess_generate_masks(src, settings={}):
1696
1695
 
1697
1696
  from .io import preprocess_img_data, _load_and_concatenate_arrays
1698
- from .plot import plot_merged, plot_arrays
1699
- from .utils import _pivot_counts_table, check_mask_folder, adjust_cell_masks
1700
- from .settings import set_default_settings_preprocess_generate_masks, set_default_plot_merge_settings
1697
+ from .plot import plot_image_mask_overlay, plot_arrays
1698
+ from .utils import _pivot_counts_table, check_mask_folder, adjust_cell_masks, print_progress
1699
+ from .settings import set_default_settings_preprocess_generate_masks
1701
1700
 
1702
1701
  settings = set_default_settings_preprocess_generate_masks(src, settings)
1703
1702
  settings_df = pd.DataFrame(list(settings.items()), columns=['Key', 'Value'])
@@ -1745,6 +1744,10 @@ def preprocess_generate_masks(src, settings={}):
1745
1744
  if check_mask_folder(src, 'pathogen_mask_stack'):
1746
1745
  generate_cellpose_masks(mask_src, settings, 'pathogen')
1747
1746
 
1747
+ #if settings['organelle'] != None:
1748
+ # if check_mask_folder(src, 'organelle_mask_stack'):
1749
+ # generate_cellpose_masks(mask_src, settings, 'organelle')
1750
+
1748
1751
  if settings['adjust_cells']:
1749
1752
  if settings['pathogen_channel'] != None and settings['cell_channel'] != None and settings['nucleus_channel'] != None:
1750
1753
 
@@ -1752,12 +1755,8 @@ def preprocess_generate_masks(src, settings={}):
1752
1755
  cell_folder = os.path.join(mask_src, 'cell_mask_stack')
1753
1756
  nuclei_folder = os.path.join(mask_src, 'nucleus_mask_stack')
1754
1757
  parasite_folder = os.path.join(mask_src, 'pathogen_mask_stack')
1755
- #image_folder = os.path.join(src, 'stack')
1758
+ #organelle_folder = os.path.join(mask_src, 'organelle_mask_stack')
1756
1759
 
1757
- #process_masks(cell_folder, image_folder, settings['cell_channel'], settings['batch_size'], n_clusters=2, plot=settings['plot'])
1758
- #process_masks(nuclei_folder, image_folder, settings['nucleus_channel'], settings['batch_size'], n_clusters=2, plot=settings['plot'])
1759
- #process_masks(parasite_folder, image_folder, settings['pathogen_channel'], settings['batch_size'], n_clusters=2, plot=settings['plot'])
1760
-
1761
1760
  adjust_cell_masks(parasite_folder, cell_folder, nuclei_folder, overlap_threshold=5, perimeter_threshold=30)
1762
1761
  stop = time.time()
1763
1762
  adjust_time = (stop-start)/60
@@ -1771,38 +1770,28 @@ def preprocess_generate_masks(src, settings={}):
1771
1770
 
1772
1771
  if settings['plot']:
1773
1772
  if not settings['timelapse']:
1774
- plot_dims = len(settings['channels'])
1775
- overlay_channels = [2,1,0]
1776
- cell_mask_dim = nucleus_mask_dim = pathogen_mask_dim = None
1777
- plot_counter = plot_dims
1778
-
1779
- if settings['cell_channel'] is not None:
1780
- cell_mask_dim = plot_counter
1781
- plot_counter += 1
1782
-
1783
- if settings['nucleus_channel'] is not None:
1784
- nucleus_mask_dim = plot_counter
1785
- plot_counter += 1
1786
-
1787
- if settings['pathogen_channel'] is not None:
1788
- pathogen_mask_dim = plot_counter
1789
-
1790
- overlay_channels = [settings['nucleus_channel'], settings['pathogen_channel'], settings['cell_channel']]
1791
- overlay_channels = [element for element in overlay_channels if element is not None]
1792
-
1793
- plot_settings = set_default_plot_merge_settings()
1794
- plot_settings['channel_dims'] = settings['channels']
1795
- plot_settings['cell_mask_dim'] = cell_mask_dim
1796
- plot_settings['nucleus_mask_dim'] = nucleus_mask_dim
1797
- plot_settings['pathogen_mask_dim'] = pathogen_mask_dim
1798
- plot_settings['overlay_chans'] = overlay_channels
1799
- plot_settings['nr'] = settings['examples_to_plot']
1800
1773
 
1801
1774
  if settings['test_mode'] == True:
1802
- plot_settings['nr'] = len(os.path.join(src,'merged'))
1775
+ settings['examples_to_plot'] = len(os.path.join(src,'merged'))
1803
1776
 
1804
1777
  try:
1805
- fig = plot_merged(src=os.path.join(src,'merged'), settings=plot_settings)
1778
+ merged_src = os.path.join(src,'merged')
1779
+ files = os.listdir(merged_src)
1780
+ random.shuffle(files)
1781
+ time_ls = []
1782
+
1783
+ for i, file in enumerate(files):
1784
+ start = time.time()
1785
+ if i+1 <= settings['examples_to_plot']:
1786
+ file_path = os.path.join(merged_src, file)
1787
+ plot_image_mask_overlay(file_path, settings['channels'], settings['cell_channel'], settings['nucleus_channel'], settings['pathogen_channel'], figuresize=10, normalize=True, thickness=3, save_pdf=True)
1788
+ stop = time.time()
1789
+ duration = stop-start
1790
+ time_ls.append(duration)
1791
+ files_processed = i+1
1792
+ files_to_process = settings['examples_to_plot']
1793
+ print_progress(files_processed, files_to_process, n_jobs=1, time_ls=time_ls, batch_size=None, operation_type="Plot mask outlines")
1794
+ print("Successfully completed run")
1806
1795
  except Exception as e:
1807
1796
  print(f'Failed to plot image mask overly. Error: {e}')
1808
1797
  else:
@@ -2010,7 +1999,7 @@ def generate_cellpose_masks(src, settings, object_type):
2010
1999
 
2011
2000
  if object_type == 'pathogen' and not settings['pathogen_model'] is None:
2012
2001
  model_name = settings['pathogen_model']
2013
-
2002
+
2014
2003
  model = _choose_model(model_name, device, object_type=object_type, restore_type=None, object_settings=object_settings)
2015
2004
 
2016
2005
  chans = [2, 1] if model_name == 'cyto2' else [0,0] if model_name == 'nucleus' else [2,0] if model_name == 'cyto' else [2, 0] if model_name == 'cyto3' else [2, 0]
@@ -2022,16 +2011,19 @@ def generate_cellpose_masks(src, settings, object_type):
2022
2011
 
2023
2012
  average_sizes = []
2024
2013
  time_ls = []
2014
+
2015
+ files_to_process = len(paths)
2025
2016
  for file_index, path in enumerate(paths):
2026
2017
  name = os.path.basename(path)
2027
2018
  name, ext = os.path.splitext(name)
2028
2019
  output_folder = os.path.join(os.path.dirname(path), object_type+'_mask_stack')
2029
2020
  os.makedirs(output_folder, exist_ok=True)
2030
2021
  overall_average_size = 0
2022
+
2031
2023
  with np.load(path) as data:
2032
2024
  stack = data['data']
2033
2025
  filenames = data['filenames']
2034
-
2026
+
2035
2027
  for i, filename in enumerate(filenames):
2036
2028
  output_path = os.path.join(output_folder, filename)
2037
2029
 
@@ -2057,11 +2049,9 @@ def generate_cellpose_masks(src, settings, object_type):
2057
2049
  batch_size = len(stack)
2058
2050
  print(f'Cut batch at indecies: {timelapse_frame_limits}, New batch_size: {batch_size} ')
2059
2051
 
2060
- files_processed = 0
2061
2052
  for i in range(0, stack.shape[0], batch_size):
2062
- mask_stack = []
2063
2053
  start = time.time()
2064
-
2054
+ mask_stack = []
2065
2055
  if stack.shape[3] == 1:
2066
2056
  batch = stack[i: i+batch_size, :, :, [0,0]].astype(stack.dtype)
2067
2057
  else:
@@ -2072,7 +2062,6 @@ def generate_cellpose_masks(src, settings, object_type):
2072
2062
  if not settings['plot']:
2073
2063
  batch, batch_filenames = _check_masks(batch, batch_filenames, output_folder)
2074
2064
  if batch.size == 0:
2075
- print(f'Processing {file_index}/{len(paths)}: Images/npz {batch.shape[0]}')
2076
2065
  continue
2077
2066
 
2078
2067
  batch = prepare_batch_for_cellpose(batch)
@@ -2083,16 +2072,14 @@ def generate_cellpose_masks(src, settings, object_type):
2083
2072
  save_path = os.path.join(movie_path, f'timelapse_{object_type}_{name}.mp4')
2084
2073
  _npz_to_movie(batch, batch_filenames, save_path, fps=2)
2085
2074
 
2086
- if settings['verbose']:
2087
- print(f'Processing {file_index}/{len(paths)}: Images/npz {batch.shape[0]}')
2088
-
2089
- #cellpose_normalize_dict = {'lowhigh':[0.0,1.0], #pass in normalization values for 0.0 and 1.0 as list [low, high] if None all other keys ignored
2090
- # 'sharpen':object_settings['diameter']/4, #recommended to be 1/4-1/8 diameter of cells in pixels
2091
- # 'normalize':True, #(if False, all following parameters ignored)
2092
- # 'percentile':[2,98], #[perc_low, perc_high]
2093
- # 'tile_norm':224, #normalize by tile set to e.g. 100 for normailize window to be 100 px
2094
- # 'norm3D':True} #compute normalization across entire z-stack rather than plane-by-plane in stitching mode.
2095
-
2075
+ stop = time.time()
2076
+ duration = (stop - start)
2077
+ time_ls.append(duration)
2078
+ files_processed = (file_index+1)*len(batch_filenames)
2079
+ files_processed = len(paths)*batch.shape[0]
2080
+ print('file_index', file_index, 'len(paths)', len(paths), 'batch.shape[0]', batch.shape[0])
2081
+ print_progress(files_processed, files_to_process, n_jobs=1, time_ls=time_ls, batch_size=batch.shape[0], operation_type=f'{object_type}_mask_gen')
2082
+
2096
2083
  output = model.eval(x=batch,
2097
2084
  batch_size=cellpose_batch_size,
2098
2085
  normalize=False,
@@ -2202,16 +2189,8 @@ def generate_cellpose_masks(src, settings, object_type):
2202
2189
 
2203
2190
  average_sizes.append(average_obj_size)
2204
2191
  overall_average_size = np.mean(average_sizes) if len(average_sizes) > 0 else 0
2192
+ print(f'object_size:{object_type}: {overall_average_size:.3f} px2')
2205
2193
 
2206
- stop = time.time()
2207
- duration = (stop - start)
2208
- time_ls.append(duration)
2209
- files_processed += len(batch_filenames)
2210
- #files_processed = (file_index+1)*(batch_size+1)
2211
- files_to_process = (len(paths))*(batch_size)
2212
- print_progress(files_processed, files_to_process, n_jobs=1, time_ls=time_ls, batch_size=batch_size, operation_type=f'{object_type}_mask_gen')
2213
- print(f'object_size:{object_type}): {overall_average_size:.3f} px2')
2214
-
2215
2194
  if not timelapse:
2216
2195
  if settings['plot']:
2217
2196
  plot_masks(batch, mask_stack, flows, figuresize=figuresize, cmap='inferno', nr=batch_size)
@@ -2222,6 +2201,7 @@ def generate_cellpose_masks(src, settings, object_type):
2222
2201
  np.save(output_filename, mask)
2223
2202
  mask_stack = []
2224
2203
  batch_filenames = []
2204
+
2225
2205
  gc.collect()
2226
2206
  torch.cuda.empty_cache()
2227
2207
  return
@@ -15,30 +15,29 @@ class MainApp(tk.Tk):
15
15
  self.title("SpaCr GUI Collection")
16
16
  self.configure(bg='#333333') # Set window background to dark gray
17
17
 
18
- # Initialize style and apply dark style to the main window
19
18
  style = ttk.Style()
20
19
  self.color_settings = set_dark_style(style, parent_frame=self)
21
- self.main_buttons = {} # Initialize main_buttons dictionary here
22
- self.additional_buttons = {} # Initialize additional_buttons dictionary here
20
+ self.main_buttons = {}
21
+ self.additional_buttons = {}
23
22
 
24
23
  self.main_gui_apps = {
25
- "Mask": (lambda frame: initiate_root(frame, 'mask'), "Generate cellpose masks for cells, nuclei and pathogen images."),
26
- "Measure": (lambda frame: initiate_root(frame, 'measure'), "Measure single object intensity and morphological feature. Crop and save single object image"),
27
- "Annotate": (lambda frame: initiate_root(frame, 'annotate'), "Annotation single object images on a grid. Annotations are saved to database."),
28
- "Make Masks": (lambda frame: initiate_root(frame, 'make_masks'), "Adjust pre-existing Cellpose models to your specific dataset for improved performance"),
29
- "Classify": (lambda frame: initiate_root(frame, 'classify'), "Train Torch Convolutional Neural Networks (CNNs) or Transformers to classify single object images."),
24
+ "Mask": (lambda frame: initiate_root(self, 'mask'), "Generate cellpose masks for cells, nuclei and pathogen images."),
25
+ "Measure": (lambda frame: initiate_root(self, 'measure'), "Measure single object intensity and morphological feature. Crop and save single object image"),
26
+ "Annotate": (lambda frame: initiate_root(self, 'annotate'), "Annotation single object images on a grid. Annotations are saved to database."),
27
+ "Make Masks": (lambda frame: initiate_root(self, 'make_masks'), "Adjust pre-existing Cellpose models to your specific dataset for improved performance"),
28
+ "Classify": (lambda frame: initiate_root(self, 'classify'), "Train Torch Convolutional Neural Networks (CNNs) or Transformers to classify single object images."),
30
29
  }
31
30
 
32
31
  self.additional_gui_apps = {
33
- "Sequencing": (lambda frame: initiate_root(frame, 'sequencing'), "Analyze sequencing data."),
34
- "Umap": (lambda frame: initiate_root(frame, 'umap'), "Generate UMAP embeddings with datapoints represented as images."),
35
- "Train Cellpose": (lambda frame: initiate_root(frame, 'train_cellpose'), "Train custom Cellpose models."),
36
- "ML Analyze": (lambda frame: initiate_root(frame, 'ml_analyze'), "Machine learning analysis of data."),
37
- "Cellpose Masks": (lambda frame: initiate_root(frame, 'cellpose_masks'), "Generate Cellpose masks."),
38
- "Cellpose All": (lambda frame: initiate_root(frame, 'cellpose_all'), "Run Cellpose on all images."),
39
- "Map Barcodes": (lambda frame: initiate_root(frame, 'map_barcodes'), "Map barcodes to data."),
40
- "Regression": (lambda frame: initiate_root(frame, 'regression'), "Perform regression analysis."),
41
- "Recruitment": (lambda frame: initiate_root(frame, 'recruitment'), "Analyze recruitment data.")
32
+ "Sequencing": (lambda frame: initiate_root(self, 'sequencing'), "Analyze sequencing data."),
33
+ "Umap": (lambda frame: initiate_root(self, 'umap'), "Generate UMAP embeddings with datapoints represented as images."),
34
+ "Train Cellpose": (lambda frame: initiate_root(self, 'train_cellpose'), "Train custom Cellpose models."),
35
+ "ML Analyze": (lambda frame: initiate_root(self, 'ml_analyze'), "Machine learning analysis of data."),
36
+ "Cellpose Masks": (lambda frame: initiate_root(self, 'cellpose_masks'), "Generate Cellpose masks."),
37
+ "Cellpose All": (lambda frame: initiate_root(self, 'cellpose_all'), "Run Cellpose on all images."),
38
+ "Map Barcodes": (lambda frame: initiate_root(self, 'map_barcodes'), "Map barcodes to data."),
39
+ "Regression": (lambda frame: initiate_root(self, 'regression'), "Perform regression analysis."),
40
+ "Recruitment": (lambda frame: initiate_root(self, 'recruitment'), "Analyze recruitment data.")
42
41
  }
43
42
 
44
43
  self.selected_app = tk.StringVar()
@@ -50,32 +49,25 @@ class MainApp(tk.Tk):
50
49
  self.load_app(default_app, self.additional_gui_apps[default_app][0])
51
50
 
52
51
  def create_widgets(self):
53
- # Create the menu bar
54
52
  create_menu_bar(self)
55
53
 
56
- # Create a canvas to hold the selected app and other elements
57
54
  self.canvas = tk.Canvas(self, highlightthickness=0)
58
55
  self.canvas.grid(row=0, column=0, sticky="nsew")
59
56
  self.grid_rowconfigure(0, weight=1)
60
57
  self.grid_columnconfigure(0, weight=1)
61
58
 
62
- # Create a frame inside the canvas to hold the main content
63
59
  self.content_frame = tk.Frame(self.canvas)
64
60
  self.content_frame.grid(row=0, column=0, sticky="nsew")
65
61
 
66
- # Center the content frame within the canvas
67
62
  self.canvas.create_window((self.winfo_screenwidth() // 2, self.winfo_screenheight() // 2), window=self.content_frame, anchor="center")
68
63
 
69
- # Apply dark style to canvas and content_frame
70
64
  set_dark_style(ttk.Style(), containers=[self.canvas, self.content_frame])
71
65
 
72
- # Create startup screen with buttons for each main GUI app and drop-down for additional apps
73
66
  self.create_startup_screen()
74
67
 
75
68
  def create_startup_screen(self):
76
69
  self.clear_frame(self.content_frame)
77
70
 
78
- # Create frames for the grids
79
71
  main_buttons_frame = tk.Frame(self.content_frame)
80
72
  main_buttons_frame.pack(pady=10)
81
73
  set_dark_style(ttk.Style(), containers=[main_buttons_frame])
@@ -84,41 +76,33 @@ class MainApp(tk.Tk):
84
76
  additional_buttons_frame.pack(pady=10)
85
77
  set_dark_style(ttk.Style(), containers=[additional_buttons_frame])
86
78
 
87
- # Create a frame for the description below the icon grids
88
79
  description_frame = tk.Frame(self.content_frame, height=70)
89
80
  description_frame.pack(fill=tk.X, pady=10)
90
- description_frame.pack_propagate(False) # Prevent the frame from resizing based on its content
81
+ description_frame.pack_propagate(False)
91
82
  set_dark_style(ttk.Style(), containers=[description_frame])
92
83
 
93
- # Use a Label widget to display descriptions
94
84
  self.description_label = tk.Label(description_frame, text="", wraplength=800, justify="center", font=('Helvetica', 12), fg=self.color_settings['fg_color'], bg=self.color_settings['bg_color'])
95
85
  self.description_label.pack(fill=tk.BOTH, pady=10)
96
86
 
97
- # Load the logo image and place it in the main apps row
98
87
  logo_button = spacrButton(main_buttons_frame, text="SpaCr", command=lambda: self.load_app("logo_spacr", initiate_root), icon_name="logo_spacr", size=100, show_text=False)
99
88
  logo_button.grid(row=0, column=0, padx=5, pady=5)
100
89
  self.main_buttons[logo_button] = "SpaCr provides a flexible toolset to extract single-cell images and measurements from high-content cell painting experiments, train deep-learning models to classify cellular/subcellular phenotypes, simulate, and analyze pooled CRISPR-Cas9 imaging screens.."
101
90
 
102
- # Create icon buttons for the main apps
103
91
  for i, (app_name, app_data) in enumerate(self.main_gui_apps.items()):
104
92
  app_func, app_desc = app_data
105
93
  button = spacrButton(main_buttons_frame, text=app_name, command=lambda app_name=app_name, app_func=app_func: self.load_app(app_name, app_func), icon_name=app_name.lower(), size=100, show_text=False)
106
94
  button.grid(row=0, column=i + 1, padx=5, pady=5)
107
95
  self.main_buttons[button] = app_desc
108
96
 
109
- # Create icon buttons for the additional apps
110
97
  for i, (app_name, app_data) in enumerate(self.additional_gui_apps.items()):
111
98
  app_func, app_desc = app_data
112
99
  button = spacrButton(additional_buttons_frame, text=app_name, command=lambda app_name=app_name, app_func=app_func: self.load_app(app_name, app_func), icon_name=app_name.lower(), size=75, show_text=False)
113
100
  button.grid(row=0, column=i, padx=5, pady=5)
114
101
  self.additional_buttons[button] = app_desc
115
102
 
116
- # Update description initially
117
103
  self.update_description()
118
- #
119
104
 
120
105
  def update_description(self):
121
- # Check all buttons and update description if any has the active color
122
106
  for button, desc in {**self.main_buttons, **self.additional_buttons}.items():
123
107
  if button.canvas.itemcget(button.button_bg, "fill") == self.color_settings['active_color']:
124
108
  self.show_description(desc)
@@ -128,18 +112,16 @@ class MainApp(tk.Tk):
128
112
  def show_description(self, description):
129
113
  if self.description_label.winfo_exists():
130
114
  self.description_label.config(text=description)
131
- self.description_label.update_idletasks() # Ensure the label updates immediately
115
+ self.description_label.update_idletasks()
132
116
 
133
117
  def clear_description(self):
134
118
  if self.description_label.winfo_exists():
135
119
  self.description_label.config(text="")
136
- self.description_label.update_idletasks() # Ensure the label updates immediately
120
+ self.description_label.update_idletasks()
137
121
 
138
122
  def load_app(self, app_name, app_func):
139
- # Clear the current content frame
140
123
  self.clear_frame(self.canvas)
141
124
 
142
- # Initialize the selected app
143
125
  app_frame = tk.Frame(self.canvas)
144
126
  app_frame.pack(fill=tk.BOTH, expand=True)
145
127
  set_dark_style(ttk.Style(), containers=[app_frame])
@@ -155,4 +137,4 @@ def gui_app():
155
137
 
156
138
  if __name__ == "__main__":
157
139
  set_start_method('spawn', force=True)
158
- gui_app()
140
+ gui_app()