diff --git a/src/jabs/ui/dialogs/__init__.py b/src/jabs/ui/dialogs/__init__.py new file mode 100644 index 00000000..d9e10510 --- /dev/null +++ b/src/jabs/ui/dialogs/__init__.py @@ -0,0 +1,29 @@ +"""JABS UI Dialogs Package.""" + +from .about_dialog import AboutDialog +from .annotation_edit_dialog import AnnotationEditDialog +from .annotation_info_dialog import AnnotationInfoDialog +from .archive_behavior_dialog import ArchiveBehaviorDialog +from .behavior_search_dialog import BehaviorSearchDialog, BehaviorSearchQuery +from .license_dialog import LicenseAgreementDialog +from .message_dialog import MessageDialog +from .progress_dialog import CustomProgressDialog +from .project_pruning_dialog import ProjectPruningDialog +from .training_report import TrainingReportDialog +from .update_check_dialog import UpdateCheckDialog +from .user_guide_dialog import UserGuideDialog + +__all__ = [ + "AboutDialog", + "AnnotationEditDialog", + "AnnotationInfoDialog", + "ArchiveBehaviorDialog", + "BehaviorSearchDialog", + "CustomProgressDialog", + "LicenseAgreementDialog", + "MessageDialog", + "ProjectPruningDialog", + "TrainingReportDialog", + "UpdateCheckDialog", + "UserGuideDialog", +] diff --git a/src/jabs/ui/about_dialog.py b/src/jabs/ui/dialogs/about_dialog.py similarity index 100% rename from src/jabs/ui/about_dialog.py rename to src/jabs/ui/dialogs/about_dialog.py diff --git a/src/jabs/ui/annotation_edit_dialog.py b/src/jabs/ui/dialogs/annotation_edit_dialog.py similarity index 100% rename from src/jabs/ui/annotation_edit_dialog.py rename to src/jabs/ui/dialogs/annotation_edit_dialog.py diff --git a/src/jabs/ui/player_widget/overlays/annotation_info_dialog.py b/src/jabs/ui/dialogs/annotation_info_dialog.py similarity index 97% rename from src/jabs/ui/player_widget/overlays/annotation_info_dialog.py rename to src/jabs/ui/dialogs/annotation_info_dialog.py index 1be93c9e..86e414f8 100644 --- a/src/jabs/ui/player_widget/overlays/annotation_info_dialog.py +++ b/src/jabs/ui/dialogs/annotation_info_dialog.py @@ -14,11 +14,12 @@ ) from qt_material_icons import MaterialIcon -from jabs.ui.annotation_edit_dialog import AnnotationEditDialog from jabs.ui.util import find_central_widget +from .annotation_edit_dialog import AnnotationEditDialog + if TYPE_CHECKING: - from jabs.ui.central_widget import CentralWidget + from ..main_window.central_widget import CentralWidget # Swatch size constant for color display SWATCH_SIZE = 20 diff --git a/src/jabs/ui/archive_behavior_dialog.py b/src/jabs/ui/dialogs/archive_behavior_dialog.py similarity index 100% rename from src/jabs/ui/archive_behavior_dialog.py rename to src/jabs/ui/dialogs/archive_behavior_dialog.py diff --git a/src/jabs/ui/behavior_search_dialog.py b/src/jabs/ui/dialogs/behavior_search_dialog.py similarity index 100% rename from src/jabs/ui/behavior_search_dialog.py rename to src/jabs/ui/dialogs/behavior_search_dialog.py diff --git a/src/jabs/ui/license_dialog.py b/src/jabs/ui/dialogs/license_dialog.py similarity index 98% rename from src/jabs/ui/license_dialog.py rename to src/jabs/ui/dialogs/license_dialog.py index 06f50343..c60ca341 100644 --- a/src/jabs/ui/license_dialog.py +++ b/src/jabs/ui/dialogs/license_dialog.py @@ -10,7 +10,7 @@ QVBoxLayout, ) -from ..constants import APP_NAME, APP_NAME_LONG +from jabs.constants import APP_NAME, APP_NAME_LONG def _read_license_text() -> str: diff --git a/src/jabs/ui/message_dialog.py b/src/jabs/ui/dialogs/message_dialog.py similarity index 100% rename from src/jabs/ui/message_dialog.py rename to src/jabs/ui/dialogs/message_dialog.py diff --git a/src/jabs/ui/progress_dialog.py b/src/jabs/ui/dialogs/progress_dialog.py similarity index 100% rename from src/jabs/ui/progress_dialog.py rename to src/jabs/ui/dialogs/progress_dialog.py diff --git a/src/jabs/ui/project_pruning_dialog.py b/src/jabs/ui/dialogs/project_pruning_dialog.py similarity index 100% rename from src/jabs/ui/project_pruning_dialog.py rename to src/jabs/ui/dialogs/project_pruning_dialog.py diff --git a/src/jabs/ui/training_report.py b/src/jabs/ui/dialogs/training_report.py similarity index 100% rename from src/jabs/ui/training_report.py rename to src/jabs/ui/dialogs/training_report.py diff --git a/src/jabs/ui/update_check_dialog.py b/src/jabs/ui/dialogs/update_check_dialog.py similarity index 100% rename from src/jabs/ui/update_check_dialog.py rename to src/jabs/ui/dialogs/update_check_dialog.py diff --git a/src/jabs/ui/user_guide_dialog.py b/src/jabs/ui/dialogs/user_guide_dialog.py similarity index 100% rename from src/jabs/ui/user_guide_dialog.py rename to src/jabs/ui/dialogs/user_guide_dialog.py diff --git a/src/jabs/ui/main_control_widget/__init__.py b/src/jabs/ui/main_control_widget/__init__.py new file mode 100644 index 00000000..3798945c --- /dev/null +++ b/src/jabs/ui/main_control_widget/__init__.py @@ -0,0 +1,5 @@ +"""JABS Main Control Widget""" + +from .main_control_widget import MainControlWidget + +__all__ = ["MainControlWidget"] diff --git a/src/jabs/ui/k_fold_slider_widget.py b/src/jabs/ui/main_control_widget/k_fold_slider_widget.py similarity index 100% rename from src/jabs/ui/k_fold_slider_widget.py rename to src/jabs/ui/main_control_widget/k_fold_slider_widget.py diff --git a/src/jabs/ui/label_count_widget.py b/src/jabs/ui/main_control_widget/label_count_widget.py similarity index 100% rename from src/jabs/ui/label_count_widget.py rename to src/jabs/ui/main_control_widget/label_count_widget.py diff --git a/src/jabs/ui/main_control_widget.py b/src/jabs/ui/main_control_widget/main_control_widget.py similarity index 99% rename from src/jabs/ui/main_control_widget.py rename to src/jabs/ui/main_control_widget/main_control_widget.py index 2c2bbca6..1446020f 100644 --- a/src/jabs/ui/main_control_widget.py +++ b/src/jabs/ui/main_control_widget/main_control_widget.py @@ -17,9 +17,8 @@ from jabs.classifier import Classifier from jabs.types import ClassifierType -from jabs.ui.ear_tag_icons import EarTagIconManager -from .colors import ( +from ..colors import ( BEHAVIOR_BUTTON_COLOR_BRIGHT, BEHAVIOR_BUTTON_DISABLED_COLOR, BEHAVIOR_COLOR, @@ -27,6 +26,7 @@ NOT_BEHAVIOR_COLOR, NOT_BEHAVIOR_COLOR_BRIGHT, ) +from ..ear_tag_icons import EarTagIconManager from .k_fold_slider_widget import KFoldSliderWidget from .label_count_widget import FrameLabelCountWidget diff --git a/src/jabs/ui/main_window/README.md b/src/jabs/ui/main_window/README.md index e3742dd5..fff8abf7 100644 --- a/src/jabs/ui/main_window/README.md +++ b/src/jabs/ui/main_window/README.md @@ -7,15 +7,20 @@ This package contains the refactored MainWindow implementation, split into focus ``` main_window/ ├── __init__.py # Package interface, re-exports MainWindow +├── central_widget.py # Widget managing the main content area of JABS, set as MainWindow's central widget ├── constants.py # Configuration constants and settings keys ├── menu_builder.py # Menu construction logic ├── menu_handlers.py # Menu action callbacks ├── main_window.py # Core MainWindow class -└── README.md # This file +├── README.md # This file +└── video_list_widget.py # Video List Widget, implemented as a dockable widget that can be attached to MainWindow ``` ## Module Responsibilities +### `central_widget.py` +- `CentralWidget` class: Manages the main content area of the JABS Window + ### `constants.py` - Application settings keys (e.g., `RECENT_PROJECTS_KEY`, `SESSION_TRACKING_ENABLED_KEY`) - Default values (e.g., `DEFAULT_WINDOW_WIDTH`, `DEFAULT_WINDOW_HEIGHT`) @@ -48,6 +53,9 @@ main_window/ **When to add here:** Core window functionality, widget initialization, keyboard shortcuts, or methods that need access to Qt's object model. +### `video_list_widget.py` +- `VideoListWidget` class: Dockable widget for displaying the list of videos in the project. By default, it is docked to the left side of MainWindow. It can dragged to the top or right sides, or floated as a separate window. It can be shown/hidden via the View menu. + ## Design Principles ### 1. Tight Coupling is Intentional diff --git a/src/jabs/ui/central_widget.py b/src/jabs/ui/main_window/central_widget.py similarity index 99% rename from src/jabs/ui/central_widget.py rename to src/jabs/ui/main_window/central_widget.py index fe5a51a6..94ccecd6 100644 --- a/src/jabs/ui/central_widget.py +++ b/src/jabs/ui/main_window/central_widget.py @@ -15,17 +15,16 @@ from jabs.pose_estimation import PoseEstimation, PoseEstimationV8 from jabs.project import Project, TimelineAnnotations, TrackLabels, VideoLabels from jabs.types import ClassifierType -from jabs.ui.search_bar_widget import SearchBarWidget - -from .annotation_edit_dialog import AnnotationEditDialog -from .classification_thread import ClassifyThread -from .exceptions import ThreadTerminatedError -from .main_control_widget import MainControlWidget -from .player_widget import PlayerWidget -from .progress_dialog import create_cancelable_progress_dialog -from .stacked_timeline_widget import StackedTimelineWidget -from .training_report import TrainingReportDialog -from .training_thread import TrainingThread + +from ..classification_thread import ClassifyThread +from ..dialogs import AnnotationEditDialog, TrainingReportDialog +from ..dialogs.progress_dialog import create_cancelable_progress_dialog +from ..exceptions import ThreadTerminatedError +from ..main_control_widget import MainControlWidget +from ..player_widget import PlayerWidget +from ..search_bar_widget import SearchBarWidget +from ..stacked_timeline_widget import StackedTimelineWidget +from ..training_thread import TrainingThread _CLICK_THRESHOLD = 20 _DEBOUNCE_SEARCH_DELAY_MS = 100 diff --git a/src/jabs/ui/main_window/main_window.py b/src/jabs/ui/main_window/main_window.py index 84c3c2d7..9611fd29 100644 --- a/src/jabs/ui/main_window/main_window.py +++ b/src/jabs/ui/main_window/main_window.py @@ -9,12 +9,11 @@ from jabs.utils.process_pool_manager import ProcessPoolManager from jabs.version import version_str -from ..central_widget import CentralWidget -from ..license_dialog import LicenseAgreementDialog +from ..dialogs import LicenseAgreementDialog +from ..dialogs.progress_dialog import create_progress_dialog from ..player_widget import PlayerWidget -from ..progress_dialog import create_progress_dialog from ..project_loader_thread import ProjectLoaderThread -from ..video_list_widget import VideoListDockWidget +from .central_widget import CentralWidget from .constants import ( DEFAULT_WINDOW_HEIGHT, DEFAULT_WINDOW_WIDTH, @@ -26,6 +25,7 @@ ) from .menu_builder import MenuBuilder from .menu_handlers import MenuHandlers +from .video_list_widget import VideoListDockWidget logger = logging.getLogger(__name__) diff --git a/src/jabs/ui/main_window/menu_handlers.py b/src/jabs/ui/main_window/menu_handlers.py index a91b6309..2c263f16 100644 --- a/src/jabs/ui/main_window/menu_handlers.py +++ b/src/jabs/ui/main_window/menu_handlers.py @@ -16,16 +16,18 @@ from jabs.project import export_training_data from jabs.utils import FINAL_TRAIN_SEED, check_for_update -from ..about_dialog import AboutDialog -from ..archive_behavior_dialog import ArchiveBehaviorDialog -from ..behavior_search_dialog import BehaviorSearchDialog -from ..license_dialog import LicenseAgreementDialog +from ..dialogs import ( + AboutDialog, + ArchiveBehaviorDialog, + BehaviorSearchDialog, + LicenseAgreementDialog, + ProjectPruningDialog, + UpdateCheckDialog, + UserGuideDialog, +) from ..player_widget import PlayerWidget -from ..project_pruning_dialog import ProjectPruningDialog from ..settings_dialog import SettingsDialog from ..stacked_timeline_widget import StackedTimelineWidget -from ..update_check_dialog import UpdateCheckDialog -from ..user_guide_dialog import UserGuideDialog from ..util import send_file_to_recycle_bin from .constants import SESSION_TRACKING_ENABLED_KEY, USE_NATIVE_FILE_DIALOG diff --git a/src/jabs/ui/video_list_widget.py b/src/jabs/ui/main_window/video_list_widget.py similarity index 100% rename from src/jabs/ui/video_list_widget.py rename to src/jabs/ui/main_window/video_list_widget.py diff --git a/src/jabs/ui/player_widget/overlays/annotation_overlay.py b/src/jabs/ui/player_widget/overlays/annotation_overlay.py index 1be1ead5..5985d5d2 100644 --- a/src/jabs/ui/player_widget/overlays/annotation_overlay.py +++ b/src/jabs/ui/player_widget/overlays/annotation_overlay.py @@ -3,7 +3,7 @@ from PySide6 import QtCore, QtGui -from .annotation_info_dialog import AnnotationInfoDialog +from ...dialogs import AnnotationInfoDialog from .overlay import Overlay if TYPE_CHECKING: diff --git a/src/jabs/ui/search_bar_widget.py b/src/jabs/ui/search_bar_widget.py index 3e2baf9c..70a0dd4b 100644 --- a/src/jabs/ui/search_bar_widget.py +++ b/src/jabs/ui/search_bar_widget.py @@ -73,7 +73,7 @@ def __init__(self, parent=None): layout.addWidget(self.done_button) - self.setAttribute(QtCore.Qt.WA_StyledBackground, True) + self.setAttribute(QtCore.Qt.WidgetAttribute.WA_StyledBackground, True) self.setObjectName("SearchBarWidget") self.setStyleSheet(""" #SearchBarWidget { diff --git a/tests/ui/test_training_report_dialog.py b/tests/ui/test_training_report_dialog.py index 6057213f..a6df287c 100644 --- a/tests/ui/test_training_report_dialog.py +++ b/tests/ui/test_training_report_dialog.py @@ -9,9 +9,10 @@ try: from PySide6.QtWidgets import QApplication - from jabs.ui.training_report import TrainingReportDialog + from jabs.ui.dialogs import TrainingReportDialog SKIP_UI_TESTS = False + SKIP_REASON = None except ImportError as e: # Qt/PySide6 not available (likely headless environment) SKIP_UI_TESTS = True