Skip to content
Snippets Groups Projects
Commit 7dc5d643 authored by alice grosch's avatar alice grosch
Browse files

Merge branch 'BugFixes' into 'master'

Bug fixes

See merge request jupyter4jsc/j4j_extras/pvlink!2
parents c652c9f9 66c14f36
Branches
Tags 0.2.1
No related merge requests found
%% Cell type:markdown id: tags:
## Content
- SimpleRenderer
- Usage
- Sizing
- RemoteRenderer
- Usage
- Using Jupyter Server Proxy
- Using a custom protocol
- Usage with a pvserver and choosen port¶
%% Cell type:markdown id: tags:
# SimpleRenderer
%% Cell type:markdown id: tags:
Does nothing more than show the ParaViewWeb server application in the Output Area.
%% Cell type:markdown id: tags:
## Usage
%% Cell type:markdown id: tags:
Start a ParaViewWeb server application over the command line (see the [ParaView example](https://kitware.github.io/paraviewweb/examples/RemoteRenderer.html#Using-ParaView-as-server))
```bash
pvpython pv_server.py --port 1234 --authKey wslink-secret
```
%% Cell type:code id: tags:
``` python
from pvlink import SimpleRenderer
simple = SimpleRenderer(sessionURL='ws://localhost:1234/ws', authKey='wslink-secret')
display(simple)
```
%% Output
%% Cell type:markdown id: tags:
## Sizing
Widgets scales with container size.
%% Cell type:code id: tags:
``` python
from ipywidgets import Box
Box(children=[simple], layout={'height':'500px'})
```
%% Output
%% Cell type:markdown id: tags:
# RemoteRenderer
This renderer requires the `paraview.simple` and `paraview.web` modules.
Upon initialization, the RemoteRenderer starts a webserver for you. If nothing is specified, the webserver will try to start on port 8080 or the next free port thereafter and create a random authentication key. You can pass your own arguments to the webserver. To display help on the possible arguments, you can call `<yourRenderer>.webserver_arguments_help()`.
If `pvserver_host` and `pvserver_port` (default 11111) are specified, the webserver will try to establish a connection to the ParaView server (pvserver) at the given host and port. The pvserver can then take over the heavy lifting and handle very large geometries. Locally, only the image data of the processed data is recieved. To prevent data from being rendered locally, we recommend using the `SetRecommendedRenderSettings` function from `pvlink.utilities` on your displayed view.
%% Cell type:markdown id: tags:
## Usage
### Default
%% Cell type:code id: tags:
``` python
from pvlink import RemoteRenderer
renderer = RemoteRenderer(port=8082)
# Alternatively, if you want to render your data with a ParaView server:
# Start a pvserver over the command line and run the following line
# renderer = RemoteRenderer(pvserver_host='localhost', pvserver_port=11111, p=8082)
display(renderer)
```
%% Output
%% Cell type:markdown id: tags:
At this point, there is no view or sources, so the output will be a blank canvas.
%% Cell type:code id: tags:
``` python
from paraview import simple
from pvlink.utility import SetRecommendedRenderSettings
# Create a view and...
view = simple.CreateView('RenderView', 'example')
# ...disable interactor-based render calls and
# ensure pvserver-side rendering (if applicable)
SetRecommendedRenderSettings(view)
# Create and show a source
source = simple.Cone()
simple.Show(source, view)
# Update the renderer widget to display the changes
renderer.update_render()
```
%% Cell type:markdown id: tags:
### Using Jupyter Server Proxy
If you want to access your webserver using Jupyter Server Proxy, you need to set `use_jupyter_server_proxy` to True and specify the baseURL.
If you want to access your webserver using Jupyter Server Proxy, you need to set `use_jupyter_server_proxy` to True and specify the baseURL. Setting `use_jupyter_server_proxy_https` to true, allows to use the https certificates of the proxy, to use an encrypted connection between client brwoser and the jupyter Server Proxy.
Example: If your notebook url is `http://localhost:8888` and you would access a process using `http://localhost:88888/proxy/8080`, the baseURL would be the part before 'proxy, `localhost:8888`.
%% Cell type:code id: tags:
``` python
proxied_renderer = RemoteRenderer(baseURL='localhost:8888', use_jupyter_server_proxy=True,
port=8080, ws='pvwebserver/ws')
use_jupyter_server_proxy_https=True, port=8080, ws='pvwebserver/ws')
```
%% Cell type:markdown id: tags:
If no `viewID` is specified, the widget will always show the active view. To bind it to a view, we need the GlobalID of the view and pass it to the renderer.
%% Cell type:code id: tags:
``` python
proxied_renderer.viewID = view.GetGlobalIDAsString()
display(proxied_renderer)
```
%% Output
%% Cell type:markdown id: tags:
You can display help for the possible webserver arugments.
%% Cell type:code id: tags:
``` python
proxied_renderer.webserver_arguments_help()
```
%% Output
ParaView Web Server
optional arguments:
-h, --help show this help message and exit
-d, --debug log debugging messages to stdout
-s, --nosignalhandlers
Prevent Twisted to install the signal handlers so it
can be started inside a thread.
-i HOST, --host HOST the interface for the web-server to listen on
(default: localhost)
-p PORT, --port PORT port number for the web-server to listen on (default:
8080)
-t TIMEOUT, --timeout TIMEOUT
timeout for reaping process on idle in seconds
(default: 300s)
-c CONTENT, --content CONTENT
root for web-pages to serve (default: none)
-a AUTHKEY, --authKey AUTHKEY
Authentication key for clients to connect to the
WebSocket.
-f, --force-flush If provided, this option will force additional padding
content to the output. Useful when application is
triggered by a session manager.
-k SSLKEY, --sslKey SSLKEY
SSL key. Use this and --sslCert to start the server on
https.
-j SSLCERT, --sslCert SSLCERT
SSL certificate. Use this and --sslKey to start the
server on https.
-ws WS, --ws-endpoint WS
Specify WebSocket endpoint. (e.g. foo/bar/ws, Default:
ws)
--no-ws-endpoint If provided, disables the websocket endpoint
--fs-endpoints FSENDPOINTS
add another fs location to a specific endpoint (i.e:
data=/Users/seb/Download|images=/Users/seb/Pictures)
--upload-directory UPLOADPATH
path to root upload directory
%% Cell type:markdown id: tags:
## Using a custom protocol
You can define your own pipeline which should be run when the webserver starts up.
First, define a class which inherits from `pv_wslink.PVServerProtocol` and add your pipeline there. Per default, the RemoteRenderer shows the active view.
To ensure that the view you created in the pipeline is the one shown in the rendering widget and does not get replaced by a new active view, we need to make it available to the outside and later on bind the viewID to the rendering widget.
%% Cell type:code id: tags:
``` python
from paraview import simple
from paraview.web import pv_wslink
from paraview.web import protocols as pv_protocols
from pvlink.utility import SetRecommendedRenderSettings
class DemoServer(pv_wslink.PVServerProtocol):
authKey = 'wslink-secret'
def initialize(self):
# Bring used components
self.registerVtkWebProtocol(pv_protocols.ParaViewWebMouseHandler())
self.registerVtkWebProtocol(pv_protocols.ParaViewWebViewPort())
self.registerVtkWebProtocol(pv_protocols.ParaViewWebViewPortImageDelivery())
# Update authentication key to use
self.updateSecret(DemoServer.authKey)
# Your pipeline
demo_view = simple.CreateView('RenderView', 'SphereView')
SetRecommendedRenderSettings(demo_view)
demo_viewID = demo_view.GetGlobalIDAsString()
# Make the viewID available so we can bind our widget to the correct view
self.setSharedObject('viewID', demo_viewID)
sphere = simple.Sphere()
simple.Show(sphere, demo_view)
```
%% Cell type:code id: tags:
``` python
custom_renderer = RemoteRenderer(protocol=DemoServer, a='mysecretkey')
# Bind the viewID to the widget to avoid it showing a different view
# when a different view is set to the active view
custom_renderer.viewID = custom_renderer.protocol.getSharedObject('viewID')
display(custom_renderer)
```
%% Output
%% Cell type:markdown id: tags:
Since we did not bind a view to `renderer`, the sphere should now also be visible in the `renderer` [output](#Default) after an update call.
%% Cell type:code id: tags:
``` python
renderer.update_render()
```
%% Cell type:markdown id: tags:
## Usage with a pvserver and choosen port
This shows, how to connect to a pvserver on startup and open a specific port. Additionaly debug outpout is enabled, in case an error is searched. The pvserver adress and port can be specified. This only works with the default protocol, as the connection is established there. So in case of a custom protocol, the connection to the pvserver has to be started by the user (or in the custom protocol).
%% Cell type:code id: tags:
``` python
renderer = RemoteRenderer(pvserver_host="localhost", pvserver_port = 11111, baseURL="localhost", ws="pvwebserver/ws",
debug=True, port = 1234)
```
......
......@@ -52,8 +52,8 @@
"@jupyter-widgets/base": "^1.1.10 || ^2",
"hammerjs": "^2.0.8",
"monologue.js": "^0.3.5",
"paraviewweb": "3.2.12",
"wslink": "0.1.13"
"paraviewweb": "^3.2.12",
"wslink": "^0.1.13"
},
"devDependencies": {
"@phosphor/application": "^1.6.0",
......
......@@ -9,4 +9,4 @@ Information about the frontend package of the widgets.
"""
module_name = "pvlink"
module_version = "^0.1.0"
module_version = "0.2.1"
......@@ -20,7 +20,7 @@ import binascii
import os
import psutil
from ipywidgets import DOMWidget
from ipywidgets import DOMWidget, register
from traitlets import Int, Unicode
from ._frontend import module_name, module_version
......@@ -28,7 +28,7 @@ from .server import start_webserver
from wslink import server
server.start_webserver = start_webserver
@register
class RemoteRenderer(DOMWidget):
"""
A ParaViewWeb RemoteRenderer Widget which automatically starts a
......@@ -61,6 +61,9 @@ class RemoteRenderer(DOMWidget):
whether the connection should be established using Jupyter Server Proxy.
If True, the baseURL needs to be adjusted accordingly.
Default = False
use_jupyter_server_proxy_https: bool
Access using wss, to Jupyter server proxy. There it will be decrypted and send as ws to socket
Default = False
protocol: pv_wslink.PVServerProtocol
a custom PVServerProtocol class which handles clients requests and run
a default pipeline exactly once
......@@ -100,10 +103,11 @@ class RemoteRenderer(DOMWidget):
# Placeholder to force rendering updates on change.
_update = Int(0).tag(sync=True)
def __init__(self, pvserver_host=None, pvserver_port=11111, baseURL='localhost', use_jupyter_server_proxy=False, protocol=None, *args, **kwargs):
def __init__(self, pvserver_host=None, pvserver_port=11111, baseURL='localhost', use_jupyter_server_proxy=False, use_jupyter_server_proxy_https=False, protocol=None, *args, **kwargs):
super().__init__(*args, **kwargs)
self.baseURL = baseURL
self.use_jupyter_server_proxy = use_jupyter_server_proxy
self.use_jupyter_server_proxy_https = use_jupyter_server_proxy_https
self.pvserver_host = pvserver_host
self.pvserver_port = pvserver_port
self.protocol = protocol
......@@ -154,12 +158,12 @@ class RemoteRenderer(DOMWidget):
if key == 'p' or key == 'port':
arg_list.append(str(value))
elif value == True and (key != 'f' and key != 'force-flush'):
break
pass
else:
arg_list.append(value)
# If no port is given, check for the next free port starting from 8080
if 'p' not in kwargs.keys() and 'port' not in kwargs.keys():
port = self._find_next_free_port(8080)
port = _find_next_free_port(8080)
arg_list.append('-p')
arg_list.append(str(port))
# If no authKey is given, create a randon authentication key
......@@ -183,23 +187,32 @@ class RemoteRenderer(DOMWidget):
self.authKey = args.authKey
self.port = args.port
self.sessionURL = '{wsProtocol}://{baseURL}{use_proxy}{port}/{ws_endpoint}'.format(
wsProtocol='wss' if args.sslKey and args.sslCert else 'ws',
wsProtocol='wss' if (args.sslKey and args.sslCert) or self.use_jupyter_server_proxy_https else 'ws',
use_proxy='/proxy/' if self.use_jupyter_server_proxy else ':',
baseURL=self.baseURL, port=args.port, ws_endpoint=args.ws
)
def _find_next_free_port(self, port):
def _find_next_free_port(start_port):
"""
Finds next free port starting from a given port using the psutil module.
For port numbers smaller than 1, starts search with the first registered port (1024),
returns None, if no free port can be found between start_port and the biggest port number (65535)
"""
free = False
while not free:
max_port = 65535
if start_port < 1:
port = 1024
else:
port = start_port
while port <= max_port:
for conn in psutil.net_connections():
if conn.status == 'LISTEN' and conn.laddr.port == port:
port += 1
break
else:
free = True
return port
return None
def update_render(self):
"""Explicit call for the renderer on the javascript side to render."""
......
......@@ -11,11 +11,11 @@ This requires a VTK Web or ParaViewWeb server application, see
https://kitware.github.io/paraviewweb/examples/RemoteRenderer.html#RemoteRenderer.
"""
from ipywidgets import DOMWidget
from ipywidgets import DOMWidget, register
from traitlets import Int, Unicode
from ._frontend import module_name, module_version
@register
class SimpleRenderer(DOMWidget):
"""
A simple ParaViewWeb RemoteRenderer Widget.
......
......@@ -91,9 +91,9 @@ setup_args = dict(
include_package_data = True,
install_requires = [
'ipywidgets>=7.0.0',
'psutil==5.7.0',
'psutil>=4.0.0',
'twisted>=19.2.1',
'wslink==0.1.13',
'wslink>=0.1.13',
],
extras_require = {
'test': [
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please to comment