Skip to content

timelapse

timelapse #

Timelapse — pictures scrubbed along their capture instants.

Timelapse #

Bases: Component

images keyed by instant, scrubbed in time order with a range slider.

Built with of, each frame links to its entry's page; given plainly, the frames link nowhere.

cg.ui.Timelapse(target.sessions.photo)
cg.ui.Timelapse.of(target.sessions, lambda s: crop(s.photo))
Source code in capturegraph-lib/capturegraph/ui/timelapse.py
class Timelapse(Component):
    """``images`` keyed by instant, scrubbed in time order with a range slider.

    Built with ``of``, each frame links to its entry's page; given plainly, the
    frames link nowhere.

    ```python
    cg.ui.Timelapse(target.sessions.photo)
    cg.ui.Timelapse.of(target.sessions, lambda s: crop(s.photo))
    ```
    """

    images: Map
    entries: Entries | None = None

    @classmethod
    def of[K, E](
        cls,
        container: Map[K, E],
        image: Callable[[E], StillImageFile | None],
    ) -> Self:
        """A timelapse of ``image`` over ``container``'s elements, each linked to its entry's page.

        Args:
            container: A ``Map`` keyed by ``Time``, such as ``target.sessions``.
            image: The frame for one element — any still image, such as a crop;
                ``None`` (or ``Missing``) leaves that entry out.

        ```python
        cg.ui.Timelapse.of(target.sessions, lambda s: s.photo)
        ```
        """
        images, entries = entries_of(container, image, cls.__name__)
        return cls(images, entries=entries)

    def html(self, render: Render) -> str:
        """A figure showing the first frame, a slider to scrub, and every frame inline.

        The frames read in caption order, which for an instant is its time order.
        The figure takes the first frame's aspect ratio and keeps it, so frames
        of other sizes are fitted inside rather than resizing the page.
        """
        items = items_of(self.images)
        hrefs = item_hrefs(render, self.entries, len(items))
        ordered = sorted(
            (
                (caption(key), render.stage(image), href)
                for (_, key, image), href in zip(items, hrefs, strict=True)
            ),
            key=lambda frame: frame[0],
        )
        if not ordered:
            return element("figure", {"class": "cg-timelapse"}, text("No frames."))
        frames: list[JSONValue] = [
            {"caption": moment, "src": source, "href": href} for moment, source, href in ordered
        ]
        moment, source, href = ordered[0]
        identifier = render.identifier("timelapse")
        caption_parts = [element("span", None, text(moment))]
        if any(link is not None for _, _, link in ordered):
            caption_parts.append(element("a", {"href": href or ""}, text("Open")))
        return element(
            "figure",
            {"class": "cg-timelapse", "id": identifier},
            element("img", {"src": source, "alt": moment}),
            element("figcaption", None, " ".join(caption_parts)),
            element(
                "input",
                {"type": "range", "min": 0, "max": len(frames) - 1, "value": 0, "step": 1},
            ),
            element(
                "script",
                None,
                _SCRUB % {"id": inline_json(identifier), "frames": inline_json(frames)},
            ),
        )

html(render) #

A figure showing the first frame, a slider to scrub, and every frame inline.

The frames read in caption order, which for an instant is its time order. The figure takes the first frame's aspect ratio and keeps it, so frames of other sizes are fitted inside rather than resizing the page.

Source code in capturegraph-lib/capturegraph/ui/timelapse.py
def html(self, render: Render) -> str:
    """A figure showing the first frame, a slider to scrub, and every frame inline.

    The frames read in caption order, which for an instant is its time order.
    The figure takes the first frame's aspect ratio and keeps it, so frames
    of other sizes are fitted inside rather than resizing the page.
    """
    items = items_of(self.images)
    hrefs = item_hrefs(render, self.entries, len(items))
    ordered = sorted(
        (
            (caption(key), render.stage(image), href)
            for (_, key, image), href in zip(items, hrefs, strict=True)
        ),
        key=lambda frame: frame[0],
    )
    if not ordered:
        return element("figure", {"class": "cg-timelapse"}, text("No frames."))
    frames: list[JSONValue] = [
        {"caption": moment, "src": source, "href": href} for moment, source, href in ordered
    ]
    moment, source, href = ordered[0]
    identifier = render.identifier("timelapse")
    caption_parts = [element("span", None, text(moment))]
    if any(link is not None for _, _, link in ordered):
        caption_parts.append(element("a", {"href": href or ""}, text("Open")))
    return element(
        "figure",
        {"class": "cg-timelapse", "id": identifier},
        element("img", {"src": source, "alt": moment}),
        element("figcaption", None, " ".join(caption_parts)),
        element(
            "input",
            {"type": "range", "min": 0, "max": len(frames) - 1, "value": 0, "step": 1},
        ),
        element(
            "script",
            None,
            _SCRUB % {"id": inline_json(identifier), "frames": inline_json(frames)},
        ),
    )

of(container, image) classmethod #

A timelapse of image over container's elements, each linked to its entry's page.

Parameters:

Name Type Description Default
container Map[K, E]

A Map keyed by Time, such as target.sessions.

required
image Callable[[E], StillImageFile | None]

The frame for one element — any still image, such as a crop; None (or Missing) leaves that entry out.

required
cg.ui.Timelapse.of(target.sessions, lambda s: s.photo)
Source code in capturegraph-lib/capturegraph/ui/timelapse.py
@classmethod
def of[K, E](
    cls,
    container: Map[K, E],
    image: Callable[[E], StillImageFile | None],
) -> Self:
    """A timelapse of ``image`` over ``container``'s elements, each linked to its entry's page.

    Args:
        container: A ``Map`` keyed by ``Time``, such as ``target.sessions``.
        image: The frame for one element — any still image, such as a crop;
            ``None`` (or ``Missing``) leaves that entry out.

    ```python
    cg.ui.Timelapse.of(target.sessions, lambda s: s.photo)
    ```
    """
    images, entries = entries_of(container, image, cls.__name__)
    return cls(images, entries=entries)