Source code for pysepal.scripts.thread_controller

"""Controller for running long-running tasks in a separate thread."""

import threading
from typing import Callable, List, Optional

import pysepal.sepalwidgets as sw


[docs] class TaskController:
[docs] def __init__( self, function: Callable, callback: Optional[Callable] = None, alert: Optional[sw.Alert] = None, start_button: Optional[sw.Btn] = None, stop_button: Optional[sw.Btn] = None, disable_components: Optional[List] = None, *function_args, **function_kwargs, ): """Initializes the TaskController. Args: function: The long-running function to execute. callback: A function to call with the result after the task completes. alert: An optional alert widget for displaying messages. start_button: An optional button to start the task. stop_button: An optional button to stop the task. disable_components: A list of components to disable while the task is running. *function_args: Positional arguments for the function. **function_kwargs: Keyword arguments for the function. """ self.alert = alert self.function = function self.function_args = function_args self.function_kwargs = function_kwargs self.callback = callback self.disable_components = disable_components or [] self.start_button = start_button self.stop_button = stop_button self.task_thread = None self.stop_event = threading.Event() # Set up event handlers if buttons are provided if self.start_button is not None: self.start_button.on_event("click", self.start_task) if self.stop_button is not None: self.stop_button.on_event("click", self.stop_task)
[docs] def start_task(self, *args): """Starts the long-running task in a separate thread.""" if self.task_thread is not None and self.task_thread.is_alive(): # Task is already running return try: # Clear the stop event self.stop_event.clear() # Disable components self.set_components_enabled(False) # Reset the alert if provided if self.alert is not None: self.alert.reset() # Start the task thread self.task_thread = threading.Thread(target=self._run_task) self.task_thread.start() except Exception as e: print(f"Exception in start_task: {e}")
def _run_task(self): """Runs the long-running task and handles completion.""" try: if self.start_button is not None: self.start_button.loading = True # Run the user's function result = self.function(*self.function_args, **self.function_kwargs) # Call the callback with the result, if provided if self.callback: self.callback(result) except Exception as e: # Handle exceptions and display an error message print(f"Exception in _run_task: {e}") if self.alert is not None: self.alert.append_msg(f"Error occurred: {e}", type_="error") finally: # Re-enable components self.set_components_enabled(True) if self.start_button is not None: self.start_button.loading = False if self.stop_button is not None: self.stop_button.loading = False
[docs] def stop_task(self, *args): """Signals the task to stop.""" if self.stop_button is not None: self.stop_button.loading = True # Signal the task to stop self.stop_event.set() if self.alert is not None: self.alert.append_msg("The process was interrupted by the user.", type_="warning")
[docs] def set_components_enabled(self, enabled: bool): """Enables or disables UI components.""" for component in self.disable_components: component.disabled = not enabled