{ "cells": [ { "cell_type": "markdown", "id": "e4ae7020", "metadata": {}, "source": [ "# Extending view components\n", "\n", "If you want to use our Bokeh `CartoApp` framework, we provide a way to customize the GUI. \n", "The custom UI components are put at the right panel of the view, which are handled by the method `CartoApp.install_right_panel_views()`." ] }, { "cell_type": "markdown", "id": "94244caf", "metadata": {}, "source": [ "## Import UI components\n", "\n", "`CartoApp.install_right_panel_views()` use three sources and collect then into a list in order:\n", "\n", "1. `ProbeDesp.extra_controls()` returns probe-specific components.\n", "2. user config file. It not set, use `['blueprint', 'atlas']`.\n", "3. command-line options `--view`\n", "\n", "Elements in that list should be recognised by `init_view()`, there are:\n", "\n", "* `None`: skip\n", "* a `ViewBase` sub-class instance or sub-type\n", "\n", " * If it is a `ExtensionView`, also check whether the probe is supportted.\n", "\n", "* a `ImageHandler` instance or sub-type (with zero args `__init__`). It will be wrapped with `ImageView`.\n", "* a str `'file'`: use `FileImageView` (experimental feature)\n", "* a str `'atlas'`: use `AtlasBrainView`\n", "* a str `'blueprint'`: use `BlueprintView`\n", "* a str `'script'`: use `BlueprintScriptView` (experimental feature)\n", "* a path str `image_path`: use `ImageView`\n", "* a str `[ROOT:]module_path:view_name`: dynamic load the corresponding component, and apply above rule again.\n", "\n", "and a special rule:\n", "\n", "* a str `'-'`: remove source 2, 3 before it." ] }, { "cell_type": "markdown", "id": "272f1184", "metadata": {}, "source": [ "### Debuging a view component.\n", "\n", "With the UI-importing rules, you can prepare a file `my_view.py` and put under folder `extend` (it doesn't in the `PYTHONPATH`)." ] }, { "cell_type": "code", "execution_count": null, "id": "7b235008", "metadata": {}, "outputs": [], "source": [ "# some imports\n", "\n", "class MyView(ViewBase):\n", " def __init__(self, config: CartoConfig):\n", " super().__init__(config, logger='neurocarto.view.my_view')\n", " ... # other abstract methods implemtation\n", " \n", "if __name__ == '__main__':\n", " import sys\n", " from neurocarto.config import parse_cli\n", " from neurocarto.main_app import main\n", "\n", " main(parse_cli([\n", " *sys.argv[1:],\n", " '--debug',\n", " '--view=-',\n", " '--view=extend:my_view:MyView',\n", " ]))" ] }, { "cell_type": "markdown", "id": "57de3909", "metadata": {}, "source": [ "Then you can run this file direct and test your custom component.\n", "\n", " python extend/my_view.py" ] }, { "cell_type": "markdown", "id": "c1bb238f", "metadata": {}, "source": [ "### Import Probe specific UI components\n", "\n", "A probe implementation can provide their specific components by `extra_controls()`. For example, `NpxProbeDesp` has one probe-specific UI component:" ] }, { "cell_type": "code", "execution_count": null, "id": "6ab9fd49", "metadata": {}, "outputs": [], "source": [ "class NpxProbeDesp:\n", " def extra_controls(self, config: CartoConfig):\n", " from .views import NpxReferenceControl\n", " return [NpxReferenceControl]" ] }, { "cell_type": "markdown", "id": "394a0bf7", "metadata": {}, "source": [ "A probe implementation can provide some probe-specific functions which are required by some UI components. We use Protocol to declare the require methods." ] }, { "cell_type": "markdown", "id": "ae3eec84", "metadata": {}, "source": [ "## Customize UI components\n", "\n", "We provide a framework, a base view component `ViewBase` to interact with `CartoApp`. Based on the base, we built several classes and tools to support different kinds of visualizing." ] }, { "cell_type": "markdown", "id": "69bb490f", "metadata": {}, "source": [ "### Implement ViewBase\n", "\n", "All UI components should be a subclass of `neurocarto.views.base.ViewBase`. \n", "`ViewBase` provides a layout framework." ] }, { "cell_type": "code", "execution_count": null, "id": "70c7e8ba", "metadata": {}, "outputs": [], "source": [ "class MyView(ViewBase):\n", " def __init__(self, config: CartoConfig):\n", " super().__init__(config, logger='neurocarto.view.my_view')\n", " @property\n", " def name(self) -> str:\n", " return 'Title of my view' # show in