Skip to content

Comments

[feat] Plugin to monitor and control the camera cooling#3367

Open
pieleric wants to merge 1 commit intodelmic:masterfrom
pieleric:feat-plugin-to-monitor-and-control-the-camera-cooling
Open

[feat] Plugin to monitor and control the camera cooling#3367
pieleric wants to merge 1 commit intodelmic:masterfrom
pieleric:feat-plugin-to-monitor-and-control-the-camera-cooling

Conversation

@pieleric
Copy link
Member

Provides an extra menu entry "Acquisition/Camera temperature..." which opens a
small window to monitor and control the camera cooling.

Copilot AI review requested due to automatic review settings February 13, 2026 17:32
@coderabbitai
Copy link

coderabbitai bot commented Feb 13, 2026

📝 Walkthrough
🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title '[feat] Plugin to monitor and control the camera cooling' directly and clearly summarizes the main change: adding a plugin for camera cooling monitoring and control.
Description check ✅ Passed The description relates to the changeset by explaining that the plugin provides a menu entry to monitor and control camera cooling, which aligns with the plugin's functionality.
Docstring Coverage ✅ Passed Docstring coverage is 88.89% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Tip

Issue Planner is now in beta. Read the docs and try it out! Share your feedback on Discord.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@plugins/camera_temperature.py`:
- Around line 55-72: The metadata-default block accesses
self._camera.targetTemperature.value before verifying the VA exists, causing an
AttributeError; move the entire block that reads metadata, checks
MD_TARGET_COOLING_TEMP, reads target_temp and calls updateMetadata (using
MD_TARGET_COOLING_TEMP, COOLING_OFF_TEMP, self._camera.getMetadata(),
self._camera.targetTemperature.value, and self._camera.updateMetadata()) to
after the two model.hasVA(self._camera, "temperature") and
model.hasVA(self._camera, "targetTemperature") guards so targetTemperature is
only accessed once its VA presence is confirmed.
- Around line 197-206: The VA callback _on_temperature_update can run after the
dialog is destroyed because call_in_wx_main uses wx.CallAfter; add a simple
guard: introduce an _is_closed boolean on the dialog (default False), set
_is_closed = True in _on_close before calling Destroy(), and at the start of
_on_temperature_update return immediately if self._is_closed (or if
self._temp_display is None) to avoid touching destroyed widgets; keep using
call_in_wx_main but ensure the early-return guard prevents access to
_temp_display after close.
🧹 Nitpick comments (1)
plugins/camera_temperature.py (1)

229-255: FIXME on line 242: target cooling temp initialization.

The comment acknowledges the issue — the previous target should be captured at init time rather than relying solely on metadata that may not exist yet. This is partially addressed by the metadata initialization in __init__ of the plugin, but if the plugin starts with cooling already off (target ≥ 25°C), no value gets stored, and DEFAULT_COOLING_TEMP is used as the fallback, which may not match the user's preference.

Would you like me to open an issue to track resolving this FIXME, or generate a fix that captures and stores the initial target temperature more robustly?

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new GUI plugin that exposes a dedicated menu entry to monitor and control camera cooling via the camera’s temperature / targetTemperature vigilant attributes.

Changes:

  • Introduces CameraTemperaturePlugin to add the menu entry Acquisition/Camera temperature....
  • Implements a non-modal wx.Dialog that subscribes to live temperature/target updates and allows toggling cooling + setting a target temperature.
  • Persists/restores the last cooling target temperature via camera metadata.

@pieleric pieleric force-pushed the feat-plugin-to-monitor-and-control-the-camera-cooling branch from 5ca23a8 to e1838bb Compare February 16, 2026 08:26
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@plugins/camera_temperature.py`:
- Around line 278-289: Remove the dialog-owned clearing of the plugin reference
from TemperatureControlDialog._on_close (delete the line that sets self._dlg =
None) and move plugin cleanup into CameraTemperaturePlugin: when creating the
dialog, bind the dialog close/destroy event to a new plugin method (e.g.,
CameraTemperaturePlugin._on_dialog_closed) that sets
CameraTemperaturePlugin._dlg = None and lets the event continue, and update
CameraTemperaturePlugin._on_menu_item to defensively check the dialog before
calling IsShown (e.g., ensure self._dlg is not None and hasattr(self._dlg,
"IsShown") or wrap IsShown in a try/except) so the plugin never calls IsShown on
a destroyed wx.Dialog.
🧹 Nitpick comments (1)
plugins/camera_temperature.py (1)

104-110: Docstring is missing the temp_cooling_off parameter.

The __init__ docstring documents parent and camera but omits the temp_cooling_off parameter.

📝 Proposed fix
         """
         Initialize the temperature control dialog.
 
         :param parent: Parent window
         :param camera: Camera component with temperature and targetTemperature VAs
+        :param temp_cooling_off: Temperature value that indicates cooling is off
         """

Provides an extra menu entry "Acquisition/Camera temperature..." which opens a
small window to monitor and control the camera cooling.
@pieleric pieleric force-pushed the feat-plugin-to-monitor-and-control-the-camera-cooling branch from e1838bb to 92eb884 Compare February 17, 2026 10:30
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
plugins/camera_temperature.py (2)

104-110: Docstring missing temp_cooling_off parameter.

♻️ Suggested fix
         """
         Initialize the temperature control dialog.
 
         :param parent: Parent window
         :param camera: Camera component with temperature and targetTemperature VAs
+        :param temp_cooling_off: Temperature value (°C) that indicates cooling is off
         """
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@plugins/camera_temperature.py` around lines 104 - 110, Docstring for __init__
in plugins/camera_temperature.py is missing the temp_cooling_off parameter;
update the method docstring for def __init__(self, parent: wx.Window, camera:
model.HwComponent, temp_cooling_off: float) -> None to include a :param
temp_cooling_off: line describing that value (e.g., temperature threshold or
value used to disable cooling) and its units/type, so callers and maintainers
understand the purpose of temp_cooling_off.

46-47: Missing type hint for main_app and missing docstring for __init__.

Per coding guidelines, all function parameters should have type hints and all functions should have docstrings.

♻️ Suggested fix
-    def __init__(self, microscope: model.Microscope, main_app) -> None:
+    def __init__(self, microscope: model.Microscope, main_app: "wx.App") -> None:
+        """
+        Initialize the camera temperature plugin.
+
+        :param microscope: The microscope model.
+        :param main_app: The main GUI application.
+        """
         super().__init__(microscope, main_app)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@plugins/camera_temperature.py` around lines 46 - 47, Add a type hint for the
main_app parameter and a short docstring to the __init__ method: update the
signature of __init__(self, microscope: model.Microscope, main_app) -> None to
include the concrete type for main_app (e.g., main_app: MainApp or the
appropriate class/interface used across the project) and add a one- or two-line
docstring describing what the constructor does and its parameters; modify the
method that calls super().__init__(microscope, main_app) to match the new
signature.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@plugins/camera_temperature.py`:
- Around line 245-252: The restored previous_target (from MD_TARGET_COOLING_TEMP
or DEFAULT_COOLING_TEMP) must be clipped to the camera's allowed
targetTemperature.range before assigning to self._camera.targetTemperature.value
and _target_spin.SetValue; fetch the allowed min/max from
self._camera.targetTemperature.range (or equivalent attributes), clamp
previous_target into [min, max], then use the clamped value for
self._camera.targetTemperature.value, _target_spin.SetValue, and the logging
call so no out-of-range value can raise an exception.

---

Duplicate comments:
In `@plugins/camera_temperature.py`:
- Around line 84-96: The plugin retains a reference to a destroyed wx.Dialog
which makes calling self._dlg.IsShown() unsafe; update _on_menu_item to first
check that self._dlg is not destroyed by using self._dlg.IsBeingDeleted() (or
guard with hasattr/validity checks) before calling IsShown(), and ensure the
dialog clears the plugin reference when closed by binding the dialog's
destroy/close event (e.g., in TemperatureControlDialog or when creating it) to a
handler that sets self._dlg = None (or uses EVT_WINDOW_DESTROY/EVT_CLOSE to call
a plugin method like _on_close to clear the reference) so subsequent menu clicks
never call methods on a destroyed native window.
- Around line 203-212: The wx.CallAfter-backed callback _on_temperature_update
can run after the dialog/widgets are destroyed; add a simple guard at the top of
_on_temperature_update (and mirror it in _on_target_temperature_update) to
return early if the dialog or target widget no longer exists. For example, check
that self still exists and that the target widget attribute (self._temp_display
and the target widget used in _on_target_temperature_update) is present/non-None
and not destroyed (e.g., if not getattr(self, "_temp_display", None) or
getattr(self, "_temp_display", "destroyed") == "destroyed": return) before
calling SetLabel; this prevents accessing destroyed widgets when call_in_wx_main
(wx.CallAfter) invokes the callback. Ensure the same pattern is applied to the
_on_target_temperature_update handler.

---

Nitpick comments:
In `@plugins/camera_temperature.py`:
- Around line 104-110: Docstring for __init__ in plugins/camera_temperature.py
is missing the temp_cooling_off parameter; update the method docstring for def
__init__(self, parent: wx.Window, camera: model.HwComponent, temp_cooling_off:
float) -> None to include a :param temp_cooling_off: line describing that value
(e.g., temperature threshold or value used to disable cooling) and its
units/type, so callers and maintainers understand the purpose of
temp_cooling_off.
- Around line 46-47: Add a type hint for the main_app parameter and a short
docstring to the __init__ method: update the signature of __init__(self,
microscope: model.Microscope, main_app) -> None to include the concrete type for
main_app (e.g., main_app: MainApp or the appropriate class/interface used across
the project) and add a one- or two-line docstring describing what the
constructor does and its parameters; modify the method that calls
super().__init__(microscope, main_app) to match the new signature.

Comment on lines +245 to +252
if is_cooling:
# Turn cooling on: restore previous target or use default
metadata = self._camera.getMetadata()
previous_target = metadata.get(MD_TARGET_COOLING_TEMP, DEFAULT_COOLING_TEMP)
self._camera.targetTemperature.value = previous_target
self._target_spin.SetValue(previous_target)
self._target_spin.Enable(True)
logging.info("Camera cooling enabled, target temperature: %.1f °C", previous_target)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Restored target temperature may be out of VA range.

previous_target (from metadata or DEFAULT_COOLING_TEMP = -75) is assigned directly to the VA without clipping. If the stored value or default falls outside the camera's targetTemperature.range, this will raise an exception.

🐛 Proposed fix
             previous_target = metadata.get(MD_TARGET_COOLING_TEMP, DEFAULT_COOLING_TEMP)
+            previous_target = self._camera.targetTemperature.clip(previous_target)
             self._camera.targetTemperature.value = previous_target
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@plugins/camera_temperature.py` around lines 245 - 252, The restored
previous_target (from MD_TARGET_COOLING_TEMP or DEFAULT_COOLING_TEMP) must be
clipped to the camera's allowed targetTemperature.range before assigning to
self._camera.targetTemperature.value and _target_spin.SetValue; fetch the
allowed min/max from self._camera.targetTemperature.range (or equivalent
attributes), clamp previous_target into [min, max], then use the clamped value
for self._camera.targetTemperature.value, _target_spin.SetValue, and the logging
call so no out-of-range value can raise an exception.

# Main sizer
main_sizer = wx.BoxSizer(wx.VERTICAL)

# Current temperature display
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can create all widgets easily using the

class SettingsPanel(wx.Panel):

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants