From 4876f45c67060064b56cdcc1917a0e37abf0c053 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Fri, 1 May 2026 17:17:47 +0000 Subject: [PATCH 1/4] Add notebook integration getting-started example Adds a new self-contained Jupyter notebook tutorial under `jupyter_notebooks/getting_started/` that mirrors the "Using the integration" and "Layout Management" sections of https://docs.foxglove.dev/docs/notebook. The notebook is Colab-first: dependencies are installed via `%pip install "foxglove-sdk[notebook]"` in the first cell so it runs end-to-end in a fresh kernel with no external assets. Cells cover: - Creating a notebook buffer with `foxglove.init_notebook_buffer()` - Logging messages with explicit `log_time` and rendering them in the embedded Foxglove viewer - Logging richer 3D scene data via `SceneUpdateChannel` - Building a single-panel layout programmatically with the `foxglove.layouts` API - Composing `SplitContainer` and `TabContainer` to drive a 3D scene + plot side-by-side from real data Also extends `.utils/generate_readme.py` and the README template to support a new `colab_url` front-matter field so Colab links can be surfaced in the auto-generated root README, and regenerates README.md to include the new tutorial. Co-authored-by: Roman Shtylman --- .utils/README_template.md.j2 | 3 + .utils/generate_readme.py | 1 + README.md | 2 + .../getting_started/GettingStarted.ipynb | 305 ++++++++++++++++++ jupyter_notebooks/getting_started/README.md | 34 ++ 5 files changed, 345 insertions(+) create mode 100644 jupyter_notebooks/getting_started/GettingStarted.ipynb create mode 100644 jupyter_notebooks/getting_started/README.md diff --git a/.utils/README_template.md.j2 b/.utils/README_template.md.j2 index b05897f..21a21d7 100644 --- a/.utils/README_template.md.j2 +++ b/.utils/README_template.md.j2 @@ -18,6 +18,9 @@ Below is a list of all tutorials available in this repository: {%- if tutorial.visualize_url %} - 📊 [Visualize]({{ tutorial.visualize_url }}) {%- endif %} +{%- if tutorial.colab_url %} +- 📓 [Open in Colab]({{ tutorial.colab_url }}) +{%- endif %} {%- endfor %} {% endfor %} Join the Foxglove [Discord](https://discord.gg/UEuytgVkks) and follow [our blog](https://foxglove.dev/blog) for more ideas on how to integrate Foxglove into your robotics development workflows. diff --git a/.utils/generate_readme.py b/.utils/generate_readme.py index 84d46c3..404df0b 100644 --- a/.utils/generate_readme.py +++ b/.utils/generate_readme.py @@ -55,6 +55,7 @@ def scan_tutorials(): "blog_post_url": metadata.get("blog_post_url", ""), "video_url": metadata.get("video_url", ""), "visualize_url": metadata.get("visualize_url", ""), + "colab_url": metadata.get("colab_url", ""), }) return sorted(tutorials, key=lambda t: t["path"]) diff --git a/README.md b/README.md index c587800..0f487af 100644 --- a/README.md +++ b/README.md @@ -93,5 +93,7 @@ Below is a list of all tutorials available in this repository: ### [Analyze Your Robotics Data with Jupyter Notebooks](jupyter_notebooks/data_platform/README.md) - 📝 Load and analyze data in Jupyter Notebooks using Foxglove Data Management - 🔗 [Related Blog Post](https://foxglove.dev/blog/analyze-your-robotics-data-with-jupyter-notebooks) +### [Get Started with the Foxglove Notebook Integration](jupyter_notebooks/getting_started/README.md) +- 📝 Embed the Foxglove viewer in a Jupyter / Colab notebook and drive it with programmatic layouts Join the Foxglove [Discord](https://discord.gg/UEuytgVkks) and follow [our blog](https://foxglove.dev/blog) for more ideas on how to integrate Foxglove into your robotics development workflows. \ No newline at end of file diff --git a/jupyter_notebooks/getting_started/GettingStarted.ipynb b/jupyter_notebooks/getting_started/GettingStarted.ipynb new file mode 100644 index 0000000..d36763d --- /dev/null +++ b/jupyter_notebooks/getting_started/GettingStarted.ipynb @@ -0,0 +1,305 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "f1955e62", + "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": "f0322810", + "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": "7519d1b7", + "metadata": {}, + "outputs": [], + "source": [ + "%pip install -q \"foxglove-sdk[notebook]\"" + ] + }, + { + "cell_type": "markdown", + "id": "10f6f719", + "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": "c22fa265", + "metadata": {}, + "outputs": [], + "source": [ + "import foxglove\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", + "# 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()" + ] + }, + { + "cell_type": "markdown", + "id": "ccf216a0", + "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": "ddec45cd", + "metadata": {}, + "outputs": [], + "source": [ + "import math\n", + "\n", + "import foxglove\n", + "from foxglove.channels import SceneUpdateChannel\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", + "nb_buffer.show()" + ] + }, + { + "cell_type": "markdown", + "id": "4e933df7", + "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": "74fc69ca", + "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": "7ba5be27", + "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": "f55a59bd", + "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": "254f224c", + "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 +} diff --git a/jupyter_notebooks/getting_started/README.md b/jupyter_notebooks/getting_started/README.md new file mode 100644 index 0000000..390fd64 --- /dev/null +++ b/jupyter_notebooks/getting_started/README.md @@ -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: "TODO_COLAB_URL" # Uncomment and fill in once the Colab share link is available +--- +# 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 + +[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](TODO_COLAB_URL) + +> The Colab share URL will be filled in once the notebook is published. In the meantime you can run it locally — see below. + +## 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. From 57796f3f8e73097f0d51821fc78cacd85ca48dba Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Fri, 1 May 2026 22:37:34 +0000 Subject: [PATCH 2/4] Use github-path Colab URL in getting-started README Match the data_platform tutorial's Colab badge style: link directly to the notebook in this repo via the https://colab.research.google.com/github///blob// pattern instead of waiting on a separately-generated Colab share URL. Also wires the same URL into the README front-matter as colab_url so the auto-generated root README surfaces a 'Open in Colab' bullet for this tutorial. Co-authored-by: Roman Shtylman --- README.md | 1 + jupyter_notebooks/getting_started/README.md | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 0f487af..d086a04 100644 --- a/README.md +++ b/README.md @@ -95,5 +95,6 @@ Below is a list of all tutorials available in this repository: - 🔗 [Related Blog Post](https://foxglove.dev/blog/analyze-your-robotics-data-with-jupyter-notebooks) ### [Get Started with the Foxglove Notebook Integration](jupyter_notebooks/getting_started/README.md) - 📝 Embed the Foxglove viewer in a Jupyter / Colab notebook and drive it with programmatic layouts +- 📓 [Open in Colab](https://colab.research.google.com/github/foxglove/tutorials/blob/main/jupyter_notebooks/getting_started/GettingStarted.ipynb) Join the Foxglove [Discord](https://discord.gg/UEuytgVkks) and follow [our blog](https://foxglove.dev/blog) for more ideas on how to integrate Foxglove into your robotics development workflows. \ No newline at end of file diff --git a/jupyter_notebooks/getting_started/README.md b/jupyter_notebooks/getting_started/README.md index 390fd64..52ce511 100644 --- a/jupyter_notebooks/getting_started/README.md +++ b/jupyter_notebooks/getting_started/README.md @@ -1,7 +1,7 @@ --- 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: "TODO_COLAB_URL" # Uncomment and fill in once the Colab share link is available +colab_url: "https://colab.research.google.com/github/foxglove/tutorials/blob/main/jupyter_notebooks/getting_started/GettingStarted.ipynb" --- # Foxglove Notebook Integration — Getting Started @@ -16,9 +16,9 @@ This notebook walks through: ## Run in Colab -[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](TODO_COLAB_URL) +Launch the sample notebook directly in Colab. -> The Colab share URL will be filled in once the notebook is published. In the meantime you can run it locally — see below. +[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/foxglove/tutorials/blob/main/jupyter_notebooks/getting_started/GettingStarted.ipynb) ## Run locally From ee4656e9e4b8f06cfe477b7f8f499293ceb741db Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Fri, 1 May 2026 22:42:53 +0000 Subject: [PATCH 3/4] Add default Raw Messages layout to first viewer cell The minimal /hello example was opening the embedded viewer with the default empty layout, leaving the user looking at no panels and no data even though the buffer was full of /hello messages. Pre-configure a single Raw Messages panel pointed at /hello so the viewer renders the buffered data immediately. This is also a nice teaser for the Layout Management section later in the notebook. Co-authored-by: Roman Shtylman --- .../getting_started/GettingStarted.ipynb | 36 ++++++++++++------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/jupyter_notebooks/getting_started/GettingStarted.ipynb b/jupyter_notebooks/getting_started/GettingStarted.ipynb index d36763d..11b2a12 100644 --- a/jupyter_notebooks/getting_started/GettingStarted.ipynb +++ b/jupyter_notebooks/getting_started/GettingStarted.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "markdown", - "id": "f1955e62", + "id": "2dacd758", "metadata": {}, "source": [ "# Get Started with the Foxglove Notebook Integration\n", @@ -18,7 +18,7 @@ }, { "cell_type": "markdown", - "id": "f0322810", + "id": "b1577ad1", "metadata": {}, "source": [ "## Install the integration\n", @@ -29,7 +29,7 @@ { "cell_type": "code", "execution_count": null, - "id": "7519d1b7", + "id": "190b74d1", "metadata": {}, "outputs": [], "source": [ @@ -38,7 +38,7 @@ }, { "cell_type": "markdown", - "id": "10f6f719", + "id": "c7edbbde", "metadata": {}, "source": [ "## Using the integration\n", @@ -53,11 +53,12 @@ { "cell_type": "code", "execution_count": null, - "id": "c22fa265", + "id": "c40bcbe1", "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", @@ -67,14 +68,23 @@ " 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()" + "nb_buffer.show(layout=layout)" ] }, { "cell_type": "markdown", - "id": "ccf216a0", + "id": "2863e2ac", "metadata": {}, "source": [ "### Logging richer data\n", @@ -87,7 +97,7 @@ { "cell_type": "code", "execution_count": null, - "id": "ddec45cd", + "id": "5f1b0de4", "metadata": {}, "outputs": [], "source": [ @@ -137,7 +147,7 @@ }, { "cell_type": "markdown", - "id": "4e933df7", + "id": "7d55512a", "metadata": {}, "source": [ "## Layout management\n", @@ -150,7 +160,7 @@ { "cell_type": "code", "execution_count": null, - "id": "74fc69ca", + "id": "96a88b53", "metadata": {}, "outputs": [], "source": [ @@ -170,7 +180,7 @@ }, { "cell_type": "markdown", - "id": "7ba5be27", + "id": "6deebaaf", "metadata": {}, "source": [ "### Composing panels with containers\n", @@ -187,7 +197,7 @@ { "cell_type": "code", "execution_count": null, - "id": "f55a59bd", + "id": "93e3248c", "metadata": {}, "outputs": [], "source": [ @@ -277,7 +287,7 @@ }, { "cell_type": "markdown", - "id": "254f224c", + "id": "403a6c65", "metadata": {}, "source": [ "## Where to next\n", From 806f04bb5c820763d8908e38010f5ef48dcd5ac9 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Fri, 1 May 2026 22:45:55 +0000 Subject: [PATCH 4/4] Pre-configure 3D panel for the SceneUpdate cell Same fix as the previous commit but for the SceneUpdate cube example: ship a Layout with a ThreeDeePanel subscribed to /scene (and a base grid layer) so the embedded viewer immediately renders the animated cube instead of opening on an empty default layout. Co-authored-by: Roman Shtylman --- .../getting_started/GettingStarted.ipynb | 45 +++++++++++++------ 1 file changed, 32 insertions(+), 13 deletions(-) diff --git a/jupyter_notebooks/getting_started/GettingStarted.ipynb b/jupyter_notebooks/getting_started/GettingStarted.ipynb index 11b2a12..eda1b90 100644 --- a/jupyter_notebooks/getting_started/GettingStarted.ipynb +++ b/jupyter_notebooks/getting_started/GettingStarted.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "markdown", - "id": "2dacd758", + "id": "3cb6f824", "metadata": {}, "source": [ "# Get Started with the Foxglove Notebook Integration\n", @@ -18,7 +18,7 @@ }, { "cell_type": "markdown", - "id": "b1577ad1", + "id": "f09dc6bc", "metadata": {}, "source": [ "## Install the integration\n", @@ -29,7 +29,7 @@ { "cell_type": "code", "execution_count": null, - "id": "190b74d1", + "id": "b47856b0", "metadata": {}, "outputs": [], "source": [ @@ -38,7 +38,7 @@ }, { "cell_type": "markdown", - "id": "c7edbbde", + "id": "b56daf55", "metadata": {}, "source": [ "## Using the integration\n", @@ -53,7 +53,7 @@ { "cell_type": "code", "execution_count": null, - "id": "c40bcbe1", + "id": "d4860aa2", "metadata": {}, "outputs": [], "source": [ @@ -84,7 +84,7 @@ }, { "cell_type": "markdown", - "id": "2863e2ac", + "id": "435a2b05", "metadata": {}, "source": [ "### Logging richer data\n", @@ -97,7 +97,7 @@ { "cell_type": "code", "execution_count": null, - "id": "5f1b0de4", + "id": "7e59a45e", "metadata": {}, "outputs": [], "source": [ @@ -105,6 +105,13 @@ "\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", @@ -142,12 +149,24 @@ " log_time=int(timestamp * 1e9),\n", " )\n", "\n", - "nb_buffer.show()" + "# 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": "7d55512a", + "id": "11460217", "metadata": {}, "source": [ "## Layout management\n", @@ -160,7 +179,7 @@ { "cell_type": "code", "execution_count": null, - "id": "96a88b53", + "id": "8a874a71", "metadata": {}, "outputs": [], "source": [ @@ -180,7 +199,7 @@ }, { "cell_type": "markdown", - "id": "6deebaaf", + "id": "cb40cd1d", "metadata": {}, "source": [ "### Composing panels with containers\n", @@ -197,7 +216,7 @@ { "cell_type": "code", "execution_count": null, - "id": "93e3248c", + "id": "821f6f93", "metadata": {}, "outputs": [], "source": [ @@ -287,7 +306,7 @@ }, { "cell_type": "markdown", - "id": "403a6c65", + "id": "191d273b", "metadata": {}, "source": [ "## Where to next\n",