Closed cardinalgeo closed 10 months ago
Indeed, the following example, which produces a child_server
from the first app for use with the second, results in the expected behavior (i.e., for both apps, the opacity of the actor updates automatically and without requiring a click).
# cell 1
from trame.app import get_server
from trame.ui.vuetify import SinglePageLayout
from trame.widgets import vuetify
import pyvista as pv
class Cone():
def __init__(self, server=None):
server = get_server(server)
p = pv.Plotter()
p.add_mesh(pv.Cone(), name="cone")
self.plotter = p
with SinglePageLayout(server) as layout:
with layout.content:
with vuetify.VContainer(fluid=True, classes="pa-0 fill-height"):
pv.trame.PyVistaRemoteView(p)
self.server = server
self.ui = layout
def update_opacity(self, value):
self.plotter.actors["cone"].prop.opacity = value
self.plotter.update()
def make_child_server(self):
child_server = self.server.create_child_server()
return child_server
# cell 2
cone_1 = Cone()
await cone_1.ui.ready
cone_1.ui
# cell 3
child_server = cone_1.make_child_server()
cone_2 = Cone(child_server)
await cone_2.ui.ready
cone_2.ui
# cell 4
cone_1.update_opacity(0.5) # cone opacity changes automatically
# cell 5
cone_2.update_opacity(0.5) # cone opacity changes automatically
However, trying the same child_server
approach with a different trame app results in failure. In the example below, a cone app, as above, and a scatter plot app are run in the same notebook. The latter stalls at the loading phase when a child_server
is used (and succeeds when get_server()
is used without the child_server
):
Specs: MacOS Ventura, M1 python==3.10.0 ipykernel==6.27.1 jupyter==1.0.0 plotly==5.18.0 pyvista==0.43.0 trame==3.4.0 trame-vtk==2.6.2 trame-vuetify==2.3.1 trame-plotly==3.0.2
Example:
# cell 1
from trame.app import get_server
from trame.ui.vuetify import SinglePageLayout
from trame.widgets import vuetify
import pyvista as pv
class Cone():
def __init__(self, server=None):
server = get_server(server)
p = pv.Plotter()
p.add_mesh(pv.Cone(), name="cone")
self.plotter = p
with SinglePageLayout(server) as layout:
with layout.content:
with vuetify.VContainer(fluid=True, classes="pa-0 fill-height"):
pv.trame.PyVistaRemoteView(p)
self.server = server
self.ui = layout
def update_opacity(self, value):
self.plotter.actors["cone"].prop.opacity = value
self.plotter.update()
def make_child_server(self):
child_server = self.server.create_child_server()
return child_server
# cell 2
cone_1 = Cone()
await cone_1.ui.ready
cone_1.ui
# cell 3
from plotly import express as px
from trame.widgets import plotly
import pandas as pd
class ScatterPlot:
def __init__(self, data, x, y, server="cat", *args, **kwargs):
server = get_server(server)
self.ctrl = server.controller
self.data = data
fig = px.scatter(data, x=x, y=y, **kwargs)
self.fig = fig
with SinglePageLayout(server) as layout:
with layout.content:
with vuetify.VContainer(fluid=True,classes="fill-height pa-0 ma-0"):
html_plot = plotly.Figure()
self.ctrl.plotly_plot_view_update = html_plot.update
self.server = server
self.ui = layout
self.ctrl.plotly_plot_view_update(fig)
# cell 4
data = pd.DataFrame({"x":[0,1,2], "y":[0,1,2]})
child_server = cone_1.make_child_server()
scatter = ScatterPlot(data, "x", "y", server=child_server)
await scatter.ui.ready
scatter.ui
Any ideas on why the child_server
approach fails here, as well as what a more flexible solution to this general dilemma would be?
Well make_child_server
should be used with a prefix to prevent state collision (purpose of child server). Then the main UI should also be used with a different template name. Otherwise, the ui(s) will collide.
Not sure if it relates to what you are seeing (being stuck), but definitely something to consider when properly testing this use case.
BTW, thanks for reporting in the issue.
Thanks for the corrections Seb — I've implemented them in the example below (I think!), though they do not resolve the "stuck" ScatterPlot app.
# cell 1
from trame.app import get_server
from trame.ui.vuetify import SinglePageLayout
from trame.widgets import vuetify
import pyvista as pv
class Cone():
def __init__(self, name=None, server=None):
self.name = name
server = get_server(server)
server.client_type = "vue2"
p = pv.Plotter()
p.add_mesh(pv.Cone(), name="cone")
self.plotter = p
with SinglePageLayout(server, template_name=self.name) as layout:
with layout.content:
with vuetify.VContainer(fluid=True, classes="pa-0 fill-height"):
pv.trame.PyVistaRemoteView(p)
self.server = server
self.ui = layout
def update_opacity(self, value):
self.plotter.actors["cone"].prop.opacity = value
self.plotter.update()
def make_child_server(self):
child_server = self.server.create_child_server(prefix=self.name)
return child_server
# cell 2
cone_1 = Cone(name="cone_1")
await cone_1.ui.ready
cone_1.ui
# cell 3
from plotly import express as px
from trame.widgets import plotly
import pandas as pd
class ScatterPlot:
def __init__(self, data, x, y, name=None, server="cat", *args, **kwargs):
self.name = name
server = get_server(server)
server.client_type="vue2"
self.ctrl = server.controller
self.data = data
fig = px.scatter(data, x=x, y=y, **kwargs)
self.fig = fig
with SinglePageLayout(server, template_name=self.name) as layout:
with layout.content:
with vuetify.VContainer(fluid=True,classes="fill-height pa-0 ma-0"):
html_plot = plotly.Figure()
self.ctrl.plotly_plot_view_update = html_plot.update
self.server = server
self.ui = layout
self.ctrl.plotly_plot_view_update(fig)
# cell 4
data = pd.DataFrame({"x":[0,1,2], "y":[0,1,2]})
child_server = cone_1.make_child_server()
scatter = ScatterPlot(data, "x", "y", name="scatter", server=child_server)
await scatter.ui.ready
scatter.ui
Interestingly, when I run two of the ScatterPlot
apps in the same jupyter notebook, the "stuck loading" problem disappears and everything works fine! Any ideas on why this would be the case (i.e., that running two Cone
apps works, two ScatterPlot
apps works, but a Cone
app and ScatterPlot
app doesn't)? If this is moving away from the original issue, I can open a new one (here? or in vtk-server
)?
Two ScatterPlot
example:
# cell 1
from trame.app import get_server
from trame.ui.vuetify import SinglePageLayout
from trame.widgets import vuetify, plotly
import pyvista as pv
from plotly import express as px
import pandas as pd
class ScatterPlot:
def __init__(self, data, x, y, name=None, server="cat", *args, **kwargs):
self.name = name
server = get_server(server)
server.client_type="vue2"
self.ctrl = server.controller
self.data = data
fig = px.scatter(data, x=x, y=y, **kwargs)
self.fig = fig
with SinglePageLayout(server, template_name=self.name) as layout:
with layout.content:
with vuetify.VContainer(fluid=True,classes="fill-height pa-0 ma-0"):
html_plot = plotly.Figure()
self.ctrl.plotly_plot_view_update = html_plot.update
self.server = server
self.ui = layout
self.ctrl.plotly_plot_view_update(fig)
def make_child_server(self):
child_server = self.server.create_child_server(prefix=self.name)
return child_server
# cell 2
data_1 = pd.DataFrame({"x":[0,1,2], "y":[0,1,2]})
scatter_1 = ScatterPlot(data_1, "x", "y", name="scatter_1")
await scatter_1.ui.ready
scatter_1.ui
# cell 3
data_2 = pd.DataFrame({"x":[0,1,2], "y":[0,1,2]})
child_server = scatter_1.make_child_server()
scatter_2 = ScatterPlot(data_2, "x", "y", name="scatter_1", server=child_server)
await scatter_2.ui.ready
scatter_2.ui
They are indeed different problems.
But since you are using the same server (child), you don't need to await the second app. If you skip it, does it work? If so, I just need to fix the logic on the ready part with child server....
I commented out await scatter.ui.ready
in the Cone
-ScatterPlot
example, but the "stuck loading" problem persists. I'll go ahead and open another issue on this child_server
problem so that the current issue can stay on topic for the more general problem of running two apps in a jupyter notebook (i.e., without needing to use a child_server
). Would this child_server
issue be best opened in this repo or trame-server
(or another)?
Sorry looking at your code and you are missing the point of the prefix for the child server.
server = get_server()
s1 = server.create_child_server("s1")
s2 = server.create_child_server("s2")
app1 = ScatterPlot(s1)
app2 = ScatterPlot(s2)
The prefix is "the same thing" as get_server("s1")
and get_server("s2")
.
But then if you don't assign a template_name=
for your layout, they will collide.
Sorry about that — typo on my part! Fixing it still produces the same behavior, in that the ScatterPlot
-ScatterPlot
example behaves correctly as it did before. Fortunately, I did not make that mistake in the the previous, Cone
-ScatterPlot
example (in that each passes a different name for both the prefix
and template_name
). Unfortunately, it means that example is still producing the "stuck loading" behavior.
Actually, if I try the pattern you've outlined (i.e., creating the server followed by the child server(s), and then passing the child server(s) when instantiating the app object(s)), a different error arises — the iframe is blank with the error message JS Error => ReferenceError: view_P_0x1467b3640_1Id is not defined
at the bottom.
An example that reproduces this:
# cell 1
from trame.app import get_server
from trame.ui.vuetify import SinglePageLayout
from trame.widgets import vuetify
import pyvista as pv
class Cone():
def __init__(self, name=None, server=None):
self.name = name
p = pv.Plotter()
p.add_mesh(pv.Cone(), name="cone")
self.plotter = p
with SinglePageLayout(server, template_name=self.name) as layout:
with layout.content:
with vuetify.VContainer(fluid=True, classes="pa-0 fill-height"):
pv.trame.PyVistaRemoteView(p)
self.server = server
self.ui = layout
# cell 2
server = get_server()
server.client_type = "vue2"
s1 = server.create_child_server(prefix="s1")
# cell 3
cone_1 = Cone(name="cone_1", server=s1)
await cone_1.ui.ready
cone_1.ui
Just to clarify, would it be best to split off these child_server
problems into a separate issue (and in which repo)?
It seems to me that there is 3 issue:
I was aware of the 1. I would like to reproduce the locking issue. But doesn't seem to be an issue on my machine unless I'm not doing what you are doing. Then the child_server it is a separate topic.
Steps to reproduce that issue
from trame.app import get_server
from trame.decorators import TrameApp, change
from trame.widgets import vuetify, vtk as vtk_widgets
from trame.ui.vuetify import SinglePageLayout
from vtkmodules.vtkFiltersSources import vtkConeSource
from vtkmodules.vtkRenderingCore import (
vtkRenderer,
vtkRenderWindow,
vtkRenderWindowInteractor,
vtkPolyDataMapper,
vtkActor,
)
# VTK factory initialization
from vtkmodules.vtkInteractionStyle import vtkInteractorStyleSwitch # noqa
import vtkmodules.vtkRenderingOpenGL2 # noqa
# -----------------------------------------------------------------------------
@TrameApp()
class Cone:
def __init__(self, server_or_name=None):
self.server = get_server(server_or_name, client_type="vue2")
self._vtk_rw, self._vtk_cone = self._vtk_setup()
self.ui = self._generate_ui()
@property
def ctrl(self):
return self.server.controller
@property
def state(self):
return self.server.state
@change("resolution")
def on_resolution_change(self, resolution, **kwargs):
self._vtk_cone.SetResolution(resolution)
self.ctrl.view_update()
@property
def resolution(self):
return self.state.resolution
@resolution.setter
def resolution(self, v):
with self.state:
self.state.resolution = v
def reset_resolution(self):
self.resolution = 6
def _vtk_setup(self):
renderer = vtkRenderer()
renderWindow = vtkRenderWindow()
renderWindow.AddRenderer(renderer)
renderWindow.OffScreenRenderingOn()
renderWindowInteractor = vtkRenderWindowInteractor()
renderWindowInteractor.SetRenderWindow(renderWindow)
renderWindowInteractor.GetInteractorStyle().SetCurrentStyleToTrackballCamera()
cone_source = vtkConeSource()
mapper = vtkPolyDataMapper()
actor = vtkActor()
mapper.SetInputConnection(cone_source.GetOutputPort())
actor.SetMapper(mapper)
renderer.AddActor(actor)
renderer.ResetCamera()
renderWindow.Render()
return renderWindow, cone_source
def _generate_ui(self):
with SinglePageLayout(self.server) as layout:
layout.title.set_text("Trame demo")
with layout.toolbar as toolbar:
toolbar.dense = True
vuetify.VSpacer()
vuetify.VSlider(
v_model=("resolution", 6),
min=3,
max=60,
step=1,
hide_details=True,
style="max-width: 300px;",
)
with vuetify.VBtn(icon=True, click=self.reset_resolution):
vuetify.VIcon("mdi-lock-reset")
with vuetify.VBtn(icon=True, click=self.ctrl.view_reset_camera):
vuetify.VIcon("mdi-crop-free")
with layout.content:
with vuetify.VContainer(fluid=True, classes="pa-0 fill-height"):
view = vtk_widgets.VtkRemoteView(self._vtk_rw)
self.ctrl.view_update = view.update
self.ctrl.view_reset_camera = view.reset_camera
return layout
c1 = Cone("c1")
await c1.ui.ready
c1.ui.iframe_style = "border: none; width: 100%; height: 200px;"
c1.ui
c2 = Cone("c2")
await c2.ui.ready
c2.ui.iframe_style = "border: none; width: 100%; height: 200px;"
c2.ui
BTW, I just fixed it... I just need to clean the code and check that I didn't break some use cases...
Steps to reproduce that issue
^I'm not sure I understand which issue is which per your wording. I can see that your code reproduces the behavior that prompted the original discussion in the main Trame
repo — Is this referring to the locking issue or the vtk remote rendering issue?
BTW, I just fixed it... I just need to clean the code and check that I didn't break some use cases...
Ah great! So you've resolved the issue from the original discussion (as you reproduced above)?
fixed in 2.7.0
Can confirm that the original multi-server problem is resolved on my end!
Let me know if it's still worth creating issues for the child server problem (e.g., the example referenced below still fails for me):
Actually, if I try the pattern you've outlined (i.e., creating the server followed by the child server(s), and then passing the child server(s) when instantiating the app object(s)), a different error arises — the iframe is blank with the error message
JS Error => ReferenceError: view_P_0x1467b3640_1Id is not defined
at the bottom.
You can but when I tested on my end (with my code and scenario), it was fine. So you will need to provide the full example that is failing for you.
Hi all,
As suggested by @jourdain, I'm transitioning the discussion here to an issue in the trame-vtk repo. The issue is as follows:
When I run two trame apps within a jupyter notebook, only the last is automatically updated upon changing, e.g., the opacity of an actor. The first is updated only upon clicking within the viewer. In contrast, I'd expect each to update automatically, independent of the number of trame apps running. Here's a simple example that illustrates the issue:
In the previous discussion, @jourdain noted that it was an issue with remote vtk and having more than one server within the same interpreter and thought that using a
child_server
might provide a quick fix.