spacr 0.2.45__py3-none-any.whl → 0.2.53__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. spacr/core.py +27 -21
  2. spacr/gui.py +0 -2
  3. spacr/gui_core.py +113 -127
  4. spacr/gui_elements.py +367 -152
  5. spacr/gui_utils.py +71 -66
  6. spacr/io.py +24 -19
  7. spacr/measure.py +196 -145
  8. spacr/plot.py +2 -42
  9. spacr/resources/font/open_sans/OFL.txt +93 -0
  10. spacr/resources/font/open_sans/OpenSans-Italic-VariableFont_wdth,wght.ttf +0 -0
  11. spacr/resources/font/open_sans/OpenSans-VariableFont_wdth,wght.ttf +0 -0
  12. spacr/resources/font/open_sans/README.txt +100 -0
  13. spacr/resources/font/open_sans/static/OpenSans-Bold.ttf +0 -0
  14. spacr/resources/font/open_sans/static/OpenSans-BoldItalic.ttf +0 -0
  15. spacr/resources/font/open_sans/static/OpenSans-ExtraBold.ttf +0 -0
  16. spacr/resources/font/open_sans/static/OpenSans-ExtraBoldItalic.ttf +0 -0
  17. spacr/resources/font/open_sans/static/OpenSans-Italic.ttf +0 -0
  18. spacr/resources/font/open_sans/static/OpenSans-Light.ttf +0 -0
  19. spacr/resources/font/open_sans/static/OpenSans-LightItalic.ttf +0 -0
  20. spacr/resources/font/open_sans/static/OpenSans-Medium.ttf +0 -0
  21. spacr/resources/font/open_sans/static/OpenSans-MediumItalic.ttf +0 -0
  22. spacr/resources/font/open_sans/static/OpenSans-Regular.ttf +0 -0
  23. spacr/resources/font/open_sans/static/OpenSans-SemiBold.ttf +0 -0
  24. spacr/resources/font/open_sans/static/OpenSans-SemiBoldItalic.ttf +0 -0
  25. spacr/resources/font/open_sans/static/OpenSans_Condensed-Bold.ttf +0 -0
  26. spacr/resources/font/open_sans/static/OpenSans_Condensed-BoldItalic.ttf +0 -0
  27. spacr/resources/font/open_sans/static/OpenSans_Condensed-ExtraBold.ttf +0 -0
  28. spacr/resources/font/open_sans/static/OpenSans_Condensed-ExtraBoldItalic.ttf +0 -0
  29. spacr/resources/font/open_sans/static/OpenSans_Condensed-Italic.ttf +0 -0
  30. spacr/resources/font/open_sans/static/OpenSans_Condensed-Light.ttf +0 -0
  31. spacr/resources/font/open_sans/static/OpenSans_Condensed-LightItalic.ttf +0 -0
  32. spacr/resources/font/open_sans/static/OpenSans_Condensed-Medium.ttf +0 -0
  33. spacr/resources/font/open_sans/static/OpenSans_Condensed-MediumItalic.ttf +0 -0
  34. spacr/resources/font/open_sans/static/OpenSans_Condensed-Regular.ttf +0 -0
  35. spacr/resources/font/open_sans/static/OpenSans_Condensed-SemiBold.ttf +0 -0
  36. spacr/resources/font/open_sans/static/OpenSans_Condensed-SemiBoldItalic.ttf +0 -0
  37. spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-Bold.ttf +0 -0
  38. spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-BoldItalic.ttf +0 -0
  39. spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-ExtraBold.ttf +0 -0
  40. spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-ExtraBoldItalic.ttf +0 -0
  41. spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-Italic.ttf +0 -0
  42. spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-Light.ttf +0 -0
  43. spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-LightItalic.ttf +0 -0
  44. spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-Medium.ttf +0 -0
  45. spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-MediumItalic.ttf +0 -0
  46. spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-Regular.ttf +0 -0
  47. spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-SemiBold.ttf +0 -0
  48. spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-SemiBoldItalic.ttf +0 -0
  49. spacr/sequencing.py +107 -13
  50. spacr/settings.py +27 -84
  51. spacr/utils.py +9 -9
  52. {spacr-0.2.45.dist-info → spacr-0.2.53.dist-info}/METADATA +6 -4
  53. spacr-0.2.53.dist-info/RECORD +100 -0
  54. spacr-0.2.45.dist-info/RECORD +0 -60
  55. {spacr-0.2.45.dist-info → spacr-0.2.53.dist-info}/LICENSE +0 -0
  56. {spacr-0.2.45.dist-info → spacr-0.2.53.dist-info}/WHEEL +0 -0
  57. {spacr-0.2.45.dist-info → spacr-0.2.53.dist-info}/entry_points.txt +0 -0
  58. {spacr-0.2.45.dist-info → spacr-0.2.53.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,100 @@
1
+ Open Sans Variable Font
2
+ =======================
3
+
4
+ This download contains Open Sans as both variable fonts and static fonts.
5
+
6
+ Open Sans is a variable font with these axes:
7
+ wdth
8
+ wght
9
+
10
+ This means all the styles are contained in these files:
11
+ OpenSans-VariableFont_wdth,wght.ttf
12
+ OpenSans-Italic-VariableFont_wdth,wght.ttf
13
+
14
+ If your app fully supports variable fonts, you can now pick intermediate styles
15
+ that aren’t available as static fonts. Not all apps support variable fonts, and
16
+ in those cases you can use the static font files for Open Sans:
17
+ static/OpenSans_Condensed-Light.ttf
18
+ static/OpenSans_Condensed-Regular.ttf
19
+ static/OpenSans_Condensed-Medium.ttf
20
+ static/OpenSans_Condensed-SemiBold.ttf
21
+ static/OpenSans_Condensed-Bold.ttf
22
+ static/OpenSans_Condensed-ExtraBold.ttf
23
+ static/OpenSans_SemiCondensed-Light.ttf
24
+ static/OpenSans_SemiCondensed-Regular.ttf
25
+ static/OpenSans_SemiCondensed-Medium.ttf
26
+ static/OpenSans_SemiCondensed-SemiBold.ttf
27
+ static/OpenSans_SemiCondensed-Bold.ttf
28
+ static/OpenSans_SemiCondensed-ExtraBold.ttf
29
+ static/OpenSans-Light.ttf
30
+ static/OpenSans-Regular.ttf
31
+ static/OpenSans-Medium.ttf
32
+ static/OpenSans-SemiBold.ttf
33
+ static/OpenSans-Bold.ttf
34
+ static/OpenSans-ExtraBold.ttf
35
+ static/OpenSans_Condensed-LightItalic.ttf
36
+ static/OpenSans_Condensed-Italic.ttf
37
+ static/OpenSans_Condensed-MediumItalic.ttf
38
+ static/OpenSans_Condensed-SemiBoldItalic.ttf
39
+ static/OpenSans_Condensed-BoldItalic.ttf
40
+ static/OpenSans_Condensed-ExtraBoldItalic.ttf
41
+ static/OpenSans_SemiCondensed-LightItalic.ttf
42
+ static/OpenSans_SemiCondensed-Italic.ttf
43
+ static/OpenSans_SemiCondensed-MediumItalic.ttf
44
+ static/OpenSans_SemiCondensed-SemiBoldItalic.ttf
45
+ static/OpenSans_SemiCondensed-BoldItalic.ttf
46
+ static/OpenSans_SemiCondensed-ExtraBoldItalic.ttf
47
+ static/OpenSans-LightItalic.ttf
48
+ static/OpenSans-Italic.ttf
49
+ static/OpenSans-MediumItalic.ttf
50
+ static/OpenSans-SemiBoldItalic.ttf
51
+ static/OpenSans-BoldItalic.ttf
52
+ static/OpenSans-ExtraBoldItalic.ttf
53
+
54
+ Get started
55
+ -----------
56
+
57
+ 1. Install the font files you want to use
58
+
59
+ 2. Use your app's font picker to view the font family and all the
60
+ available styles
61
+
62
+ Learn more about variable fonts
63
+ -------------------------------
64
+
65
+ https://developers.google.com/web/fundamentals/design-and-ux/typography/variable-fonts
66
+ https://variablefonts.typenetwork.com
67
+ https://medium.com/variable-fonts
68
+
69
+ In desktop apps
70
+
71
+ https://theblog.adobe.com/can-variable-fonts-illustrator-cc
72
+ https://helpx.adobe.com/nz/photoshop/using/fonts.html#variable_fonts
73
+
74
+ Online
75
+
76
+ https://developers.google.com/fonts/docs/getting_started
77
+ https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Fonts/Variable_Fonts_Guide
78
+ https://developer.microsoft.com/en-us/microsoft-edge/testdrive/demos/variable-fonts
79
+
80
+ Installing fonts
81
+
82
+ MacOS: https://support.apple.com/en-us/HT201749
83
+ Linux: https://www.google.com/search?q=how+to+install+a+font+on+gnu%2Blinux
84
+ Windows: https://support.microsoft.com/en-us/help/314960/how-to-install-or-remove-a-font-in-windows
85
+
86
+ Android Apps
87
+
88
+ https://developers.google.com/fonts/docs/android
89
+ https://developer.android.com/guide/topics/ui/look-and-feel/downloadable-fonts
90
+
91
+ License
92
+ -------
93
+ Please read the full license text (OFL.txt) to understand the permissions,
94
+ restrictions and requirements for usage, redistribution, and modification.
95
+
96
+ You can use them in your products & projects – print or digital,
97
+ commercial or otherwise.
98
+
99
+ This isn't legal advice, please consider consulting a lawyer and see the full
100
+ license for all details.
spacr/sequencing.py CHANGED
@@ -1,4 +1,4 @@
1
- import os, gc, gzip, re, time, math, subprocess
1
+ import os, gc, gzip, re, time, math, subprocess, traceback
2
2
  import pandas as pd
3
3
  import numpy as np
4
4
  from tqdm import tqdm
@@ -21,6 +21,96 @@ from sklearn.preprocessing import FunctionTransformer, MinMaxScaler
21
21
  from scipy.stats import shapiro
22
22
  from patsy import dmatrices
23
23
 
24
+ import os
25
+ import gzip
26
+ from Bio import SeqIO
27
+ from Bio.Seq import Seq
28
+ from Bio.SeqRecord import SeqRecord
29
+
30
+ def consensus_sequence(fastq_r1, fastq_r2, output_file, chunk_size=1000000):
31
+
32
+ total_reads = 0
33
+ chunk_count = 0
34
+
35
+ with gzip.open(fastq_r1, "rt") as r1_handle, gzip.open(fastq_r2, "rt") as r2_handle, gzip.open(output_file, "wt") as output_handle:
36
+ r1_iter = SeqIO.parse(r1_handle, "fastq")
37
+ r2_iter = SeqIO.parse(r2_handle, "fastq")
38
+
39
+ while True:
40
+ r1_chunk = [rec for rec in (next(r1_iter, None) for _ in range(chunk_size)) if rec is not None]
41
+ r2_chunk = [rec for rec in (next(r2_iter, None) for _ in range(chunk_size)) if rec is not None]
42
+
43
+ # If either chunk is empty, we have reached the end of one or both files
44
+ if not r1_chunk or not r2_chunk:
45
+ break
46
+
47
+ chunk_count += 1
48
+ total_reads += len(r1_chunk)
49
+
50
+ for r1_record, r2_record in zip(r1_chunk, r2_chunk):
51
+ best_sequence = []
52
+ best_quality = []
53
+ for base1, base2, qual1, qual2 in zip(r1_record.seq, r2_record.seq, r1_record.letter_annotations["phred_quality"], r2_record.letter_annotations["phred_quality"]):
54
+ if qual1 >= qual2:
55
+ best_sequence.append(base1)
56
+ best_quality.append(qual1)
57
+ else:
58
+ best_sequence.append(base2)
59
+ best_quality.append(qual2)
60
+
61
+ consensus_seq = Seq("".join(best_sequence))
62
+
63
+ # Create a new SeqRecord for the consensus sequence
64
+ consensus_record = SeqRecord(consensus_seq, id=r1_record.id, description="", letter_annotations={"phred_quality": best_quality})
65
+
66
+ # Write the consensus sequence to the output file
67
+ SeqIO.write(consensus_record, output_handle, "fastq")
68
+
69
+ print(f"Progress: Chunk {chunk_count} with {total_reads} reads.")
70
+
71
+ def parse_gz_files(folder_path):
72
+ """
73
+ Parses the .fastq.gz files in the specified folder path and returns a dictionary
74
+ containing the sample names and their corresponding file paths.
75
+
76
+ Args:
77
+ folder_path (str): The path to the folder containing the .fastq.gz files.
78
+
79
+ Returns:
80
+ dict: A dictionary where the keys are the sample names and the values are
81
+ dictionaries containing the file paths for the 'R1' and 'R2' read directions.
82
+ """
83
+ files = os.listdir(folder_path)
84
+ gz_files = [f for f in files if f.endswith('.fastq.gz')]
85
+
86
+ samples_dict = {}
87
+ for gz_file in gz_files:
88
+ parts = gz_file.split('_')
89
+ sample_name = parts[0]
90
+ read_direction = parts[1]
91
+
92
+ if sample_name not in samples_dict:
93
+ samples_dict[sample_name] = {}
94
+
95
+ if read_direction == "R1":
96
+ samples_dict[sample_name]['R1'] = os.path.join(folder_path, gz_file)
97
+ elif read_direction == "R2":
98
+ samples_dict[sample_name]['R2'] = os.path.join(folder_path, gz_file)
99
+
100
+ return samples_dict
101
+
102
+ def generate_consensus_sequence(src, chunk_size):
103
+ samples_dict = parse_gz_files(src)
104
+ for key in samples_dict:
105
+ if samples_dict[key]['R1'] and samples_dict[key]['R2']:
106
+ R1 = samples_dict[key]['R1']
107
+ R2 = samples_dict[key]['R2']
108
+ consensus_dir = os.path.join(os.path.dirname(R1), 'consensus')
109
+ os.makedirs(consensus_dir, exist_ok=True) # Use os.makedirs() instead of os.mkdir()
110
+ consensus = os.path.join(consensus_dir, f"{key}_consensus.fastq.gz")
111
+ consensus_sequence(R1, R2, consensus, chunk_size)
112
+
113
+
24
114
  def analyze_reads(settings):
25
115
  """
26
116
  Analyzes reads from gzipped fastq files and combines them based on specified settings.
@@ -291,12 +381,15 @@ def analyze_reads(settings):
291
381
  qc_df.to_csv(qc_file_path, index=False)
292
382
 
293
383
  from .settings import get_analyze_reads_default_settings
294
-
295
- settings = get_analyze_reads_default_settings(settings)
296
-
297
- samples_dict = parse_gz_files(settings['src'])
298
- combine_reads(samples_dict, settings['src'], settings['chunk_size'], settings['barecode_length_1'], settings['barecode_length_2'], settings['upstream'], settings['downstream'])
299
-
384
+ try:
385
+ settings = get_analyze_reads_default_settings(settings)
386
+ samples_dict = parse_gz_files(settings['src'])
387
+ combine_reads(samples_dict, settings['src'], settings['chunk_size'], settings['barecode_length_1'], settings['barecode_length_2'], settings['upstream'], settings['downstream'])
388
+ except Exception as e:
389
+ print(e)
390
+ Error = traceback.format_exc()
391
+ print(Error)
392
+
300
393
  def map_barcodes(h5_file_path, settings={}):
301
394
  """
302
395
  Maps barcodes and performs quality control on sequencing data.
@@ -461,9 +554,7 @@ def map_barcodes(h5_file_path, settings={}):
461
554
  return filtered_df
462
555
 
463
556
  from .settings import get_map_barcodes_default_settings
464
-
465
557
  settings = get_map_barcodes_default_settings(settings)
466
-
467
558
  fldr = os.path.splitext(h5_file_path)[0]
468
559
  file_name = os.path.basename(fldr)
469
560
 
@@ -729,7 +820,12 @@ def grna_plate_heatmap(path, specific_grna=None, min_max='all', cmap='viridis',
729
820
 
730
821
  return fig
731
822
 
732
- def map_barcodes_folder(src, settings={}):
823
+ def map_barcodes_folder(settings={}):
824
+ from .settings import get_map_barcodes_default_settings
825
+ settings = get_map_barcodes_default_settings(settings)
826
+
827
+ print(settings)
828
+ src = settings['src']
733
829
  for file in os.listdir(src):
734
830
  if file.endswith('.h5'):
735
831
  print(file)
@@ -1846,6 +1942,4 @@ def perform_regression(df, settings):
1846
1942
 
1847
1943
  print('Significant Genes')
1848
1944
  display(significant)
1849
- return coef_df
1850
-
1851
-
1945
+ return coef_df
spacr/settings.py CHANGED
@@ -220,6 +220,7 @@ def get_measure_crop_settings(settings):
220
220
 
221
221
  settings.setdefault('src', 'path')
222
222
  settings.setdefault('verbose', False)
223
+ settings.setdefault('experiment', 'exp')
223
224
 
224
225
  # Test mode
225
226
  settings.setdefault('test_mode', False)
@@ -252,8 +253,6 @@ def get_measure_crop_settings(settings):
252
253
 
253
254
  # Operational settings
254
255
  settings.setdefault('plot',False)
255
- settings.setdefault('plot_filtration',False)
256
- settings.setdefault('representative_images', False)
257
256
  settings.setdefault('n_jobs', os.cpu_count()-2)
258
257
 
259
258
  # Object settings
@@ -268,24 +267,9 @@ def get_measure_crop_settings(settings):
268
267
  settings.setdefault('cytoplasm_min_size',0)
269
268
  settings.setdefault('merge_edge_pathogen_cells', True)
270
269
 
271
- # Miscellaneous settings
272
- settings.setdefault('experiment', 'exp')
273
- settings.setdefault('cells', ['HeLa'])
274
- settings.setdefault('cell_loc', None)
275
- settings.setdefault('pathogens', ['ME49Dku80WT', 'ME49Dku80dgra8:GRA8', 'ME49Dku80dgra8', 'ME49Dku80TKO'])
276
- settings.setdefault('pathogen_loc', [['c1', 'c2', 'c3', 'c4', 'c5', 'c6'], ['c7', 'c8', 'c9', 'c10', 'c11', 'c12'], ['c13', 'c14', 'c15', 'c16', 'c17', 'c18'], ['c19', 'c20', 'c21', 'c22', 'c23', 'c24']])
277
- settings.setdefault('treatments', ['BR1', 'BR2', 'BR3'])
278
- settings.setdefault('treatment_loc', [['c1', 'c2', 'c7', 'c8', 'c13', 'c14', 'c19', 'c20'], ['c3', 'c4', 'c9', 'c10', 'c15', 'c16', 'c21', 'c22'], ['c5', 'c6', 'c11', 'c12', 'c17', 'c18', 'c23', 'c24']])
279
- settings.setdefault('channel_of_interest', 2)
280
- settings.setdefault('compartments', ['pathogen', 'cytoplasm'])
281
- settings.setdefault('measurement', 'mean_intensity')
282
- settings.setdefault('nr_imgs', 32)
283
- settings.setdefault('um_per_pixel', 0.1)
284
-
285
270
  if settings['test_mode']:
286
271
  settings['verbose'] = True
287
272
  settings['plot'] = True
288
- settings['plot_filtration'] = True
289
273
  test_imgs = settings['test_nr']
290
274
  print(f'Test mode enabled with {test_imgs} images, plotting set to True')
291
275
 
@@ -384,6 +368,7 @@ def get_analyze_recruitment_default_settings(settings):
384
368
  return settings
385
369
 
386
370
  def get_analyze_reads_default_settings(settings):
371
+ settings.setdefault('src', 'path')
387
372
  settings.setdefault('upstream', 'CTTCTGGTAAATGGGGATGTCAAGTT')
388
373
  settings.setdefault('downstream', 'GTTTAAGAGCTATGCTGGAAACAGCAG') #This is the reverce compliment of the column primer starting from the end #TGCTGTTTAAGAGCTATGCTGGAAACAGCA
389
374
  settings.setdefault('barecode_length_1', 8)
@@ -396,7 +381,7 @@ def get_map_barcodes_default_settings(settings):
396
381
  settings.setdefault('src', 'path')
397
382
  settings.setdefault('grna', '/home/carruthers/Documents/grna_barcodes.csv')
398
383
  settings.setdefault('barcodes', '/home/carruthers/Documents/SCREEN_BARCODES.csv')
399
- settings.setdefault('plate_dict', {'EO1': 'plate1', 'EO2': 'plate2', 'EO3': 'plate3', 'EO4': 'plate4', 'EO5': 'plate5', 'EO6': 'plate6', 'EO7': 'plate7', 'EO8': 'plate8'})
384
+ settings.setdefault('plate_dict', "{'EO1': 'plate1', 'EO2': 'plate2', 'EO3': 'plate3', 'EO4': 'plate4', 'EO5': 'plate5', 'EO6': 'plate6', 'EO7': 'plate7', 'EO8': 'plate8'}")
400
385
  settings.setdefault('test', False)
401
386
  settings.setdefault('verbose', True)
402
387
  settings.setdefault('pc', 'TGGT1_220950_1')
@@ -554,8 +539,6 @@ expected_types = {
554
539
  "png_dims": list,
555
540
  "normalize_by": str,
556
541
  "save_measurements": bool,
557
- "representative_images": bool,
558
- "plot_filtration": bool,
559
542
  "include_uninfected": bool,
560
543
  "dialate_pngs": bool,
561
544
  "dialate_png_ratios": list,
@@ -742,60 +725,6 @@ expected_types = {
742
725
  "fraction_threshold": float,
743
726
  }
744
727
 
745
- def check_settings_v1(vars_dict, expected_types,q=None):
746
- from .gui_utils import parse_list
747
- settings = {}
748
- # Define the expected types for each key, including None where applicable
749
-
750
- for key, (label, widget, var) in vars_dict.items():
751
- if key not in expected_types:
752
- if key not in ["General","Nucleus","Cell","Pathogen","Timelapse","Plot","Object Image","Annotate Data","Measurements","Advanced","Miscellaneous","Test"]:
753
- q.put(f"Key {key} not found in expected types.")
754
- continue
755
-
756
- value = var.get()
757
- expected_type = expected_types.get(key, str)
758
-
759
- try:
760
- if key in ["png_size", "pathogen_plate_metadata", "treatment_plate_metadata"]:
761
- parsed_value = ast.literal_eval(value) if value else None
762
- if isinstance(parsed_value, list):
763
- if all(isinstance(i, list) for i in parsed_value) or all(not isinstance(i, list) for i in parsed_value):
764
- settings[key] = parsed_value
765
- else:
766
- raise ValueError("Invalid format: Mixed list and list of lists")
767
- else:
768
- raise ValueError("Invalid format for list or list of lists")
769
- elif expected_type == list:
770
- settings[key] = parse_list(value) if value else None
771
- elif expected_type == bool:
772
- settings[key] = value if isinstance(value, bool) else value.lower() in ['true', '1', 't', 'y', 'yes']
773
- elif expected_type == (int, type(None)):
774
- settings[key] = int(value) if value else None
775
- elif expected_type == (float, type(None)):
776
- settings[key] = float(value) if value else None
777
- elif expected_type == (int, float):
778
- settings[key] = float(value) if '.' in value else int(value)
779
- elif expected_type == (str, type(None)):
780
- settings[key] = str(value) if value else None
781
- elif isinstance(expected_type, tuple):
782
- for typ in expected_type:
783
- try:
784
- settings[key] = typ(value) if value else None
785
- break
786
- except (ValueError, TypeError):
787
- continue
788
- else:
789
- raise ValueError
790
- else:
791
- settings[key] = expected_type(value) if value else None
792
- except (ValueError, SyntaxError):
793
- expected_type_name = ' or '.join([t.__name__ for t in expected_type]) if isinstance(expected_type, tuple) else expected_type.__name__
794
- q.put(f"Error: Invalid format for {key}. Expected type: {expected_type_name}.")
795
- return
796
-
797
- return settings
798
-
799
728
  def check_settings(vars_dict, expected_types, q=None):
800
729
  from .gui_utils import parse_list
801
730
 
@@ -805,9 +734,9 @@ def check_settings(vars_dict, expected_types, q=None):
805
734
 
806
735
  settings = {}
807
736
 
808
- for key, (label, widget, var) in vars_dict.items():
737
+ for key, (label, widget, var, _) in vars_dict.items():
809
738
  if key not in expected_types:
810
- if key not in ["General", "Nucleus", "Cell", "Pathogen", "Timelapse", "Plot", "Object Image", "Annotate Data", "Measurements", "Advanced", "Miscellaneous", "Test"]:
739
+ if key not in ["General", "Nucleus", "Cell", "Pathogen", "Timelapse", "Plot", "Object Image", "Annotate Data", "Measurements", "Advanced", "Miscellaneous", "Test", "Paths"]:
811
740
  q.put(f"Key {key} not found in expected types.")
812
741
  continue
813
742
 
@@ -836,6 +765,20 @@ def check_settings(vars_dict, expected_types, q=None):
836
765
  settings[key] = float(value) if '.' in value else int(value)
837
766
  elif expected_type == (str, type(None)):
838
767
  settings[key] = str(value) if value else None
768
+ elif expected_type == dict:
769
+ try:
770
+ # Ensure that the value is a string that can be converted to a dictionary
771
+ if isinstance(value, str):
772
+ settings[key] = ast.literal_eval(value)
773
+ else:
774
+ raise ValueError("Expected a string representation of a dictionary.")
775
+
776
+ # Check if the result is actually a dictionary
777
+ if not isinstance(settings[key], dict):
778
+ raise ValueError("Value is not a valid dictionary.")
779
+ except (ValueError, SyntaxError) as e:
780
+ settings[key] = {}
781
+ q.put(f"Error: Invalid format for {key}. Expected type: dict. Error: {e}")
839
782
  elif isinstance(expected_type, tuple):
840
783
  for typ in expected_type:
841
784
  try:
@@ -856,7 +799,7 @@ def check_settings(vars_dict, expected_types, q=None):
856
799
 
857
800
  def generate_fields(variables, scrollable_frame):
858
801
  from .gui_utils import create_input_field
859
- from .gui_elements import spacrToolTip
802
+ from .gui_elements import set_dark_style, spacrToolTip
860
803
  row = 1
861
804
  vars_dict = {}
862
805
  tooltips = {
@@ -1014,7 +957,6 @@ def generate_fields(variables, scrollable_frame):
1014
957
  "plot_by_cluster": "(bool) - Whether to plot images by clusters.",
1015
958
  "plot_cluster_grids": "(bool) - Whether to plot grids of clustered images.",
1016
959
  "plot_control": "(dict) - Control settings for plotting.",
1017
- "plot_filtration": "(bool) - Whether to plot the filtration steps.",
1018
960
  "plot_images": "(bool) - Whether to plot images.",
1019
961
  "plot_nr": "(int) - Number of plots to generate.",
1020
962
  "plot_outlines": "(bool) - Whether to plot outlines of segmented objects.",
@@ -1036,7 +978,6 @@ def generate_fields(variables, scrollable_frame):
1036
978
  "remove_image_canvas": "(bool) - Whether to remove the image canvas after plotting.",
1037
979
  "remove_low_variance_features": "(bool) - Whether to remove low variance features from the analysis.",
1038
980
  "remove_row_column_effect": "(bool) - Whether to remove row and column effects from the data.",
1039
- "representative_images": "(bool) - Whether to save representative images of the segmented objects (Not working yet).",
1040
981
  "resize": "(bool) - Resize factor for the images.",
1041
982
  "resample": "(bool) - Whether to resample the images during processing.",
1042
983
  "rescale": "(float) - Rescaling factor for the images.",
@@ -1080,17 +1021,19 @@ def generate_fields(variables, scrollable_frame):
1080
1021
  "um_per_pixel": "(float) - The micrometers per pixel for the images."
1081
1022
  }
1082
1023
 
1083
-
1084
1024
  for key, (var_type, options, default_value) in variables.items():
1085
- label, widget, var = create_input_field(scrollable_frame.scrollable_frame, key, row, var_type, options, default_value)
1086
- vars_dict[key] = (label, widget, var) # Store the label, widget, and variable
1025
+ label, widget, var, frame = create_input_field(scrollable_frame.scrollable_frame, key, row, var_type, options, default_value)
1026
+ vars_dict[key] = (label, widget, var, frame) # Store the label, widget, and variable
1087
1027
 
1088
1028
  # Add tooltip to the label if it exists in the tooltips dictionary
1089
1029
  if key in tooltips:
1090
1030
  spacrToolTip(label, tooltips[key])
1031
+
1091
1032
  row += 1
1033
+
1092
1034
  return vars_dict
1093
1035
 
1036
+
1094
1037
  categories = {
1095
1038
  "General": ["src", "metadata_type", "custom_regex", "experiment", "channels", "magnification", "channel_dims"],
1096
1039
  "Paths":["grna", "barcodes"],
@@ -1100,9 +1043,9 @@ categories = {
1100
1043
  "Cell": ["cell_intensity_range", "cell_size_range", "cell_chann_dim", "cell_channel", "cell_background", "cell_Signal_to_noise", "cell_CP_prob", "cell_FT", "remove_background_cell", "cell_min_size", "cell_mask_dim", "cytoplasm", "cytoplasm_min_size", "include_uninfected", "merge_edge_pathogen_cells", "adjust_cells"],
1101
1044
  "Pathogen": ["pathogen_intensity_range", "pathogen_size_range", "pathogen_chann_dim", "pathogen_channel", "pathogen_background", "pathogen_Signal_to_noise", "pathogen_CP_prob", "pathogen_FT", "pathogen_model", "remove_background_pathogen", "pathogen_min_size", "pathogen_mask_dim"],
1102
1045
  "Timelapse": ["fps", "timelapse_displacement", "timelapse_memory", "timelapse_frame_limits", "timelapse_remove_transient", "timelapse_mode", "timelapse_objects", "compartments"],
1103
- "Plot": ["plot_control", "plot_nr", "plot_filtration", "examples_to_plot", "normalize_plots", "normalize", "cmap", "figuresize", "plot_cluster_grids", "img_zoom", "row_limit", "color_by", "plot_images", "smooth_lines", "plot_points", "plot_outlines", "black_background", "plot_by_cluster", "heatmap_feature","grouping","min_max","cmap","save_figure"],
1046
+ "Plot": ["plot_control", "plot_nr", "examples_to_plot", "normalize_plots", "normalize", "cmap", "figuresize", "plot_cluster_grids", "img_zoom", "row_limit", "color_by", "plot_images", "smooth_lines", "plot_points", "plot_outlines", "black_background", "plot_by_cluster", "heatmap_feature","grouping","min_max","cmap","save_figure"],
1104
1047
  "Object Image": ["save_png", "dialate_pngs", "dialate_png_ratios", "png_size", "png_dims", "save_arrays", "normalize_by", "dialate_png_ratios", "crop_mode", "dialate_pngs", "normalize", "use_bounding_box"],
1105
- "Annotate Data": ["nc_loc", "pc_loc", "nc", "pc", "cell_plate_metadata","pathogen_types", "pathogen_plate_metadata", "treatment_plate_metadata", "metadata_types", "cell_types", "target","positive_control","negative_control", "location_column", "treatment_loc", "cells", "cell_loc", "pathogens", "pathogen_loc", "channel_of_interest", "measurement", "treatments", "representative_images", "um_per_pixel", "nr_imgs", "exclude", "exclude_conditions", "mix", "pos", "neg"],
1048
+ "Annotate Data": ["nc_loc", "pc_loc", "nc", "pc", "cell_plate_metadata","pathogen_types", "pathogen_plate_metadata", "treatment_plate_metadata", "metadata_types", "cell_types", "target","positive_control","negative_control", "location_column", "treatment_loc", "cells", "cell_loc", "pathogens", "pathogen_loc", "channel_of_interest", "measurement", "treatments", "um_per_pixel", "nr_imgs", "exclude", "exclude_conditions", "mix", "pos", "neg"],
1106
1049
  "Measurements": ["remove_image_canvas", "remove_highly_correlated", "homogeneity", "homogeneity_distances", "radial_dist", "calculate_correlation", "manders_thresholds", "save_measurements", "tables", "image_nr", "dot_size", "filter_by", "remove_highly_correlated_features", "remove_low_variance_features", "channel_of_interest"],
1107
1050
  "Advanced": ["plate_dict", "target_intensity_min", "cells_per_well", "include_multinucleated", "include_multiinfected", "include_noninfected", "backgrounds", "plot", "timelapse", "schedule", "test_size","exclude","n_repeats","top_features", "model_type","minimum_cell_count","n_estimators","preprocess", "remove_background", "normalize", "lower_percentile", "merge_pathogens", "batch_size", "filter", "save", "masks", "verbose", "randomize", "n_jobs", "train_mode","amsgrad","use_checkpoint","gradient_accumulation","gradient_accumulation_steps","intermedeate_save","pin_memory","n_jobs","channels","augment"],
1108
1051
  "Clustering": ["eps","min_samples","analyze_clusters","clustering","remove_cluster_noise"],
spacr/utils.py CHANGED
@@ -1,4 +1,4 @@
1
- import sys, os, re, sqlite3, torch, torchvision, random, string, shutil, cv2, tarfile, glob, psutil, platform, signal
1
+ import sys, os, re, sqlite3, torch, torchvision, random, string, shutil, cv2, tarfile, glob, psutil, platform
2
2
 
3
3
  import numpy as np
4
4
  from cellpose import models as cp_models
@@ -90,9 +90,9 @@ from scipy import stats
90
90
 
91
91
  def print_progress(files_processed, files_to_process, n_jobs, time_ls=None, batch_size=None, operation_type=""):
92
92
  if isinstance(files_processed, list):
93
- files_processed = len(files_processed)
93
+ files_processed = len(set(files_processed))
94
94
  if isinstance(files_to_process, list):
95
- files_to_process = len(files_to_process)
95
+ files_to_process = len(set(files_to_process))
96
96
  if isinstance(batch_size, list):
97
97
  batch_size = len(batch_size)
98
98
 
@@ -3628,22 +3628,22 @@ def delete_folder(folder_path):
3628
3628
  def measure_test_mode(settings):
3629
3629
 
3630
3630
  if settings['test_mode']:
3631
- if not os.path.basename(settings['input_folder']) == 'test':
3632
- all_files = os.listdir(settings['input_folder'])
3631
+ if not os.path.basename(settings['src']) == 'test':
3632
+ all_files = os.listdir(settings['src'])
3633
3633
  random_files = random.sample(all_files, settings['test_nr'])
3634
3634
 
3635
- src = os.path.join(os.path.dirname(settings['input_folder']),'test', 'merged')
3635
+ src = os.path.join(os.path.dirname(settings['src']),'test', 'merged')
3636
3636
  if os.path.exists(src):
3637
3637
  delete_folder(src)
3638
3638
  os.makedirs(src, exist_ok=True)
3639
3639
 
3640
3640
  for file in random_files:
3641
- shutil.copy(os.path.join(settings['input_folder'], file), os.path.join(src,file))
3641
+ shutil.copy(os.path.join(settings['src'], file), os.path.join(src,file))
3642
3642
 
3643
- settings['input_folder'] = src
3643
+ settings['src'] = src
3644
3644
  print(f'Changed source folder to {src} for test mode')
3645
3645
  else:
3646
- print(f'Test mode enabled, using source folder {settings["input_folder"]}')
3646
+ print(f'Test mode enabled, using source folder {settings["src"]}')
3647
3647
 
3648
3648
  return settings
3649
3649
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: spacr
3
- Version: 0.2.45
3
+ Version: 0.2.53
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
@@ -9,9 +9,9 @@ Classifier: Programming Language :: Python :: 3
9
9
  Classifier: License :: OSI Approved :: MIT License
10
10
  Classifier: Operating System :: OS Independent
11
11
  License-File: LICENSE
12
- Requires-Dist: torch <3.0,>=2.2.1
13
- Requires-Dist: torchvision <1.0,>=0.17.1
14
- Requires-Dist: torch-geometric <3.0,>=2.5.1
12
+ Requires-Dist: torch <3.0,>=2.0
13
+ Requires-Dist: torchvision <1.0,>=0.1
14
+ Requires-Dist: torch-geometric <3.0,>=2.5
15
15
  Requires-Dist: numpy <2.0,>=1.26.4
16
16
  Requires-Dist: pandas <3.0,>=2.2.1
17
17
  Requires-Dist: statsmodels <1.0,>=0.14.1
@@ -42,6 +42,8 @@ Requires-Dist: lxml <6.0,>=5.1.0
42
42
  Requires-Dist: psutil <6.0,>=5.9.8
43
43
  Requires-Dist: gputil <2.0,>=1.4.0
44
44
  Requires-Dist: gpustat <2.0,>=1.1.1
45
+ Requires-Dist: pyautogui <1.0,>=0.9.54
46
+ Requires-Dist: tables <4.0,>=3.8.0
45
47
  Requires-Dist: huggingface-hub <0.25,>=0.24.0
46
48
  Provides-Extra: dev
47
49
  Requires-Dist: pytest <3.11,>=3.9 ; extra == 'dev'