{ "cells": [ { "cell_type": "markdown", "id": "f3f105b7", "metadata": {}, "source": [ "# Widgets and Layout Customization\n", "\n", "As of version 0.7 DataMapPlot supports a flexible widget system for adding interactive UI components to your data map visualizations. There are widgets for titles, search, topic browsing, minimaps, layer visibility toggles, annotations, histograms, colormap switching, and more — and you can place any of them in the four corners of the plot or in slide-out drawers on either side.\n", "\n", "This notebook will walk you through the widget system from the basics to a per-widget showcase, covering:\n", "\n", "- How the legacy parameter style still works (and is converted to widgets behind the scenes)\n", "- How to use widget classes directly for full control over placement and configuration\n", "- A tour of every built-in widget type with working examples\n", "- Layout presets and JSON configuration for reusable setups\n", "\n", "Let's start by importing DataMapPlot and loading some sample data." ] }, { "cell_type": "code", "execution_count": 1, "id": "2f4359e9", "metadata": { "execution": { "iopub.execute_input": "2026-03-22T01:14:16.580595Z", "iopub.status.busy": "2026-03-22T01:14:16.580475Z", "iopub.status.idle": "2026-03-22T01:14:21.929103Z", "shell.execute_reply": "2026-03-22T01:14:21.928649Z", "shell.execute_reply.started": "2026-03-22T01:14:16.580581Z" } }, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "/work/home/lmmcinn/.conda/envs/datamapplot/lib/python3.12/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n", " from .autonotebook import tqdm as notebook_tqdm\n" ] } ], "source": [ "import datamapplot\n", "import numpy as np\n", "import pandas as pd" ] }, { "cell_type": "markdown", "id": "3a48da98", "metadata": {}, "source": [ "## Loading Sample Data\n", "\n", "We'll use the ArXiv ML data map that appears throughout the DataMapPlot documentation. It contains 2-D coordinates for roughly 118 000 machine-learning papers, five layers of cluster labels at different levels of detail, and per-point hover text with paper titles." ] }, { "cell_type": "code", "execution_count": 2, "id": "4ea8dc3b", "metadata": { "execution": { "iopub.execute_input": "2026-03-22T01:14:21.929943Z", "iopub.status.busy": "2026-03-22T01:14:21.929580Z", "iopub.status.idle": "2026-03-22T01:14:23.913474Z", "shell.execute_reply": "2026-03-22T01:14:23.913024Z", "shell.execute_reply.started": "2026-03-22T01:14:21.929927Z" } }, "outputs": [], "source": [ "newsgroup_df = pd.read_parquet(\"hf://datasets/lmcinnes/20newsgroups_topics/newsgroups_with_topics_customised.parquet\")\n", "news_data_map = np.stack(newsgroup_df[\"map\"])\n", "news_hover_data = np.stack(newsgroup_df[\"post\"])\n", "truncated_news_hover_data = np.array([text[:127] + \"…\" if len(text) > 128 else text for text in news_hover_data])\n", "news_label_layers = [newsgroup_df[f\"layer_{i}_topics\"].values for i in range(5)]" ] }, { "cell_type": "markdown", "id": "42f66ed2", "metadata": {}, "source": [ "## The Legacy Way — Parameters You Already Know\n", "\n", "If you've been using ``create_interactive_plot`` already, nothing changes. Parameters like ``title``, ``enable_search``, ``logo``, and ``darkmode`` still work exactly the way they always have — behind the scenes they are simply converted into the corresponding widget objects. Let's start with a familiar example." ] }, { "cell_type": "markdown", "id": "fa7e138b", "metadata": {}, "source": [ "That gives us a nice looking plot with a title, a search bar, and a logo — all placed in their default locations. Under the hood DataMapPlot created a ``TitleWidget`` (top-left), a ``SearchWidget`` (top-left, below the title), and a ``LogoWidget`` (bottom-right). But what if you want the search bar somewhere else, or you need a minimap, or you want to tuck a topic tree into a slide-out drawer? That's where widget classes come in." ] }, { "cell_type": "code", "execution_count": 3, "id": "8855db19", "metadata": { "execution": { "iopub.execute_input": "2026-03-22T01:14:23.914170Z", "iopub.status.busy": "2026-03-22T01:14:23.914015Z", "iopub.status.idle": "2026-03-22T01:14:24.726446Z", "shell.execute_reply": "2026-03-22T01:14:24.725987Z", "shell.execute_reply.started": "2026-03-22T01:14:23.914157Z" } }, "outputs": [ { "data": { "text/html": [ "\n", " \n", " " ], "text/plain": [ "" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Using legacy parameters (automatically converted to widgets)\n", "plot = datamapplot.create_interactive_plot(\n", " news_data_map,\n", " *news_label_layers,\n", " hover_text=truncated_news_hover_data,\n", " font_family=\"Playfair Display SC\",\n", " title=\"20-Newsgroups Data Map\",\n", " sub_title=\"A data map of posts from the classic 20-Newsgroups dataset\",\n", " logo=\"https://raw.githubusercontent.com/TutteInstitute/.github/refs/heads/main/profile/images/tutte_logo_redesign_horizontal_original_colours.png\",\n", " logo_width=180,\n", " enable_search=True,\n", " darkmode=True,\n", ")\n", "plot" ] }, { "cell_type": "markdown", "id": "b1a386f0", "metadata": {}, "source": [ "## Introducing Widget Classes\n", "\n", "Every UI element in the plot is represented by a widget class. To use them, import the ones you need from ``datamapplot.widgets`` (or directly from ``datamapplot``), configure them, and pass a list to the ``widgets`` parameter of ``create_interactive_plot``.\n", "\n", "Each widget accepts two placement parameters:\n", "\n", "| Parameter | Description |\n", "|-----------|-------------|\n", "| ``location`` | Where the widget appears. One of ``\"top-left\"``, ``\"top-right\"``, ``\"bottom-left\"``, ``\"bottom-right\"``, ``\"drawer-left\"``, or ``\"drawer-right\"``. |\n", "| ``order`` | Stacking order within a location. Lower numbers appear first (top for corners, top of drawer for drawers). |\n", "\n", "Let's recreate the previous plot with explicit widget classes — this time placing the search bar in the top-right corner instead of its default top-left position." ] }, { "cell_type": "code", "execution_count": 4, "id": "a95cdb2f", "metadata": { "execution": { "iopub.execute_input": "2026-03-22T01:14:24.727225Z", "iopub.status.busy": "2026-03-22T01:14:24.727029Z", "iopub.status.idle": "2026-03-22T01:14:25.711343Z", "shell.execute_reply": "2026-03-22T01:14:25.710983Z", "shell.execute_reply.started": "2026-03-22T01:14:24.727211Z" } }, "outputs": [ { "data": { "text/html": [ "\n", " \n", " " ], "text/plain": [ "" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from datamapplot.widgets import TitleWidget, SearchWidget, LogoWidget, TopicTreeWidget\n", "\n", "widgets = [\n", " TitleWidget(\n", " title=\"20-Newsgroups Data Map\",\n", " sub_title=\"A data map of posts from the classic 20-Newsgroups dataset\",\n", " title_font_size=32,\n", " location=\"top-left\",\n", " order=0,\n", " darkmode=True,\n", " ),\n", " SearchWidget(\n", " search_field=\"hover_text\",\n", " location=\"top-right\", # moved to top-right!\n", " order=0,\n", " ),\n", " LogoWidget(\n", " logo=\"https://raw.githubusercontent.com/TutteInstitute/.github/refs/heads/main/profile/images/tutte_logo_redesign_horizontal_original_colours.png\",\n", " logo_width=128,\n", " location=\"bottom-right\",\n", " order=0,\n", " ),\n", "]\n", "\n", "plot = datamapplot.create_interactive_plot(\n", " news_data_map,\n", " *news_label_layers,\n", " hover_text=truncated_news_hover_data,\n", " font_family=\"Playfair Display SC\",\n", " darkmode=True,\n", " widgets=widgets,\n", ")\n", "plot" ] }, { "cell_type": "markdown", "id": "ee633c72", "metadata": {}, "source": [ "Moving the search bar was trivial — just change ``location``. Now let's go further: we can place widgets in **slide-out drawers** that stay hidden until the user clicks the handle on the edge of the screen (or uses a keyboard shortcut). This keeps the map view clean while giving access to heavier tools like search and topic browsing.\n", "\n", "**Keyboard shortcuts:**\n", "- ``Ctrl + [`` — Toggle left drawer\n", "- ``Ctrl + ]`` — Toggle right drawer\n", "- ``Escape`` — Close all drawers\n", "\n", "Below we'll move search and a ``TopicTreeWidget`` into the left drawer. Notice that ``order`` controls stacking within the drawer — search (order 0) appears above the topic tree (order 1)." ] }, { "cell_type": "code", "execution_count": 5, "id": "67ef7527", "metadata": { "execution": { "iopub.execute_input": "2026-03-22T01:14:25.711941Z", "iopub.status.busy": "2026-03-22T01:14:25.711775Z", "iopub.status.idle": "2026-03-22T01:14:26.810437Z", "shell.execute_reply": "2026-03-22T01:14:26.810067Z", "shell.execute_reply.started": "2026-03-22T01:14:25.711927Z" } }, "outputs": [ { "data": { "text/html": [ "\n", "