mariobuikhuizen / voila-embed

Embed jupyter widgets in existing websites
Other
52 stars 7 forks source link

clarification on new Event structure #8

Open havok2063 opened 4 years ago

havok2063 commented 4 years ago

I had some questions on the new Event demos from the recent PR #3. 1. Is it strictly necessary to create a subclass of a Vuetify object just to add a simple event accessible to front-end components? My case involves a single v.Row component containing three bqplot components I need updated on a drop-down select change. Inside my notebook, I subclassed v.Row to include my on-change event function.

class UpdateRow(v.Row):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.on_event('change', update_data)    

UpdateRow(_metadata={'mount_id': 'protospec'}, dense=True, row=True, wrap=True, align_center=True, children=[

    # load the histogram and slider content
    v.Col(xs12=True, lg6=True, xl4=True, children=[
        img
    ]),

    # load the line plot content
    v.Col(xs12=True, xl4=True, children=[
        spec2d
    ]),
    # load the line plot content
    v.Col(xs12=True, xl4=True, children=[
        fig
    ]),       
])

with my update_data function defined as

def update_data(widget, event, data):
    # get new data from selected target
    selected_src = int(data)
    hdu1d, hdu, cut = prep_data(selected_src)

    # update spec1d data
    wave, flux = get_xy(hdu1d[1].data)
    plot.y = flux
    plot.x = wave

    # update spec2d data
    i = PercentileInterval(95)
    heat.color = i(hdu[1].data)

    # update image cutout data
    image.color = cut[0].data
    img.title = 'Src {0}'.format(selected_src)

However if I need to attach my event to multiple components, I'd have to subclass every single one. Is it possible to expand event function definitions so they could be linked to one or multiple components, without subclassing every component you need to attach to it? Something like

v.Row(_metadata={'mount_id': 'protospec'} .... )

def update_data(widget, event, data, mount_to=['protospec:change']):
    ...
  1. Is there an advantage to putting the requestWidget call inside Vue's created block? My front-end looks like
    <v-row>
        <v-col class='col-md-2'>
            <v-select :items="items" dense=True label="Outside Component Select" @change='update_data'></v-select>
        </v-col>
    </v-row>

    with the update_data method defined as

        methods: {
            update_data(event) {
                requestWidget({
                    voilaUrl: 'http://localhost:8000',
                    notebook: 'spec_viewers.ipynb',
                    mountId: 'protospec',
                }).then(sevent => {
                    sevent.send({event: 'change', data: event})
                });
            }
        }

    Is this reasonable or does this cause unnecessary network calls?

mariobuikhuizen commented 4 years ago
  1. You could do this:
    
    def update_data(widget, event, data):
    ...

row1 = v.Row(.... ) row1.on_event('change', update_data) ... rowN = v.Row(...) rowN.on_event('change', update_data)


2. In the example we wanted the widget-models to be available as early as possible, so the create lifecycle method was the first choice. We kept a reference to it for further interactions. Calling `requestWidget` does not cause any network calls, it just returns a promise to a widget-model that will be loaded anyway. So your solution is just as efficient and, when using the model only in one place, more concise. In hindsight, we could've used this in the example, we just didn't come up with the idea.

I want to point out that since you already use vue template syntax in the front-end, you may want to use this in the notebook/python too. This is not very well document yet, but I think you can get a long way with the examples: https://github.com/mariobuikhuizen/ipyvuetify/blob/master/examples/Examples%20template.ipynb. This is also being used in https://github.com/spacetelescope/jdaviz and https://github.com/glue-viz/glue-jupyter. The events work differently in this mode, so if you decide to use it we have to think about question 1 again. 
havok2063 commented 4 years ago

Thanks for the clarification! This is helpful. Yeah your example makes sense if you need to access the model multiple times from multiple places. And good to know there's no downside to where you place the requestWidget in the Vue lifecycle.

I think you're right and we'll end up using the template syntax. Since our plan is to ultimately utilize jdaviz, I think we'll end up building a custom template that plugs into jdaviz. Our team is also planning on building out separate Vue templates and components for other services, that will exist independently of Voila and notebooks, but where we still want the option of plugging in Voila served components. So I'm exploring mechanisms for mixing front-end templates with Voila-embedded templates and controlling widgets from alternately-defined front-end components.

maartenbreddels commented 4 years ago

Our team is also planning on building out separate Vue templates and components for other services, that will exist independently of Voila and notebooks, but where we still want the option of plugging in Voila served components.

Good to hear, and looking forward to feedback on how that works out.

Slightly related,

In vaex, i started to put the templates in separate files, e.g.: https://github.com/vaexio/vaex/blob/54d5d30e28360cbc0d35de9cba0c09c3c6ab016e/packages/vaex-jupyter/vaex/jupyter/widgets.py#L323 Which uses: https://github.com/vaexio/vaex/blob/54d5d30e28360cbc0d35de9cba0c09c3c6ab016e/packages/vaex-jupyter/vaex/jupyter/columnlist.vue

This makes is possible to use a .vue file in both a Python widget and vue (although not everything is supported in the widget case). And it also gives you syntax highlighting.

havok2063 commented 4 years ago

Thanks Maarten! That's really interesting and a good example to pull from of how these things can sync up! This is helpful!