From f21c4a900481adfdb82a6333bfc244979172c5a9 Mon Sep 17 00:00:00 2001 From: Edoardo Pasca <14138589+paskino@users.noreply.github.com> Date: Thu, 1 Aug 2024 22:23:40 +0100 Subject: [PATCH 1/4] use headless renderwindow for CILViewer via trame --- Wrappers/Python/ccpi/web_viewer/trame_viewer.py | 11 +++++++---- Wrappers/Python/ccpi/web_viewer/web_app.py | 2 +- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/Wrappers/Python/ccpi/web_viewer/trame_viewer.py b/Wrappers/Python/ccpi/web_viewer/trame_viewer.py index 84e141a7..0fc6dd6b 100644 --- a/Wrappers/Python/ccpi/web_viewer/trame_viewer.py +++ b/Wrappers/Python/ccpi/web_viewer/trame_viewer.py @@ -17,9 +17,11 @@ # import inspect import os +import vtk from trame.app import get_server -from trame.widgets import vtk, vuetify +from trame.widgets import vuetify +from trame.widgets import vtk as vtktrame from trame.ui.vuetify import SinglePageWithDrawerLayout from vtkmodules.util import colors from vtkmodules.vtkIOImage import vtkMetaImageReader @@ -35,7 +37,6 @@ class TrameViewer: """ This class is intended as a base class and not to be used outside of one of the TrameViewer2D and TrameViewer3D classes. """ - def __init__(self, viewer, list_of_files: list = None): # Load files and setup the CILViewer if list_of_files is None: @@ -52,7 +53,9 @@ def __init__(self, viewer, list_of_files: list = None): # Create the relevant CILViewer if inspect.isclass(viewer): - self.cil_viewer = viewer() + renderWindow = vtk.vtkRenderWindow() + renderWindow.OffScreenRenderingOn() + self.cil_viewer = viewer(renWin=renderWindow) else: self.cil_viewer = viewer self.load_file(self.default_file) @@ -75,7 +78,7 @@ def __init__(self, viewer, list_of_files: list = None): # VtkLocalView would allow the user to use their own GPU locally, but requires serialisation of various VTK actors that the # framework is not capable of serialisation, hence using RemoteView and being dependent on a web server with a GPU available to it. - self.html_view = vtk.VtkRemoteView(self.cil_viewer.renWin, trame_server=server, ref="view") + self.html_view = vtktrame.VtkRemoteView(self.cil_viewer.renWin, trame_server=server, ref="view") ctrl.view_update = self.html_view.update ctrl.view_reset_camera = self.html_view.reset_camera ctrl.on_server_ready.add(self.html_view.update) diff --git a/Wrappers/Python/ccpi/web_viewer/web_app.py b/Wrappers/Python/ccpi/web_viewer/web_app.py index a45f03c0..0daa55f3 100644 --- a/Wrappers/Python/ccpi/web_viewer/web_app.py +++ b/Wrappers/Python/ccpi/web_viewer/web_app.py @@ -24,7 +24,7 @@ from ccpi.web_viewer.trame_viewer2D import TrameViewer2D from ccpi.web_viewer.trame_viewer3D import TrameViewer3D -server = get_server() +server = get_server(client_type="vue2") state, ctrl = server.state, server.controller TRAME_VIEWER = None From 52d4a07bdffcc906c2f975aeae3b148185cd192e Mon Sep 17 00:00:00 2001 From: Edoardo Pasca <14138589+paskino@users.noreply.github.com> Date: Thu, 1 Aug 2024 22:25:37 +0100 Subject: [PATCH 2/4] update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index de290093..39591bf9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ Enhancements: Bugfix: - Edit slider min value #420 + - Use headless render window for viewer via trame (#429) ## v24.0.1 From a00b262ce188680b9fdd3271f5f0a33c21d77d97 Mon Sep 17 00:00:00 2001 From: Edoardo Pasca <14138589+paskino@users.noreply.github.com> Date: Thu, 1 Aug 2024 22:46:56 +0100 Subject: [PATCH 3/4] pass render window from the web_app interface --- .../Python/ccpi/web_viewer/trame_viewer.py | 10 ++---- .../Python/ccpi/web_viewer/trame_viewer2D.py | 4 +-- .../Python/ccpi/web_viewer/trame_viewer3D.py | 4 +-- Wrappers/Python/ccpi/web_viewer/web_app.py | 31 ++++++++++++++----- 4 files changed, 31 insertions(+), 18 deletions(-) diff --git a/Wrappers/Python/ccpi/web_viewer/trame_viewer.py b/Wrappers/Python/ccpi/web_viewer/trame_viewer.py index 0fc6dd6b..5d357678 100644 --- a/Wrappers/Python/ccpi/web_viewer/trame_viewer.py +++ b/Wrappers/Python/ccpi/web_viewer/trame_viewer.py @@ -17,11 +17,9 @@ # import inspect import os -import vtk from trame.app import get_server -from trame.widgets import vuetify -from trame.widgets import vtk as vtktrame +from trame.widgets import vtk, vuetify from trame.ui.vuetify import SinglePageWithDrawerLayout from vtkmodules.util import colors from vtkmodules.vtkIOImage import vtkMetaImageReader @@ -53,9 +51,7 @@ def __init__(self, viewer, list_of_files: list = None): # Create the relevant CILViewer if inspect.isclass(viewer): - renderWindow = vtk.vtkRenderWindow() - renderWindow.OffScreenRenderingOn() - self.cil_viewer = viewer(renWin=renderWindow) + self.cil_viewer = viewer() else: self.cil_viewer = viewer self.load_file(self.default_file) @@ -78,7 +74,7 @@ def __init__(self, viewer, list_of_files: list = None): # VtkLocalView would allow the user to use their own GPU locally, but requires serialisation of various VTK actors that the # framework is not capable of serialisation, hence using RemoteView and being dependent on a web server with a GPU available to it. - self.html_view = vtktrame.VtkRemoteView(self.cil_viewer.renWin, trame_server=server, ref="view") + self.html_view = vtk.VtkRemoteView(self.cil_viewer.renWin, trame_server=server, ref="view") ctrl.view_update = self.html_view.update ctrl.view_reset_camera = self.html_view.reset_camera ctrl.on_server_ready.add(self.html_view.update) diff --git a/Wrappers/Python/ccpi/web_viewer/trame_viewer2D.py b/Wrappers/Python/ccpi/web_viewer/trame_viewer2D.py index f4819671..1cc2d701 100644 --- a/Wrappers/Python/ccpi/web_viewer/trame_viewer2D.py +++ b/Wrappers/Python/ccpi/web_viewer/trame_viewer2D.py @@ -27,9 +27,9 @@ class TrameViewer2D(TrameViewer): - def __init__(self, list_of_files: list = None): + def __init__(self, list_of_files: list = None, viewer=CILViewer2D): self.first_load = True - super().__init__(list_of_files=list_of_files, viewer=CILViewer2D) + super().__init__(list_of_files=list_of_files, viewer=viewer) self.model_choice = None self.background_choice = None diff --git a/Wrappers/Python/ccpi/web_viewer/trame_viewer3D.py b/Wrappers/Python/ccpi/web_viewer/trame_viewer3D.py index 5361ef61..445041a7 100644 --- a/Wrappers/Python/ccpi/web_viewer/trame_viewer3D.py +++ b/Wrappers/Python/ccpi/web_viewer/trame_viewer3D.py @@ -47,8 +47,8 @@ def colormaps(): class TrameViewer3D(TrameViewer): - def __init__(self, list_of_files=None): - super().__init__(list_of_files=list_of_files, viewer=CILViewer) + def __init__(self, list_of_files=None, viewer=CILViewer): + super().__init__(list_of_files=list_of_files, viewer=viewer) # Define attributes that will be constructed in methods outside of __init__ diff --git a/Wrappers/Python/ccpi/web_viewer/web_app.py b/Wrappers/Python/ccpi/web_viewer/web_app.py index 0daa55f3..d093d2c1 100644 --- a/Wrappers/Python/ccpi/web_viewer/web_app.py +++ b/Wrappers/Python/ccpi/web_viewer/web_app.py @@ -21,8 +21,10 @@ from trame.app import get_server +from ccpi.viewer import CILViewer, CILViewer2D from ccpi.web_viewer.trame_viewer2D import TrameViewer2D from ccpi.web_viewer.trame_viewer3D import TrameViewer3D +import vtk server = get_server(client_type="vue2") state, ctrl = server.state, server.controller @@ -45,22 +47,26 @@ def arg_parser(): Parse the passed arguments to the current :return: """ - help_string = "web_app.py [optional args: -h, -d] \n" \ + help_string = "web_app.py [optional args: -h, -d, --headless] \n" \ "Args:\n" \ "-h: Show this help and exit the program\n" \ - "-d, --2D: Use the 2D viewer instead of the 3D viewer, the default is to just use the 3D viewer." + "-d, --2D: Use the 2D viewer instead of the 3D viewer, the default is to just use the 3D viewer.\n" \ + "--headless: Run the program in headless mode, this is useful for remote rendering." try: - opts, args = getopt.getopt(sys.argv[1:], "hd", ["2D"]) + opts, args = getopt.getopt(sys.argv[1:], "hd", ["2D", "headless"]) except getopt.GetoptError: print(help_string) sys.exit(2) + headless = False for opt, arg in opts: if opt == '-h': print(help_string) elif opt in ("-d", "--2D"): global VIEWER_2D VIEWER_2D = True - return data_finder() + elif opt == '--headless': + headless = True + return data_finder(), headless def data_finder(): @@ -89,13 +95,24 @@ def main() -> int: Create the main class and run the TrameViewer :return: int, exit code for the program """ - data_files = arg_parser() + data_files, headless = arg_parser() + if headless: + renderWindow = vtk.vtkRenderWindow() + renderWindow.OffScreenRenderingOn() global TRAME_VIEWER if not VIEWER_2D: - TRAME_VIEWER = TrameViewer3D(data_files) + if headless: + viewer = CILViewer.CILViewer(renWin=renderWindow) + TRAME_VIEWER = TrameViewer3D(data_files, viewer=viewer) + else: + TRAME_VIEWER = TrameViewer3D(data_files) TRAME_VIEWER.start() else: - TRAME_VIEWER = TrameViewer2D(data_files) + if headless: + viewer = CILViewer2D.CILViewer2D(renWin=renderWindow) + TRAME_VIEWER = TrameViewer2D(data_files, viewer=viewer) + else: + TRAME_VIEWER = TrameViewer2D(data_files) TRAME_VIEWER.start() return 0 From 86327d550d579da61ab7871f503267fa74cb603a Mon Sep 17 00:00:00 2001 From: Edoardo Pasca <14138589+paskino@users.noreply.github.com> Date: Thu, 1 Aug 2024 22:47:59 +0100 Subject: [PATCH 4/4] add empty line --- Wrappers/Python/ccpi/web_viewer/trame_viewer.py | 1 + 1 file changed, 1 insertion(+) diff --git a/Wrappers/Python/ccpi/web_viewer/trame_viewer.py b/Wrappers/Python/ccpi/web_viewer/trame_viewer.py index 5d357678..8664a7af 100644 --- a/Wrappers/Python/ccpi/web_viewer/trame_viewer.py +++ b/Wrappers/Python/ccpi/web_viewer/trame_viewer.py @@ -35,6 +35,7 @@ class TrameViewer: """ This class is intended as a base class and not to be used outside of one of the TrameViewer2D and TrameViewer3D classes. """ + def __init__(self, viewer, list_of_files: list = None): # Load files and setup the CILViewer if list_of_files is None: