Supervisely
About SuperviselyEcosystemContact usSlack
  • 💻Supervisely Developer Portal
  • 🎉Getting Started
    • Installation
    • Basics of authentication
    • Intro to Python SDK
    • Environment variables
    • Supervisely annotation format
      • Project Structure
      • Project Meta: Classes, Tags, Settings
      • Objects
      • Tags
      • Image Annotation
      • Video Annotation
      • Point Clouds Annotation
      • Point Cloud Episode Annotation
      • Volumes Annotation
    • Python SDK tutorials
      • Images
        • Images
        • Image and object tags
        • Spatial labels on images
        • Keypoints (skeletons)
        • Multispectral images
        • Multiview images
        • Advanced: Optimized Import
        • Advanced: Export
      • Videos
        • Videos
        • Video and object tags
        • Spatial labels on videos
      • Point Clouds
        • Point Clouds (LiDAR)
        • Point Cloud Episodes and object tags
        • 3D point cloud object segmentation based on sensor fusion and 2D mask guidance
        • 3D segmentation masks projection on 2D photo context image
      • Volumes
        • Volumes (DICOM)
        • Spatial labels on volumes
      • Common
        • Iterate over a project
        • Iterate over a local project
        • Progress Bar tqdm
        • Cloning projects for development
    • Command Line Interface (CLI)
      • Enterprise CLI Tool
        • Instance administration
        • Workflow automation
      • Supervisely SDK CLI
    • Connect your computer
      • Linux
      • Windows WSL
      • Troubleshooting
  • 🔥App development
    • Basics
      • Create app from any py-script
      • Configuration file
        • config.json
        • Example 1. Headless
        • Example 2. App with GUI
        • v1 - Legacy
          • Example 1. v1 Modal Window
          • Example 2. v1 app with GUI
      • Add private app
      • Add public app
      • App Compatibility
    • Apps with GUI
      • Hello World!
      • App in the Image Labeling Tool
      • App in the Video Labeling Tool
      • In-browser app in the Labeling Tool
    • Custom import app
      • Overview
      • From template - simple
      • From scratch - simple
      • From scratch GUI - advanced
      • Finding directories with specific markers
    • Custom export app
      • Overview
      • From template - simple
      • From scratch - advanced
    • Neural Network integration
      • Overview
      • Serving App
        • Introduction
        • Instance segmentation
        • Object detection
        • Semantic segmentation
        • Pose estimation
        • Point tracking
        • Object tracking
        • Mask tracking
        • Image matting
        • How to customize model inference
        • Example: Custom model inference with probability maps
      • Serving App with GUI
        • Introduction
        • How to use default GUI template
        • Default GUI template customization
        • How to create custom user interface
      • Inference API
      • Training App
        • Overview
        • Tensorboard template
        • Object detection
      • High level scheme
      • Custom inference pipeline
      • Train and predict automation model pipeline
    • Advanced
      • Advanced debugging
      • How to make your own widget
      • Tutorial - App Engine v1
        • Chapter 1 Headless
          • Part 1 — Hello world! [From your Python script to Supervisely APP]
          • Part 2 — Errors handling [Catching all bugs]
          • Part 3 — Site Packages [Customize your app]
          • Part 4 — SDK Preview [Lemons counter app]
          • Part 5 — Integrate custom tracker into Videos Annotator tool [OpenCV Tracker]
        • Chapter 2 Modal Window
          • Part 1 — Modal window [What is it?]
          • Part 2 — States and Widgets [Customize modal window]
        • Chapter 3 UI
          • Part 1 — While True Script [It's all what you need]
          • Part 2 — UI Rendering [Simplest UI Application]
          • Part 3 — APP Handlers [Handle Events and Errors]
          • Part 4 — State and Data [Mutable Fields]
          • Part 5 — Styling your app [Customizing the UI]
        • Chapter 4 Additionals
          • Part 1 — Remote Developing with PyCharm [Docker SSH Server]
      • Custom Configuration
        • Fixing SSL Certificate Errors in Supervisely
        • Fixing 400 HTTP errors when using HTTP instead of HTTPS
      • Autostart
      • Coordinate System
      • MLOps Workflow integration
    • Widgets
      • Input
        • Input
        • InputNumber
        • InputTag
        • BindedInputNumber
        • DatePicker
        • DateTimePicker
        • ColorPicker
        • TimePicker
        • ClassesMapping
        • ClassesColorMapping
      • Controls
        • Button
        • Checkbox
        • RadioGroup
        • Switch
        • Slider
        • TrainValSplits
        • FileStorageUpload
        • Timeline
        • Pagination
      • Text Elements
        • Text
        • TextArea
        • Editor
        • Copy to Clipboard
        • Markdown
        • Tooltip
        • ElementTag
        • ElementTagsList
      • Media
        • Image
        • LabeledImage
        • GridGallery
        • Video
        • VideoPlayer
        • ImagePairSequence
        • Icons
        • ObjectClassView
        • ObjectClassesList
        • ImageSlider
        • Carousel
        • TagMetaView
        • TagMetasList
        • ImageAnnotationPreview
        • ClassesMappingPreview
        • ClassesListPreview
        • TagsListPreview
        • MembersListPreview
      • Selection
        • Select
        • SelectTeam
        • SelectWorkspace
        • SelectProject
        • SelectDataset
        • SelectItem
        • SelectTagMeta
        • SelectAppSession
        • SelectString
        • Transfer
        • DestinationProject
        • TeamFilesSelector
        • FileViewer
        • Dropdown
        • Cascader
        • ClassesListSelector
        • TagsListSelector
        • MembersListSelector
        • TreeSelect
        • SelectCudaDevice
      • Thumbnails
        • ProjectThumbnail
        • DatasetThumbnail
        • VideoThumbnail
        • FolderThumbnail
        • FileThumbnail
      • Status Elements
        • Progress
        • NotificationBox
        • DoneLabel
        • DialogMessage
        • TaskLogs
        • Badge
        • ModelInfo
        • Rate
        • CircleProgress
      • Layouts and Containers
        • Card
        • Container
        • Empty
        • Field
        • Flexbox
        • Grid
        • Menu
        • OneOf
        • Sidebar
        • Stepper
        • RadioTabs
        • Tabs
        • TabsDynamic
        • ReloadableArea
        • Collapse
        • Dialog
        • IFrame
      • Tables
        • Table
        • ClassicTable
        • RadioTable
        • ClassesTable
        • RandomSplitsTable
        • FastTable
      • Charts and Plots
        • LineChart
        • GridChart
        • HeatmapChart
        • ApexChart
        • ConfusionMatrix
        • LinePlot
        • GridPlot
        • ScatterChart
        • TreemapChart
        • PieChart
      • Compare Data
        • MatchDatasets
        • MatchTagMetas
        • MatchObjClasses
        • ClassBalance
        • CompareAnnotations
      • Widgets demos on github
  • 😎Advanced user guide
    • Objects binding
    • Automate with Python SDK & API
      • Start and stop app
      • User management
      • Labeling Jobs
  • 🖥️UI widgets
    • Element UI library
    • Supervisely UI widgets
    • Apexcharts - modern & interactive charts
    • Plotly graphing library
  • 📚API References
    • REST API Reference
    • Python SDK Reference
Powered by GitBook
On this page
  • How to create your own widget
  • Simple widget example

Was this helpful?

Edit on GitHub
  1. App development
  2. Advanced

How to make your own widget

Guide explains how to create and add your own widget to Supervisely SDK

PreviousAdvanced debuggingNextTutorial - App Engine v1

Last updated 1 day ago

Was this helpful?

In this tutorial you will learn how to create your own widget, add it to Supervisely SDK and make small demo.

How to create your own widget

  1. Clone SDK repo:

    git clone https://github.com/supervisely/supervisely
  2. Clone repo with widgets samples:

    git clone https://github.com/supervisely-ecosystem/ui-widgets-demos
  3. Create SDK folder symlink in ui-widgets-demos

    ln -s ./supervisely/supervisely ./ui-widgets-demos/supervisely
  4. Create folder in supervisely/app/widgets/<your_widget_folder> like that

    your_widget_folder
    |____ __init__.py
    |____ template.html
    |____ script.js   # optional: you can add javascript file
    |____ your_widget.py
  5. Declare a new class with inheritance from Widget in your_widget.py

    from supervisely.app.widgets import Widget
    from supervisely.app.content import StateJson, DataJson 
    
    
    class YourWidget(Widget):
        # ------ Required methods ------
        def __init__(
                self,
                data_1: list 
                data_2: dict 
            ):
            self._data_1 = data_1
            self._data_2 = data_2
            self._some_state_attribute_1 = True 
            self._some_state_attribute_2 = 42 
    
            super(YourWidget, self).__init__(widget_id=widget_id, file_path=__file__)
    
            # ------ Optional | Add your javascript file ------
            # script_path = "./sly/css/app/widgets/your_widget_folder/script.js" # change <your_widget_folder> 
            # JinjaWidgets().context["__widget_scripts__"][self.__class__.__name__] = script_path
    
        def get_json_data(self):
            """ This method will be used in template.html to get widget data """
            return {
                "data_1": self._data_1,
                "data_2": self._data_2,
            } 
    
        def get_json_state(self):
            """ This method will be used in template.html to get widget state """
            return {
                "some_state_attribute_1": self._some_state_attribute_1,
                "some_state_attribute_2": self._some_state_attribute_2,
            } 
    
        # ------ Optional methods | Just for example ------
        def your_method_for_change_data(self):
            # make changes in widget data
            self._data_1 = self._data_1 + [1, 2, 3, 4, 5]
            self._data_2 = {'a': 11}
            # save this changes of widget data in local storage(RAM) 
            DataJson()[self.widget_id]['data_1'] = self._data_1
            DataJson()[self.widget_id]['data_2'] = self._data_2
            # sync this changes to remote server 
            DataJson().send_changes()
        
        def your_method_for_change_state(self):
            # change state attribute
            self._some_state_attribute_1 = False
            self._some_state_attribute_2 = 0.1
            # save this changes of widget data in local storage(RAM) 
            StateJson()[self.widget_id]["some_state_attribute_1"] = self._some_state_attribute_1
            StateJson()[self.widget_id]["some_state_attribute_2"] = self._some_state_attribute_2
            # sync this changes to remote server 
            StateJson().send_changes()
  6. Construct template.html for your widget using other widgets from SDK or any HTML elements

    <div>
        <!-- Elements from SDK had the "sly" prefix -->
        <sly-field :title="data.{{{widget.widget_id}}}.data_1" :description="state.{{{widget.widget_id}}}.some_state_attribute_1">
            <div>
                {{data.{{{widget.widget_id}}}.data_2}}
            </div>
            <div>
                {{state.{{{widget.widget_id}}}.some_state_attribute_2}}
            </div>
        </sly-field>
        
        <!-- Just simple HTML element with Javascript function from your scipt.js -->
        <button
            :value="data.{{{widget.widget_id}}}.data_1"
            @click="myFunction()" 
        ></button>
    
        <!-- Also simple HTML element, but from UI library of HTML elements - https://element.eleme.io -->
        <el-button :value="data.{{{widget.widget_id}}}.data_1"></el-button>
    <div>
  7. (Optional) Prepare file script.js for your widget if you need to implement your own Vue JS component. 📗 See for more details.

    const myFunction = function (name) {
        console.log('Hello world')
    }
  8. Import new widget as part of widgets module. Just add import in supervisely/app/widgets/__init__.py

    # imports for other widgets as example
    from supervisely.app.widgets.radio_tabs.radio_tabs import RadioTabs
    from supervisely.app.widgets.train_val_splits.train_val_splits import TrainValSplits
    from supervisely.app.widgets.editor.editor import Editor
    
    # import for your widget
    from supervisely.app.widgets.your_widget_folder.your_widget_py_file import YourWidgetClass
  9. Create folder with example of your widget in ui-widgets-demos/NEW widgets/<your_widget_example_folder> like that

    your_widget_folder
    |____src
      |____main.py

    Example of main.py

    import os
    import supervisely as sly
    from dotenv import load_dotenv
    from supervisely.app.widgets import (
        Card, 
        YourWidget #  <-- import your widget
        )
    
    load_dotenv("local.env")
    load_dotenv(os.path.expanduser("~/supervisely.env"))
    
    api = sly.Api()
    
    your_widget = YourWidget(data_1=[...], data_2={'c': 0.3355})
    card = Card(title="My card", content=your_widget)
    app = sly.Application(layout=card)
  10. To see the demo of your widget, you need to run the server. You got 2 options for that:

    a) Just run the server from command line

    python -m uvicorn "NEW widgets.grid_plot.src.main:app" --host 0.0.0.0 --port 8000 --ws websockets --reload

    b) If you are VSCode user, then just edit ui-widgets-demos/.vscode/launch.json as showed below and run the server from "run & debug" menu

    {
        "version": "0.2.0",
        "configurations": [
            {
                "name": "Uvicorn",
                "type": "python",
                "request": "launch",
                "module": "uvicorn",
                "args": [
                    <!-- change "your_widget_example_folder" to the name of the folder with your widget -->
                    "NEW widgets.your_widget_example_folder.src.main:app",
                    "--host",
                    "0.0.0.0",
                    "--port",
                    "8000",
                    "--ws",
                    "websockets",
                    "--reload"
                ],
                "jinja": true,
                "justMyCode": true,
                "env": {
                    "PYTHONPATH": "${workspaceFolder}:${PYTHONPATH}",
                    "LOG_LEVEL": "DEBUG",
                }
            }
        ]
    }
  11. After all steps, test your widget in browser http://0.0.0.0:8000 and if it ok make a commit & push & create the pull request in both repo(SDK and ui-widget-demos).

    Send the link to pull-request to Maxim Kolomeychenko(@Max) in Slack. He will run a new SDK build.

    Success!

Simple widget example

supervisely/app/widgets/textarea/textarea.py

from supervisely.app import DataJson, StateJson
from supervisely.app.widgets import Widget


class TextArea(Widget):
    def __init__(
        self,
        value: str = None,
        placeholder: str = "Please input",
        rows: int = 2,
        autosize: bool = True,
        readonly: bool = False,
        widget_id=None,
    ):
        self._value = value
        self._placeholder = placeholder
        self._rows = rows
        self._autosize = autosize
        self._readonly = readonly
        super().__init__(widget_id=widget_id, file_path=__file__)

    def get_json_data(self):
        return {
            "value": self._value,
            "placeholder": self._placeholder,
            "rows": self._rows,
            "autosize": self._autosize,
            "readonly": self._readonly,
        }

    def get_json_state(self):
        return {"value": self._value}

    def set_value(self, value):
        self._value = value
        StateJson()[self.widget_id]["value"] = value
        StateJson().send_changes()

    def get_value(self):
        return StateJson()[self.widget_id]["value"]

    def is_readonly(self):
        return DataJson()[self.widget_id]["readonly"]

    def enable_readonly(self):
        self._readonly = True
        DataJson()[self.widget_id]["readonly"] = True
        DataJson().send_changes()

    def disable_readonly(self):
        self._readonly = False
        DataJson()[self.widget_id]["readonly"] = False
        DataJson().send_changes()

supervisely/app/widgets/textarea/template.html

<el-input
  type="textarea"
  :placeholder="data.{{{widget.widget_id}}}.placeholder"
  :rows="data.{{{widget.widget_id}}}.rows"
  :autosize="data.{{{widget.widget_id}}}.autosize"
  v-model="data.{{{widget.widget_id}}}.value">
</el-input>

supervisely/app/widgets/__init__.py

from supervisely.app.widgets.textarea.textarea import TextArea

NEW widgets/textarea/src/main.py

import supervisely as sly
from dotenv import load_dotenv
from supervisely.app.widgets import Card, TextArea

load_dotenv("local.env")
load_dotenv(os.path.expanduser("~/supervisely.env"))

api = sly.Api()

text_area = TextArea(value="Some text in the text area.", autosize=False)
card = Card(title="My card", content=text_area)
app = sly.Application(layout=card)
🔥
this example