@@ -87,16 +87,12 @@ def dlc_camera_id(self, value: str | None) -> None:
8787
8888 def showEvent (self , event ):
8989 super ().showEvent (event )
90- try :
91- # Reset cleanup guard so close cleanup runs for each session
92- self ._cleanup_done = False
93- except Exception :
94- pass
90+ # Reset cleanup guard so close cleanup runs for each session
91+ self ._cleanup_done = False
9592
9693 # Rebuild the working copy from the latest “accepted” settings
9794 self ._working_settings = self ._multi_camera_settings .model_copy (deep = True )
9895 self ._current_edit_index = None
99- self ._populate_from_settings ()
10096
10197 # Maintain overlay geometry when resizing
10298 def resizeEvent (self , event ):
@@ -301,6 +297,9 @@ def _mark_dirty(*_args):
301297 # -------------------------------
302298 # UI state updates
303299 # -------------------------------
300+ def _is_preview_live (self ) -> bool :
301+ return self ._preview .state in (PreviewState .ACTIVE , PreviewState .LOADING )
302+
304303 def _set_apply_dirty (self , dirty : bool ) -> None :
305304 """Visually mark Apply Settings button as 'dirty' (pending edits)."""
306305 if dirty :
@@ -391,7 +390,7 @@ def _refresh_camera_labels(self) -> None:
391390
392391 def _format_camera_label (self , cam : CameraSettings , index : int = - 1 ) -> str :
393392 status = "✓" if cam .enabled else "○"
394- this_id = f"{ cam .backend } :{ cam .index } "
393+ this_id = f"{ ( cam .backend or '' ). lower () } :{ cam .index } "
395394 dlc_indicator = " [DLC]" if this_id == self ._dlc_camera_id and cam .enabled else ""
396395 return f"{ status } { cam .name } [{ cam .backend } :{ cam .index } ]{ dlc_indicator } "
397396
@@ -690,7 +689,7 @@ def _on_active_camera_selected(self, row: int) -> None:
690689 return
691690
692691 # Stop any running preview when selection changes
693- if self ._preview . state in ( PreviewState . ACTIVE , PreviewState . LOADING ):
692+ if self .is_preview_live ( ):
694693 self ._stop_preview ()
695694
696695 self ._current_edit_index = row
@@ -726,6 +725,9 @@ def _add_selected_camera(self) -> None:
726725 return
727726 item = self .available_cameras_list .item (row )
728727 detected = item .data (Qt .ItemDataRole .UserRole )
728+ if not isinstance (detected , DetectedCamera ):
729+ QMessageBox .warning (self , "Invalid Selection" , "Selected item is not a valid camera." )
730+ return
729731 # make sure this is to lower for comparison against camera_identity_key
730732 backend = (self .backend_combo .currentData () or "opencv" ).lower ()
731733
@@ -765,6 +767,8 @@ def _add_selected_camera(self) -> None:
765767 self ._start_probe_for_camera (new_cam )
766768
767769 def _remove_selected_camera (self ) -> None :
770+ if self ._is_preview_live ():
771+ self ._stop_preview ()
768772 if not self ._commit_pending_edits (reason = "before removing a camera" ):
769773 return
770774 row = self .active_cameras_list .currentRow ()
@@ -779,6 +783,8 @@ def _remove_selected_camera(self) -> None:
779783 self ._update_button_states ()
780784
781785 def _move_camera_up (self ) -> None :
786+ if self ._is_preview_live ():
787+ self ._stop_preview ()
782788 if not self ._commit_pending_edits (reason = "before reordering cameras" ):
783789 return
784790 row = self .active_cameras_list .currentRow ()
@@ -792,6 +798,8 @@ def _move_camera_up(self) -> None:
792798 self ._refresh_camera_labels ()
793799
794800 def _move_camera_down (self ) -> None :
801+ if self ._is_preview_live ():
802+ self ._stop_preview ()
795803 if not self ._commit_pending_edits (reason = "before reordering cameras" ):
796804 return
797805 row = self .active_cameras_list .currentRow ()
@@ -948,9 +956,6 @@ def _apply_camera_settings(self) -> bool:
948956
949957 diff = CameraSettings .check_diff (current_model , new_model )
950958
951- self ._working_settings .cameras [row ] = new_model
952- self ._update_active_list_item (row , new_model )
953-
954959 LOGGER .debug (
955960 "[Apply] backend=%s idx=%s changes=%s" ,
956961 getattr (new_model , "backend" , None ),
@@ -1043,7 +1048,7 @@ def _reset_selected_camera(self, *, clear_backend_cache: bool = False) -> None:
10431048 return
10441049
10451050 # Stop preview to avoid fighting an open capture
1046- if self ._preview . state in ( PreviewState . ACTIVE , PreviewState . LOADING ):
1051+ if self .is_preview_live ( ):
10471052 self ._stop_preview ()
10481053
10491054 cam = self ._working_settings .cameras [row ]
@@ -1110,7 +1115,7 @@ def _start_probe_for_camera(self, cam: CameraSettings, *, apply_to_requested: bo
11101115 requested width/height/fps with detected device values.
11111116 """
11121117 # Don’t probe if preview is active/loading
1113- if self ._preview . state in ( PreviewState . ACTIVE , PreviewState . LOADING ):
1118+ if self ._is_preview_live ( ):
11141119 return
11151120
11161121 # Track probe intent
@@ -1357,7 +1362,7 @@ def _start_preview(self) -> None:
13571362 """Start camera preview asynchronously (no UI freeze)."""
13581363 if not self ._commit_pending_edits (reason = "before starting preview" ):
13591364 return
1360- if self ._preview . state in ( PreviewState . ACTIVE , PreviewState . LOADING ):
1365+ if self ._is_preview_live ( ):
13611366 return
13621367
13631368 row = self ._current_edit_index
0 commit comments