-
Notifications
You must be signed in to change notification settings - Fork 8
Add notebook integration getting-started example #39
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
4876f45
Add notebook integration getting-started example
cursoragent 57796f3
Use github-path Colab URL in getting-started README
cursoragent ee4656e
Add default Raw Messages layout to first viewer cell
cursoragent 806f04b
Pre-configure 3D panel for the SceneUpdate cell
cursoragent File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,334 @@ | ||
| { | ||
| "cells": [ | ||
| { | ||
| "cell_type": "markdown", | ||
| "id": "3cb6f824", | ||
| "metadata": {}, | ||
| "source": [ | ||
| "# Get Started with the Foxglove Notebook Integration\n", | ||
| "\n", | ||
| "[Foxglove](https://foxglove.dev/) is a multimodal robotics observability platform. The [Python SDK](https://docs.foxglove.dev/docs/getting-started/python) ships an optional **notebook integration** that lets you embed a fully-featured Foxglove viewer directly inside Jupyter, JupyterLab, Google Colab, or VS Code.\n", | ||
| "\n", | ||
| "This notebook is a runnable companion to the [Foxglove notebook integration docs](https://docs.foxglove.dev/docs/notebook). It walks through:\n", | ||
| "\n", | ||
| "1. Installing the integration\n", | ||
| "2. Creating a notebook buffer and logging messages\n", | ||
| "3. Building layouts programmatically with the `foxglove.layouts` API\n" | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "markdown", | ||
| "id": "f09dc6bc", | ||
| "metadata": {}, | ||
| "source": [ | ||
| "## Install the integration\n", | ||
| "\n", | ||
| "The notebook integration is shipped as the optional `notebook` extra of the [`foxglove-sdk`](https://pypi.org/project/foxglove-sdk/) package. In Colab or any other fresh kernel, install it like this:" | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "code", | ||
| "execution_count": null, | ||
| "id": "b47856b0", | ||
| "metadata": {}, | ||
| "outputs": [], | ||
| "source": [ | ||
| "%pip install -q \"foxglove-sdk[notebook]\"" | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "markdown", | ||
| "id": "b56daf55", | ||
| "metadata": {}, | ||
| "source": [ | ||
| "## Using the integration\n", | ||
| "\n", | ||
| "Once the integration is installed, create a notebook buffer with [`foxglove.init_notebook_buffer()`](https://docs.foxglove.dev/docs/notebook#using-the-integration). The buffer collects every message you log to the SDK's default context. When you're ready to visualize the data, call `nb_buffer.show()` — **it must be the last expression in the cell** so Jupyter renders the embedded widget.\n", | ||
| "\n", | ||
| "> ⚠️ **Always set `log_time` explicitly** when calling `foxglove.log(...)`. If you don't, the SDK uses the wall-clock time at the moment each message is logged, which can lead to surprising playback behavior in the viewer.\n", | ||
| "\n", | ||
| "The example below logs 300 `count` messages on the `/hello` topic, spaced 33 ms apart (roughly 10 seconds of buffered data), then displays the embedded Foxglove viewer." | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "code", | ||
| "execution_count": null, | ||
| "id": "d4860aa2", | ||
| "metadata": {}, | ||
| "outputs": [], | ||
| "source": [ | ||
| "import foxglove\n", | ||
| "from foxglove.layouts import Layout, RawMessagesConfig, RawMessagesPanel\n", | ||
| "\n", | ||
| "# Create a notebook buffer to collect messages.\n", | ||
| "nb_buffer = foxglove.init_notebook_buffer()\n", | ||
| "\n", | ||
| "# Log 300 messages spaced 33ms apart (~10 seconds of data).\n", | ||
| "for t in range(10 * 30):\n", | ||
| " timestamp = t * 0.033\n", | ||
| " foxglove.log(\"/hello\", {\"count\": t}, log_time=int(timestamp * 1e9))\n", | ||
| "\n", | ||
| "# Pre-configure a Raw Messages panel pointed at /hello so the embedded\n", | ||
| "# viewer shows the buffered data immediately instead of an empty default layout.\n", | ||
| "layout = Layout(\n", | ||
| " content=RawMessagesPanel(\n", | ||
| " title=\"/hello\",\n", | ||
| " config=RawMessagesConfig(topic_path=\"/hello\"),\n", | ||
| " ),\n", | ||
| ")\n", | ||
| "\n", | ||
| "# Display the data in the embedded Foxglove viewer.\n", | ||
| "# This must be the last expression in the cell for the widget to render.\n", | ||
| "nb_buffer.show(layout=layout)" | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "markdown", | ||
| "id": "435a2b05", | ||
| "metadata": {}, | ||
| "source": [ | ||
| "### Logging richer data\n", | ||
| "\n", | ||
| "The SDK ships typed channels for the [Foxglove schemas](https://docs.foxglove.dev/docs/visualization/message-schemas/introduction) so you can log 3D scenes, images, point clouds, and more — and they all render in the embedded viewer just like they would in the desktop app.\n", | ||
| "\n", | ||
| "Here we log an animated cube on the `/scene` topic. We'll reuse this data later when we build a custom layout." | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "code", | ||
| "execution_count": null, | ||
| "id": "7e59a45e", | ||
| "metadata": {}, | ||
| "outputs": [], | ||
| "source": [ | ||
| "import math\n", | ||
| "\n", | ||
| "import foxglove\n", | ||
| "from foxglove.channels import SceneUpdateChannel\n", | ||
| "from foxglove.layouts import (\n", | ||
| " BaseRendererGridLayerSettings,\n", | ||
| " BaseRendererSceneUpdateTopicSettings,\n", | ||
| " Layout,\n", | ||
| " ThreeDeeConfig,\n", | ||
| " ThreeDeePanel,\n", | ||
| ")\n", | ||
| "from foxglove.messages import (\n", | ||
| " Color,\n", | ||
| " CubePrimitive,\n", | ||
| " SceneEntity,\n", | ||
| " SceneUpdate,\n", | ||
| " Vector3,\n", | ||
| ")\n", | ||
| "\n", | ||
| "# Start with a fresh buffer so this cell stands on its own.\n", | ||
| "nb_buffer = foxglove.init_notebook_buffer()\n", | ||
| "\n", | ||
| "scene_channel = SceneUpdateChannel(\"/scene\")\n", | ||
| "\n", | ||
| "# Animate a cube whose size pulses over ~10 seconds.\n", | ||
| "for t in range(10 * 30):\n", | ||
| " timestamp = t * 0.033\n", | ||
| " size = abs(math.sin(timestamp)) + 1.0\n", | ||
| "\n", | ||
| " # Also log a scalar so we have something to plot later.\n", | ||
| " foxglove.log(\"/hello\", {\"count\": t}, log_time=int(timestamp * 1e9))\n", | ||
| "\n", | ||
| " scene_channel.log(\n", | ||
| " SceneUpdate(\n", | ||
| " entities=[\n", | ||
| " SceneEntity(\n", | ||
| " cubes=[\n", | ||
| " CubePrimitive(\n", | ||
| " size=Vector3(x=size, y=size, z=size),\n", | ||
| " color=Color(r=1.0, g=0.8, b=0.0, a=1.0),\n", | ||
| " ),\n", | ||
| " ],\n", | ||
| " ),\n", | ||
| " ],\n", | ||
| " ),\n", | ||
| " log_time=int(timestamp * 1e9),\n", | ||
| " )\n", | ||
| "\n", | ||
| "# Pre-configure a 3D panel subscribed to /scene so the embedded viewer\n", | ||
| "# immediately renders the animated cube instead of an empty default layout.\n", | ||
| "layout = Layout(\n", | ||
| " content=ThreeDeePanel(\n", | ||
| " title=\"Scene\",\n", | ||
| " config=ThreeDeeConfig(\n", | ||
| " layers={\"grid\": BaseRendererGridLayerSettings()},\n", | ||
| " topics={\"/scene\": BaseRendererSceneUpdateTopicSettings(visible=True)},\n", | ||
| " ),\n", | ||
| " ),\n", | ||
| ")\n", | ||
| "\n", | ||
| "nb_buffer.show(layout=layout)" | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "markdown", | ||
| "id": "11460217", | ||
| "metadata": {}, | ||
| "source": [ | ||
| "## Layout management\n", | ||
| "\n", | ||
| "A [layout](https://docs.foxglove.dev/docs/visualization/layouts/) is the arrangement of panels — 3D scenes, plots, images, raw messages, etc. — that turns raw data into an opinionated view of your robot. The SDK ships a Python API in [`foxglove.layouts`](https://docs.foxglove.dev/docs/notebook/layouts) for building layouts programmatically and passing them to the embedded viewer via `nb_buffer.show(layout=...)`.\n", | ||
| "\n", | ||
| "The simplest layout is a single panel. Below we build one with a `MarkdownPanel` plus a global variable, mirroring the example from the docs." | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "code", | ||
| "execution_count": null, | ||
| "id": "8a874a71", | ||
| "metadata": {}, | ||
| "outputs": [], | ||
| "source": [ | ||
| "import foxglove\n", | ||
| "from foxglove.layouts import Layout, MarkdownConfig, MarkdownPanel\n", | ||
| "\n", | ||
| "layout = Layout(\n", | ||
| " content=MarkdownPanel(\n", | ||
| " config=MarkdownConfig(markdown=\"Hello, world!\"),\n", | ||
| " ),\n", | ||
| " variables={\"my_variable\": 1},\n", | ||
| ")\n", | ||
| "\n", | ||
| "nb_buffer = foxglove.init_notebook_buffer()\n", | ||
| "nb_buffer.show(layout=layout)" | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "markdown", | ||
| "id": "cb40cd1d", | ||
| "metadata": {}, | ||
| "source": [ | ||
| "### Composing panels with containers\n", | ||
| "\n", | ||
| "Real layouts usually have multiple panels arranged side-by-side or in tabs. The `foxglove.layouts` module ships three containers for that:\n", | ||
| "\n", | ||
| "- [`SplitContainer`](https://docs.foxglove.dev/docs/notebook/layouts) — arranges children side-by-side in a `\"row\"` or `\"column\"`.\n", | ||
| "- [`TabContainer`](https://docs.foxglove.dev/docs/notebook/layouts) — arranges children in tabs, with one visible at a time.\n", | ||
| "- [`StackContainer`](https://docs.foxglove.dev/docs/notebook/layouts) — arranges children in a vertically-scrolling list.\n", | ||
| "\n", | ||
| "Below we re-log the cube and counter from the previous cells, then build a layout that puts the 3D scene and a plot of `/hello.count` side-by-side inside a tab. This gives us a small but realistic example of nested containers driving real data." | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "code", | ||
| "execution_count": null, | ||
| "id": "821f6f93", | ||
| "metadata": {}, | ||
| "outputs": [], | ||
| "source": [ | ||
| "import math\n", | ||
| "\n", | ||
| "import foxglove\n", | ||
| "from foxglove.channels import SceneUpdateChannel\n", | ||
| "from foxglove.layouts import (\n", | ||
| " BaseRendererGridLayerSettings,\n", | ||
| " BaseRendererSceneUpdateTopicSettings,\n", | ||
| " Layout,\n", | ||
| " PlotConfig,\n", | ||
| " PlotPanel,\n", | ||
| " PlotSeries,\n", | ||
| " SplitContainer,\n", | ||
| " SplitItem,\n", | ||
| " TabContainer,\n", | ||
| " TabItem,\n", | ||
| " ThreeDeeConfig,\n", | ||
| " ThreeDeePanel,\n", | ||
| ")\n", | ||
| "from foxglove.messages import (\n", | ||
| " Color,\n", | ||
| " CubePrimitive,\n", | ||
| " SceneEntity,\n", | ||
| " SceneUpdate,\n", | ||
| " Vector3,\n", | ||
| ")\n", | ||
| "\n", | ||
| "# Fresh buffer + re-log the data so this cell is self-contained.\n", | ||
| "nb_buffer = foxglove.init_notebook_buffer()\n", | ||
| "scene_channel = SceneUpdateChannel(\"/scene\")\n", | ||
| "\n", | ||
| "for t in range(10 * 30):\n", | ||
| " timestamp = t * 0.033\n", | ||
| " size = abs(math.sin(timestamp)) + 1.0\n", | ||
| "\n", | ||
| " foxglove.log(\"/hello\", {\"count\": t}, log_time=int(timestamp * 1e9))\n", | ||
| "\n", | ||
| " scene_channel.log(\n", | ||
| " SceneUpdate(\n", | ||
| " entities=[\n", | ||
| " SceneEntity(\n", | ||
| " cubes=[\n", | ||
| " CubePrimitive(\n", | ||
| " size=Vector3(x=size, y=size, z=size),\n", | ||
| " color=Color(r=0.1, g=0.7, b=1.0, a=1.0),\n", | ||
| " ),\n", | ||
| " ],\n", | ||
| " ),\n", | ||
| " ],\n", | ||
| " ),\n", | ||
| " log_time=int(timestamp * 1e9),\n", | ||
| " )\n", | ||
| "\n", | ||
| "# A 3D panel showing the cube on /scene, with a base grid layer for context.\n", | ||
| "scene_panel = ThreeDeePanel(\n", | ||
| " title=\"Scene\",\n", | ||
| " config=ThreeDeeConfig(\n", | ||
| " layers={\"grid\": BaseRendererGridLayerSettings()},\n", | ||
| " topics={\"/scene\": BaseRendererSceneUpdateTopicSettings(visible=True)},\n", | ||
| " ),\n", | ||
| ")\n", | ||
| "\n", | ||
| "# A plot panel showing /hello.count over time.\n", | ||
| "plot_panel = PlotPanel(\n", | ||
| " title=\"Counter\",\n", | ||
| " config=PlotConfig(\n", | ||
| " paths=[PlotSeries(value=\"/hello.count\", enabled=True)],\n", | ||
| " show_legend=True,\n", | ||
| " ),\n", | ||
| ")\n", | ||
| "\n", | ||
| "# Side-by-side row, wrapped in a single-tab TabContainer to demonstrate both APIs.\n", | ||
| "split = SplitContainer(\n", | ||
| " direction=\"row\",\n", | ||
| " items=[\n", | ||
| " SplitItem(content=scene_panel),\n", | ||
| " SplitItem(content=plot_panel),\n", | ||
| " ],\n", | ||
| ")\n", | ||
| "tabs = TabContainer(tabs=[TabItem(title=\"Overview\", content=split)])\n", | ||
| "\n", | ||
| "layout = Layout(content=tabs)\n", | ||
| "nb_buffer.show(layout=layout)" | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "markdown", | ||
| "id": "191d273b", | ||
| "metadata": {}, | ||
| "source": [ | ||
| "## Where to next\n", | ||
| "\n", | ||
| "- 📖 [Notebook integration docs](https://docs.foxglove.dev/docs/notebook) — the full reference, including buffer management and updating an existing viewer with `viewer.refresh()`.\n", | ||
| "- 🧱 [Layouts API reference](https://docs.foxglove.dev/docs/notebook/layouts) — every panel and container the `foxglove.layouts` module ships.\n", | ||
| "- 🐍 [Python SDK getting started](https://docs.foxglove.dev/docs/getting-started/python) — using the SDK outside of notebooks.\n", | ||
| "- 📊 [`data_platform` notebook](../data_platform/README.md) — a more advanced example that pulls data from Foxglove Data Management for offline analysis.\n", | ||
| "- 💬 [Foxglove Discord](https://foxglove.dev/community) — questions, feedback, and what we're working on next.\n" | ||
| ] | ||
| } | ||
| ], | ||
| "metadata": { | ||
| "kernelspec": { | ||
| "display_name": "Python 3", | ||
| "language": "python", | ||
| "name": "python3" | ||
| }, | ||
| "language_info": { | ||
| "name": "python" | ||
| } | ||
| }, | ||
| "nbformat": 4, | ||
| "nbformat_minor": 5 | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| --- | ||
| title: "Get Started with the Foxglove Notebook Integration" | ||
| short_description: "Embed the Foxglove viewer in a Jupyter / Colab notebook and drive it with programmatic layouts" | ||
| colab_url: "https://colab.research.google.com/github/foxglove/tutorials/blob/main/jupyter_notebooks/getting_started/GettingStarted.ipynb" | ||
| --- | ||
| # Foxglove Notebook Integration — Getting Started | ||
|
|
||
| A minimal, runnable companion to the [Foxglove notebook integration docs](https://docs.foxglove.dev/docs/notebook). | ||
|
|
||
| This notebook walks through: | ||
|
|
||
| - Installing `foxglove-sdk[notebook]` | ||
| - Creating a notebook buffer with `foxglove.init_notebook_buffer()` | ||
| - Logging messages and rendering them with the embedded Foxglove viewer | ||
| - Building layouts programmatically with the [`foxglove.layouts`](https://docs.foxglove.dev/docs/notebook/layouts) API, including nested `SplitContainer` and `TabContainer` examples driving real data | ||
|
|
||
| ## Run in Colab | ||
|
|
||
| Launch the sample notebook directly in Colab. | ||
|
|
||
| [](https://colab.research.google.com/github/foxglove/tutorials/blob/main/jupyter_notebooks/getting_started/GettingStarted.ipynb) | ||
|
|
||
| ## Run locally | ||
|
|
||
| ```bash | ||
| pip install "foxglove-sdk[notebook]" jupyterlab | ||
| jupyter lab GettingStarted.ipynb | ||
| ``` | ||
|
|
||
| The notebook works in JupyterLab, classic Jupyter, VS Code, and Google Colab — anywhere [`anywidget`](https://anywidget.dev/) is supported. | ||
|
|
||
| ## Stay in touch | ||
|
|
||
| Join our [Discord](https://foxglove.dev/community) to ask questions, share feedback, and stay up to date on what our team is working on. |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thoughts on adding a link to the Playground?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🤔 For now I'll defer. We don't promote playground and its a very different experience than notebooks (no external libraries).