Skip to content

docks

sleap.gui.widgets.docks

Module for creating dock widgets for the MainWindow.

Classes:

Name Description
DockWidget

'Abstract' class for a dockable widget attached to the MainWindow.

InstancesDock

Dock widget for displaying instances.

SkeletonDock

Dock widget for displaying skeleton information.

SuggestionsDock

Dock widget for displaying suggestions.

VideosDock

Dock widget for displaying video information.

DockWidget

Bases: QDockWidget

'Abstract' class for a dockable widget attached to the MainWindow.

Methods:

Name Description
add_to_window

Add the dock to the MainWindow.

create_models

Create the model for the table in the dock (if any).

create_tables

Add a table to the dock.

lay_everything_out

Lay out the dock widget, adding/creating other widgets if needed.

setup_dock

Create a dock widget.

Source code in sleap/gui/widgets/docks.py
class DockWidget(QDockWidget):
    """'Abstract' class for a dockable widget attached to the `MainWindow`."""

    def __init__(
        self,
        name: str,
        main_window: Optional[QMainWindow] = None,
        model_type: Optional[
            Union[Type[GenericTableModel], List[Type[GenericTableModel]]]
        ] = None,
        widgets: Optional[Iterable[QWidget]] = None,
        tab_with: Optional[QLayout] = None,
    ):
        # Create the dock and add it to the main window.
        super().__init__(name)
        self.name = name
        self.main_window = main_window
        self.setup_dock(widgets, tab_with)

        # Create the model and table for the dock.
        self.model_type = model_type
        if self.model_type is None:
            self.model = None
            self.table = None
        else:
            self.model = self.create_models()
            self.table = self.create_tables()

        # Lay out the dock widget, adding/creating other widgets if needed.
        self.lay_everything_out()

    @property
    def wgt_layout(self) -> QVBoxLayout:
        return self.widget().layout()

    def setup_dock(self, widgets, tab_with):
        """Create a dock widget.

        Args:
            widgets: The widgets to add to the dock.
            tab_with: The `QLayout` to tabify the `DockWidget` with.
        """

        self.setObjectName(self.name + "Dock")
        self.setAllowedAreas(Qt.LeftDockWidgetArea | Qt.RightDockWidgetArea)

        dock_widget = QWidget()
        dock_widget.setObjectName(self.name + "Widget")
        layout = QVBoxLayout()

        widgets = widgets or []
        for widget in widgets:
            layout.addWidget(widget)

        dock_widget.setLayout(layout)
        self.setWidget(dock_widget)

        self.add_to_window(self.main_window, tab_with)

    def add_to_window(self, main_window: QMainWindow, tab_with: QVBoxLayout):
        """Add the dock to the `MainWindow`.

        Args:
            tab_with: The `QLayout` to tabify the `DockWidget` with.
        """
        self.main_window = main_window
        self.main_window.addDockWidget(Qt.RightDockWidgetArea, self)
        self.main_window.viewMenu.addAction(self.toggleViewAction())

        if tab_with is not None:
            self.main_window.tabifyDockWidget(tab_with, self)

    def add_button(self, to: QLayout, label: str, action: Callable, key=None):
        key = key or label.lower()
        btn = QPushButton(label)
        btn.clicked.connect(action)
        to.addWidget(btn)
        self.main_window._buttons[key] = btn
        return btn

    def create_models(self) -> GenericTableModel:
        """Create the model for the table in the dock (if any).

        Implement this in the subclass.
        Ex:
            self.model = self.model_type(items=[], context=self.main_window.commands)

        Returns:
            The model.
        """
        raise NotImplementedError

    def create_tables(self) -> GenericTableView:
        """Add a table to the dock.

        Implement this in the subclass.
        Ex:
            self.table = GenericTableView(
                state=self.main_window.state,
                model=self.model or self.create_models(),
            )
            self.wgt_layout.addWidget(self.table)

        Returns:
            The table widget.
        """
        raise NotImplementedError

    def lay_everything_out(self) -> None:
        """Lay out the dock widget, adding/creating other widgets if needed.

        Implement this in the subclass. No example as this is extremely custom.
        """
        raise NotImplementedError

add_to_window(main_window, tab_with)

Add the dock to the MainWindow.

Parameters:

Name Type Description Default
tab_with QVBoxLayout

The QLayout to tabify the DockWidget with.

required
Source code in sleap/gui/widgets/docks.py
def add_to_window(self, main_window: QMainWindow, tab_with: QVBoxLayout):
    """Add the dock to the `MainWindow`.

    Args:
        tab_with: The `QLayout` to tabify the `DockWidget` with.
    """
    self.main_window = main_window
    self.main_window.addDockWidget(Qt.RightDockWidgetArea, self)
    self.main_window.viewMenu.addAction(self.toggleViewAction())

    if tab_with is not None:
        self.main_window.tabifyDockWidget(tab_with, self)

create_models()

Create the model for the table in the dock (if any).

Implement this in the subclass. Ex: self.model = self.model_type(items=[], context=self.main_window.commands)

Returns:

Type Description
GenericTableModel

The model.

Source code in sleap/gui/widgets/docks.py
def create_models(self) -> GenericTableModel:
    """Create the model for the table in the dock (if any).

    Implement this in the subclass.
    Ex:
        self.model = self.model_type(items=[], context=self.main_window.commands)

    Returns:
        The model.
    """
    raise NotImplementedError

create_tables()

Add a table to the dock.

Implement this in the subclass. Ex: self.table = GenericTableView( state=self.main_window.state, model=self.model or self.create_models(), ) self.wgt_layout.addWidget(self.table)

Returns:

Type Description
GenericTableView

The table widget.

Source code in sleap/gui/widgets/docks.py
def create_tables(self) -> GenericTableView:
    """Add a table to the dock.

    Implement this in the subclass.
    Ex:
        self.table = GenericTableView(
            state=self.main_window.state,
            model=self.model or self.create_models(),
        )
        self.wgt_layout.addWidget(self.table)

    Returns:
        The table widget.
    """
    raise NotImplementedError

lay_everything_out()

Lay out the dock widget, adding/creating other widgets if needed.

Implement this in the subclass. No example as this is extremely custom.

Source code in sleap/gui/widgets/docks.py
def lay_everything_out(self) -> None:
    """Lay out the dock widget, adding/creating other widgets if needed.

    Implement this in the subclass. No example as this is extremely custom.
    """
    raise NotImplementedError

setup_dock(widgets, tab_with)

Create a dock widget.

Parameters:

Name Type Description Default
widgets

The widgets to add to the dock.

required
tab_with

The QLayout to tabify the DockWidget with.

required
Source code in sleap/gui/widgets/docks.py
def setup_dock(self, widgets, tab_with):
    """Create a dock widget.

    Args:
        widgets: The widgets to add to the dock.
        tab_with: The `QLayout` to tabify the `DockWidget` with.
    """

    self.setObjectName(self.name + "Dock")
    self.setAllowedAreas(Qt.LeftDockWidgetArea | Qt.RightDockWidgetArea)

    dock_widget = QWidget()
    dock_widget.setObjectName(self.name + "Widget")
    layout = QVBoxLayout()

    widgets = widgets or []
    for widget in widgets:
        layout.addWidget(widget)

    dock_widget.setLayout(layout)
    self.setWidget(dock_widget)

    self.add_to_window(self.main_window, tab_with)

InstancesDock

Bases: DockWidget

Dock widget for displaying instances.

Source code in sleap/gui/widgets/docks.py
class InstancesDock(DockWidget):
    """Dock widget for displaying instances."""

    def __init__(self, main_window: QMainWindow, tab_with: Optional[QLayout] = None):
        super().__init__(
            name="Instances",
            main_window=main_window,
            model_type=LabeledFrameTableModel,
            tab_with=tab_with,
        )

    def create_models(self) -> LabeledFrameTableModel:
        self.model = self.model_type(
            items=self.main_window.state["labeled_frame"],
            context=self.main_window.commands,
        )
        return self.model

    def create_tables(self) -> GenericTableView:
        # InstancesTableView adds shift/ctrl multi-select so a second instance
        # can be picked as the merge donor (see Merge Instance).
        self.table = InstancesTableView(
            state=self.main_window.state,
            row_name="instance",
            name_prefix="",
            model=self.model,
        )

        # Apply initial visibility for the optional "mean node score" column,
        # and keep it in sync with the View-menu toggle.
        self._apply_mean_node_score_visibility()
        self.main_window.state.connect(
            "show mean node score",
            lambda _: self._apply_mean_node_score_visibility(),
        )

        # Keep the per-instance checkbox columns ("visibility"/"view only")
        # narrow so they don't crowd out the informational columns.
        self._size_checkbox_columns()

        return self.table

    def _size_checkbox_columns(self) -> None:
        """Resize the visibility/view-only checkbox columns to their contents."""
        header = self.table.horizontalHeader()
        for key in (
            LabeledFrameTableModel.VISIBILITY_KEY,
            LabeledFrameTableModel.VIEW_ONLY_KEY,
        ):
            try:
                col_idx = self.model.properties.index(key)
            except ValueError:
                continue
            header.setSectionResizeMode(col_idx, QHeaderView.ResizeToContents)

    def _apply_mean_node_score_visibility(self) -> None:
        """Hide or show the 'mean node score' column based on the View toggle."""
        if not hasattr(self, "table") or self.table is None:
            return
        try:
            col_idx = self.model.properties.index("mean node score")
        except ValueError:
            return
        show = bool(self.main_window.state.get("show mean node score", default=False))
        self.table.setColumnHidden(col_idx, not show)

    def lay_everything_out(self) -> None:
        self.wgt_layout.addWidget(self.table)

        table_edit_buttons = self.create_table_edit_buttons()
        self.wgt_layout.addWidget(table_edit_buttons)

    def create_table_edit_buttons(self) -> QWidget:
        main_window = self.main_window

        hb = QHBoxLayout()
        self.add_button(
            hb, "New Instance", lambda x: main_window.commands.newInstance(offset=10)
        )
        self.add_button(
            hb, "Delete Instance", main_window.commands.deleteSelectedInstance
        )
        self.add_button(
            hb, "Merge Instance", lambda *_: main_window.commands.mergeInstance()
        )

        hbw = QWidget()
        hbw.setLayout(hb)
        return hbw

SkeletonDock

Bases: DockWidget

Dock widget for displaying skeleton information.

Methods:

Name Description
create_project_skeleton_groupbox

Create the groupbox for the project skeleton.

create_templates_groupbox

Create the groupbox for the skeleton templates.

lay_everything_out

Lay out the dock widget, adding/creating other widgets if needed.

Source code in sleap/gui/widgets/docks.py
class SkeletonDock(DockWidget):
    """Dock widget for displaying skeleton information."""

    def __init__(
        self,
        main_window: Optional[QMainWindow] = None,
        tab_with: Optional[QLayout] = None,
    ):
        self.nodes_model_type = SkeletonNodesTableModel
        self.edges_model_type = SkeletonEdgesTableModel
        super().__init__(
            name="Skeleton",
            main_window=main_window,
            model_type=[self.nodes_model_type, self.edges_model_type],
            tab_with=tab_with,
        )

    def create_models(self) -> GenericTableModel:
        main_window = self.main_window
        self.nodes_model = self.nodes_model_type(
            items=main_window.state["skeleton"], context=main_window.commands
        )
        self.edges_model = self.edges_model_type(
            items=main_window.state["skeleton"], context=main_window.commands
        )
        return [self.nodes_model, self.edges_model]

    def create_tables(self) -> GenericTableView:
        if self.model is None:
            self.create_models()

        main_window = self.main_window
        self.nodes_table = GenericTableView(
            state=main_window.state,
            row_name="node",
            model=self.nodes_model,
        )

        self.edges_table = GenericTableView(
            state=main_window.state,
            row_name="edge",
            model=self.edges_model,
        )

        return [self.nodes_table, self.edges_table]

    def create_project_skeleton_groupbox(self) -> QGroupBox:
        """Create the groupbox for the project skeleton."""
        main_window = self.main_window
        gb = QGroupBox("Project Skeleton")
        vgb = QVBoxLayout()

        nodes_widget = QWidget()
        vb = QVBoxLayout()
        graph_tabs = QTabWidget()

        vb.addWidget(self.nodes_table)
        hb = QHBoxLayout()
        self.add_button(hb, "New Node", main_window.commands.newNode)
        self.add_button(hb, "Delete Node", main_window.commands.deleteNode)

        hbw = QWidget()
        hbw.setLayout(hb)
        vb.addWidget(hbw)
        nodes_widget.setLayout(vb)
        graph_tabs.addTab(nodes_widget, "Nodes")

        def _update_edge_src():
            self.skeletonEdgesDst.model().skeleton = main_window.state["skeleton"]

        edges_widget = QWidget()

        vb = QVBoxLayout()
        vb.addWidget(self.edges_table)

        hb = QHBoxLayout()
        self.skeletonEdgesSrc = QComboBox()
        self.skeletonEdgesSrc.setEditable(False)
        self.skeletonEdgesSrc.currentIndexChanged.connect(_update_edge_src)
        self.skeletonEdgesSrc.setModel(SkeletonNodeModel(main_window.state["skeleton"]))
        hb.addWidget(self.skeletonEdgesSrc)
        hb.addWidget(QLabel("to"))
        self.skeletonEdgesDst = QComboBox()
        self.skeletonEdgesDst.setEditable(False)
        hb.addWidget(self.skeletonEdgesDst)
        self.skeletonEdgesDst.setModel(
            SkeletonNodeModel(
                main_window.state["skeleton"],
                lambda: self.skeletonEdgesSrc.currentText(),
            )
        )

        def new_edge():
            src_node = self.skeletonEdgesSrc.currentText()
            dst_node = self.skeletonEdgesDst.currentText()
            main_window.commands.newEdge(src_node, dst_node)

        self.add_button(hb, "Add Edge", new_edge)
        self.add_button(hb, "Delete Edge", main_window.commands.deleteEdge)
        hbw = QWidget()
        hbw.setLayout(hb)
        vb.addWidget(hbw)
        edges_widget.setLayout(vb)
        graph_tabs.addTab(edges_widget, "Edges")
        vgb.addWidget(graph_tabs)

        hb = QHBoxLayout()
        self.add_button(hb, "Load From File...", main_window.commands.openSkeleton)
        self.add_button(hb, "Save As...", main_window.commands.saveSkeleton)

        hbw = QWidget()
        hbw.setLayout(hb)
        vgb.addWidget(hbw)

        # Add graph tabs to "Project Skeleton" group box
        gb.setLayout(vgb)
        return gb

    def create_templates_groupbox(self) -> QGroupBox:
        """Create the groupbox for the skeleton templates."""
        main_window = self.main_window

        gb = CollapsibleWidget("Templates")
        vb = QVBoxLayout()
        hb = QHBoxLayout()

        skeletons_folder = get_package_file("skeletons")
        skeletons_json_files = find_files_by_suffix(
            skeletons_folder, suffix=".json", depth=1
        )
        skeletons_names = [json.name.split(".")[0] for json in skeletons_json_files]
        self.skeleton_templates = QComboBox()
        self.skeleton_templates.addItems(skeletons_names)
        self.skeleton_templates.setEditable(False)
        hb.addWidget(self.skeleton_templates)
        self.add_button(hb, "Load", main_window.commands.openSkeletonTemplate)
        hbw = QWidget()
        hbw.setLayout(hb)
        vb.addWidget(hbw)

        hb = QHBoxLayout()
        self.skeleton_preview_image = QLabel("Preview Skeleton")
        hb.addWidget(self.skeleton_preview_image)
        hb.setAlignment(self.skeleton_preview_image, Qt.AlignLeft)

        self.skeleton_description = QLabel(
            f"<strong>Description:</strong> {main_window.state['skeleton_description']}"
        )
        self.skeleton_description.setWordWrap(True)
        hb.addWidget(self.skeleton_description)
        hb.setAlignment(self.skeleton_description, Qt.AlignLeft)

        hbw = QWidget()
        hbw.setLayout(hb)
        vb.addWidget(hbw)

        def updatePreviewImage(preview_image_bytes: bytes):
            # Decode the preview image
            preview_image = decode_preview_image(preview_image_bytes)

            # Create a QImage from the Image
            preview_image = QtGui.QImage(
                preview_image.tobytes(),
                preview_image.size[0],
                preview_image.size[1],
                QtGui.QImage.Format_RGBA8888,  # Format for RGBA images (see Image.mode)
            )

            preview_image = QtGui.QPixmap.fromImage(preview_image)

            self.skeleton_preview_image.setPixmap(preview_image)

        def decode_preview_image(img_b64: bytes, return_bytes: bool = False):
            """Decode a skeleton preview img byte string repr to a `PIL.Image`

            Args:
                img_b64: a byte string representation of a skeleton preview image
                return_bytes: whether to return the decoded image as bytes

            Returns:
                Either a PIL.Image of the skeleton preview image or the decoded image
                as bytes
                (if `return_bytes` is True).
            """
            bytes = base64.b64decode(img_b64)
            if return_bytes:
                return bytes

            buffer = BytesIO(bytes)
            img = Image.open(buffer)
            return img

        def update_skeleton_preview(idx: int):
            with open(skeletons_json_files[idx], "r") as f:
                skeleton_data = json.load(f)
            skel = SkeletonDecoder().decode(data=skeleton_data["nx_graph"])
            description = f"{skel.name}"
            main_window.state["skeleton_description"] = (
                f"<strong>Skeleton name:</strong> {description}<br><br>"
                f"<strong>Nodes({len(skel.node_names)}):</strong>"
                f"{', '.join(skel.node_names)}"
            )
            self.skeleton_description.setText(main_window.state["skeleton_description"])
            updatePreviewImage(
                decode_preview_image(
                    skeleton_data["preview_image"]["py/b64"], return_bytes=True
                )
            )

        self.skeleton_templates.currentIndexChanged.connect(update_skeleton_preview)
        update_skeleton_preview(idx=0)

        gb.set_content_layout(vb)
        return gb

    def lay_everything_out(self):
        """Lay out the dock widget, adding/creating other widgets if needed."""
        templates_gb = self.create_templates_groupbox()
        self.wgt_layout.addWidget(templates_gb)

        project_skeleton_groupbox = self.create_project_skeleton_groupbox()
        self.wgt_layout.addWidget(project_skeleton_groupbox)

create_project_skeleton_groupbox()

Create the groupbox for the project skeleton.

Source code in sleap/gui/widgets/docks.py
def create_project_skeleton_groupbox(self) -> QGroupBox:
    """Create the groupbox for the project skeleton."""
    main_window = self.main_window
    gb = QGroupBox("Project Skeleton")
    vgb = QVBoxLayout()

    nodes_widget = QWidget()
    vb = QVBoxLayout()
    graph_tabs = QTabWidget()

    vb.addWidget(self.nodes_table)
    hb = QHBoxLayout()
    self.add_button(hb, "New Node", main_window.commands.newNode)
    self.add_button(hb, "Delete Node", main_window.commands.deleteNode)

    hbw = QWidget()
    hbw.setLayout(hb)
    vb.addWidget(hbw)
    nodes_widget.setLayout(vb)
    graph_tabs.addTab(nodes_widget, "Nodes")

    def _update_edge_src():
        self.skeletonEdgesDst.model().skeleton = main_window.state["skeleton"]

    edges_widget = QWidget()

    vb = QVBoxLayout()
    vb.addWidget(self.edges_table)

    hb = QHBoxLayout()
    self.skeletonEdgesSrc = QComboBox()
    self.skeletonEdgesSrc.setEditable(False)
    self.skeletonEdgesSrc.currentIndexChanged.connect(_update_edge_src)
    self.skeletonEdgesSrc.setModel(SkeletonNodeModel(main_window.state["skeleton"]))
    hb.addWidget(self.skeletonEdgesSrc)
    hb.addWidget(QLabel("to"))
    self.skeletonEdgesDst = QComboBox()
    self.skeletonEdgesDst.setEditable(False)
    hb.addWidget(self.skeletonEdgesDst)
    self.skeletonEdgesDst.setModel(
        SkeletonNodeModel(
            main_window.state["skeleton"],
            lambda: self.skeletonEdgesSrc.currentText(),
        )
    )

    def new_edge():
        src_node = self.skeletonEdgesSrc.currentText()
        dst_node = self.skeletonEdgesDst.currentText()
        main_window.commands.newEdge(src_node, dst_node)

    self.add_button(hb, "Add Edge", new_edge)
    self.add_button(hb, "Delete Edge", main_window.commands.deleteEdge)
    hbw = QWidget()
    hbw.setLayout(hb)
    vb.addWidget(hbw)
    edges_widget.setLayout(vb)
    graph_tabs.addTab(edges_widget, "Edges")
    vgb.addWidget(graph_tabs)

    hb = QHBoxLayout()
    self.add_button(hb, "Load From File...", main_window.commands.openSkeleton)
    self.add_button(hb, "Save As...", main_window.commands.saveSkeleton)

    hbw = QWidget()
    hbw.setLayout(hb)
    vgb.addWidget(hbw)

    # Add graph tabs to "Project Skeleton" group box
    gb.setLayout(vgb)
    return gb

create_templates_groupbox()

Create the groupbox for the skeleton templates.

Source code in sleap/gui/widgets/docks.py
def create_templates_groupbox(self) -> QGroupBox:
    """Create the groupbox for the skeleton templates."""
    main_window = self.main_window

    gb = CollapsibleWidget("Templates")
    vb = QVBoxLayout()
    hb = QHBoxLayout()

    skeletons_folder = get_package_file("skeletons")
    skeletons_json_files = find_files_by_suffix(
        skeletons_folder, suffix=".json", depth=1
    )
    skeletons_names = [json.name.split(".")[0] for json in skeletons_json_files]
    self.skeleton_templates = QComboBox()
    self.skeleton_templates.addItems(skeletons_names)
    self.skeleton_templates.setEditable(False)
    hb.addWidget(self.skeleton_templates)
    self.add_button(hb, "Load", main_window.commands.openSkeletonTemplate)
    hbw = QWidget()
    hbw.setLayout(hb)
    vb.addWidget(hbw)

    hb = QHBoxLayout()
    self.skeleton_preview_image = QLabel("Preview Skeleton")
    hb.addWidget(self.skeleton_preview_image)
    hb.setAlignment(self.skeleton_preview_image, Qt.AlignLeft)

    self.skeleton_description = QLabel(
        f"<strong>Description:</strong> {main_window.state['skeleton_description']}"
    )
    self.skeleton_description.setWordWrap(True)
    hb.addWidget(self.skeleton_description)
    hb.setAlignment(self.skeleton_description, Qt.AlignLeft)

    hbw = QWidget()
    hbw.setLayout(hb)
    vb.addWidget(hbw)

    def updatePreviewImage(preview_image_bytes: bytes):
        # Decode the preview image
        preview_image = decode_preview_image(preview_image_bytes)

        # Create a QImage from the Image
        preview_image = QtGui.QImage(
            preview_image.tobytes(),
            preview_image.size[0],
            preview_image.size[1],
            QtGui.QImage.Format_RGBA8888,  # Format for RGBA images (see Image.mode)
        )

        preview_image = QtGui.QPixmap.fromImage(preview_image)

        self.skeleton_preview_image.setPixmap(preview_image)

    def decode_preview_image(img_b64: bytes, return_bytes: bool = False):
        """Decode a skeleton preview img byte string repr to a `PIL.Image`

        Args:
            img_b64: a byte string representation of a skeleton preview image
            return_bytes: whether to return the decoded image as bytes

        Returns:
            Either a PIL.Image of the skeleton preview image or the decoded image
            as bytes
            (if `return_bytes` is True).
        """
        bytes = base64.b64decode(img_b64)
        if return_bytes:
            return bytes

        buffer = BytesIO(bytes)
        img = Image.open(buffer)
        return img

    def update_skeleton_preview(idx: int):
        with open(skeletons_json_files[idx], "r") as f:
            skeleton_data = json.load(f)
        skel = SkeletonDecoder().decode(data=skeleton_data["nx_graph"])
        description = f"{skel.name}"
        main_window.state["skeleton_description"] = (
            f"<strong>Skeleton name:</strong> {description}<br><br>"
            f"<strong>Nodes({len(skel.node_names)}):</strong>"
            f"{', '.join(skel.node_names)}"
        )
        self.skeleton_description.setText(main_window.state["skeleton_description"])
        updatePreviewImage(
            decode_preview_image(
                skeleton_data["preview_image"]["py/b64"], return_bytes=True
            )
        )

    self.skeleton_templates.currentIndexChanged.connect(update_skeleton_preview)
    update_skeleton_preview(idx=0)

    gb.set_content_layout(vb)
    return gb

lay_everything_out()

Lay out the dock widget, adding/creating other widgets if needed.

Source code in sleap/gui/widgets/docks.py
def lay_everything_out(self):
    """Lay out the dock widget, adding/creating other widgets if needed."""
    templates_gb = self.create_templates_groupbox()
    self.wgt_layout.addWidget(templates_gb)

    project_skeleton_groupbox = self.create_project_skeleton_groupbox()
    self.wgt_layout.addWidget(project_skeleton_groupbox)

SuggestionsDock

Bases: DockWidget

Dock widget for displaying suggestions.

Source code in sleap/gui/widgets/docks.py
class SuggestionsDock(DockWidget):
    """Dock widget for displaying suggestions."""

    def __init__(self, main_window: QMainWindow, tab_with: Optional[QLayout] = None):
        super().__init__(
            name="Labeling Suggestions",
            main_window=main_window,
            model_type=SuggestionsTableModel,
            tab_with=tab_with,
        )

    def create_models(self) -> SuggestionsTableModel:
        self.model = self.model_type(
            items=self.main_window.labels.suggestions, context=self.main_window.commands
        )
        return self.model

    def create_tables(self) -> GenericTableView:
        self.table = GenericTableView(
            state=self.main_window.state,
            is_sortable=True,
            model=self.model,
        )

        # Connect some actions to the table
        def goto_suggestion(*args):
            selected_frame = self.table.getSelectedRowItem()
            self.main_window.commands.gotoVideoAndFrame(
                selected_frame.video, selected_frame.frame_idx
            )

        self.table.doubleClicked.connect(goto_suggestion)
        self.main_window.state.connect("suggestion_idx", self.table.selectRow)

        return self.table

    def lay_everything_out(self) -> None:
        self.wgt_layout.addWidget(self.table)

        table_edit_buttons = self.create_table_edit_buttons()
        self.wgt_layout.addWidget(table_edit_buttons)

        table_nav_buttons = self.create_table_nav_buttons()
        self.wgt_layout.addWidget(table_nav_buttons)

        self.suggestions_form_widget = self.create_suggestions_form()
        self.wgt_layout.addWidget(self.suggestions_form_widget)

    def create_table_nav_buttons(self) -> QWidget:
        main_window = self.main_window
        hb = QHBoxLayout()

        self.add_button(
            hb,
            "Previous",
            main_window.process_events_then(main_window.commands.prevSuggestedFrame),
            "goto previous suggestion",
        )

        self.suggested_count_label = QLabel()
        hb.addWidget(self.suggested_count_label)

        self.add_button(
            hb,
            "Next",
            main_window.process_events_then(main_window.commands.nextSuggestedFrame),
            "goto next suggestion",
        )

        hbw = QWidget()
        hbw.setLayout(hb)
        return hbw

    def create_suggestions_form(self) -> QWidget:
        main_window = self.main_window
        suggestions_form_widget = YamlFormWidget.from_name(
            "suggestions",
            title="Generate Suggestions",
        )
        suggestions_form_widget.mainAction.connect(
            main_window.process_events_then(main_window.commands.generateSuggestions)
        )
        return suggestions_form_widget

    def create_table_edit_buttons(self) -> QWidget:
        main_window = self.main_window
        hb = QHBoxLayout()

        self.add_button(
            hb,
            "Add current frame",
            main_window.process_events_then(
                main_window.commands.addCurrentFrameAsSuggestion
            ),
            "add current frame as suggestion",
        )

        self.add_button(
            hb,
            "Remove",
            main_window.process_events_then(main_window.commands.removeSuggestion),
            "remove suggestion",
        )

        self.add_button(
            hb,
            "Clear all",
            main_window.process_events_then(main_window.commands.clearSuggestions),
            "clear suggestions",
        )

        hbw = QWidget()
        hbw.setLayout(hb)
        return hbw

VideosDock

Bases: DockWidget

Dock widget for displaying video information.

Methods:

Name Description
create_video_edit_and_nav_buttons

Create the buttons for editing and navigating videos in table.

lay_everything_out

Lay out the dock widget, adding/creating other widgets if needed.

Source code in sleap/gui/widgets/docks.py
class VideosDock(DockWidget):
    """Dock widget for displaying video information."""

    def __init__(
        self,
        main_window: Optional[QMainWindow] = None,
    ):
        super().__init__(
            name="Videos", main_window=main_window, model_type=VideosTableModel
        )

    def create_models(self) -> VideosTableModel:
        self.model = self.model_type(
            items=self.main_window.labels.videos, context=self.main_window.commands
        )
        return self.model

    def create_tables(self) -> GenericTableView:
        if self.model is None:
            self.create_models()

        main_window = self.main_window
        self.table = GenericTableView(
            state=main_window.state,
            row_name="video",
            is_activatable=True,
            model=self.model,
            ellipsis_left=True,
            multiple_selection=True,
        )

        return self.table

    def create_video_edit_and_nav_buttons(self) -> QWidget:
        """Create the buttons for editing and navigating videos in table."""
        main_window = self.main_window

        hb = QHBoxLayout()
        self.add_button(hb, "Toggle Grayscale", main_window.commands.toggleGrayscale)
        self.add_button(hb, "Show Video", self.table.activateSelected)
        self.add_button(hb, "Add Videos", main_window.commands.addVideo)
        self.add_button(hb, "Replace Videos", main_window.commands.replaceVideo)
        self.add_button(hb, "Remove Video", main_window.commands.removeVideo)
        hbw = QWidget()
        hbw.setLayout(hb)
        return hbw

    def lay_everything_out(self):
        """Lay out the dock widget, adding/creating other widgets if needed."""
        self.wgt_layout.addWidget(self.table)

        video_edit_and_nav_buttons = self.create_video_edit_and_nav_buttons()
        self.wgt_layout.addWidget(video_edit_and_nav_buttons)

create_video_edit_and_nav_buttons()

Create the buttons for editing and navigating videos in table.

Source code in sleap/gui/widgets/docks.py
def create_video_edit_and_nav_buttons(self) -> QWidget:
    """Create the buttons for editing and navigating videos in table."""
    main_window = self.main_window

    hb = QHBoxLayout()
    self.add_button(hb, "Toggle Grayscale", main_window.commands.toggleGrayscale)
    self.add_button(hb, "Show Video", self.table.activateSelected)
    self.add_button(hb, "Add Videos", main_window.commands.addVideo)
    self.add_button(hb, "Replace Videos", main_window.commands.replaceVideo)
    self.add_button(hb, "Remove Video", main_window.commands.removeVideo)
    hbw = QWidget()
    hbw.setLayout(hb)
    return hbw

lay_everything_out()

Lay out the dock widget, adding/creating other widgets if needed.

Source code in sleap/gui/widgets/docks.py
def lay_everything_out(self):
    """Lay out the dock widget, adding/creating other widgets if needed."""
    self.wgt_layout.addWidget(self.table)

    video_edit_and_nav_buttons = self.create_video_edit_and_nav_buttons()
    self.wgt_layout.addWidget(video_edit_and_nav_buttons)