Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/test_and_release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@ jobs:
run: |
pip install ".[turbo]"
pip install -r tests/requirements.txt
# Install requirements for playwright
playwright install
pytest -s ./tests
env:
CI: true
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ dist
.env.*.local

# Log files
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
Expand Down
18 changes: 4 additions & 14 deletions examples/00_vanilla/vanilla_example.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
from numpy import asarray
from pathlib import Path
from PIL import Image
from trame.app import TrameApp
from trame.app.testing import enable_testing
from trame.decorators import TrameApp, change
from trame.decorators import change
from trame_rca.widgets import rca
from trame.app import get_server
from trame.ui.vuetify3 import SinglePageLayout
from trame.widgets import vuetify3 as v3
from trame_client.module.vue3 import www
Expand Down Expand Up @@ -50,22 +50,13 @@ def process_interaction_event(self, event):
self._image_angle += spin * self.rotation_step


@TrameApp()
class VanillaApp:
class VanillaApp(TrameApp):
def __init__(self, server=None):
self.server = get_server(server, client_type="vue3")
super().__init__(server)
image_path = Path(www) / "logo.png"
self.window = RotatableImageWindow(image_path)
self._build_ui()

@property
def state(self):
return self.server.state

@property
def ctrl(self):
return self.server.controller

@change("rotation_step")
def update_rotation_step(self, rotation_step, **kwargs):
self.window.rotation_step = rotation_step
Expand Down Expand Up @@ -100,7 +91,6 @@ def _build_ui(self):
classes="pa-0 fill-height position-relative",
):
view = rca.RemoteControlledArea(
name="view",
display="image",
image_style=({},), # restore default style with width: 100%
)
Expand Down
30 changes: 8 additions & 22 deletions examples/01_vtk/vtk_cone_bare_jpg.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
import json
import asyncio

from trame.app import get_server, asynchronous
from trame.app import TrameApp, asynchronous
from trame.app.testing import enable_testing
from trame.decorators import TrameApp, change, life_cycle
from trame.decorators import change
from trame.ui.vuetify3 import SinglePageLayout
from trame.widgets import vuetify3 as v3

Expand Down Expand Up @@ -155,23 +155,13 @@ def on_interaction(self, origin, event):
vtkRemoteInteractionAdapter.ProcessEvent(self._iren, event_str)


@TrameApp()
class ConeApp:
class ConeApp(TrameApp):
def __init__(self, server=None):
self.server = get_server(server, client_type="vue3")
super().__init__(server)

self.render_window, self.cone_source = self.setup_vtk()
self.view_handler = ViewAdapter(self.render_window, "view")
self.build_ui()

@property
def state(self):
return self.server.state

@property
def ctrl(self):
return self.server.controller

def setup_vtk(self):
renderer = vtkRenderer()
renderWindow = vtkRenderWindow()
Expand Down Expand Up @@ -215,13 +205,14 @@ def build_ui(self):
fluid=True,
classes="pa-0 fill-height position-relative",
):
rca.RemoteControlledArea(
name="view",
view = rca.RemoteControlledArea(
display="image",
)
self.view_handler = ViewAdapter(self.render_window, view.name)
view.add_view_handler(self.view_handler)
with v3.VCard(classes="pa-4 ma-0", style=STATS_STYLES):
rca.StatisticsDisplay(
name="view",
name=view.name,
fps_delta=1.5,
stat_window_size=10,
history_window_size=30,
Expand All @@ -233,11 +224,6 @@ def update_cone(self, resolution, **kwargs):
self.cone_source.SetResolution(resolution)
self.view_handler.render()

@life_cycle.server_ready
def on_server_ready(self, **_):
# can only be called when server is ready
self.ctrl.rc_area_register(self.view_handler)

def update_reset_resolution(self):
self.state.resolution = DEFAULT_RESOLUTION

Expand Down
30 changes: 8 additions & 22 deletions examples/01_vtk/vtk_cone_bare_raw.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
import json
import asyncio

from trame.app import get_server, asynchronous
from trame.app import TrameApp, asynchronous
from trame.app.testing import enable_testing
from trame.decorators import TrameApp, change, life_cycle
from trame.decorators import change
from trame.ui.vuetify3 import SinglePageLayout
from trame.widgets import vuetify3 as v3

Expand Down Expand Up @@ -137,23 +137,13 @@ def on_interaction(self, origin, event):
vtkRemoteInteractionAdapter.ProcessEvent(self._iren, event_str)


@TrameApp()
class ConeApp:
class ConeApp(TrameApp):
def __init__(self, server=None):
self.server = get_server(server, client_type="vue3")
super().__init__(server)

self.render_window, self.cone_source = self.setup_vtk()
self.view_handler = ViewAdapter(self.render_window, "view")
self.build_ui()

@property
def state(self):
return self.server.state

@property
def ctrl(self):
return self.server.controller

def setup_vtk(self):
renderer = vtkRenderer()
renderWindow = vtkRenderWindow()
Expand Down Expand Up @@ -197,13 +187,14 @@ def build_ui(self):
fluid=True,
classes="pa-0 fill-height position-relative",
):
rca.RemoteControlledArea(
name="view",
view = rca.RemoteControlledArea(
display="raw-image",
)
self.view_handler = ViewAdapter(self.render_window, view.name)
view.add_view_handler(self.view_handler)
with v3.VCard(classes="pa-4 ma-0", style=STATS_STYLES):
rca.StatisticsDisplay(
name="view",
name=view.name,
fps_delta=1.5,
stat_window_size=10,
history_window_size=30,
Expand All @@ -215,11 +206,6 @@ def update_cone(self, resolution, **kwargs):
self.cone_source.SetResolution(resolution)
self.view_handler.render()

@life_cycle.server_ready
def on_server_ready(self, **_):
# can only be called when server is ready
self.ctrl.rc_area_register(self.view_handler)

def update_reset_resolution(self):
self.state.resolution = DEFAULT_RESOLUTION

Expand Down
21 changes: 5 additions & 16 deletions examples/01_vtk/vtk_cone_simple.py
Comment thread
jourdain marked this conversation as resolved.
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
# local rendering, but doesn't hurt to include it
import asyncio

from trame.app import get_server, asynchronous
from trame.app import TrameApp, asynchronous
from trame.app.testing import enable_testing
from trame.decorators import TrameApp, change, life_cycle
from trame.decorators import change, life_cycle
from trame.ui.vuetify3 import SinglePageLayout
from trame.widgets import vuetify3 as v3

Expand Down Expand Up @@ -37,27 +37,17 @@
"""


@TrameApp()
class ConeApp:
class ConeApp(TrameApp):
def __init__(self, server=None):
self.server = get_server(server, client_type="vue3")
super().__init__(server)

self.server.cli.add_argument("--encoder", default="jpeg")
args, _ = self.server.cli.parse_known_args()
self.state.encoder = args.encoder

self.view_handler = None
self.render_window, self.cone_source = self.setup_vtk()
self.build_ui()

@property
def state(self):
return self.server.state

@property
def ctrl(self):
return self.server.controller

def setup_vtk(self):
renderer = vtkRenderer()
renderWindow = vtkRenderWindow()
Expand Down Expand Up @@ -133,7 +123,6 @@ def build_ui(self):
classes="pa-0 fill-height position-relative",
):
view = rca.RemoteControlledArea(
name="view",
display="image",
)
self.view_handler = view.create_view_handler(
Expand All @@ -142,7 +131,7 @@ def build_ui(self):
)
with v3.VCard(classes="pa-4 ma-0", style=STATS_STYLES):
rca.StatisticsDisplay(
name="view",
name=view.name,
fps_delta=1.5,
stat_window_size=10,
history_window_size=30,
Expand Down
Binary file removed examples/02_doom/doom.wad
Binary file not shown.
2 changes: 1 addition & 1 deletion tests/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ pillow-avif-plugin
pixelmatch
pytest
pytest-asyncio
pytest-playwright
pytest-xprocess
seleniumbase
trame-server>=3
trame-vuetify
trame>=3.6
Expand Down
27 changes: 15 additions & 12 deletions tests/test_rca_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@
from pathlib import Path
from unittest.mock import MagicMock

from selenium.webdriver import ActionChains
from seleniumbase import SB
from playwright.sync_api import expect, sync_playwright

import pytest
from PIL import Image
Expand Down Expand Up @@ -185,22 +184,26 @@ async def test_groups_close_request_render_together(

@pytest.mark.parametrize("server_path", ["examples/01_vtk/vtk_cone_simple.py"])
def test_rca_view_is_interactive(server):
with SB() as sb:
assert server.port

with sync_playwright() as p:
url = f"http://127.0.0.1:{server.port}/"
sb.open(url)
browser = p.chromium.launch()
page = browser.new_page()
page.goto(url)

element = sb.find_element("img")
element = page.locator("img")
expect(element).to_be_visible()
initial_img_url = element.get_attribute("src")

ActionChains(sb.driver).move_to_element(element).perform()
ActionChains(sb.driver).click_and_hold(element).move_by_offset(
100, 0
).release().perform()
box = element.bounding_box()
assert box is not None

# Expect image to have been updated following user action
page.mouse.move(box["x"] + box["width"] / 2, box["y"] + box["height"] / 2)
page.mouse.down()
page.mouse.move(box["x"] + box["width"] / 2 + 100, box["y"] + box["height"] / 2)
page.mouse.up()
page.wait_for_timeout(100)
new_img_url = element.get_attribute("src")

assert initial_img_url != new_img_url


Expand Down
17 changes: 15 additions & 2 deletions trame_rca/widgets/rca.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,14 @@ def __init__(self, _elem_name, children=None, **kwargs):

# Expose your vue component(s)
class RemoteControlledArea(HtmlElement):
_next_id = 0

def __init__(self, **kwargs):
super().__init__(
"remote-controlled-area",
**kwargs,
)
RemoteControlledArea._next_id += 1
self._attr_names += [
"name",
"origin",
Expand All @@ -38,9 +41,19 @@ def __init__(self, **kwargs):
("send_mouse_move", "sendMouseMove"),
("event_throttle_ms", "eventThrottleMs"),
]

self.name = kwargs.get("name") or f"trame_rca_{RemoteControlledArea._next_id}"
self._handlers = []
self.ctrl.on_server_ready.add(self._on_ready)

def add_view_handler(self, view_handler):
if view_handler in self._handlers:
return

self._handlers.append(view_handler)
if self.server.running:
self.server.root_server.controller.rc_area_register(view_handler)

def create_view_handler(
self,
render_window,
Expand All @@ -59,7 +72,7 @@ def create_view_handler(
rca_encoder=encoder,
)
view_handler = RcaViewAdapter(render_window, self.name, scheduler=scheduler)
self._handlers.append(view_handler)
self.add_view_handler(view_handler)
return view_handler

def create_vtk_handler(
Expand All @@ -82,7 +95,7 @@ def create_vtk_handler(

def _on_ready(self, **_):
Comment thread
jourdain marked this conversation as resolved.
for handler in self._handlers:
self.server.controller.rc_area_register(handler)
self.server.root_server.controller.rc_area_register(handler)


class DisplayArea(HtmlElement):
Expand Down