class ProgressView(QWidget): """ :type batch_manager: CalculationManager """ def __init__(self, parent, batch_manager): QWidget.__init__(self, parent) self.calculation_manager = batch_manager self.whole_progress = QProgressBar(self) self.whole_progress.setMinimum(0) self.whole_progress.setMaximum(1) self.whole_progress.setFormat("%v of %m") self.whole_progress.setTextVisible(True) self.part_progress = QProgressBar(self) self.part_progress.setMinimum(0) self.part_progress.setMaximum(1) self.part_progress.setFormat("%v of %m") self.whole_label = QLabel("All batch progress:", self) self.part_label = QLabel("Single batch progress:", self) self.logs = ExceptionList(self) self.logs.setToolTip("Logs") self.task_que = QListWidget() self.process_num_timer = QTimer() self.process_num_timer.setInterval(1000) self.process_num_timer.setSingleShot(True) self.process_num_timer.timeout.connect(self.change_number_of_workers) self.number_of_process = QSpinBox(self) self.number_of_process.setRange(1, multiprocessing.cpu_count()) self.number_of_process.setValue(1) self.number_of_process.setToolTip( "Number of process used in batch calculation") self.number_of_process.valueChanged.connect( self.process_num_timer_start) layout = QGridLayout() layout.addWidget(self.whole_label, 0, 0, Qt.AlignRight) layout.addWidget(self.whole_progress, 0, 1, 1, 2) layout.addWidget(self.part_label, 1, 0, Qt.AlignRight) layout.addWidget(self.part_progress, 1, 1, 1, 2) lab = QLabel("Number of process:") lab.setToolTip("Number of process used in batch calculation") layout.addWidget(lab, 2, 0) layout.addWidget(self.number_of_process, 2, 1) layout.addWidget(self.logs, 3, 0, 1, 3) layout.addWidget(self.task_que, 0, 4, 0, 1) layout.setColumnMinimumWidth(2, 10) layout.setColumnStretch(2, 1) self.setLayout(layout) self.preview_timer = QTimer() self.preview_timer.setInterval(1000) self.preview_timer.timeout.connect(self.update_info) def new_task(self): self.whole_progress.setMaximum( self.calculation_manager.calculation_size) if not self.preview_timer.isActive(): self.update_info() self.preview_timer.start() def update_info(self): res = self.calculation_manager.get_results() for el in res.errors: if el[0]: QListWidgetItem(el[0], self.logs) ExceptionListItem(el[1], self.logs) if (state_store.report_errors and parsed_version.is_devrelease and not isinstance(el[1][0], SegmentationLimitException) and isinstance(el[1][1], tuple)): with sentry_sdk.push_scope() as scope: scope.set_tag("auto_report", "true") sentry_sdk.capture_event(el[1][1][0]) self.whole_progress.setValue(res.global_counter) working_search = True for i, (progress, total) in enumerate(res.jobs_status): if working_search and progress != total: self.part_progress.setMaximum(total) self.part_progress.setValue(progress) working_search = False if i < self.task_que.count(): item = self.task_que.item(i) item.setText("Task {} ({}/{})".format(i, progress, total)) else: self.task_que.addItem("Task {} ({}/{})".format( i, progress, total)) if not self.calculation_manager.has_work: print( "[ProgressView.update_info]", self.calculation_manager.has_work, self.calculation_manager.batch_manager.has_work, self.calculation_manager.writer.writing_finished(), ) self.part_progress.setValue(self.part_progress.maximum()) self.preview_timer.stop() logging.info("Progress stop") def process_num_timer_start(self): self.process_num_timer.start() def update_progress(self, total_progress, part_progress): self.whole_progress.setValue(total_progress) self.part_progress.setValue(part_progress) def set_total_size(self, size): self.whole_progress.setMaximum(size) def set_part_size(self, size): self.part_progress.setMaximum(size) def change_number_of_workers(self): self.calculation_manager.set_number_of_workers( self.number_of_process.value())
class AlgorithmOptions(QWidget): def __init__(self, settings: StackSettings, image_view: StackImageView): super().__init__() self.settings = settings self.view_name = image_view.name self.show_result = QEnumComboBox( enum_class=LabelEnum) # QCheckBox("Show result") self._set_show_label_from_settings() self.opacity = QDoubleSpinBox() self.opacity.setRange(0, 1) self.opacity.setSingleStep(0.1) self._set_opacity_from_settings() self.only_borders = QCheckBox("Only borders") self._set_border_mode_from_settings() self.borders_thick = QSpinBox() self.borders_thick.setRange(1, 25) self.borders_thick.setSingleStep(1) self._set_border_thick_from_settings() self.execute_in_background_btn = QPushButton("Execute in background") self.execute_in_background_btn.setToolTip( "Run calculation in background. Put result in multiple files panel" ) self.execute_btn = QPushButton("Execute") self.execute_btn.setStyleSheet("QPushButton{font-weight: bold;}") self.execute_all_btn = QPushButton("Execute all") self.execute_all_btn.setToolTip( "Execute in batch mode segmentation with current parameter. File list need to be specified in image tab." ) self.execute_all_btn.setDisabled(True) self.save_parameters_btn = QPushButton("Save parameters") self.block_execute_all_btn = False self.algorithm_choose_widget = AlgorithmChoose(settings, mask_algorithm_dict) self.algorithm_choose_widget.result.connect(self.execution_result_set) self.algorithm_choose_widget.finished.connect(self.execution_finished) self.algorithm_choose_widget.progress_signal.connect( self.progress_info) # self.stack_layout = QStackedLayout() self.keep_chosen_components_chk = QCheckBox("Save selected components") self.keep_chosen_components_chk.setToolTip( "Save chosen components when loading segmentation form file\n or from multiple file widget." ) self.keep_chosen_components_chk.stateChanged.connect( self.set_keep_chosen_components) self.keep_chosen_components_chk.setChecked( settings.keep_chosen_components) self.show_parameters = QPushButton("Show parameters") self.show_parameters.setToolTip( "Show parameters of segmentation for each components") self.show_parameters_widget = SegmentationInfoDialog( self.settings, self.algorithm_choose_widget.change_algorithm) self.show_parameters.clicked.connect(self.show_parameters_widget.show) self.choose_components = ChosenComponents() self.choose_components.check_change_signal.connect( image_view.refresh_selected) self.choose_components.mouse_leave.connect(image_view.component_unmark) self.choose_components.mouse_enter.connect(image_view.component_mark) # WARNING works only with one channels algorithms # SynchronizeValues.add_synchronization("channels_chose", widgets_list) self.chosen_list = [] self.progress_bar2 = QProgressBar() self.progress_bar2.setHidden(True) self.progress_bar = QProgressBar() self.progress_bar.setHidden(True) self.progress_info_lab = QLabel() self.progress_info_lab.setHidden(True) self.file_list = [] self.batch_process = BatchProceed() self.batch_process.progress_signal.connect(self.progress_info) self.batch_process.error_signal.connect(self.execution_all_error) self.batch_process.execution_done.connect(self.execution_all_done) self.batch_process.range_signal.connect(self.progress_bar.setRange) self.is_batch_process = False self.setContentsMargins(0, 0, 0, 0) main_layout = QVBoxLayout() # main_layout.setSpacing(0) opt_layout = QHBoxLayout() opt_layout.setContentsMargins(0, 0, 0, 0) opt_layout.addWidget(self.show_result) opt_layout.addWidget(right_label("Opacity:")) opt_layout.addWidget(self.opacity) main_layout.addLayout(opt_layout) opt_layout2 = QHBoxLayout() opt_layout2.setContentsMargins(0, 0, 0, 0) opt_layout2.addWidget(self.only_borders) opt_layout2.addWidget(right_label("Border thick:")) opt_layout2.addWidget(self.borders_thick) main_layout.addLayout(opt_layout2) btn_layout = QGridLayout() btn_layout.setContentsMargins(0, 0, 0, 0) btn_layout.addWidget(self.execute_btn, 0, 0) btn_layout.addWidget(self.execute_in_background_btn, 0, 1) btn_layout.addWidget(self.execute_all_btn, 1, 0) btn_layout.addWidget(self.save_parameters_btn, 1, 1) main_layout.addLayout(btn_layout) main_layout.addWidget(self.progress_bar2) main_layout.addWidget(self.progress_bar) main_layout.addWidget(self.progress_info_lab) main_layout.addWidget(self.algorithm_choose_widget, 1) # main_layout.addWidget(self.algorithm_choose) # main_layout.addLayout(self.stack_layout, 1) main_layout.addWidget(self.choose_components) down_layout = QHBoxLayout() down_layout.addWidget(self.keep_chosen_components_chk) down_layout.addWidget(self.show_parameters) main_layout.addLayout(down_layout) main_layout.addStretch() main_layout.setContentsMargins(0, 0, 0, 0) # main_layout.setSpacing(0) self.setLayout(main_layout) # noinspection PyUnresolvedReferences self.execute_in_background_btn.clicked.connect( self.execute_in_background) self.execute_btn.clicked.connect(self.execute_action) self.execute_all_btn.clicked.connect(self.execute_all_action) self.save_parameters_btn.clicked.connect(self.save_parameters) # noinspection PyUnresolvedReferences self.opacity.valueChanged.connect(self._set_opacity) # noinspection PyUnresolvedReferences self.show_result.currentEnumChanged.connect(self._set_show_label) self.only_borders.stateChanged.connect(self._set_border_mode) # noinspection PyUnresolvedReferences self.borders_thick.valueChanged.connect(self._set_border_thick) image_view.component_clicked.connect( self.choose_components.other_component_choose) settings.chosen_components_widget = self.choose_components settings.components_change_list.connect( self.choose_components.new_choose) settings.image_changed.connect( self.choose_components.remove_components) settings.connect_to_profile( f"{self.view_name}.image_state.only_border", self._set_border_mode_from_settings) settings.connect_to_profile( f"{self.view_name}.image_state.border_thick", self._set_border_thick_from_settings) settings.connect_to_profile(f"{self.view_name}.image_state.opacity", self._set_opacity_from_settings) settings.connect_to_profile(f"{self.view_name}.image_state.show_label", self._set_show_label_from_settings) def _set_border_mode(self, value: bool): self.settings.set_in_profile( f"{self.view_name}.image_state.only_border", value) def _set_border_thick(self, value: int): self.settings.set_in_profile( f"{self.view_name}.image_state.border_thick", value) def _set_opacity(self, value: float): self.settings.set_in_profile(f"{self.view_name}.image_state.opacity", value) def _set_show_label(self, value: LabelEnum): self.settings.set_in_profile( f"{self.view_name}.image_state.show_label", value) def _set_border_mode_from_settings(self): self.only_borders.setChecked( self.settings.get_from_profile( f"{self.view_name}.image_state.only_border", True)) def _set_border_thick_from_settings(self): self.borders_thick.setValue( self.settings.get_from_profile( f"{self.view_name}.image_state.border_thick", 1)) def _set_opacity_from_settings(self): self.opacity.setValue( self.settings.get_from_profile( f"{self.view_name}.image_state.opacity", 1.0)) def _set_show_label_from_settings(self): self.show_result.setCurrentEnum( self.settings.get_from_profile( f"{self.view_name}.image_state.show_label", LabelEnum.Show_results)) @Slot(int) def set_keep_chosen_components(self, val): self.settings.set_keep_chosen_components(val) def save_parameters(self): dial = PSaveDialog(io_functions.save_parameters_dict, system_widget=False, settings=self.settings, path=IO_SAVE_DIRECTORY) if not dial.exec_(): return res = dial.get_result() res.save_class.save(res.save_destination, self.algorithm_choose_widget.current_parameters()) def file_list_change(self, val): self.file_list = val if len(self.file_list) > 0 and not self.block_execute_all_btn: self.execute_all_btn.setEnabled(True) else: self.execute_all_btn.setDisabled(True) def get_chosen_components(self): return sorted(self.choose_components.get_chosen()) @property def segmentation(self): return self.settings.roi @segmentation.setter def segmentation(self, val): self.settings.roi = val def _image_changed(self): self.settings.roi = None self.choose_components.set_chose([], []) def _execute_in_background_init(self): if self.batch_process.isRunning(): return self.progress_bar2.setVisible(True) self.progress_bar2.setRange(0, self.batch_process.queue.qsize()) self.progress_bar2.setValue(self.batch_process.index) self.progress_bar.setVisible(True) self.progress_bar.setValue(0) self.execute_btn.setDisabled(True) self.batch_process.start() def execute_in_background(self): # TODO check if components are properly passed widget = self.algorithm_choose_widget.current_widget() segmentation_profile = widget.get_segmentation_profile() task = BatchTask(self.settings.get_project_info(), segmentation_profile, None) self.batch_process.add_task(task) self.progress_bar2.setRange(0, self.progress_bar2.maximum() + 1) self._execute_in_background_init() def execute_all_action(self): dial = PSaveDialog( SaveROI, settings=self.settings, system_widget=False, path="io.save_batch", file_mode=PSaveDialog.Directory, ) if not dial.exec_(): return folder_path = str(dial.selectedFiles()[0]) widget = self.algorithm_choose_widget.current_widget() save_parameters = dial.values segmentation_profile = widget.get_segmentation_profile() for file_path in self.file_list: task = BatchTask(file_path, segmentation_profile, (folder_path, save_parameters)) self.batch_process.add_task(task) self.progress_bar2.setRange( 0, self.progress_bar2.maximum() + len(self.file_list)) self._execute_in_background_init() def execution_all_error(self, text): QMessageBox.warning(self, "Proceed error", text) def execution_all_done(self): if not self.batch_process.queue.empty(): self._execute_in_background_init() return self.execute_btn.setEnabled(True) self.block_execute_all_btn = False if len(self.file_list) > 0: self.execute_all_btn.setEnabled(True) self.progress_bar.setHidden(True) self.progress_bar2.setHidden(True) self.progress_info_lab.setHidden(True) def execute_action(self): self.execute_btn.setDisabled(True) self.execute_all_btn.setDisabled(True) self.block_execute_all_btn = True self.is_batch_process = False self.progress_bar.setRange(0, 0) self.choose_components.setDisabled(True) chosen = sorted(self.choose_components.get_chosen()) blank = get_mask(self.settings.roi, self.settings.mask, chosen) if blank is not None: # Problem with handling time data in algorithms # TODO Fix This blank = blank[0] self.progress_bar.setHidden(False) widget: AlgorithmSettingsWidget = self.algorithm_choose_widget.current_widget( ) widget.set_mask(blank) self.progress_bar.setRange(0, widget.algorithm.get_steps_num()) widget.execute() self.chosen_list = chosen def progress_info(self, text, num, file_name="", file_num=0): self.progress_info_lab.setVisible(True) if file_name != "": self.progress_info_lab.setText(file_name + "\n" + text) else: self.progress_info_lab.setText(text) self.progress_bar.setValue(num) self.progress_bar2.setValue(file_num) def execution_finished(self): self.execute_btn.setEnabled(True) self.block_execute_all_btn = False if len(self.file_list) > 0: self.execute_all_btn.setEnabled(True) self.progress_bar.setHidden(True) self.progress_info_lab.setHidden(True) self.choose_components.setDisabled(False) def execution_result_set(self, result): self.settings.set_segmentation_result(result) def showEvent(self, _): widget = self.algorithm_choose_widget.current_widget() widget.image_changed(self.settings.image)
class ProgressDialog(QDialog): """Progress dialog. Parameters ---------- label - string or list of strings task - QThread or list of QThread (implementing task interface) parent - QObject """ canceled = Signal() def __init__(self, label, task, parent=None): """Constructor.""" super().__init__(parent) self._label = label self._task = task if (isinstance(self._label, (list, tuple)) and isinstance(self._task, (list, tuple)) and len(self._label) == len(self._task)) or \ (isinstance(self._label, str) and isinstance(self._task, QThread)): pass else: raise ValueError() self._setup_ui() def _setup_ui(self): self.layout = QVBoxLayout() self.dlg_label = QLabel(self) self.cur_item_label = QLabel(self) self.progress_bar = QProgressBar(self) self.cancel = QPushButton('Cancel', self) self.layout.addWidget(self.dlg_label) self.layout.addWidget(self.cur_item_label) self.layout.addWidget(self.progress_bar) self.layout.addWidget(self.cancel) self.setLayout(self.layout) # Set initial value self.progress_bar.setValue(0) # Set progress bar limits and connect signals self.progress_bar.setMinimum(0) if hasattr(self._task, '__iter__'): self.dlg_label.setText(self._label[0]) self._task[0].currentItem.connect(self.cur_item_label.setText) self._task[0].itemDone.connect(self.inc_value) pb_max = self._task[0].size() for i in range(1, len(self._task)): self._task[i].currentItem.connect(self.cur_item_label.setText) self._task[i].itemDone.connect(self.inc_value) self._task[i - 1].completed.connect(self._update_label) self._task[i - 1].completed.connect(self._task[i].start) pb_max += self._task[i].size() self.progress_bar.setMaximum(pb_max) self._task[-1].completed.connect(lambda: self._exit_dlg(1)) else: self.dlg_label.setText(self._label) self._task.currentItem.connect(self.cur_item_label.setText) self._task.itemDone.connect(self.inc_value) self._task.completed.connect(lambda: self._exit_dlg(1)) self.progress_bar.setMaximum(self._task.size()) self.cancel.pressed.connect(self.canceled.emit) self.canceled.connect(lambda: self._exit_dlg(0)) self.progress_bar.valueChanged.connect(self._is_finished) def _update_label(self): next_idx = self._label.index(self.dlg_label.text()) + 1 if next_idx < len(self._label): self.dlg_label.setText(self._label[next_idx]) def _exit_dlg(self, result): if hasattr(self._task, '__iter__'): for task in self._task: task.exit_task() self._wait_task(self._task[-1]) self._task[-1].deleteLater() else: self._task.exit_task() self._wait_task(self._task) self._task.deleteLater() if result == 1: self.accept() elif result == 0: self.reject() def _wait_task(self, task): init = time.time() try: while task.isRunning(): time.sleep(0.1) if time.time() - init > 10: self._exit_dlg(0) raise Exception('Thread will not leave') except RuntimeError: pass def _is_finished(self): if self.progress_bar.value() == self.progress_bar.maximum(): self._exit_dlg(1) def set_value(self, value): """Set progress bar value.""" self.progress_bar.setValue(value) def inc_value(self): """Increase value.""" self.progress_bar.setValue(self.progress_bar.value()+1) def exec_(self): """Override.""" if hasattr(self._task, '__iter__'): self._task[0].start() else: self._task.start() return super().exec_()
class ProgressView(QWidget): """ :type batch_manager: CalculationManager """ def __init__(self, parent, batch_manager): super().__init__(parent) self.task_count = 0 self.calculation_manager = batch_manager self.whole_progress = QProgressBar(self) self.whole_progress.setMinimum(0) self.whole_progress.setMaximum(1) self.whole_progress.setFormat("%v of %m") self.whole_progress.setTextVisible(True) self.part_progress = QProgressBar(self) self.part_progress.setMinimum(0) self.part_progress.setMaximum(1) self.part_progress.setFormat("%v of %m") self.whole_label = QLabel("All batch progress:", self) self.part_label = QLabel("Single batch progress:", self) self.cancel_remove_btn = QPushButton("Remove task") self.cancel_remove_btn.setDisabled(True) self.logs = ExceptionList(self) self.logs.setToolTip("Logs") self.task_view = QListView() self.task_que = QStandardItemModel(self) self.task_view.setModel(self.task_que) self.process_num_timer = QTimer() self.process_num_timer.setInterval(1000) self.process_num_timer.setSingleShot(True) self.process_num_timer.timeout.connect(self.change_number_of_workers) self.number_of_process = QSpinBox(self) self.number_of_process.setRange(1, multiprocessing.cpu_count()) self.number_of_process.setValue(1) self.number_of_process.setToolTip( "Number of process used in batch calculation") self.number_of_process.valueChanged.connect( self.process_num_timer_start) self.progress_item_dict = {} layout = QGridLayout() layout.addWidget(self.whole_label, 0, 0, Qt.AlignRight) layout.addWidget(self.whole_progress, 0, 1, 1, 2) layout.addWidget(self.part_label, 1, 0, Qt.AlignRight) layout.addWidget(self.part_progress, 1, 1, 1, 2) lab = QLabel("Number of process:") lab.setToolTip("Number of process used in batch calculation") layout.addWidget(lab, 2, 0) layout.addWidget(self.number_of_process, 2, 1) layout.addWidget(self.logs, 3, 0, 2, 3) layout.addWidget(self.task_view, 0, 4, 4, 1) layout.addWidget(self.cancel_remove_btn, 4, 4, 1, 1) layout.setColumnMinimumWidth(2, 10) layout.setColumnStretch(2, 1) self.setLayout(layout) self.preview_timer = QTimer() self.preview_timer.setInterval(1000) self.preview_timer.timeout.connect(self.update_info) self.task_view.selectionModel().currentChanged.connect( self.task_selection_change) self.cancel_remove_btn.clicked.connect(self.task_cancel_remove) def task_selection_change(self, new, old): task: CalculationProcessItem = self.task_que.item( new.row(), new.column()) if task is None: self.cancel_remove_btn.setDisabled(True) return self.cancel_remove_btn.setEnabled(True) if task.is_finished(): self.cancel_remove_btn.setText(f"Remove task {task.num}") else: self.cancel_remove_btn.setText(f"Cancel task {task.num}") def task_cancel_remove(self): index = self.task_view.selectionModel().currentIndex() task: CalculationProcessItem = self.task_que.item( index.row(), index.column()) if task.is_finished(): self.calculation_manager.remove_calculation(task.calculation) self.task_que.takeRow(index.row()) else: self.calculation_manager.cancel_calculation(task.calculation) print(task) def new_task(self): self.whole_progress.setMaximum( self.calculation_manager.calculation_size) if not self.preview_timer.isActive(): self.update_info() self.preview_timer.start() def update_info(self): res = self.calculation_manager.get_results() for el in res.errors: if el[0]: QListWidgetItem(el[0], self.logs) ExceptionListItem(el[1], self.logs) if (state_store.report_errors and parsed_version.is_devrelease and not isinstance(el[1][0], SegmentationLimitException) and isinstance(el[1][1], tuple)): with sentry_sdk.push_scope() as scope: scope.set_tag("auto_report", "true") sentry_sdk.capture_event(el[1][1][0]) self.whole_progress.setValue(res.global_counter) working_search = True for uuid, progress in res.jobs_status.items(): calculation = self.calculation_manager.calculation_dict[uuid] total = len(calculation.file_list) if uuid in self.progress_item_dict: item = self.progress_item_dict[uuid] item.update_count(progress) else: item = CalculationProcessItem(calculation, self.task_count, progress) self.task_count += 1 self.task_que.appendRow(item) self.progress_item_dict[uuid] = item if working_search and progress != total: self.part_progress.setMaximum(total) self.part_progress.setValue(progress) working_search = False if not self.calculation_manager.has_work: self.part_progress.setValue(self.part_progress.maximum()) self.preview_timer.stop() logging.info("Progress stop") def process_num_timer_start(self): self.process_num_timer.start() def update_progress(self, total_progress, part_progress): self.whole_progress.setValue(total_progress) self.part_progress.setValue(part_progress) def set_total_size(self, size): self.whole_progress.setMaximum(size) def set_part_size(self, size): self.part_progress.setMaximum(size) def change_number_of_workers(self): self.calculation_manager.set_number_of_workers( self.number_of_process.value())