From: Konstantin Leontev Date: Wed, 28 Aug 2024 11:04:25 +0000 (+0100) Subject: [bos #38044][EDF] (2023-T3) Support for automatic reparation. Emitter now returns... X-Git-Url: http://git.salome-platform.org/gitweb/?a=commitdiff_plain;h=d270b0ab087002f40c5d553bdb04965b7ccac9c2;p=modules%2Fgeom.git [bos #38044][EDF] (2023-T3) Support for automatic reparation. Emitter now returns bool value that is False if the job was canceled. It should be used for early return from the running script to finish the thread gracefully. A timer was added to terminate the thread as a last resort. --- diff --git a/src/RepairGUIAdv/geomrepairadv_progress.py b/src/RepairGUIAdv/geomrepairadv_progress.py index dd8911837..badf7068b 100644 --- a/src/RepairGUIAdv/geomrepairadv_progress.py +++ b/src/RepairGUIAdv/geomrepairadv_progress.py @@ -98,8 +98,7 @@ class RepairProgressDialog(QDialog, QPlainTextEdit): self.close() else: # Terminates the execution of the thread. - # TODO: find out if we can do it with requestInterruption() - self.thread.terminate() + self.thread.cancel() self.progress.setLabelText('Canceled!') self.progress.setCancelButtonText('Close') diff --git a/src/RepairGUIAdv/geomrepairadv_worker.py b/src/RepairGUIAdv/geomrepairadv_worker.py index 40aaefde5..553b06650 100644 --- a/src/RepairGUIAdv/geomrepairadv_worker.py +++ b/src/RepairGUIAdv/geomrepairadv_worker.py @@ -22,7 +22,7 @@ import inspect from traceback import format_exc -from qtsalome import QApplication, pyqtSignal, QThread, Qt +from qtsalome import QApplication, pyqtSignal, QThread, Qt, QTimer from .geomrepairadv_logger import logger @@ -58,6 +58,11 @@ class Worker(QThread): # Set a variable for result self.result = None + # Create a timer to terminate the thread if we can't exit gracefully + self.terminate_timer = QTimer() + self.terminate_timer.timeout.connect(self.terminate) + self.terminate_timer.setSingleShot(True) + def run(self): """ @@ -72,7 +77,9 @@ class Worker(QThread): # Reset the progress when finished self.progress_update.emit(100) - self.work_completed.emit() + + if not self.is_canceled(): + self.work_completed.emit() except Exception: logger.error(format_exc()) @@ -82,16 +89,42 @@ class Worker(QThread): QApplication.restoreOverrideCursor() - def terminate(self): + def is_canceled(self): """ - Overrides default terminate() to add some clean up. + Returns true if the job was canceled. + """ + + return not self.progress_emitter.is_running + + + def cancel(self): + """ + Cancels the job. """ - super().terminate() + # Set emitter to know that we canceled the job and + # can return from the running function gracefully. + self.progress_emitter.stop() # Termination doesn't call a final block inside run() QApplication.restoreOverrideCursor() + # Set a timer for a case when the running function + # doesn't use emitter or does so heavy job that it would take + # a lot of time for waiting and we need to terminate it. + self.terminate_timer.start(1000) + + + def terminate(self): + """ + Overrides default terminate() to add some clean up. + """ + + # Check if the job is still running and need to be terminated + if self.isRunning(): + logger.warning('Thread will be terminated!') + super().terminate() + def get_result(self): """ @@ -114,11 +147,25 @@ class ProgressEmitter(): self.progress_percent = total_lines / 100.0 logger.debug('self.progress_percent: %f', self.progress_percent) + # Flag to check if we need to return from a function running in the thread + self.is_running = True + + + def stop(self): + """ + Call this methid from the Worker for early return + from a function executed in the separated thread. + """ + + self.is_running = False + def emit(self): """ Call this methid in a target function to update a progress value based on a currently executed line number. + Returns False if the job was stopped. Should be used in the algo scripts + for early return from the running function and finish the thread. """ line = inspect.getframeinfo(inspect.stack()[1][0]).lineno @@ -127,3 +174,5 @@ class ProgressEmitter(): logger.debug('progress_value: %d', progress_value) self.progress_update.emit(int(progress_value)) + + return self.is_running diff --git a/src/RepairGUIAdv/reparation_plugin.py b/src/RepairGUIAdv/reparation_plugin.py index 1a2437300..f269df780 100755 --- a/src/RepairGUIAdv/reparation_plugin.py +++ b/src/RepairGUIAdv/reparation_plugin.py @@ -45,10 +45,10 @@ class Reparation_plugin(BaseDlg): layout.setContentsMargins(0, 0, 0, 0) # Min/max values widgets - decimals = 2 + decimals = 7 max_value = sys.float_info.max - self._min_widget = DlgRef_1Spin_QTD('Tol Min', 0, decimals, max_value) - self._max_widget = DlgRef_1Spin_QTD('Tol Max', 1000, decimals, max_value) + self._min_widget = DlgRef_1Spin_QTD('Tol Min', 0.001, decimals, max_value) + self._max_widget = DlgRef_1Spin_QTD('Tol Max', 0.015, decimals, max_value) self._min_widget.SpinBox_DX.valueChanged.connect(self.on_limit_changed) self._max_widget.SpinBox_DX.valueChanged.connect(self.on_limit_changed) diff --git a/src/RepairGUIAdv/reparation_plugin_algo.py b/src/RepairGUIAdv/reparation_plugin_algo.py index 9a478004b..6f57118d7 100644 --- a/src/RepairGUIAdv/reparation_plugin_algo.py +++ b/src/RepairGUIAdv/reparation_plugin_algo.py @@ -32,7 +32,6 @@ from salome.geom import geomBuilder from qtsalome import QFileDialog, QApplication, pyqtSignal import GEOM - salome.salome_init() geompy = geomBuilder.New() @@ -53,9 +52,7 @@ def run(args_dict, progress_emitter): sleep(1) if ('source_solid' not in args_dict or - 'selected_ids' not in args_dict or 'result_name' not in args_dict or - 'selection_level' not in args_dict or 'Tol_min' not in args_dict or 'Tol_max' not in args_dict or 'Debug' not in args_dict): @@ -64,45 +61,28 @@ def run(args_dict, progress_emitter): return False source_solid = args_dict['source_solid'] - selected_ids = args_dict['selected_ids'] result_name = args_dict['result_name'] - selection_level = args_dict['selection_level'] - Tol_min = args_dict['Tol_min'] - Tol_max = args_dict['Tol_max'] + minTol = args_dict['Tol_min'] + maxTol = args_dict['Tol_max'] Debug = args_dict['Debug'] # Replace the lines below with an actual algorithm logging.info('Received arguments:') logging.info('\tsource_solid: %s', source_solid) - logging.info('\tselected_ids: %s', selected_ids) logging.info('\tresult_name: %s', result_name) - logging.info('\tselection_level: %s', selection_level) - logging.info('\Tol_min: %s', Tol_min) - logging.info('\Tol_max: %s', Tol_max) + logging.info('\Tol_min: %s', minTol) + logging.info('\Tol_max: %s', maxTol) logging.info('\Debug: %s', Debug) progress_emitter.emit() - sleep(1) - - # Make a group - # geomBuilder uses their own types - geomBuilder.ShapeType - geom_builder_types = { GEOM.EDGE : 'EDGE', GEOM.FACE : 'FACE', GEOM.SOLID : 'SOLID' } - type_str = geom_builder_types[selection_level] - shape_type = geompy.ShapeType[type_str] - group = geompy.CreateGroup(source_solid, shape_type, theName = result_name) - - logging.info('Step1.') - progress_emitter.emit() - sleep(1) - # Add sub-shapes into the group - for subshape_id in selected_ids: - geompy.AddObject(group, subshape_id) - - logging.info('Step2.') - progress_emitter.emit() + for i in range(5000): + vertex = geompy.MakeVertex(0,0,0) + logging.info('\I: %s', i) + if not progress_emitter.emit(): + return False - return group + return vertex def test():