I was following the doc’s here Create Custom Widget to try to build some custom widgets that i didn’t thing were overly complicated. But, i’m having real issues with performance. I’m trying to build widgets that a user can instantiate and add to a display dynamically. One of these has about 4 parameters and panel widgets that are instantiated with .from_param()
but it seems like each of those .from_param()
calls takes about 100ms to complete, it then takes a further 1/2 second to add that widget to the display. So for adding a real simple little UI element with 4 inputs it takes a full second for the UI to respond.
What am i doing wrong? It seems like it should be fast, in my example i also instantiated the panel objects themselves which is quick, tho still very slow to add to the display (and doesn’t have any reactivity configured).
Here’s a test script with some simple logging to measure some timing.
import datetime
import logging
import panel as pn
import param
from panel.custom import PyComponent
from panel.widgets.base import WidgetBase
# Create module-specific logger.
_LOGGER = logging.getLogger(__name__)
_LOGGER.propagate = False # Prevent messages from bubbling up to the root logger
_LOGGER.setLevel(logging.DEBUG)
# Clear any existing handlers (e.g. from panel or other libraries)
if _LOGGER.hasHandlers():
_LOGGER.handlers.clear()
formatter = logging.Formatter(
"%(asctime)s.%(msecs)03d %(levelname)s {%(module)s} [%(funcName)s] %(message)s",
datefmt="%Y-%m-%d %H:%M:%S",
)
handler = logging.StreamHandler()
handler.setFormatter(formatter)
_LOGGER.addHandler(handler)
class SlowTest(WidgetBase, PyComponent):
start = param.Integer(doc="Start")
end = param.Number(doc="End")
opts = param.Selector(
default="a",
objects=["a", "b", "c"],
doc="Options",
)
test = param.String(doc="Test")
def __init__(self, **params):
tic = datetime.datetime.now()
super().__init__(**params)
toc = datetime.datetime.now() - tic
_LOGGER.debug("Init took: %.3f seconds", toc.total_seconds())
tic = datetime.datetime.now()
self.start_disp = pn.widgets.IntInput.from_param(self.param.start)
toc = datetime.datetime.now() - tic
_LOGGER.debug("Start took: %.3f seconds", toc.total_seconds())
tic = datetime.datetime.now()
self.end_disp = pn.widgets.FloatInput.from_param(self.param.end)
toc = datetime.datetime.now() - tic
_LOGGER.debug("End took: %.3f seconds", toc.total_seconds())
tic = datetime.datetime.now()
self.opts_disp = pn.widgets.Select.from_param(self.param.opts)
toc = datetime.datetime.now() - tic
_LOGGER.debug("Opts took: %.3f seconds", toc.total_seconds())
tic = datetime.datetime.now()
self.test_disp = pn.widgets.TextInput.from_param(self.param.test)
toc = datetime.datetime.now() - tic
_LOGGER.debug("Test took: %.3f seconds", toc.total_seconds())
tic = datetime.datetime.now()
self._display = pn.Column(
self.start_disp,
self.end_disp,
self.opts_disp,
self.test_disp,
)
toc = datetime.datetime.now() - tic
_LOGGER.debug("Display took: %.3f seconds", toc.total_seconds())
def __panel__(self):
return self._display
class FastTest(WidgetBase, PyComponent):
start = param.Integer(doc="Start")
end = param.Number(doc="End")
opts = param.Selector(
default="a",
objects=["a", "b", "c"],
doc="Options",
)
test = param.String(doc="Test")
def __init__(self, **params):
tic = datetime.datetime.now()
super().__init__(**params)
toc = datetime.datetime.now() - tic
_LOGGER.debug("Init took: %.3f seconds", toc.total_seconds())
tic = datetime.datetime.now()
self.start_disp = pn.widgets.IntInput(value=self.start)
toc = datetime.datetime.now() - tic
_LOGGER.debug("Start took: %.3f seconds", toc.total_seconds())
tic = datetime.datetime.now()
self.end_disp = pn.widgets.FloatInput(value=self.end)
toc = datetime.datetime.now() - tic
_LOGGER.debug("End took: %.3f seconds", toc.total_seconds())
tic = datetime.datetime.now()
self.opts_disp = pn.widgets.Select(value=self.opts)
toc = datetime.datetime.now() - tic
_LOGGER.debug("Opts took: %.3f seconds", toc.total_seconds())
tic = datetime.datetime.now()
self.test_disp = pn.widgets.TextInput(value=self.test)
toc = datetime.datetime.now() - tic
_LOGGER.debug("Test took: %.3f seconds", toc.total_seconds())
tic = datetime.datetime.now()
self._display = pn.Column(
self.start_disp,
self.end_disp,
self.opts_disp,
self.test_disp,
)
toc = datetime.datetime.now() - tic
_LOGGER.debug("Display took: %.3f seconds", toc.total_seconds())
def __panel__(self):
return self._display
class TestApp(WidgetBase, PyComponent):
tests = param.List(doc="Tests")
def __init__(self, **params):
super().__init__(**params)
self.add_button = pn.widgets.Button(name="Add SlowTest")
self.add_button.on_click(self.add_test)
self.add_button_fast = pn.widgets.Button(name="Add FastTest")
self.add_button_fast.on_click(self.add_test_fast)
self._display = pn.Column(
pn.Row(self.add_button, self.add_button_fast),
*self.tests,
)
def add_test(self, event):
tic = datetime.datetime.now()
new_test = SlowTest()
self.tests.append(new_test)
self._display.append(new_test)
toc = datetime.datetime.now() - tic
_LOGGER.debug("Add Test took: %.3f seconds", toc.total_seconds())
def add_test_fast(self, event):
tic = datetime.datetime.now()
new_test = FastTest()
self.tests.append(new_test)
self._display.append(new_test)
toc = datetime.datetime.now() - tic
_LOGGER.debug("Add Test took: %.3f seconds", toc.total_seconds())
def __panel__(self):
return self._display
tic = datetime.datetime.now()
TestApp().servable()
toc = datetime.datetime.now() - tic
_LOGGER.debug("Initial Render took: %.3f seconds", toc.total_seconds())
2025-03-03 10:58:07.783 DEBUG {test_panel} [__init__] Fast Init took: 0.001 seconds
2025-03-03 10:58:07.784 DEBUG {test_panel} [__init__] Fast Start took: 0.001 seconds
2025-03-03 10:58:07.786 DEBUG {test_panel} [__init__] Fast End took: 0.001 seconds
2025-03-03 10:58:07.792 DEBUG {test_panel} [__init__] Fast Opts took: 0.006 seconds
2025-03-03 10:58:07.794 DEBUG {test_panel} [__init__] Fast Test took: 0.001 seconds
2025-03-03 10:58:07.818 DEBUG {test_panel} [__init__] Display took: 0.024 seconds
2025-03-03 10:58:08.225 DEBUG {test_panel} [add_test_fast] Add Fast Test took: 0.443 seconds
2025-03-03 10:58:09.186 DEBUG {test_panel} [__init__] Init took: 0.000 seconds
2025-03-03 10:58:09.286 DEBUG {test_panel} [__init__] Start took: 0.100 seconds
2025-03-03 10:58:09.383 DEBUG {test_panel} [__init__] End took: 0.097 seconds
2025-03-03 10:58:09.483 DEBUG {test_panel} [__init__] Opts took: 0.100 seconds
2025-03-03 10:58:09.578 DEBUG {test_panel} [__init__] Test took: 0.094 seconds
2025-03-03 10:58:09.601 DEBUG {test_panel} [__init__] Display took: 0.022 seconds
2025-03-03 10:58:10.176 DEBUG {test_panel} [add_test] Add Slow Test took: 0.990 seconds