diff --git a/src/damaris/data/Temperature.py b/src/damaris/data/Temperature.py index 8ae5f05..67be0ba 100644 --- a/src/damaris/data/Temperature.py +++ b/src/damaris/data/Temperature.py @@ -33,23 +33,17 @@ class TemperatureResult(Resultable, Drawable): self.ylabel = "Temperature (C)" # Initialize data lists + self.x = [] if x is None else x + self.y = [] if y is None else y self.setpoint = [] if setpoint is None else setpoint - - if (x is None) and (y is None) and (desc is None) and (job_id is None) and (job_date is None): - # Empty initialization - will be filled later - self.x = [] - self.y = [] - - elif (x is not None) and (y is not None) and (desc is not None) and (job_id is not None) and (job_date is not None): - # Full initialization - self.x = x - self.y = y + + # Set metadata if provided + if desc is not None: self.description = desc + if job_id is not None: self.job_id = job_id + if job_date is not None: self.job_date = job_date - - else: - raise ValueError("Wrong usage of __init__!") def get_number_of_channels(self): """Returns 1 for temperature data.""" diff --git a/src/damaris/gui/DamarisGUI.py b/src/damaris/gui/DamarisGUI.py index 9b41c95..23b205b 100644 --- a/src/damaris/gui/DamarisGUI.py +++ b/src/damaris/gui/DamarisGUI.py @@ -998,8 +998,14 @@ class DamarisGUI: """ # Check if temperature monitoring is enabled if not config.get("enable_temperature_monitoring", False): + # Stop any existing monitor if temperature monitoring is disabled + self.stop_temperature_monitoring() return + # Stop any existing monitor before starting a new one + if self.temp_monitor is not None: + self.stop_temperature_monitoring() + controller_type = config.get("temperature_controller_type", "eurotherm") temp_interval = config.get("temperature_interval", 2.0) diff --git a/src/damaris/gui/TemperatureMonitor.py b/src/damaris/gui/TemperatureMonitor.py index dfbcf10..1b71e83 100644 --- a/src/damaris/gui/TemperatureMonitor.py +++ b/src/damaris/gui/TemperatureMonitor.py @@ -10,6 +10,14 @@ import threading import time import datetime +try: + import gi + gi.require_version('Gdk', '3.0') + from gi.repository import Gdk as gdk + HAS_GTK = True +except (ImportError, ValueError): + HAS_GTK = False + from damaris.data import TemperatureResult from damaris.tools.temperature import TemperatureController @@ -89,10 +97,42 @@ class TemperatureMonitor(threading.Thread): self.result.job_date = datetime.datetime.now() self.result.job_id = id(self) - # Optional: store in data_pool + # Optional: store in data_pool (thread-safe for GTK) if self.data_pool is not None: + # Make a deep copy of the result to avoid thread issues with self._lock: - self.data_pool[self.data_key] = self.result + # Copy the data lists + x_copy = self.result.x[:] if hasattr(self.result, 'x') and self.result.x else [] + y_copy = self.result.y[:] if hasattr(self.result, 'y') and self.result.y else [] + setpoint_copy = self.result.setpoint[:] if hasattr(self.result, 'setpoint') and self.result.setpoint else [] + job_id_copy = getattr(self.result, 'job_id', None) + job_date_copy = getattr(self.result, 'job_date', None) + desc_copy = getattr(self.result, 'description', {}).copy() if hasattr(self.result, 'description') and isinstance(getattr(self.result, 'description', None), dict) else {} + xlabel_copy = getattr(self.result, 'xlabel', "Time (s)") + ylabel_copy = getattr(self.result, 'ylabel', "Temperature (C)") + + # Create copy outside the lock to minimize lock time + result_copy = TemperatureResult( + x=x_copy, + y=y_copy, + setpoint=setpoint_copy, + desc=desc_copy, + job_id=job_id_copy, + job_date=job_date_copy + ) + result_copy.xlabel = xlabel_copy + result_copy.ylabel = ylabel_copy + + # Update data_pool in GTK thread if GTK is available + if HAS_GTK: + # Use gdk.threads_enter/leave for thread-safe GTK access + gdk.threads_enter() + try: + self.data_pool[self.data_key] = result_copy + finally: + gdk.threads_leave() + else: + self.data_pool[self.data_key] = result_copy except Exception as e: print(f"TemperatureMonitor error: {e}") @@ -112,8 +152,14 @@ class TemperatureMonitor(threading.Thread): """ self._running = False self.quit_flag.set() - # Wait for the thread to finish its current iteration - self.join(timeout=self.interval + 0.5) + # Wait for the thread to finish, with a generous timeout + # Use a longer timeout to account for potential delays + if self.is_alive(): + self.join(timeout=5.0) # Wait up to 5 seconds + if self.is_alive(): + print("Warning: TemperatureMonitor thread did not stop gracefully") + # Thread is still running, but we can't force stop it + # The next start will check if it's alive and handle it def reset(self): """