celldetective 1.4.2__py3-none-any.whl → 1.5.0b0__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 (151) hide show
  1. celldetective/__init__.py +25 -0
  2. celldetective/__main__.py +62 -43
  3. celldetective/_version.py +1 -1
  4. celldetective/extra_properties.py +477 -399
  5. celldetective/filters.py +192 -97
  6. celldetective/gui/InitWindow.py +541 -411
  7. celldetective/gui/__init__.py +0 -15
  8. celldetective/gui/about.py +44 -39
  9. celldetective/gui/analyze_block.py +120 -84
  10. celldetective/gui/base/__init__.py +0 -0
  11. celldetective/gui/base/channel_norm_generator.py +335 -0
  12. celldetective/gui/base/components.py +249 -0
  13. celldetective/gui/base/feature_choice.py +92 -0
  14. celldetective/gui/base/figure_canvas.py +52 -0
  15. celldetective/gui/base/list_widget.py +133 -0
  16. celldetective/gui/{styles.py → base/styles.py} +92 -36
  17. celldetective/gui/base/utils.py +33 -0
  18. celldetective/gui/base_annotator.py +900 -767
  19. celldetective/gui/classifier_widget.py +6 -22
  20. celldetective/gui/configure_new_exp.py +777 -671
  21. celldetective/gui/control_panel.py +635 -524
  22. celldetective/gui/dynamic_progress.py +449 -0
  23. celldetective/gui/event_annotator.py +2023 -1662
  24. celldetective/gui/generic_signal_plot.py +1292 -944
  25. celldetective/gui/gui_utils.py +899 -1289
  26. celldetective/gui/interactions_block.py +658 -0
  27. celldetective/gui/interactive_timeseries_viewer.py +447 -0
  28. celldetective/gui/json_readers.py +48 -15
  29. celldetective/gui/layouts/__init__.py +5 -0
  30. celldetective/gui/layouts/background_model_free_layout.py +537 -0
  31. celldetective/gui/layouts/channel_offset_layout.py +134 -0
  32. celldetective/gui/layouts/local_correction_layout.py +91 -0
  33. celldetective/gui/layouts/model_fit_layout.py +372 -0
  34. celldetective/gui/layouts/operation_layout.py +68 -0
  35. celldetective/gui/layouts/protocol_designer_layout.py +96 -0
  36. celldetective/gui/pair_event_annotator.py +3130 -2435
  37. celldetective/gui/plot_measurements.py +586 -267
  38. celldetective/gui/plot_signals_ui.py +724 -506
  39. celldetective/gui/preprocessing_block.py +395 -0
  40. celldetective/gui/process_block.py +1678 -1831
  41. celldetective/gui/seg_model_loader.py +580 -473
  42. celldetective/gui/settings/__init__.py +0 -7
  43. celldetective/gui/settings/_cellpose_model_params.py +181 -0
  44. celldetective/gui/settings/_event_detection_model_params.py +95 -0
  45. celldetective/gui/settings/_segmentation_model_params.py +159 -0
  46. celldetective/gui/settings/_settings_base.py +77 -65
  47. celldetective/gui/settings/_settings_event_model_training.py +752 -526
  48. celldetective/gui/settings/_settings_measurements.py +1133 -964
  49. celldetective/gui/settings/_settings_neighborhood.py +574 -488
  50. celldetective/gui/settings/_settings_segmentation_model_training.py +779 -564
  51. celldetective/gui/settings/_settings_signal_annotator.py +329 -305
  52. celldetective/gui/settings/_settings_tracking.py +1304 -1094
  53. celldetective/gui/settings/_stardist_model_params.py +98 -0
  54. celldetective/gui/survival_ui.py +422 -312
  55. celldetective/gui/tableUI.py +1665 -1701
  56. celldetective/gui/table_ops/_maths.py +295 -0
  57. celldetective/gui/table_ops/_merge_groups.py +140 -0
  58. celldetective/gui/table_ops/_merge_one_hot.py +95 -0
  59. celldetective/gui/table_ops/_query_table.py +43 -0
  60. celldetective/gui/table_ops/_rename_col.py +44 -0
  61. celldetective/gui/thresholds_gui.py +382 -179
  62. celldetective/gui/viewers/__init__.py +0 -0
  63. celldetective/gui/viewers/base_viewer.py +700 -0
  64. celldetective/gui/viewers/channel_offset_viewer.py +331 -0
  65. celldetective/gui/viewers/contour_viewer.py +394 -0
  66. celldetective/gui/viewers/size_viewer.py +153 -0
  67. celldetective/gui/viewers/spot_detection_viewer.py +341 -0
  68. celldetective/gui/viewers/threshold_viewer.py +309 -0
  69. celldetective/gui/workers.py +304 -126
  70. celldetective/log_manager.py +92 -0
  71. celldetective/measure.py +1895 -1478
  72. celldetective/napari/__init__.py +0 -0
  73. celldetective/napari/utils.py +1025 -0
  74. celldetective/neighborhood.py +1914 -1448
  75. celldetective/preprocessing.py +1620 -1220
  76. celldetective/processes/__init__.py +0 -0
  77. celldetective/processes/background_correction.py +271 -0
  78. celldetective/processes/compute_neighborhood.py +894 -0
  79. celldetective/processes/detect_events.py +246 -0
  80. celldetective/processes/measure_cells.py +565 -0
  81. celldetective/processes/segment_cells.py +760 -0
  82. celldetective/processes/track_cells.py +435 -0
  83. celldetective/processes/train_segmentation_model.py +694 -0
  84. celldetective/processes/train_signal_model.py +265 -0
  85. celldetective/processes/unified_process.py +292 -0
  86. celldetective/regionprops/_regionprops.py +358 -317
  87. celldetective/relative_measurements.py +987 -710
  88. celldetective/scripts/measure_cells.py +313 -212
  89. celldetective/scripts/measure_relative.py +90 -46
  90. celldetective/scripts/segment_cells.py +165 -104
  91. celldetective/scripts/segment_cells_thresholds.py +96 -68
  92. celldetective/scripts/track_cells.py +198 -149
  93. celldetective/scripts/train_segmentation_model.py +324 -201
  94. celldetective/scripts/train_signal_model.py +87 -45
  95. celldetective/segmentation.py +844 -749
  96. celldetective/signals.py +3514 -2861
  97. celldetective/tracking.py +30 -15
  98. celldetective/utils/__init__.py +0 -0
  99. celldetective/utils/cellpose_utils/__init__.py +133 -0
  100. celldetective/utils/color_mappings.py +42 -0
  101. celldetective/utils/data_cleaning.py +630 -0
  102. celldetective/utils/data_loaders.py +450 -0
  103. celldetective/utils/dataset_helpers.py +207 -0
  104. celldetective/utils/downloaders.py +197 -0
  105. celldetective/utils/event_detection/__init__.py +8 -0
  106. celldetective/utils/experiment.py +1782 -0
  107. celldetective/utils/image_augmenters.py +308 -0
  108. celldetective/utils/image_cleaning.py +74 -0
  109. celldetective/utils/image_loaders.py +926 -0
  110. celldetective/utils/image_transforms.py +335 -0
  111. celldetective/utils/io.py +62 -0
  112. celldetective/utils/mask_cleaning.py +348 -0
  113. celldetective/utils/mask_transforms.py +5 -0
  114. celldetective/utils/masks.py +184 -0
  115. celldetective/utils/maths.py +351 -0
  116. celldetective/utils/model_getters.py +325 -0
  117. celldetective/utils/model_loaders.py +296 -0
  118. celldetective/utils/normalization.py +380 -0
  119. celldetective/utils/parsing.py +465 -0
  120. celldetective/utils/plots/__init__.py +0 -0
  121. celldetective/utils/plots/regression.py +53 -0
  122. celldetective/utils/resources.py +34 -0
  123. celldetective/utils/stardist_utils/__init__.py +104 -0
  124. celldetective/utils/stats.py +90 -0
  125. celldetective/utils/types.py +21 -0
  126. {celldetective-1.4.2.dist-info → celldetective-1.5.0b0.dist-info}/METADATA +1 -1
  127. celldetective-1.5.0b0.dist-info/RECORD +187 -0
  128. {celldetective-1.4.2.dist-info → celldetective-1.5.0b0.dist-info}/WHEEL +1 -1
  129. tests/gui/test_new_project.py +129 -117
  130. tests/gui/test_project.py +127 -79
  131. tests/test_filters.py +39 -15
  132. tests/test_notebooks.py +8 -0
  133. tests/test_tracking.py +232 -13
  134. tests/test_utils.py +123 -77
  135. celldetective/gui/base_components.py +0 -23
  136. celldetective/gui/layouts.py +0 -1602
  137. celldetective/gui/processes/compute_neighborhood.py +0 -594
  138. celldetective/gui/processes/measure_cells.py +0 -360
  139. celldetective/gui/processes/segment_cells.py +0 -499
  140. celldetective/gui/processes/track_cells.py +0 -303
  141. celldetective/gui/processes/train_segmentation_model.py +0 -270
  142. celldetective/gui/processes/train_signal_model.py +0 -108
  143. celldetective/gui/table_ops/merge_groups.py +0 -118
  144. celldetective/gui/viewers.py +0 -1354
  145. celldetective/io.py +0 -3663
  146. celldetective/utils.py +0 -3108
  147. celldetective-1.4.2.dist-info/RECORD +0 -123
  148. /celldetective/{gui/processes → processes}/downloader.py +0 -0
  149. {celldetective-1.4.2.dist-info → celldetective-1.5.0b0.dist-info}/entry_points.txt +0 -0
  150. {celldetective-1.4.2.dist-info → celldetective-1.5.0b0.dist-info}/licenses/LICENSE +0 -0
  151. {celldetective-1.4.2.dist-info → celldetective-1.5.0b0.dist-info}/top_level.txt +0 -0
@@ -1,360 +0,0 @@
1
- from multiprocessing import Process
2
- import time
3
- import datetime
4
- import os
5
- import json
6
- from pathlib import Path, PurePath
7
-
8
- from celldetective.io import auto_load_number_of_frames, load_frames, locate_labels
9
- from celldetective.utils import extract_experiment_channels, config_section_to_dict, _get_img_num_per_channel
10
- from celldetective.utils import remove_trajectory_measurements, _extract_coordinates_from_features, _remove_invalid_cols
11
- from celldetective.measure import drop_tonal_features, measure_features, measure_isotropic_intensity, measure_radial_distance_to_center, center_of_mass_to_abs_coordinates
12
-
13
- from glob import glob
14
- from tqdm import tqdm
15
- import numpy as np
16
- import concurrent.futures
17
- import pandas as pd
18
- from natsort import natsorted
19
- from art import tprint
20
- from typing import Optional, Union
21
-
22
-
23
- class MeasurementProcess(Process):
24
-
25
- pos: Optional[Union[str,Path]] = None
26
- mode: Optional[str] = None
27
- n_threads: int = 1
28
-
29
- def __init__(self, queue=None, process_args=None):
30
-
31
- super().__init__()
32
-
33
- self.queue = queue
34
-
35
- if process_args is not None:
36
- for key, value in process_args.items():
37
- setattr(self, key, value)
38
-
39
- self.column_labels = {'track': "TRACK_ID", 'time': 'FRAME', 'x': 'POSITION_X', 'y': 'POSITION_Y'}
40
-
41
- tprint("Measure")
42
-
43
- # Experiment
44
- self.prepare_folders()
45
-
46
- self.locate_experiment_config()
47
- self.extract_experiment_parameters()
48
- self.read_measurement_instructions()
49
- self.detect_movie_and_labels()
50
- self.detect_tracks()
51
- self.detect_channels()
52
-
53
- self.check_possible_measurements()
54
-
55
- self.write_log()
56
-
57
- self.sum_done = 0
58
- self.t0 = time.time()
59
-
60
- def check_possible_measurements(self):
61
-
62
- if (self.file is None) or (self.intensity_measurement_radii is None):
63
- self.do_iso_intensities = False
64
- print('Either no image, no positions or no radii were provided... Isotropic intensities will not be computed...')
65
- else:
66
- self.do_iso_intensities = True
67
-
68
- if self.label_path is None:
69
- self.do_features = False
70
- print('No labels were provided... Features will not be computed...')
71
- else:
72
- self.do_features = True
73
-
74
- if self.trajectories is None:
75
- print('Use features as a substitute for the trajectory table.')
76
- if 'label' not in self.features:
77
- self.features.append('label')
78
-
79
-
80
- def read_measurement_instructions(self):
81
-
82
- print('Looking for measurement instruction file...')
83
- instr_path = PurePath(self.exp_dir,Path(f"{self.instruction_file}"))
84
- if os.path.exists(instr_path):
85
- with open(instr_path, 'r') as f:
86
- self.instructions = json.load(f)
87
- print(f"Measurement instruction file successfully loaded...")
88
- print(f"Instructions: {self.instructions}...")
89
-
90
- if 'background_correction' in self.instructions:
91
- self.background_correction = self.instructions['background_correction']
92
- else:
93
- self.background_correction = None
94
-
95
- if 'features' in self.instructions:
96
- self.features = self.instructions['features']
97
- else:
98
- self.features = None
99
-
100
- if 'border_distances' in self.instructions:
101
- self.border_distances = self.instructions['border_distances']
102
- else:
103
- self.border_distances = None
104
-
105
- if 'spot_detection' in self.instructions:
106
- self.spot_detection = self.instructions['spot_detection']
107
- else:
108
- self.spot_detection = None
109
-
110
- if 'haralick_options' in self.instructions:
111
- self.haralick_options = self.instructions['haralick_options']
112
- else:
113
- self.haralick_options = None
114
-
115
- if 'intensity_measurement_radii' in self.instructions:
116
- self.intensity_measurement_radii = self.instructions['intensity_measurement_radii']
117
- else:
118
- self.intensity_measurement_radii = None
119
-
120
- if 'isotropic_operations' in self.instructions:
121
- self.isotropic_operations = self.instructions['isotropic_operations']
122
- else:
123
- self.isotropic_operations = None
124
-
125
- if 'clear_previous' in self.instructions:
126
- self.clear_previous = self.instructions['clear_previous']
127
- else:
128
- self.clear_previous = True
129
-
130
- else:
131
- print('No measurement instructions found. Use default measurements.')
132
- self.features = ['area', 'intensity_mean']
133
- self.border_distances = None
134
- self.haralick_options = None
135
- self.clear_previous = False
136
- self.background_correction = None
137
- self.spot_detection = None
138
- self.intensity_measurement_radii = 10
139
- self.isotropic_operations = ['mean']
140
-
141
- if self.features is None:
142
- self.features = []
143
-
144
-
145
- def detect_channels(self):
146
- self.img_num_channels = _get_img_num_per_channel(self.channel_indices, self.len_movie, self.nbr_channels)
147
-
148
- def write_log(self):
149
-
150
- features_log=f'features: {self.features}'
151
- border_distances_log=f'border_distances: {self.border_distances}'
152
- haralick_options_log=f'haralick_options: {self.haralick_options}'
153
- background_correction_log=f'background_correction: {self.background_correction}'
154
- spot_detection_log=f'spot_detection: {self.spot_detection}'
155
- intensity_measurement_radii_log=f'intensity_measurement_radii: {self.intensity_measurement_radii}'
156
- isotropic_options_log=f'isotropic_operations: {self.isotropic_operations} \n'
157
- log='\n'.join([features_log,border_distances_log,haralick_options_log,background_correction_log,spot_detection_log,intensity_measurement_radii_log,isotropic_options_log])
158
- with open(self.pos + f'log_{self.mode}.txt', 'a') as f:
159
- f.write(f'{datetime.datetime.now()} MEASURE \n')
160
- f.write(log+'\n')
161
-
162
- def prepare_folders(self):
163
-
164
- if self.mode.lower()=="target" or self.mode.lower()=="targets":
165
- self.label_folder = "labels_targets"
166
- self.table_name = "trajectories_targets.csv"
167
- self.instruction_file = os.sep.join(["configs","measurement_instructions_targets.json"])
168
-
169
- elif self.mode.lower()=="effector" or self.mode.lower()=="effectors":
170
- self.label_folder = "labels_effectors"
171
- self.table_name = "trajectories_effectors.csv"
172
- self.instruction_file = os.sep.join(["configs","measurement_instructions_effectors.json"])
173
-
174
- else:
175
- self.label_folder = f"labels_{self.mode}"
176
- self.table_name = f"trajectories_{self.mode}.csv"
177
- self.instruction_file = os.sep.join(["configs",f"measurement_instructions_{self.mode}.json"])
178
-
179
- def extract_experiment_parameters(self):
180
-
181
- self.movie_prefix = config_section_to_dict(self.config, "MovieSettings")["movie_prefix"]
182
- self.spatial_calibration = float(config_section_to_dict(self.config, "MovieSettings")["pxtoum"])
183
- self.time_calibration = float(config_section_to_dict(self.config, "MovieSettings")["frametomin"])
184
- self.len_movie = float(config_section_to_dict(self.config, "MovieSettings")["len_movie"])
185
- self.shape_x = int(config_section_to_dict(self.config, "MovieSettings")["shape_x"])
186
- self.shape_y = int(config_section_to_dict(self.config, "MovieSettings")["shape_y"])
187
-
188
- self.channel_names, self.channel_indices = extract_experiment_channels(self.exp_dir)
189
- self.nbr_channels = len(self.channel_names)
190
-
191
- def locate_experiment_config(self):
192
-
193
- parent1 = Path(self.pos).parent
194
- self.exp_dir = parent1.parent
195
- self.config = PurePath(self.exp_dir,Path("config.ini"))
196
-
197
- if not os.path.exists(self.config):
198
- print('The configuration file for the experiment was not found...')
199
- self.abort_process()
200
-
201
- def detect_tracks(self):
202
-
203
- # Load trajectories, add centroid if not in trajectory
204
- self.trajectories = self.pos+os.sep.join(['output','tables', self.table_name])
205
- if os.path.exists(self.trajectories):
206
- print('Previous table detected...')
207
- self.trajectories = pd.read_csv(self.trajectories)
208
- if 'TRACK_ID' not in list(self.trajectories.columns):
209
- print('Static measurements detected...')
210
- self.do_iso_intensities = False
211
- self.intensity_measurement_radii = None
212
- if self.clear_previous:
213
- print('Clear previous measurements...')
214
- self.trajectories = None #remove_trajectory_measurements(trajectories, column_labels)
215
- self.do_features = True
216
- self.features += ['centroid']
217
- self.column_labels.update({'track': 'ID'})
218
- else:
219
- print('Time series detected...')
220
- if self.clear_previous:
221
- print('TRACK_ID found... Clear previous measurements...')
222
- self.trajectories = remove_trajectory_measurements(self.trajectories, self.column_labels)
223
- else:
224
- self.trajectories = None
225
- self.do_features = True
226
- self.features += ['centroid']
227
- self.do_iso_intensities = False
228
-
229
- def detect_movie_and_labels(self):
230
-
231
- self.label_path = natsorted(glob(os.sep.join([self.pos, self.label_folder, '*.tif'])))
232
- if len(self.label_path)>0:
233
- print(f"Found {len(self.label_path)} segmented frames...")
234
- else:
235
- self.features = None
236
- self.haralick_options = None
237
- self.border_distances = None
238
- self.label_path = None
239
-
240
- try:
241
- self.file = glob(self.pos+os.sep.join(["movie", f"{self.movie_prefix}*.tif"]))[0]
242
- except IndexError:
243
- self.file = None
244
- self.haralick_option = None
245
- self.features = drop_tonal_features(self.features)
246
-
247
- len_movie_auto = auto_load_number_of_frames(self.file)
248
- if len_movie_auto is not None:
249
- self.len_movie = len_movie_auto
250
-
251
- def parallel_job(self, indices):
252
-
253
- measurements = []
254
-
255
- for t in tqdm(indices,desc="frame"):
256
-
257
- if self.file is not None:
258
- img = load_frames(self.img_num_channels[:,t], self.file, scale=None, normalize_input=False)
259
-
260
- if self.label_path is not None:
261
-
262
- lbl = locate_labels(self.pos, population=self.mode, frames=t)
263
- if lbl is None:
264
- continue
265
-
266
- if self.trajectories is not None:
267
-
268
- positions_at_t = self.trajectories.loc[self.trajectories[self.column_labels['time']]==t].copy()
269
-
270
- if self.do_features:
271
- feature_table = measure_features(img, lbl, features=self.features, border_dist=self.border_distances,
272
- channels=self.channel_names, haralick_options=self.haralick_options, verbose=False,
273
- normalisation_list=self.background_correction, spot_detection=self.spot_detection)
274
- if self.trajectories is None:
275
- positions_at_t = _extract_coordinates_from_features(feature_table, timepoint=t)
276
- column_labels = {'track': "ID", 'time': self.column_labels['time'], 'x': self.column_labels['x'],
277
- 'y': self.column_labels['y']}
278
- feature_table.rename(columns={'centroid-1': 'POSITION_X', 'centroid-0': 'POSITION_Y'}, inplace=True)
279
-
280
- if self.do_iso_intensities and not self.trajectories is None:
281
- iso_table = measure_isotropic_intensity(positions_at_t, img, channels=self.channel_names, intensity_measurement_radii=self.intensity_measurement_radii, column_labels=self.column_labels, operations=self.isotropic_operations, verbose=False)
282
-
283
- if self.do_iso_intensities and self.do_features and not self.trajectories is None:
284
- measurements_at_t = iso_table.merge(feature_table, how='outer', on='class_id',suffixes=('_delme', ''))
285
- measurements_at_t = measurements_at_t[[c for c in measurements_at_t.columns if not c.endswith('_delme')]]
286
- elif self.do_iso_intensities * (not self.do_features) * (not self.trajectories is None):
287
- measurements_at_t = iso_table
288
- elif self.do_features:
289
- measurements_at_t = positions_at_t.merge(feature_table, how='outer', on='class_id',suffixes=('_delme', ''))
290
- measurements_at_t = measurements_at_t[[c for c in measurements_at_t.columns if not c.endswith('_delme')]]
291
-
292
- measurements_at_t = center_of_mass_to_abs_coordinates(measurements_at_t)
293
- measurements_at_t = measure_radial_distance_to_center(measurements_at_t, volume=img.shape, column_labels=self.column_labels)
294
-
295
- self.sum_done+=1/self.len_movie*100
296
- mean_exec_per_step = (time.time() - self.t0) / (self.sum_done*self.len_movie / 100 + 1)
297
- pred_time = (self.len_movie - (self.sum_done*self.len_movie / 100 + 1)) * mean_exec_per_step
298
- self.queue.put([self.sum_done, pred_time])
299
-
300
- if measurements_at_t is not None:
301
- measurements_at_t[self.column_labels['time']] = t
302
- else:
303
- measurements_at_t = pd.DataFrame()
304
-
305
- measurements.append(measurements_at_t)
306
-
307
- return measurements
308
-
309
- def run(self):
310
-
311
- self.indices = list(range(self.img_num_channels.shape[1]))
312
- chunks = np.array_split(self.indices, self.n_threads)
313
-
314
- self.timestep_dataframes = []
315
- with concurrent.futures.ThreadPoolExecutor(max_workers=self.n_threads) as executor:
316
- results = executor.map(self.parallel_job, chunks) #list(map(lambda x: executor.submit(self.parallel_job, x), chunks))
317
- try:
318
- for i,return_value in enumerate(results):
319
- print(f'Thread {i} completed...')
320
- self.timestep_dataframes.extend(return_value)
321
- except Exception as e:
322
- print("Exception: ", e)
323
-
324
- print('Measurements successfully performed...')
325
-
326
- if len(self.timestep_dataframes)>0:
327
-
328
- df = pd.concat(self.timestep_dataframes)
329
-
330
- if self.trajectories is not None:
331
- df = df.sort_values(by=[self.column_labels['track'],self.column_labels['time']])
332
- df = df.dropna(subset=[self.column_labels['track']])
333
- else:
334
- df['ID'] = np.arange(len(df))
335
- df = df.sort_values(by=[self.column_labels['time'], 'ID'])
336
-
337
- df = df.reset_index(drop=True)
338
- df = _remove_invalid_cols(df)
339
- df = df.replace([np.inf, -np.inf], np.nan)
340
-
341
- df.to_csv(self.pos+os.sep.join(["output", "tables", self.table_name]), index=False)
342
- print(f'Measurement table successfully exported in {os.sep.join(["output", "tables"])}...')
343
- print('Done.')
344
- else:
345
- print('No measurement could be performed. Check your inputs.')
346
- print('Done.')
347
-
348
- # Send end signal
349
- self.queue.put("finished")
350
- self.queue.close()
351
-
352
- def end_process(self):
353
-
354
- self.terminate()
355
- self.queue.put("finished")
356
-
357
- def abort_process(self):
358
-
359
- self.terminate()
360
- self.queue.put("error")