From e494fac5dd4e8d176369fc58c66423174a8ca56b Mon Sep 17 00:00:00 2001 From: appleb_m Date: Wed, 10 Dec 2025 14:01:30 +0100 Subject: [PATCH 1/5] GUI: added camera selection to gonio_camera_thread initialization --- gui/src/aaregui/main_window.py | 3 ++- gui/src/aaregui/threads/axis_video_thread.py | 5 +++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/gui/src/aaregui/main_window.py b/gui/src/aaregui/main_window.py index 8bdaed3c..29ac523a 100644 --- a/gui/src/aaregui/main_window.py +++ b/gui/src/aaregui/main_window.py @@ -127,7 +127,8 @@ class MainWindow(QMainWindow): self.beamline_view_layout.addWidget(self.beamline_view) self.gonio_view = VideoGraphicsView() - self.gonio_camera_thread = VideoThread(ip="axis-accc8ed2972e.psi.ch") + #TODO add option to change cameras for gonio_camera_thread + self.gonio_camera_thread = VideoThread(ip="axis-accc8ed2972e.psi.ch", camera=3) self.gonio_camera_thread.frame_ready.connect(self.gonio_view.update_frame) self.gonio_camera_thread.frame_ready.connect(self.beamline_view_1_combined.update_frame) self.gonio_camera_thread.start() diff --git a/gui/src/aaregui/threads/axis_video_thread.py b/gui/src/aaregui/threads/axis_video_thread.py index b858cdd4..aaf4282c 100644 --- a/gui/src/aaregui/threads/axis_video_thread.py +++ b/gui/src/aaregui/threads/axis_video_thread.py @@ -10,19 +10,20 @@ class VideoThread(QThread): frame_ready = Signal(QImage) error_occurred = Signal(str) - def __init__(self, ip: str): + def __init__(self, ip: str, camera = 1): super().__init__() self.camera_ip = ip self.running = False self.session = None self.last_good_frame = None + self.camera: int = camera def set_camera_ip(self, ip: str): self.camera_ip = ip def run(self): # MJPEG URL - mjpeg_url = f"http://{self.camera_ip}/axis-cgi/mjpg/video.cgi?resolution=1280x720&fps=10" + mjpeg_url = f"http://{self.camera_ip}/axis-cgi/mjpg/video.cgi?resolution=1280x720&fps=20&compression=30&camera={self.camera}" # Create a session for connection reuse self.session = requests.Session() From b6ebcf0dbdf2c6326f356dc111b1a3081b2e148e Mon Sep 17 00:00:00 2001 From: appleb_m Date: Wed, 10 Dec 2025 14:19:25 +0100 Subject: [PATCH 2/5] GUI: added busy message to gonio and beamline camera thread --- gui/src/aaregui/main_window.py | 2 + gui/src/aaregui/threads/axis_video_thread.py | 49 +++++++++++++++++++- 2 files changed, 49 insertions(+), 2 deletions(-) diff --git a/gui/src/aaregui/main_window.py b/gui/src/aaregui/main_window.py index 29ac523a..e4440a0c 100644 --- a/gui/src/aaregui/main_window.py +++ b/gui/src/aaregui/main_window.py @@ -447,6 +447,8 @@ class MainWindow(QMainWindow): @Slot(DAQStatusModel) def update_daq_status(self, s: DAQStatusModel): + self.beamline_camera_thread.set_busy(s.busy) + self.gonio_camera_thread.set_busy(s.busy) if not self.__mounting and s.state == BeamlineStateEnum.RobotSampleExchange: self.__mounting = True self.video_tab.setCurrentIndex(3) diff --git a/gui/src/aaregui/threads/axis_video_thread.py b/gui/src/aaregui/threads/axis_video_thread.py index aaf4282c..547a9da9 100644 --- a/gui/src/aaregui/threads/axis_video_thread.py +++ b/gui/src/aaregui/threads/axis_video_thread.py @@ -1,8 +1,8 @@ import cv2 import requests import numpy as np -from PySide6.QtCore import QThread, Signal -from PySide6.QtGui import QImage +from PySide6.QtCore import QThread, Signal, QRect, QPoint +from PySide6.QtGui import QImage, QPainter, QPen, QColor, Qt, QFontMetrics, QFont from io import BytesIO @@ -17,10 +17,14 @@ class VideoThread(QThread): self.session = None self.last_good_frame = None self.camera: int = camera + self.is_busy = False def set_camera_ip(self, ip: str): self.camera_ip = ip + def set_busy(self, busy: bool): + self.is_busy = busy + def run(self): # MJPEG URL mjpeg_url = f"http://{self.camera_ip}/axis-cgi/mjpg/video.cgi?resolution=1280x720&fps=20&compression=30&camera={self.camera}" @@ -113,6 +117,9 @@ class VideoThread(QThread): height, width, channel = rgb_frame.shape bytes_per_line = 3 * width qt_image = QImage(rgb_frame.data, width, height, bytes_per_line, QImage.Format.Format_RGB888) + qt_image = qt_image.copy() + if self.is_busy: + self._draw_busy_overlay(qt_image) # Store as last good frame self.last_good_frame = qt_image @@ -123,6 +130,44 @@ class VideoThread(QThread): if self.last_good_frame is not None: self.frame_ready.emit(self.last_good_frame) + def _draw_busy_overlay(self, image: QImage): + painter = QPainter(image) + painter.save() + painter.resetTransform() + + font = QFont() + font.setPointSize(24) + font.setBold(True) + painter.setFont(font) + + painter.setPen(QPen(QColor(255, 255, 255, 200), 2, Qt.PenStyle.SolidLine)) + painter.setBrush(QColor(255, 0, 0, 150)) + + text = "ROBOT BUSY" + + fm = QFontMetrics(font) + text_rect = fm.boundingRect(text) + padding = 16 + + vw = self.viewport().width() + vh = self.viewport().height() + + bg_w = text_rect.width() + 2 * padding + bg_h = text_rect.height() + 2 * padding + position_x = int((vw - bg_w) / 2) + position_y = int((vh - bg_h) / 2) + + bg_rect = QRect(position_x, position_y, bg_w, bg_h) + + painter.setPen(QPen(QColor(255, 255, 255, 220))) + painter.setBrush(QColor(255, 215, 0, 180)) + painter.drawRoundedRect(bg_rect, 10, 10) + + painter.setPen(QPen(QColor(255, 255, 255))) + painter.drawText(QPoint(position_x + padding, position_y + padding + fm.ascent()), text) + + painter.restore() + def stop(self): self.running = False if self.session: From bb94f71aa784a7f8a5bbeeb13fca50664d3a1b08 Mon Sep 17 00:00:00 2001 From: appleb_m Date: Wed, 10 Dec 2025 15:47:54 +0100 Subject: [PATCH 3/5] GUI: number_line_edit.py block signals during set_busy otherwise dtza and high_res are unchecked --- gui/src/aaregui/widgets/number_line_edit.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/gui/src/aaregui/widgets/number_line_edit.py b/gui/src/aaregui/widgets/number_line_edit.py index a715170c..611e8f46 100644 --- a/gui/src/aaregui/widgets/number_line_edit.py +++ b/gui/src/aaregui/widgets/number_line_edit.py @@ -157,14 +157,22 @@ class CheckedLineEdit(QWidget): def set_busy(self, busy: bool): # if an external wants to change the busy state. #update self._busy and change editor read only state. After enable/disable check_box and set colour + self.blockSignals(True) self._busy = busy self.setReadOnly() + self.check_box.blockSignals(True) + if self._busy: self.check_box.setEnabled(False) self.check_box.setStyleSheet("background-color: rgb(240, 240, 240);") + if self._checked: + self.editor.force_update_value(self._internal_value) + else: self.check_box.setEnabled(True) - self.check_box.setStyleSheet("background-color: rgb(240, 240, 240);") + self.check_box.setStyleSheet("background-color: rgb(255, 255, 255);") + self.check_box.blockSignals(False) + self.blockSignals(False) def update_value(self, value): #wrapper for NumberLineEdit.update_value @@ -205,6 +213,7 @@ class CheckedLineEdit(QWidget): if not self._busy and self._checked: # Only set editor to writeable if busy is False and the checkbox is checked, should_be_ro = False self.editor.setReadOnly(False) + #state_changed = self.editor.isReadOnly() != should_be_ro self.editor.setReadOnly(should_be_ro) self.readOnlyChanged.emit(should_be_ro) From f8fbfe9642e1cbcf726c1d0c5be5c78598bbae5d Mon Sep 17 00:00:00 2001 From: appleb_m Date: Wed, 10 Dec 2025 16:02:45 +0100 Subject: [PATCH 4/5] GUI: bug in busy overlay - removing for now --- gui/src/aaregui/threads/axis_video_thread.py | 40 -------------------- 1 file changed, 40 deletions(-) diff --git a/gui/src/aaregui/threads/axis_video_thread.py b/gui/src/aaregui/threads/axis_video_thread.py index 547a9da9..24accb63 100644 --- a/gui/src/aaregui/threads/axis_video_thread.py +++ b/gui/src/aaregui/threads/axis_video_thread.py @@ -117,9 +117,6 @@ class VideoThread(QThread): height, width, channel = rgb_frame.shape bytes_per_line = 3 * width qt_image = QImage(rgb_frame.data, width, height, bytes_per_line, QImage.Format.Format_RGB888) - qt_image = qt_image.copy() - if self.is_busy: - self._draw_busy_overlay(qt_image) # Store as last good frame self.last_good_frame = qt_image @@ -130,43 +127,6 @@ class VideoThread(QThread): if self.last_good_frame is not None: self.frame_ready.emit(self.last_good_frame) - def _draw_busy_overlay(self, image: QImage): - painter = QPainter(image) - painter.save() - painter.resetTransform() - - font = QFont() - font.setPointSize(24) - font.setBold(True) - painter.setFont(font) - - painter.setPen(QPen(QColor(255, 255, 255, 200), 2, Qt.PenStyle.SolidLine)) - painter.setBrush(QColor(255, 0, 0, 150)) - - text = "ROBOT BUSY" - - fm = QFontMetrics(font) - text_rect = fm.boundingRect(text) - padding = 16 - - vw = self.viewport().width() - vh = self.viewport().height() - - bg_w = text_rect.width() + 2 * padding - bg_h = text_rect.height() + 2 * padding - position_x = int((vw - bg_w) / 2) - position_y = int((vh - bg_h) / 2) - - bg_rect = QRect(position_x, position_y, bg_w, bg_h) - - painter.setPen(QPen(QColor(255, 255, 255, 220))) - painter.setBrush(QColor(255, 215, 0, 180)) - painter.drawRoundedRect(bg_rect, 10, 10) - - painter.setPen(QPen(QColor(255, 255, 255))) - painter.drawText(QPoint(position_x + padding, position_y + padding + fm.ascent()), text) - - painter.restore() def stop(self): self.running = False From fdd6b192ce41b5028b5a7753cc0c223f91b8b2b3 Mon Sep 17 00:00:00 2001 From: appleb_m Date: Wed, 10 Dec 2025 16:03:55 +0100 Subject: [PATCH 5/5] GUI: changed from force to update valuye fopr high_res_enter when the spreadsheet is updated. Force_update_value for checkedlineedit always updates external but doesnt force cahnge to editor unless the box is unchecked --- gui/src/aaregui/panels/scan_settings_panel.py | 2 +- gui/src/aaregui/widgets/number_line_edit.py | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/gui/src/aaregui/panels/scan_settings_panel.py b/gui/src/aaregui/panels/scan_settings_panel.py index e572d271..4816ee5a 100644 --- a/gui/src/aaregui/panels/scan_settings_panel.py +++ b/gui/src/aaregui/panels/scan_settings_panel.py @@ -177,7 +177,7 @@ class ScanSettingsPanel(QWidget): # Handle target resolution (common to all panels) if (target_res := getattr(self._params, 'targetresolution', None)) is not None: self._high_res = target_res - self.high_res_enter.force_update_value(self._high_res) + self.high_res_enter.update_value(self._high_res) self.set_high_res(self._high_res) # Store metadata (common to all panels) diff --git a/gui/src/aaregui/widgets/number_line_edit.py b/gui/src/aaregui/widgets/number_line_edit.py index 611e8f46..35883624 100644 --- a/gui/src/aaregui/widgets/number_line_edit.py +++ b/gui/src/aaregui/widgets/number_line_edit.py @@ -183,9 +183,10 @@ class CheckedLineEdit(QWidget): def force_update_value(self, value): #wrapper for NumberLineEdit.force_update_value - #self._external_value = value - self.editor.force_update_value(value) - self._on_editor_value_changed(value) + self._external_value = value + if not self._checked: + self.editor.force_update_value(value) + self._on_editor_value_changed(value) @property def value(self) -> float: