Simple form using Panel

I created a panel app to create a form to take inputs to store into a database for analysis later.

Using Panel 1.0.3

In the example below there is a Customer form and an Employee form.

Code:

import panel as pn


class Customer:
    def __init__(self):
        self.customer_name = pn.widgets.TextInput(name="Customer Name", width=500)
        self.customer_address = pn.widgets.TextAreaInput(
            name="Customer Address", width=500
        )
        self.store = pn.widgets.Button(name="Store")
        self.store.on_click(self.store_cus)

    @property
    def display(self):
        return pn.Column(self.customer_name, self.customer_address, self.store)

    def store_cus(self, event):
        print("Store to Database")


class Employee:
    def __init__(self):
        self.employee_name = pn.widgets.TextInput(name="Employee Name", width=500)
        self.employee_email = pn.widgets.TextInput(name="Employee Email", width=500)
        self.store = pn.widgets.Button(name="Store")
        self.store.on_click(self.store_emp)

    @property
    def display(self):
        return pn.Column(self.employee_name, self.employee_email, self.store)

    def store_emp(self, event):
        print("Store to Database")


class Navbar:
    def __init__(self):
        self.left_nav_bar = pn.widgets.Select(
            name="Choose", options=["Customers", "Employees"]
        )
        self.customer_tabs = Customer()
        self.employee_tabs = Employee()
        self.display_form = pn.bind(self.update_on_nav_bar, navbar=self.left_nav_bar)

    def update_on_nav_bar(self, navbar):
        if navbar == "Employees":
            return self.employee_tabs.display
        else:
            return self.customer_tabs.display

        # return self.display_form

    @pn.depends("left_nav_bar.value")
    def display(self):
        if self.left_nav_bar.value == "Employees":
            return self.employee_tabs.display
        else:
            return self.customer_tabs.display


if __name__ == "__main__":
    nav = Navbar()

    template = pn.template.MaterialTemplate(title="CustEmp")

    template.main.append(
        pn.Column(
            pn.Row(
                nav.left_nav_bar,
                nav.display,
                nav.display_form,
            ),
        )
    )

    pn.serve(template)

The drop down menu is used to switch between Customer and Employee forms

Output:

On Changing the drop down menu


results in error from pn.bind (I think)

2023-05-29 18:59:41,958 ERROR: panel.reactive - Callback failed for object named "Choose" changing property {'value': 'Customers'} 
Traceback (most recent call last):
  File "C:\Users\<path-to-env>\lib\site-packages\panel\reactive.py", line 379, in _process_events
    self.param.update(**self_events)
  File "C:\Users\<path-to-env>\lib\site-packages\param\parameterized.py", line 1902, in update
    self_._batch_call_watchers()
  File "C:\Users\<path-to-env>\lib\site-packages\param\parameterized.py", line 2063, in _batch_call_watchers
    self_._execute_watcher(watcher, events)
  File "C:\Users\<path-to-env>\lib\site-packages\param\parameterized.py", line 2025, in _execute_watcher
    watcher.fn(*args, **kwargs)
  File "C:\Users\<path-to-env>\lib\site-packages\panel\param.py", line 827, in _replace_pane
    self._update_inner(new_object)
  File "C:\Users\<path-to-env>\lib\site-packages\panel\pane\base.py", line 700, in _update_inner
    new_pane, internal = self._update_from_object(
  File "C:\Users\<path-to-env>\lib\site-packages\panel\pane\base.py", line 655, in _update_from_object
    cls._recursive_update(old_object, i, old, new)
  File "C:\Users\<path-to-env>\lib\site-packages\panel\pane\base.py", line 629, in _recursive_update
    old.param.update(**new_params)
AttributeError: 'str' object has no attribute 'param'
Exception in callback functools.partial(<bound method IOLoop._discard_future_result of <tornado.platform.asyncio.AsyncIOMainLoop object at 0x0000020567533DF0>>, <Task finished name='Task-345' coro=<ServerSession.with_document_locked() done, defined at C:\Users\<path-to-env>\lib\site-packages\bokeh\server\session.py:77> exception=AttributeError("'str' object has no attribute 'param'")>)
Traceback (most recent call last):
  File "C:\Users\<path-to-env>\lib\site-packages\tornado\ioloop.py", line 738, in _run_callback
    ret = callback()
  File "C:\Users\<path-to-env>\lib\site-packages\tornado\ioloop.py", line 762, in _discard_future_result
    future.result()
  File "C:\Users\<path-to-env>\lib\site-packages\bokeh\server\session.py", line 98, in _needs_document_lock_wrapper
    result = await result
  File "C:\Users\<path-to-env>\lib\site-packages\panel\reactive.py", line 424, in _change_coroutine
    state._handle_exception(e)
  File "C:\Users\<path-to-env>\lib\site-packages\panel\io\state.py", line 432, in _handle_exception
    raise exception
  File "C:\Users\<path-to-env>\lib\site-packages\panel\reactive.py", line 422, in _change_coroutine
    self._change_event(doc)
  File "C:\Users\<path-to-env>\lib\site-packages\panel\reactive.py", line 440, in _change_event
    self._process_events(events)
  File "C:\Users\<path-to-env>\lib\site-packages\panel\reactive.py", line 379, in _process_events
    self.param.update(**self_events)
  File "C:\Users\<path-to-env>\lib\site-packages\param\parameterized.py", line 1902, in update
    self_._batch_call_watchers()
  File "C:\Users\<path-to-env>\lib\site-packages\param\parameterized.py", line 2063, in _batch_call_watchers
    self_._execute_watcher(watcher, events)
  File "C:\Users\<path-to-env>\lib\site-packages\param\parameterized.py", line 2025, in _execute_watcher
    watcher.fn(*args, **kwargs)
  File "C:\Users\<path-to-env>\lib\site-packages\panel\param.py", line 827, in _replace_pane
    self._update_inner(new_object)
  File "C:\Users\<path-to-env>\lib\site-packages\panel\pane\base.py", line 700, in _update_inner
    new_pane, internal = self._update_from_object(
  File "C:\Users\<path-to-env>\lib\site-packages\panel\pane\base.py", line 655, in _update_from_object
    cls._recursive_update(old_object, i, old, new)
  File "C:\Users\<path-to-env>\lib\site-packages\panel\pane\base.py", line 629, in _recursive_update
    old.param.update(**new_params)
AttributeError: 'str' object has no attribute 'param'

Two questions:

  1. Why does nav.display show the text and not render of the actual panel widgets?
    The code executes through display property/method as well, but only renders the text and the the actual widgets.
  2. On changing the drop down menu - why does display_form result in error? What can I do to work around it?

I have been at this for a few hours and can’t seem to make sense of this. Any ideas?

1 Like

Using param.Parameterized solves Question 1.
Each class inherits from param.Parameterized as shown below

import panel as pn
import param


class Customer(param.Parameterized):
    def __init__(self):
        self.customer_name = pn.widgets.TextInput(name="Customer Name", width=500)
        self.customer_address = pn.widgets.TextAreaInput(
            name="Customer Address", width=500
        )
        self.store = pn.widgets.Button(name="Store")
        self.store.on_click(self.store_cus)

    @property
    def display(self):
        return pn.Column(self.customer_name, self.customer_address, self.store)

    def store_cus(self, event):
        print("Store to Database")


class Employee(param.Parameterized):
    def __init__(self):
        self.employee_name = pn.widgets.TextInput(name="Employee Name", width=500)
        self.employee_email = pn.widgets.TextInput(name="Employee Email", width=500)
        self.store = pn.widgets.Button(name="Store")
        self.store.on_click(self.store_emp)

    @property
    def display(self):
        return pn.Column(self.employee_name, self.employee_email, self.store)

    def store_emp(self, event):
        print("Store to Database")


class Navbar(param.Parameterized):
    def __init__(self):
        self.left_nav_bar = pn.widgets.Select(
            name="Choose",
            options=[
                "Employees",
                "Customers",
            ],
        )
        self.customer_tabs = Customer()
        self.employee_tabs = Employee()


    @pn.depends("left_nav_bar.value")
    def display(self):
        if self.left_nav_bar.value == "Employees":
            return self.employee_tabs.display
        else:
            return self.customer_tabs.display


if __name__ == "__main__":
    nav = Navbar()

    template = pn.template.MaterialTemplate(title="CustEmp")

    template.main.append(
        pn.Column(
            pn.Row(
                nav.left_nav_bar,
                nav.display,
            ),
        )
    )

    pn.serve(template)

Question 2 - is still open. I can’t explain why the code errors out with AttributeError: 'str' object has no attribute 'param'

Also, I just reverted to 0.14.3 and this works in 0.14.3 - might point to a potential bug?


This will be fixed in the next release 1.0.4.

2 Likes