diff --git a/src/aare/gui/main_window.py b/src/aare/gui/main_window.py index 471b342a..40b98ec4 100644 --- a/src/aare/gui/main_window.py +++ b/src/aare/gui/main_window.py @@ -1631,7 +1631,10 @@ class MainWindow(QMainWindow): if view.isVisible(): view.update_pixmap(pixmap) finally: - self.prediction_thread.notify_frame_displayed() + # The slot is only connected once the subscriber exists, but the + # attribute is Optional so the guard is what the type checker needs. + if self.prediction_thread is not None: + self.prediction_thread.notify_frame_displayed() @Slot(bool) def _on_sample_camera_availability_changed(self, available: bool) -> None: diff --git a/src/aare/gui/threads/prediction_subscriber.py b/src/aare/gui/threads/prediction_subscriber.py index 1def221c..e5c40980 100644 --- a/src/aare/gui/threads/prediction_subscriber.py +++ b/src/aare/gui/threads/prediction_subscriber.py @@ -201,10 +201,14 @@ class PredictionSubscriber(QThread): backlog that piled up while we were busy, and the stream is always consumed at line rate no matter how long a frame takes to decode. """ - parts = self._sock.recv_multipart() # honours RCVTIMEO, may raise zmq.Again + # Bind locally: run() nulls the attribute on cleanup, so it is Optional + # to the type checker even though it cannot be None while run() loops. + sock = self._sock + assert sock is not None, "_recv_latest called after the socket was closed" + parts = sock.recv_multipart() # honours RCVTIMEO, may raise zmq.Again while True: try: - parts = self._sock.recv_multipart(zmq.NOBLOCK) + parts = sock.recv_multipart(zmq.NOBLOCK) except zmq.Again: return parts self._dropped += 1