Open banesullivan-kobold opened 1 month ago
You keep having the same wrong assumption again and again. If you want to dynamically change something it needs to be in the state.
The attributes on the class is to just to define the HTML content which get evaluated only once. If you reflush the layout, you will see the change but that's really not what you want to do (lot of overhead of delete/create vue component client side).
So in other word, you need to generate the interactive ratio variable name and update it at the state level. That can be done at your wrapper class directly.
What you have in mind is jupyter's trait, but that is really not what trame is doing.
You keep having the same wrong assumption again and again. If you want to dynamically change something it needs to be in the state. ... What you have in mind is jupyter's trait, but that is really not what trame is doing.
I find jupyter's traits rather intuitive. I can't help but wonder if trame could/should do this and synchronize the different attrs that may or may not be linked to state variables.
From my perspective, I don't see any reason why setting the interactive_ratio
attr on the VtkRemoteView
instance itself shouldn't propagate that change into the state.
I'm thinking something like the following:
class MyView(VtkRemoteView):
def __init__(self, plotter, ref=None, **kwargs):
self._INTERACTIVE_RATIO = f'{plotter._id_name}_interactive_ratio'
if 'interactive_ratio' not in kwargs:
kwargs['interactive_ratio'] = (self._INTERACTIVE_RATIO, 1)
else:
value = kwargs['interactive_ratio']
if isinstance(value, tuple):
self._INTERACTIVE_RATIO = value[0]
else:
kwargs['interactive_ratio'] = (self._INTERACTIVE_RATIO, value)
super().__init__(plotter.render_window, ref, **kwargs)
@property
def interactive_ratio(self):
return state[self._INTERACTIVE_RATIO]
@interactive_ratio.setter
def interactive_ratio(self, value):
state[self._INTERACTIVE_RATIO] = value
state.dirty(self._INTERACTIVE_RATIO)
Would it make sense for all attrs of of these types of classes be wrapped this way?
The attributes on the class is to just to define the HTML content which get evaluated only once. If you reflush the layout, you will see the change but that's really not what you want to do (lot of overhead of delete/create vue component client side).
So in other word, you need to generate the interactive ratio variable name and update it at the state level. That can be done at your wrapper class directly.
This is really helpful, thanks! I think this is something that should be implemented in PyVista's wrappings of these view classes and I'll try to make that proposal soon.
So while I understand the jupyter/ipywidget approach make sense for tiny ui or set of controls, it does not when you create a full fledge client/server application where you have some performance consideration to keep in mind.
So, in short I'm fine if you want to add that binding logic on your component, but I'm not ok putting it by default in trame. (BTW your code won't work because of some logic in the AbstractWidgets which we may want to fix if we can)
What we could technically do is to trigger a flush on the layout when someone modify an attribute that is not a variable... There will be some flashing on the client side, but it will have the behavior you expect. Then we can teach users to better support dynamic behaviors.
For posterity, this can be done in PyVista via the following (I plan on pushing this into PyVista directly). @jourdain, is modifying the state via self.server.state
like this okay?
from pyvista.trame.views import PyVistaRemoteView
class PyVistaRemoteInteractiveRatioView(PyVistaRemoteView):
def __init__(self, plotter, **kwargs):
self._INTERACTIVE_RATIO = f'{plotter._id_name}_interactive_ratio'
self._STILL_RATIO = f'{plotter._id_name}_still_ratio'
if 'interactive_ratio' not in kwargs:
kwargs['interactive_ratio'] = (self._INTERACTIVE_RATIO, 1)
else:
value = kwargs['interactive_ratio']
if isinstance(value, tuple):
self._INTERACTIVE_RATIO = value[0]
else:
kwargs['interactive_ratio'] = (self._INTERACTIVE_RATIO, value)
if 'still_ratio' not in kwargs:
kwargs['still_ratio'] = (self._STILL_RATIO, 1)
else:
value = kwargs['still_ratio']
if isinstance(value, tuple):
self._STILL_RATIO = value[0]
else:
kwargs['still_ratio'] = (self._STILL_RATIO, value)
super().__init__(plotter, **kwargs)
def set_interactive_ratio(self, value):
self.server.state[self._INTERACTIVE_RATIO] = value
self.server.state.flush()
def set_still_ratio(self, value):
self.server.state[self._STILL_RATIO] = value
self.server.state.flush()
In hindsight, I just noticed this is the same approach taken for toggling the remote/local modes on VtkRemoteLocalView
. @jourdain, would it make sense to do this with all attributes under @property
methods?
Anything that you plan to use dynamically in PyVista, you can do it like you described.
But I would write the update like below
def set_interactive_ratio(self, value):
with self.state:
self.state[self._INTERACTIVE_RATIO] = value
Is it possible to directly change the
interactive_ratio
from theVtkRemoteView
instance itself in Python code? Take the following example with PyVista where I get access to the underlyingVtkRemoteView
instance and attempt to change theinteractive_ratio
. However, this change does not take affect (thedirty()
call was my attempt at pushing this change):I'm able to link the interactive_ratio to a variable, so I know it can be dynamically changed, but how can I do this directly from the
VtkRemoteView
instance python-side?