]> SALOME platform Git repositories - modules/geom.git/commitdiff
Salome HOME
[bos #38044][EDF] (2023-T3) Support for automatic reparation. Initial commit for...
authorKonstantin Leontev <Konstantin.LEONTEV@opencascade.com>
Wed, 2 Oct 2024 14:12:15 +0000 (15:12 +0100)
committerKonstantin Leontev <Konstantin.LEONTEV@opencascade.com>
Wed, 2 Oct 2024 14:12:15 +0000 (15:12 +0100)
src/RepairGUIAdv/CMakeLists.txt
src/RepairGUIAdv/geomrepairadv_worker.py
src/RepairGUIAdv/geomrepairadv_worker_messages.py [new file with mode: 0644]

index fd1f024c4964af0be36802e85c09516bc5289672..bed7efa46df7e6b43462789359ddd0e490e864d7 100644 (file)
@@ -35,6 +35,7 @@ IF(SALOME_BUILD_GUI)
     geomrepairadv_logger.py
     geomrepairadv_progress.py
     geomrepairadv_worker.py
+    geomrepairadv_worker_messages.py
     locate_subshapes.py
     locate_subshapes_algo.py
     locate_subshapes_limits.py
index 553b06650d382d0f9da06c6bac4737c7ed6059ad..bfb4caf981991b1f03d10bd808195ce670d90c26 100644 (file)
 import inspect
 from traceback import format_exc
 
-from qtsalome import QApplication, pyqtSignal, QThread, Qt, QTimer
+from qtsalome import QApplication, Qt, QTimer
 from .geomrepairadv_logger import logger
+from .geomrepairadv_worker_messages import MessageHandlerFactory
+from multiprocessing import Process, Pipe
 
+import CORBA
 
-class Worker(QThread):
+
+class Worker(Process):
     """
-    Creates a tread to run a given target function with a progress dialog as a parent.
+    Creates a process to run a given target function with a progress dialog as a parent.
     """
 
-    progress_update = pyqtSignal(int)
-    thread_failed = pyqtSignal()
-    work_completed = pyqtSignal()
-
     def __init__(self, parent=None, target=None, args=None):
-        super().__init__(parent)
+        super().__init__()
 
-        # Set target function and it's arguments
+        # Set target function and its arguments
         self.target = target
         self.args = args
 
-        # Update a progress bar each time we receive an update signal
-        self.progress_update.connect(parent.setValue)
-        self.thread_failed.connect(parent.on_failed)
-        self.work_completed.connect(parent.on_completed)
+        # Create a pipe for communication
+        self.parent_conn, self.child_conn = Pipe()
+
+        # Set a timer to handle signals non-blocking way
+        self.signals_timer = QTimer()
+        self.signals_timer.timeout.connect(self.handle_signals)
+        self.signals_timer.start(1000)
+
+        # Create a message handler factory
+        self.message_handler_factory = MessageHandlerFactory()
+
+        # Store parent for signal handling
+        self.parent = parent
 
         # Calculate total amount of lines in executed function
         source_lines = inspect.getsourcelines(target)
@@ -53,7 +62,7 @@ class Worker(QThread):
         first_line = source_lines[1]
 
         # Set a progress emitter to update the progress from the target
-        self.progress_emitter = ProgressEmitter(self.progress_update, total_lines, first_line)
+        self.progress_emitter = ProgressEmitter(self.child_conn, total_lines, first_line)
 
         # Set a variable for result
         self.result = None
@@ -70,23 +79,17 @@ class Worker(QThread):
         """
 
         try:
-            # Wait mode cursor
-            QApplication.setOverrideCursor(Qt.WaitCursor)
-
             self.result = self.target(self.args, self.progress_emitter)
 
             # Reset the progress when finished
-            self.progress_update.emit(100)
+            self.child_conn.send(('progress', 100))
 
             if not self.is_canceled():
-                self.work_completed.emit()
+                self.child_conn.send(('completed', self.result))
 
         except Exception:
             logger.error(format_exc())
-            self.thread_failed.emit()
-
-        finally:
-            QApplication.restoreOverrideCursor()
+            self.child_conn.send(('failed',))
 
 
     def is_canceled(self):
@@ -106,9 +109,6 @@ class Worker(QThread):
         # 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.
@@ -121,10 +121,26 @@ class Worker(QThread):
         """
 
         # Check if the job is still running and need to be terminated
-        if self.isRunning():
-            logger.warning('Thread will be terminated!')
+        if self.is_alive():
+            logger.warning('Process will be terminated!')
+
             super().terminate()
 
+        self.on_finished()
+
+
+    def on_finished(self):
+        """
+        Called when the execution is finished.
+        """
+
+        logger.debug('on_finished called')
+
+        QApplication.restoreOverrideCursor()
+        self.signals_timer.stop()
+
+        self.join()
+
 
     def get_result(self):
         """
@@ -134,6 +150,37 @@ class Worker(QThread):
         return self.result
 
 
+    def start(self):
+        """
+        Starts the process and sets up the communication with the parent.
+        """
+        super().start()
+        self.handle_signals()
+
+        QApplication.setOverrideCursor(Qt.WaitCursor)
+
+
+    def handle_signals(self):
+        """
+        Handles signals from the child process.
+        """
+
+        logger.debug('handle_signals called')
+
+        if not self.parent_conn.poll():
+            return
+
+        msg = self.parent_conn.recv()
+        logger.debug('msg: %s', msg)
+
+        # Get a handler for the message type
+        handler = self.message_handler_factory.get_handler(msg[0])
+        if handler:
+            handler.handle(msg, self)
+        else:
+            logger.error('No handler for message type: %s', msg[0])
+
+
 class ProgressEmitter():
     """
     Helper class to reduce code repetition while update progress
@@ -173,6 +220,6 @@ class ProgressEmitter():
         progress_value = (line - self.first_line) / self.progress_percent
         logger.debug('progress_value: %d', progress_value)
 
-        self.progress_update.emit(int(progress_value))
+        self.progress_update.send(['progress', int(progress_value)])
 
         return self.is_running
diff --git a/src/RepairGUIAdv/geomrepairadv_worker_messages.py b/src/RepairGUIAdv/geomrepairadv_worker_messages.py
new file mode 100644 (file)
index 0000000..2f5bbc2
--- /dev/null
@@ -0,0 +1,119 @@
+# -*- coding: utf-8 -*-
+# Copyright (C) 2014-2024  EDF
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+#
+# See https://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+#
+# Author : Konstantin Leontev (OpenCascade S.A.S)
+
+class MessageHandler:
+    """
+    MessageHandler is a base class for handling messages
+    between child and worker processes in the progress bar Worker class.
+    """
+
+    def handle(self, msg, worker):
+        """
+        Handle the given message.
+
+        This method should be implemented by subclasses to define how to process
+        the given message.
+
+        Args:
+            msg: The message to be handled.
+            worker: The worker context or object related to the message.
+
+        Raises:
+            NotImplementedError: If the method is not implemented by a subclass.
+        """
+
+        raise NotImplementedError("Subclasses should implement this method")
+
+
+class ProgressHandler(MessageHandler):
+    """
+    ProgressHandler updates progress bar with a value from a message.
+    """
+
+    def handle(self, msg, worker):
+        """
+        Sets a new value for a worker progress bar.
+
+        Args:
+            msg (tuple): A tuple where the second element is the value to be set.
+            worker (object): The worker object that has a setValue method.
+        """
+
+        worker.parent.setValue(msg[1])
+
+
+class CompletedHandler(MessageHandler):
+    """
+    CompletedHandler handles the completion of the worker process.
+    """
+
+    def handle(self, msg, worker):
+        """
+        Handles the given message by performing actions on the worker object.
+
+        Args:
+            msg: The message to handle.
+            worker: The worker object that contains the methods to be called.
+        """
+
+        worker.result = msg[1]
+
+        worker.parent.on_completed()
+        worker.on_finished()
+
+
+class FailedHandler(MessageHandler):
+    """
+    FailedHandler handles the failure of the worker process.
+    """
+
+    def handle(self, msg, worker):
+        """
+        Handles the given message by invoking the worker's failure handler,
+        stopping the timer, and joining the worker thread.
+
+        Args:
+            msg: The message to handle.
+            worker: The worker object that contains the failure handler, timer, and worker thread.
+        """
+
+        worker.parent.on_failed()
+        worker.on_finished()
+
+
+class MessageHandlerFactory:
+    """
+    MessageHandlerFactory creates a handler for a given message type.
+    """
+
+    def __init__(self):
+        self.handlers = {
+            'progress': ProgressHandler(),
+            'completed': CompletedHandler(),
+            'failed': FailedHandler()
+        }
+
+    def get_handler(self, msg_type):
+        """
+        Returns a handler for the given message type.
+        """
+
+        return self.handlers.get(msg_type, None)