small-fish-gui 1.3.3__py3-none-any.whl → 1.3.5__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.
@@ -38,4 +38,4 @@ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
38
38
  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
39
39
 
40
40
  """
41
- __version__ = "1.3.3"
41
+ __version__ = "1.3.5"
@@ -10,7 +10,6 @@ from .prompts import input_image_prompt
10
10
  from .prompts import hub_prompt
11
11
  from .prompts import detection_parameters_promt
12
12
  from .prompts import coloc_prompt
13
- from .prompts import post_analysis_prompt
14
13
  from .prompts import output_image_prompt
15
14
  from .prompts import ask_cancel_detection
16
15
  from .prompts import ask_cancel_segmentation
@@ -21,7 +20,7 @@ from .prompts import ask_detection_confirmation
21
20
  from .layout import parameters_layout
22
21
  from .layout import bool_layout
23
22
  from .layout import path_layout
24
- from .layout import combo_layout
23
+ from .layout import combo_elmt
25
24
  from .layout import tuple_layout
26
25
  from .layout import radio_layout
27
26
  from .layout import add_header
@@ -0,0 +1,312 @@
1
+ import os
2
+ import numpy as np
3
+ import PySimpleGUI as sg
4
+ import bigfish.stack as stack
5
+ import czifile as czi
6
+
7
+ from .layout import _segmentation_layout, _detection_layout, _input_parameters_layout, _ask_channel_map_layout
8
+
9
+
10
+ from time import sleep
11
+
12
+ def get_images(filename:str) :
13
+ """returns filename if is image else return None"""
14
+
15
+ supported_types = ('.tiff', '.tif', '.png', '.czi')
16
+ if filename.endswith(supported_types) :
17
+ return [filename]
18
+ else :
19
+ return None
20
+
21
+
22
+ def get_files(path) :
23
+
24
+ filelist = os.listdir(path)
25
+ filelist = list(map(get_images,filelist))
26
+
27
+ while None in filelist : filelist.remove(None)
28
+
29
+ return filelist
30
+
31
+ def extract_files(filenames: list) :
32
+ return sum(filenames,[])
33
+
34
+ def check_file(filename:str) :
35
+
36
+ if filename.endswith('.czi') :
37
+ image = czi.imread(filename)
38
+ else :
39
+ image = stack.read_image(filename)
40
+
41
+ image = np.squeeze(image)
42
+
43
+ return image.shape
44
+
45
+ def sanity_check(
46
+ filename_list: list,
47
+ batch_folder : str,
48
+ window : sg.Window,
49
+ progress_bar: sg.ProgressBar,
50
+ ) :
51
+
52
+ filenumber = len(filename_list)
53
+ if filenumber == 0 :
54
+ print("No file to check")
55
+ progress_bar.update(current_count= 0, bar_color=('gray','gray'))
56
+ return None
57
+ else :
58
+ print("{0} files to check".format(filenumber))
59
+ progress_bar.update(current_count=0, max= filenumber)
60
+ ref_shape = check_file(batch_folder + '/' + filename_list[0])
61
+
62
+ print("Starting sanity check. This could take some time...")
63
+ for i, file in enumerate(filename_list) :
64
+ progress_bar.update(current_count= i+1, bar_color=('green','gray'))
65
+ shape = check_file(batch_folder + '/' + file)
66
+
67
+ if len(shape) != len(ref_shape) : #then dimension missmatch
68
+ print("Different number of dimensions found : {0}, {1}".format(len(ref_shape), len(shape)))
69
+ progress_bar.update(current_count=filenumber, bar_color=('red','black'))
70
+ window= window.refresh()
71
+ break
72
+
73
+ window= window.refresh()
74
+
75
+ print("Sanity check completed.")
76
+ return None if len(shape) != len(ref_shape) else shape
77
+
78
+
79
+ def get_elmt_from_key(Tab_elmt:sg.Tab, key) -> sg.Element:
80
+ elmt_list = sum(Tab_elmt.Rows,[])
81
+ for elmt in elmt_list :
82
+ if elmt.Key == key : return elmt
83
+ raise KeyError("{0} key not found amongst {1}.".format(key, [elmt.Key for elmt in elmt_list]))
84
+
85
+ def update_detection_tab(
86
+ tab_elmt:sg.Tab,
87
+ is_multichannel,
88
+ is_3D,
89
+ do_dense_region_deconvolution,
90
+ do_clustering
91
+ ) :
92
+
93
+ #Acess elements
94
+ ##Detection
95
+ channel_to_compute = get_elmt_from_key(tab_elmt, key= 'channel to compute')
96
+ voxel_size_z = get_elmt_from_key(tab_elmt, key= 'voxel_size_z')
97
+ spot_size_z = get_elmt_from_key(tab_elmt, key= 'spot_size_z')
98
+ log_kernel_size_z = get_elmt_from_key(tab_elmt, key= 'log_kernel_size_z')
99
+ minimum_distance_z = get_elmt_from_key(tab_elmt, key= 'minimum_distance_z')
100
+
101
+ ##Dense regions deconvolution
102
+ alpha = get_elmt_from_key(tab_elmt, key= 'alpha')
103
+ beta = get_elmt_from_key(tab_elmt, key= 'beta')
104
+ gamma = get_elmt_from_key(tab_elmt, key= 'gamma')
105
+ deconvolution_kernel_z = get_elmt_from_key(tab_elmt, key= 'deconvolution_kernel_z')
106
+ cluster_size = get_elmt_from_key(tab_elmt, key= 'cluster size')
107
+ min_number_of_spot = get_elmt_from_key(tab_elmt, key= 'min number of spots')
108
+ nucleus_channel_signal = get_elmt_from_key(tab_elmt, key= 'nucleus channel signal')
109
+ interactive_threshold_selector = get_elmt_from_key(tab_elmt, key= 'Interactive threshold selector')
110
+
111
+ update_dict={
112
+ 'is_3D' : is_3D,
113
+ 'is_multichannel' : is_multichannel,
114
+ 'do_dense_region_deconvolution' : do_dense_region_deconvolution,
115
+ 'do_clustering' : do_clustering,
116
+ 'always_hidden' : False
117
+ }
118
+
119
+ list_dict={
120
+ 'is_3D' : [voxel_size_z, spot_size_z, log_kernel_size_z, minimum_distance_z, deconvolution_kernel_z],
121
+ 'is_multichannel' : [channel_to_compute, nucleus_channel_signal],
122
+ 'do_dense_region_deconvolution' : [alpha,beta,gamma],
123
+ 'do_clustering' : [cluster_size, min_number_of_spot],
124
+ 'always_hidden' : [interactive_threshold_selector]
125
+
126
+ }
127
+
128
+ for key, enabled in update_dict.items() :
129
+ for elmt in list_dict.get(key) :
130
+ elmt.update(disabled=not enabled)
131
+
132
+
133
+
134
+ def update_segmentation_tab(tab_elmt : sg.Tab, do_segmentation, is_multichannel) : #TODO
135
+
136
+ #Access elements
137
+ cytoplasm_channel_elmt = get_elmt_from_key(tab_elmt, key= 'cytoplasm channel')
138
+ nucleus_channel_elmt = get_elmt_from_key(tab_elmt, key= 'nucleus channel')
139
+
140
+ #Update values
141
+ tab_elmt.update(visible=do_segmentation)
142
+ cytoplasm_channel_elmt.update(disabled = not is_multichannel)
143
+ nucleus_channel_elmt.update(disabled = not is_multichannel)
144
+
145
+ def update_map_tab() :
146
+ #TODO
147
+ pass
148
+
149
+ def batch_promp() :
150
+
151
+ files_values = [[]]
152
+
153
+
154
+ #LOAD FILES
155
+ files_table = sg.Table(values=files_values, headings=['Filenames'], col_widths=100, max_col_width= 200, def_col_width=100, num_rows= 10, auto_size_columns=False)
156
+
157
+ #Start&Stop
158
+ start_button =sg.Button('Start', button_color= 'green', disabled= True)
159
+ stop_button = sg.Button('Cancel', button_color= 'red')
160
+
161
+ #DIMENSION SANITY
162
+ sanity_progress = sg.ProgressBar(10, size_px=(500,10))
163
+ sanity_check_button = sg.Button(
164
+ 'Check',
165
+ tooltip= "Will check that all files loaded have the same dimension number and that small fish is able to open them.",
166
+ pad=(10,0))
167
+ sanity_header = sg.Text("Dimension sanity", font=('bold',15), pad=(0,10))
168
+ dimension_number_text = sg.Text("Dimension number : unknown")
169
+
170
+ #Input tab
171
+ input_layout = _input_parameters_layout(
172
+ ask_for_segmentation=True,
173
+ is_3D_stack_preset=False,
174
+ time_stack_preset=False,
175
+ multichannel_preset=False,
176
+ do_dense_regions_deconvolution_preset=False,
177
+ do_clustering_preset=False,
178
+ do_Napari_correction=False,
179
+ do_segmentation_preset=False,
180
+ )
181
+ input_layout += [[sg.Button('Ok')]]
182
+ input_tab = sg.Tab("Input", input_layout)
183
+
184
+ #Maptab
185
+ map_layout = _ask_channel_map_layout(
186
+ shape=(0,1,2,3,4),
187
+ is_3D_stack=True,
188
+ is_time_stack=True,
189
+ multichannel=True,
190
+ )
191
+ last_shape_read = sg.Text("Last shape read : None")
192
+ auto_map = sg.Button("auto-map", disabled=True, pad=(10,0))
193
+ map_layout += [[last_shape_read, auto_map]]
194
+ map_tab = sg.Tab("Map", map_layout)
195
+
196
+ #Segmentation tab
197
+ segmentation_layout = _segmentation_layout(multichannel=True, cytoplasm_model_preset='cyto3')
198
+ segmentation_tab = sg.Tab("Segmentation", segmentation_layout, visible=False)
199
+
200
+ #Detection tab
201
+ detection_layout = _detection_layout(
202
+ is_3D_stack=True,
203
+ is_multichannel=True,
204
+ do_clustering=True,
205
+ do_dense_region_deconvolution=True,
206
+ do_segmentation=True,
207
+ )
208
+
209
+
210
+ detection_tab = sg.Tab("Detection", detection_layout)
211
+
212
+
213
+
214
+ #TABS
215
+ _tab_group = sg.TabGroup([[input_tab, map_tab, segmentation_tab, detection_tab]], enable_events=True)
216
+ tab_group = sg.Column( #Allow the tab to be scrollable
217
+ [[_tab_group]],
218
+ scrollable=True,
219
+ vertical_scroll_only=True,
220
+ pad=(150,5)
221
+ )
222
+ tab_dict= {
223
+ "Input" : input_tab,
224
+ "Segmentation" : segmentation_tab,
225
+ "Detection" : detection_tab,
226
+ "Map" : map_tab,
227
+ }
228
+
229
+ layout = [
230
+ [sg.Text("Batch Processing", font=('bold',20), pad=((300,0),(0,2)))],
231
+ [sg.Text("Select a folder : "), sg.FolderBrowse(initial_folder=os.getcwd(), key='Batch_folder'), sg.Button('Load')],
232
+ [files_table],
233
+ [sanity_header, sanity_check_button, sanity_progress],
234
+ [dimension_number_text],
235
+ [tab_group],
236
+ [sg.Output(size=(100,10), pad=(30,10))],
237
+ [start_button, stop_button],
238
+ ]
239
+
240
+ window = sg.Window("small fish", layout=layout, size= (800,800), auto_size_buttons=True, auto_size_text=True)
241
+ loop = 0
242
+ timeout = 1
243
+ while True :
244
+ loop +=1
245
+ window = window.refresh()
246
+ event, values = window.read(timeout=timeout)
247
+
248
+ #Welcome message
249
+ if loop == 1 :
250
+ timeout = None
251
+ print("Welcome to small fish batch analysis. Please start by loading some files and setting parameters.")
252
+
253
+ batch_folder = values.get('Batch_folder')
254
+ is_multichanel = values.get('multichannel')
255
+ is_3D = values.get('3D stack')
256
+ do_segmentation = values.get('Segmentation')
257
+ do_dense_regions_deconvolution = values.get('Dense regions deconvolution')
258
+ do_clustering = values.get('Cluster computation')
259
+
260
+ if type(batch_folder) != type(None) and event == 'Load':
261
+ if not os.path.isdir(batch_folder) :
262
+ print("Can't open {0}".format(batch_folder))
263
+ else :
264
+ files_values = get_files(batch_folder)
265
+ files_table.update(values=files_values)
266
+
267
+ elif event == 'Check' :
268
+ filename_list = extract_files(files_values)
269
+ last_shape = sanity_check(
270
+ filename_list=filename_list,
271
+ batch_folder=batch_folder,
272
+ window=window,
273
+ progress_bar=sanity_progress
274
+ )
275
+ if isinstance(last_shape,(tuple,list)) :
276
+ dim_number = len(last_shape)
277
+ dimension_number_text.update("Dimension number : {0}".format(dim_number))
278
+ auto_map.update(disabled=False)
279
+ else :
280
+ dimension_number_text.update("Dimension number : unknown")
281
+ auto_map.update(disabled=True)
282
+
283
+ last_shape_read.update("Last shape read : {0}".format(last_shape))
284
+
285
+
286
+ elif event == _tab_group.key or event == 'Ok': #Tab switch in parameters
287
+ update_segmentation_tab(
288
+ tab_elmt=tab_dict.get("Segmentation"),
289
+ do_segmentation=do_segmentation,
290
+ is_multichannel=is_multichanel,
291
+ )
292
+
293
+ update_detection_tab(
294
+ tab_elmt=tab_dict.get("Detection"),
295
+ is_multichannel=is_multichanel,
296
+ is_3D=is_3D,
297
+ do_dense_region_deconvolution=do_dense_regions_deconvolution,
298
+ do_clustering=do_clustering,
299
+ )
300
+
301
+ elif event == 'auto-map' :
302
+ #TODO
303
+ pass
304
+
305
+ # elif event == 'apply' (map) #TODO
306
+
307
+ # elif event == 'check parameters' -> un/lock start #TODO
308
+
309
+ elif event == "Cancel" :
310
+ print(values)
311
+ elif event == None :
312
+ quit()
@@ -7,9 +7,10 @@ from cellpose.core import use_gpu
7
7
  sg.theme('DarkAmber')
8
8
 
9
9
 
10
- def add_header(header_text, layout) :
11
- header = [[sg.Text('\n{0}'.format(header_text), size= (len(header_text),3), font= 'bold 15')]]
12
- return header + layout
10
+ def add_header(header_text) :
11
+ """Returns [elmnt] not layout"""
12
+ header = [sg.Text('\n{0}'.format(header_text), size= (len(header_text),3), font= 'bold 15')]
13
+ return header
13
14
 
14
15
 
15
16
  def pad_right(string, length, pad_char) :
@@ -50,7 +51,7 @@ def parameters_layout(parameters:'list[str]' = [], unit=None, header= None, defa
50
51
  layout[line_id] += [sg.Text('{0}'.format(unit))]
51
52
 
52
53
  if isinstance(header, str) :
53
- layout = add_header(header, layout)
54
+ layout = [add_header(header)] + layout
54
55
  return layout
55
56
 
56
57
  def tuple_layout(opt=None, default_dict={}, unit:dict={}, **tuples) :
@@ -97,7 +98,7 @@ def path_layout(keys= [],look_for_dir = False, header=None, preset=os.getcwd())
97
98
  [sg.Text(pad_right(name, max_length, ' ')), Browse(key= name, initial_folder= preset)] for name in keys
98
99
  ]
99
100
  if isinstance(header, str) :
100
- layout = add_header(header, layout=layout)
101
+ layout = [add_header(header)] + layout
101
102
  return layout
102
103
 
103
104
  def bool_layout(parameters= [], header=None, preset=None) :
@@ -118,10 +119,10 @@ def bool_layout(parameters= [], header=None, preset=None) :
118
119
  [sg.Checkbox(pad_right(name, max_length, ' '), key= name, default=box_preset)] for name, box_preset in zip(parameters,preset)
119
120
  ]
120
121
  if isinstance(header, str) :
121
- layout = add_header(header, layout=layout)
122
+ layout = [add_header(header)] + layout
122
123
  return layout
123
124
 
124
- def combo_layout(values, key, header=None, read_only=True, default_value=None) :
125
+ def combo_elmt(values, key, header=None, read_only=True, default_value=None) :
125
126
  """
126
127
  drop-down list
127
128
  """
@@ -135,7 +136,7 @@ def combo_layout(values, key, header=None, read_only=True, default_value=None) :
135
136
  sg.Combo(values, default_value=default_value, readonly=read_only, key=key)
136
137
  ]
137
138
  if isinstance(header, str) :
138
- layout = add_header(header, layout=layout)
139
+ layout = add_header(header) + layout
139
140
  return layout
140
141
 
141
142
  def radio_layout(values, header=None) :
@@ -148,7 +149,7 @@ def radio_layout(values, header=None) :
148
149
  [sg.Radio(value, group_id= 0) for value in values]
149
150
  ]
150
151
  if isinstance(header, str) :
151
- layout = add_header(header, layout=layout)
152
+ layout = [add_header(header)] + layout
152
153
  return layout
153
154
 
154
155
  def _segmentation_layout(multichannel, cytoplasm_model_preset= 'cyto2', nucleus_model_preset= 'nuclei', cytoplasm_channel_preset=0, nucleus_channel_preset=0, cyto_diameter_preset=30, nucleus_diameter_preset= 30, show_segmentation_preset= False, segment_only_nuclei_preset=False, saving_path_preset=os.getcwd(), filename_preset='cell_segmentation.png',) :
@@ -162,23 +163,145 @@ def _segmentation_layout(multichannel, cytoplasm_model_preset= 'cyto2', nucleus_
162
163
  layout = [[sg.Text("GPU is currently "), sg.Text('ON', text_color= 'green') if USE_GPU else sg.Text('OFF', text_color= 'red')]]
163
164
 
164
165
  #cytoplasm parameters
165
- layout += [add_header("Cell Segmentation", [sg.Text("Choose cellpose model for cytoplasm: \n")]),
166
- [combo_layout(models_list, key='cyto_model_name', default_value= cytoplasm_model_preset)]
166
+ layout += [
167
+ add_header("Cell Segmentation"),
168
+ [sg.Text("Choose cellpose model for cytoplasm: \n")],
169
+ combo_elmt(models_list, key='cyto_model_name', default_value= cytoplasm_model_preset)
167
170
  ]
168
- if multichannel : layout += [parameters_layout(['cytoplasm channel'],default_values= [cytoplasm_channel_preset])]
169
- layout += [parameters_layout(['cytoplasm diameter'], unit= "px", default_values= [cyto_diameter_preset])]
171
+
172
+ if multichannel : layout += parameters_layout(['cytoplasm channel'],default_values= [cytoplasm_channel_preset])
173
+ layout += parameters_layout(['cytoplasm diameter'], unit= "px", default_values= [cyto_diameter_preset])
170
174
  #Nucleus parameters
171
175
  layout += [
172
- add_header("Nucleus segmentation",[sg.Text("Choose cellpose model for nucleus: \n")]),
173
- combo_layout(models_list, key='nucleus_model_name', default_value= nucleus_model_preset)
176
+ add_header("Nucleus segmentation"),
177
+ [sg.Text("Choose cellpose model for nucleus: \n")],
178
+ combo_elmt(models_list, key='nucleus_model_name', default_value= nucleus_model_preset)
174
179
  ]
175
- if multichannel : layout += [parameters_layout(['nucleus channel'], default_values= [nucleus_channel_preset])]
176
- layout += [parameters_layout([ 'nucleus diameter'],unit= "px", default_values= [nucleus_diameter_preset])]
177
- layout += [bool_layout(["Segment only nuclei"], preset=segment_only_nuclei_preset)]
180
+
181
+ if multichannel : layout += parameters_layout(['nucleus channel'], default_values= [nucleus_channel_preset])
182
+ layout += parameters_layout([ 'nucleus diameter'],unit= "px", default_values= [nucleus_diameter_preset])
183
+ layout += bool_layout(["Segment only nuclei"], preset=segment_only_nuclei_preset)
178
184
 
179
185
  #Control plots
180
- layout += [bool_layout(['show segmentation'], header= 'Segmentation plots', preset= show_segmentation_preset)]
181
- layout += [path_layout(['saving path'], look_for_dir=True, preset=saving_path_preset)]
182
- layout += [parameters_layout(['filename'], default_values=[filename_preset], size= 25)]
186
+ layout += bool_layout(['show segmentation'], header= 'Segmentation plots', preset= show_segmentation_preset)
187
+ layout += path_layout(['saving path'], look_for_dir=True, preset=saving_path_preset)
188
+ layout += parameters_layout(['filename'], default_values=[filename_preset], size= 25)
189
+
190
+ return layout
191
+
192
+ def _input_parameters_layout(
193
+ ask_for_segmentation,
194
+ is_3D_stack_preset,
195
+ time_stack_preset,
196
+ multichannel_preset,
197
+ do_dense_regions_deconvolution_preset,
198
+ do_clustering_preset,
199
+ do_segmentation_preset,
200
+ do_Napari_correction
201
+
202
+ ) :
203
+ layout_image_path = path_layout(['image path'], header= "Image")
204
+ layout_image_path += bool_layout(['3D stack', 'multichannel'], preset= [is_3D_stack_preset, time_stack_preset, multichannel_preset])
205
+
206
+ if ask_for_segmentation :
207
+ layout_image_path += bool_layout(
208
+ ['Dense regions deconvolution', 'Cluster computation', 'Segmentation', 'Napari correction'],
209
+ preset= [do_dense_regions_deconvolution_preset, do_clustering_preset, do_segmentation_preset, do_Napari_correction],
210
+ header= "Pipeline settings")
211
+ else :
212
+ layout_image_path += bool_layout(
213
+ ['Dense regions deconvolution', 'Cluster computation', 'Napari correction'],
214
+ preset= [do_dense_regions_deconvolution_preset, do_clustering_preset, do_Napari_correction],
215
+ header= "Pipeline settings")
216
+
217
+ return layout_image_path
218
+
219
+ def _detection_layout(
220
+ is_3D_stack,
221
+ is_multichannel,
222
+ do_dense_region_deconvolution,
223
+ do_clustering,
224
+ do_segmentation,
225
+ segmentation_done=False,
226
+ default_dict={},
227
+ ) :
228
+ if is_3D_stack : dim = 3
229
+ else : dim = 2
230
+
231
+ #Detection
232
+ detection_parameters = ['threshold', 'threshold penalty']
233
+ default_detection = [default_dict.setdefault('threshold',''), default_dict.setdefault('threshold penalty', '1')]
234
+ opt= [True, True]
235
+ if is_multichannel :
236
+ detection_parameters += ['channel to compute']
237
+ opt += [False]
238
+ default_detection += [default_dict.setdefault('channel to compute', '')]
239
+
240
+ layout = [[sg.Text("Green parameters", text_color= 'green'), sg.Text(" are optional parameters.")]]
241
+ layout += parameters_layout(detection_parameters, header= 'Detection', opt=opt, default_values=default_detection)
242
+
243
+ if dim == 2 : tuple_shape = ('y','x')
244
+ else : tuple_shape = ('z','y','x')
245
+ opt = {'voxel_size' : False, 'spot_size' : False, 'log_kernel_size' : True, 'minimum_distance' : True}
246
+ unit = {'voxel_size' : 'nm', 'minimum_distance' : 'nm', 'spot_size' : 'radius(nm)', 'log_kernel_size' : 'px'}
247
+
248
+ layout += tuple_layout(opt=opt, unit=unit, default_dict=default_dict, voxel_size= tuple_shape, spot_size= tuple_shape, log_kernel_size= tuple_shape, minimum_distance= tuple_shape)
249
+
250
+ #Deconvolution
251
+ if do_dense_region_deconvolution :
252
+ default_dense_regions_deconvolution = [default_dict.setdefault('alpha',0.5), default_dict.setdefault('beta',1)]
253
+ layout += parameters_layout(['alpha', 'beta',], default_values= default_dense_regions_deconvolution, header= 'Dense regions deconvolution')
254
+ layout += parameters_layout(['gamma'], unit= 'px', default_values= [default_dict.setdefault('gamma',5)])
255
+ layout += tuple_layout(opt= {"deconvolution_kernel" : True}, unit= {"deconvolution_kernel" : 'px'}, default_dict=default_dict, deconvolution_kernel = tuple_shape)
256
+
257
+ #Clustering
258
+ if do_clustering :
259
+ layout += parameters_layout(['cluster size'], unit="radius(nm)", default_values=[default_dict.setdefault('cluster size',400)])
260
+ layout += parameters_layout(['min number of spots'], default_values=[default_dict.setdefault('min number of spots', 5)])
261
+
262
+ if (do_segmentation and is_multichannel) or (is_multichannel and segmentation_done):
263
+ default_segmentation = [default_dict.setdefault('nucleus channel signal', default_dict.setdefault('nucleus channel',0))]
264
+ layout += parameters_layout(['nucleus channel signal'], default_values=default_segmentation) + [[sg.Text(" channel from which signal will be measured for nucleus features.")]]
265
+
266
+ layout += bool_layout(['Interactive threshold selector'], preset=[False])
267
+ layout += path_layout(
268
+ keys=['spots_extraction_folder'],
269
+ look_for_dir=True,
270
+ header= "Individual spot extraction",
271
+ preset= default_dict.setdefault('spots_extraction_folder', '')
272
+ )
273
+ layout += parameters_layout(
274
+ parameters=['spots_filename'],
275
+ default_values=[default_dict.setdefault('spots_filename','spots_extraction')],
276
+ size= 13
277
+ )
278
+ layout += bool_layout(
279
+ parameters= ['do_spots_csv', 'do_spots_excel', 'do_spots_feather'],
280
+ preset= [default_dict.setdefault('do_spots_csv',False), default_dict.setdefault('do_spots_excel',False),default_dict.setdefault('do_spots_feather',False)]
281
+ )
282
+
283
+ return layout
284
+
285
+ def _ask_channel_map_layout(
286
+ shape,
287
+ is_3D_stack,
288
+ multichannel,
289
+ is_time_stack,
290
+ preset_map={},
291
+ ) :
292
+
293
+ x = preset_map.setdefault('x',0)
294
+ y = preset_map.setdefault('y',0)
295
+ z = preset_map.setdefault('z',0)
296
+ c = preset_map.setdefault('c',0)
297
+ t = preset_map.setdefault('t',0)
298
+
299
+ layout = [
300
+ add_header("Dimensions mapping"), [sg.Text("Image shape : {0}".format(shape))]
301
+ ]
302
+ layout += parameters_layout(['x','y'], default_values=[x,y])
303
+ if is_3D_stack : layout += parameters_layout(['z'], default_values=[z])
304
+ if multichannel : layout += parameters_layout(['c'], default_values=[c])
305
+ if is_time_stack : layout += parameters_layout(['t'], default_values=[t])
183
306
 
184
307
  return layout
@@ -2,18 +2,24 @@ import PySimpleGUI as sg
2
2
  import pandas as pd
3
3
  import os
4
4
  import numpy as np
5
- from .layout import path_layout, parameters_layout, bool_layout, tuple_layout, combo_layout, add_header, path_layout
5
+ from .layout import path_layout, parameters_layout, bool_layout, tuple_layout, combo_elmt, add_header, path_layout
6
6
  from ..interface import open_image, check_format, FormatError
7
7
  from .help_module import ask_help
8
8
 
9
- def prompt(layout, add_ok_cancel=True, timeout=None, timeout_key='TIMEOUT_KEY') :
9
+ def prompt(layout, add_ok_cancel=True, timeout=None, timeout_key='TIMEOUT_KEY', add_scrollbar=True) :
10
10
  """
11
11
  Default event : 'Ok', 'Cancel'
12
12
  """
13
13
  if add_ok_cancel : layout += [[sg.Button('Ok'), sg.Button('Cancel')]]
14
14
 
15
+ if add_scrollbar :
16
+ size = (400,500)
17
+ col_elmt = sg.Column(layout, scrollable=True, vertical_scroll_only=True, size=size)
18
+ layout = [[col_elmt]]
19
+ else :
20
+ size = (None,None)
15
21
 
16
- window = sg.Window('small fish', layout=layout, margins=(10,10))
22
+ window = sg.Window('small fish', layout=layout, margins=(10,10), size=size, resizable=True)
17
23
  event, values = window.read(timeout=timeout, timeout_key=timeout_key)
18
24
  if event == None :
19
25
  window.close()
@@ -26,12 +32,19 @@ def prompt(layout, add_ok_cancel=True, timeout=None, timeout_key='TIMEOUT_KEY')
26
32
  window.close()
27
33
  return event, values
28
34
 
29
- def prompt_with_help(layout, help =None) :
35
+ def prompt_with_help(layout, help =None, add_scrollbar=True) :
30
36
  layout += [[]]
31
37
  layout += [[sg.Button('Help')]]
32
38
  layout += [[sg.Button('Ok'), sg.Button('Cancel')]]
33
39
 
34
- window = sg.Window('small fish', layout=layout)
40
+ if add_scrollbar :
41
+ size = (400,500)
42
+ col_elmt = sg.Column(layout, scrollable=True, vertical_scroll_only=True, size=size)
43
+ layout = [[col_elmt]]
44
+ else :
45
+ size = (None,None)
46
+
47
+ window = sg.Window('small fish', layout=layout, size=size, resizable=True)
35
48
  while True :
36
49
  event, values = window.read()
37
50
  if event == None :
@@ -79,7 +92,7 @@ def input_image_prompt(
79
92
  else :
80
93
  layout_image_path += bool_layout(['Dense regions deconvolution', 'Cluster computation', 'Napari correction'], preset= [do_dense_regions_deconvolution_preset, do_clustering_preset, do_Napari_correction], header= "Pipeline settings")
81
94
 
82
- event, values = prompt_with_help(layout_image_path, help= 'general')
95
+ event, values = prompt_with_help(layout_image_path, help= 'general', add_scrollbar=False)
83
96
 
84
97
  if event == 'Cancel' :
85
98
  return None
@@ -227,22 +240,6 @@ def detection_parameters_promt(is_3D_stack, is_multichannel, do_dense_region_dec
227
240
  else : values['dim'] = 2
228
241
  return values
229
242
 
230
- def post_analysis_prompt() :
231
- answer = events(['Save results','add_detection', 'colocalisation', 'open results in napari'])
232
-
233
- return answer
234
-
235
- def events(event_list) :
236
- """
237
- Return event chose from user
238
- """
239
-
240
- layout = [
241
- [sg.Button(event) for event in event_list]
242
- ]
243
-
244
- event, values = prompt(layout, add_ok_cancel= False)
245
- return event
246
243
 
247
244
  def ask_replace_file(filename:str) :
248
245
  layout = [
@@ -354,4 +351,5 @@ def ask_cancel_detection() :
354
351
  if event == 'No' :
355
352
  return False
356
353
  else :
357
- return True
354
+ return True
355
+
@@ -1,4 +1,5 @@
1
1
  import PySimpleGUI as sg
2
- import small_fish.gui.prompts as p
2
+ import small_fish_gui.gui.batch as prompt
3
3
 
4
- p.hub_prompt([{},{},{"cell_number" : 100, "spot_number" : 1000},{},{},{'cell_number' : 10},{}])
4
+ prompt.batch_promp()
5
+ print('end')
@@ -199,19 +199,20 @@ def threshold_selection(
199
199
  """
200
200
 
201
201
  Viewer = napari.Viewer(title= "Small fish - Threshold selector", ndisplay=2, show=True)
202
+ scale = compute_anisotropy_coef(voxel_size)
202
203
  Viewer.add_image(
203
204
  data= image,
204
205
  contrast_limits= [image.min(), image.max()],
205
206
  name= "raw signal",
206
207
  colormap= 'green',
207
- scale= voxel_size,
208
+ scale= scale,
208
209
  blending= 'additive'
209
210
  )
210
211
  Viewer.add_image(
211
212
  data= filtered_image,
212
213
  contrast_limits= [filtered_image.min(), filtered_image.max()],
213
214
  colormap= 'gray',
214
- scale=voxel_size,
215
+ scale=scale,
215
216
  blending='additive'
216
217
  )
217
218
 
@@ -122,14 +122,13 @@ def _auto_map_channels(image: np.ndarray, is_3D_stack, is_time_stack, multichann
122
122
  return map
123
123
 
124
124
  def _ask_channel_map(shape, is_3D_stack, is_time_stack, multichannel, preset_map: dict= {}) :
125
- map = preset_map
126
125
  while True :
127
126
  relaunch = False
128
- x = map.setdefault('x',0)
129
- y = map.setdefault('y',0)
130
- z = map.setdefault('z',0)
131
- c = map.setdefault('c',0)
132
- t = map.setdefault('t',0)
127
+ x = preset_map.setdefault('x',0)
128
+ y = preset_map.setdefault('y',0)
129
+ z = preset_map.setdefault('z',0)
130
+ c = preset_map.setdefault('c',0)
131
+ t = preset_map.setdefault('t',0)
133
132
 
134
133
  layout = [
135
134
  add_header("Dimensions mapping", [sg.Text("Image shape : {0}".format(shape))])
@@ -139,12 +138,12 @@ def _ask_channel_map(shape, is_3D_stack, is_time_stack, multichannel, preset_map
139
138
  if multichannel : layout += [parameters_layout(['c'], default_values=[c])]
140
139
  if is_time_stack : layout += [parameters_layout(['t'], default_values=[t])]
141
140
 
142
- event, map = prompt_with_help(layout,help= 'mapping')
141
+ event, preset_map = prompt_with_help(layout,help= 'mapping', add_scrollbar=False)
143
142
  if event == 'Cancel' : quit()
144
143
 
145
144
  #Check integrity
146
- channels_values = np.array(list(map.values()), dtype= int)
147
- total_channels = len(map)
145
+ channels_values = np.array(list(preset_map.values()), dtype= int)
146
+ total_channels = len(preset_map)
148
147
  unique_channel = len(np.unique(channels_values))
149
148
  if total_channels != unique_channel :
150
149
  sg.popup("{0} channel(s) are not uniquely mapped.".format(total_channels - unique_channel))
@@ -154,7 +153,7 @@ def _ask_channel_map(shape, is_3D_stack, is_time_stack, multichannel, preset_map
154
153
  relaunch= True
155
154
  if not relaunch : break
156
155
 
157
- return map
156
+ return preset_map
158
157
 
159
158
  def _show_mapping(shape, map, is_3D_stack, is_time_stack, multichannel) :
160
159
  layout = [
@@ -166,7 +165,7 @@ def _show_mapping(shape, map, is_3D_stack, is_time_stack, multichannel) :
166
165
  [sg.Button('Change mapping')]
167
166
  ]
168
167
 
169
- event, values = prompt_with_help(layout, help='mapping')
168
+ event, values = prompt_with_help(layout, help='mapping', add_scrollbar=False)
170
169
 
171
170
  if event == 'Ok' :
172
171
  return map
@@ -170,7 +170,7 @@ def launch_segmentation(image: np.ndarray, user_parameters: dict) :
170
170
  [sg.Button("Yes"), sg.Button("No")]
171
171
  ]
172
172
 
173
- event, values = prompt(layout=layout, add_ok_cancel=False)
173
+ event, values = prompt(layout=layout, add_ok_cancel=False, add_scrollbar=False)
174
174
  if event == "No" :
175
175
  continue
176
176
 
@@ -8,6 +8,7 @@ from ._signaltonoise import compute_snr_spots
8
8
  from ._napari_wrapper import correct_spots, _update_clusters, threshold_selection
9
9
  from ..gui import add_default_loading
10
10
  from ..gui import detection_parameters_promt, input_image_prompt
11
+ from ..utils import compute_anisotropy_coef
11
12
  from .spots import compute_Spots
12
13
  from magicgui import magicgui
13
14
  from napari.layers import Image, Points
@@ -766,9 +767,11 @@ def _create_threshold_slider(
766
767
  threshold=threshold
767
768
  )[0]
768
769
 
770
+ scale = compute_anisotropy_coef(voxel_size)
771
+
769
772
  layer_args = {
770
773
  'size': 5,
771
- 'scale' : voxel_size,
774
+ 'scale' : scale,
772
775
  'face_color' : 'transparent',
773
776
  'edge_color' : 'blue',
774
777
  'symbol' : 'ring',
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: small_fish_gui
3
- Version: 1.3.3
3
+ Version: 1.3.5
4
4
  Summary: Small Fish is a python application for the analysis of smFish images. It provides a ready to use graphical interface to combine famous python packages for cell analysis without any need for coding.
5
5
  Project-URL: Homepage, https://github.com/2Echoes/small_fish
6
6
  Project-URL: Issues, https://github.com/2Echoes/small_fish/issues
@@ -1,21 +1,22 @@
1
1
  small_fish_gui/LICENSE,sha256=-iFy8VGBYs5VsHglKpk4D-hxqQ2jMJaqmfq_ulIzDks,1303
2
2
  small_fish_gui/README.md,sha256=2c_homYDJXX6VsBiEs5obhBh3HpcTSMdyjLo-35WzE4,4062
3
3
  small_fish_gui/Segmentation example.jpg,sha256=opfiSbjmfF6z8kBs08sg_FNR2Om0AcMPU5sSwSLHdoQ,215038
4
- small_fish_gui/__init__.py,sha256=-EmCkHocthrnAlwtuVXQuTc3AOcXiAOBYAK3wVmqe8w,1941
4
+ small_fish_gui/__init__.py,sha256=EnnsjHc4PvOZYdCT6JvKMiKDF_I6MMyBvj9TIDeOWco,1941
5
5
  small_fish_gui/__main__.py,sha256=EzSCoJ7jpSdK-QbzUwQLGZeQWjybNeq8VnCBucA8MZw,1372
6
6
  small_fish_gui/napari_detection_example.png,sha256=l5EZlrbXemLiGqb5inSVsD6Kko1Opz528-go-fBfrw8,977350
7
7
  small_fish_gui/requirements.txt,sha256=9OMfUAnLdHevq6w_fVoDmVmkSMJeFofkOK_86_fu9C0,321
8
8
  small_fish_gui/utils.py,sha256=tSoMb8N69WdKTtMItPb1DYZiIAz1mjI26BCKJAi6vuc,1798
9
9
  small_fish_gui/.github/workflows/python-publish.yml,sha256=5Ltnuhw9TevhzndlBmdUgYMnS73xEAxSyd1u8DHdn5s,1084
10
- small_fish_gui/gui/__init__.py,sha256=178HC3t2z4EnP0iBnMcaP_pyh5xHwOkEE6p3WJwBQeU,911
10
+ small_fish_gui/gui/__init__.py,sha256=xQ_BfYcnQmKZtx_0leO4OmbkLNLv49ZPqEu_UXMgmDc,867
11
11
  small_fish_gui/gui/animation.py,sha256=6_Y15_NzJ_TYBYseu3sSKaVkYRp2UCsVClAWOk3dESY,714
12
+ small_fish_gui/gui/batch.py,sha256=aCd3UvzH6ljaWMdjQzOHA_D6QttWYIuv9iDK8iISSdg,10831
12
13
  small_fish_gui/gui/general_help_screenshot.png,sha256=X4E6Td5f04K-pBUPDaBJRAE3D5b8fuEdiAUKhkIDr-0,54210
13
14
  small_fish_gui/gui/help_module.py,sha256=PmgkkDs7bZ2-po83A_PK9uldQcHjehYmqre21nYb6DQ,9600
14
- small_fish_gui/gui/layout.py,sha256=_ErOS2IUejeUuPLkDmPB3FzLkoHOWR-Iaxz-aUeETks,7695
15
+ small_fish_gui/gui/layout.py,sha256=iLlSW40yBKoi4oW0TaWqbMV6ykr3P0Ar2ybf6X7VW3g,13139
15
16
  small_fish_gui/gui/mapping_help_screenshot.png,sha256=HcuRh5TYciUogUasza5vZ_QSshaiHsskQK23mh9vQS8,34735
16
- small_fish_gui/gui/prompts.py,sha256=NAR7qjKwybiZZ2caO_lB8_CEttG8i4lHdt9lxjh6ESM,13160
17
+ small_fish_gui/gui/prompts.py,sha256=KKDeinZ2qOD-zNDTUndmnff5hDrcwt0B4blkzozr1Wc,13319
17
18
  small_fish_gui/gui/segmentation_help_screenshot.png,sha256=rbSgIydT0gZtfMh1qk4mdMbEIyCaakvHmxa2eOrLwO0,118944
18
- small_fish_gui/gui/test.py,sha256=Pf-GW9AgW-0VL1mFbYtqRvPAaa8DgwCThv2dDUHCcmU,156
19
+ small_fish_gui/gui/test.py,sha256=6P8tnND4wHOE1bF7BGx26wWgE-WjaSCH9Mej7ZzauSg,101
19
20
  small_fish_gui/interface/__init__.py,sha256=PB86R4Y9kV80aGZ-vP0ZW2KeaCwGbBbCtFCmbN2yl28,275
20
21
  small_fish_gui/interface/image.py,sha256=X1L7S5svxUwdoDcI3QM1PbN-c4Nz5w30hixq3IgqSn8,1130
21
22
  small_fish_gui/interface/output.py,sha256=dyhpO1YrRCIbQYpqU_52E1DTNPf0wdktd--CB15iT3k,1712
@@ -23,16 +24,16 @@ small_fish_gui/interface/parameters.py,sha256=lUugD-4W2TZyJF3TH1q70TlktEYhhPtcPC
23
24
  small_fish_gui/interface/testing.py,sha256=MY5-GcPOUHagcrwR8A7QOjAmjZIDVC8Wz3NibLe3KQw,321
24
25
  small_fish_gui/pipeline/_colocalisation.py,sha256=peBw2Qz5m6wSejDkDz240UgvWl8ohNelrnmEgznbEsw,9635
25
26
  small_fish_gui/pipeline/_custom_errors.py,sha256=tQ-AUhgzIFpK30AZiQQrtHCHyGVRDdAoIjzL0Fk-1pA,43
26
- small_fish_gui/pipeline/_napari_wrapper.py,sha256=z5WEOlOKP-vhdawjPcfUAv3scGCilGu0UCHuSf6GCQY,9252
27
- small_fish_gui/pipeline/_preprocess.py,sha256=szNoav19Xo3USmiUTjcFgkMn9QK53ZOydbLV5aMFLws,10676
28
- small_fish_gui/pipeline/_segmentation.py,sha256=M2bQzgzw7Zt_DBeM3qvI0V4Pn0HFLwj0l8yV8M5aToo,12977
27
+ small_fish_gui/pipeline/_napari_wrapper.py,sha256=_FkkY7IBKn1QgNvyea-x6XPjw_AwsLEVzdRKzg65oCE,9290
28
+ small_fish_gui/pipeline/_preprocess.py,sha256=cbgXUx8yn3Wi_R7hFy64VuEXbbTbYw9p0qKG9cigyaM,10760
29
+ small_fish_gui/pipeline/_segmentation.py,sha256=abltW2dKHarpDvM45TgkSJ4OzQV-b63VnA1PK4w1C_g,12998
29
30
  small_fish_gui/pipeline/_signaltonoise.py,sha256=7A9t7xu7zghI6cr201Ldm-LjJ5NOuP56VSeJ8KIzcUo,8497
30
31
  small_fish_gui/pipeline/actions.py,sha256=EIGIOlwJ_DADX1NcLWwrTP_AidDX-4f4ggZV0gkIb58,7988
31
- small_fish_gui/pipeline/detection.py,sha256=MLc8Z1xniuAghn3D0LPmH64jVRobhLj05HW0nI7BDmw,31983
32
+ small_fish_gui/pipeline/detection.py,sha256=yMD9PGLJHH96PpXQRWbB5gv1EwQhtMWQ1a95g_0WU0E,32075
32
33
  small_fish_gui/pipeline/main.py,sha256=AAW-zK3b7Ece9cdHn9y6QG8lTa1HXG-8JtnvJ3m0HwA,3149
33
34
  small_fish_gui/pipeline/spots.py,sha256=yHvqf1eD25UltELpzcouYXhLkxiXI_mOL1ANSzXK5pw,1907
34
35
  small_fish_gui/pipeline/test.py,sha256=w4ZMGDmUDXxVgWTlZ2TKw19W8q5gcE9gLMKe0SWnRrw,2827
35
- small_fish_gui-1.3.3.dist-info/METADATA,sha256=hEW1l9WpymJMLiWzuwbcDSZ5K653ZQNle5kqilyDSJ8,2567
36
- small_fish_gui-1.3.3.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
37
- small_fish_gui-1.3.3.dist-info/licenses/LICENSE,sha256=-iFy8VGBYs5VsHglKpk4D-hxqQ2jMJaqmfq_ulIzDks,1303
38
- small_fish_gui-1.3.3.dist-info/RECORD,,
36
+ small_fish_gui-1.3.5.dist-info/METADATA,sha256=K4HY1GO44LtO6PXUZmK3_jNNFmDXMsS9LpWa3l8WDB8,2567
37
+ small_fish_gui-1.3.5.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
38
+ small_fish_gui-1.3.5.dist-info/licenses/LICENSE,sha256=-iFy8VGBYs5VsHglKpk4D-hxqQ2jMJaqmfq_ulIzDks,1303
39
+ small_fish_gui-1.3.5.dist-info/RECORD,,