Search code examples
pythonwidgetipywidgetsipyvuetify

python ipyvuetify how to combine multiple widgets into a class


With ipyvuetify, I have to repeatedly use a pair of Textfield and Textarea in an ExpansionPanel. Their interactions are linked as they are represent two fields from a DB line.

Is it possible to create a custom widget, which contains the different nested ipyvuetify widgets and allows to interact with the whole group as a single object and also is rendered like a widgte when called?

Something like this:

import ipyvuetify as vue

Class customWidget():
    def __init__(self, titleText, bodyText):

        title = vue.TextField(class_='mb-n2', label='Label')
        title.value = titleText
        title.on_event('click.stop', None)
        openIn= vue.Btn(text=True, max_width='10px', children=[vue.Icon(children=['open_in_new'])])
        openIn.on_event('click.stop', None)


        note = vue.Textarea(class_='ma-0 pa-0 text-xs-caption', name='markerA', solo=True, elevation=0, outlined=True)
        note.value = bodyText

        panel = vue.ExpansionPanel(class_='ma-0 pa-0 text-xs-caption', children=[
            vue.ExpansionPanelHeader(class_='ma-0 pa-0 pl-5 pr-5', children=[title, openIn]),
            vue.ExpansionPanelContent(class_='ma-0 pa-0 text-xs-caption', children=[note]),
        ])

        self.expansionPanel = vue.ExpansionPanels(v_model=[0], multiple=True, focusable=False, children=[panel])

Solution

  • The easiest solution I found so far is to make the customWidget class inherit from the ipyvuetify widget I want to display and prepopulate it with the nested other widgets in the constructor.

    This works pretty well and can be set up to still have access to each element even in deeply nested structures.

    Care should be taken not to overwrite attributes names which ipyvuetify already uses.

    In the case above my solution looks like this:

    
    import ipyvuetify as vue
    
    class customWidget(vue.ExpansionPanels):
    
        def __init__(self, titleText, bodyText, **kwargs):
    
            children = []
    
            title = vue.TextField(
                class_ = 'mb-n2',
                label = 'Label'
            )
            title.value = titleText
            title.on_event('click.stop', None)
    
    
            openIn = vue.Btn(
                text = True,
                max_width = '10px',
                children = [
                    vue.Icon(
                        children = ['open_in_new']
                    )
                ]
            )
            openIn.on_event('click.stop', None)
    
    
            note = vue.Textarea(
                class_ = 'ma-0 pa-0 text-xs-caption',
                name = 'markerA',
                solo = True,
                elevation = 0,
                outlined = True
            )
            note.value = bodyText
    
            panel = vue.ExpansionPanel(
                class_ = 'ma-0 pa-0 text-xs-caption',
                children = [
                    vue.ExpansionPanelHeader(
                        class_ = 'ma-0 pa-0 pl-5 pr-5',
                        children = [
                            title,
                            openIn
                        ]
                    ),
                    vue.ExpansionPanelContent(
                        class_ = 'ma-0 pa-0 text-xs-caption',
                        children = [
                            note
                        ]
                    ),
                ]
            )
    
            # add elements to kwargs, this allows to still pass others via the instance call
            kwargs['v_model'] = [0]
            kwargs['children'] = [panel]
            kwargs['multiple'] = True
            kwargs['focusable'] = False
            
            super().__init__(**kwargs)