Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Location component #1150

Merged
merged 28 commits into from
Apr 15, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
4fe16c6
added .vscode folder
Feb 16, 2020
dcd3272
boiler plate code to make things concrete
Feb 16, 2020
34d25fc
The bokeh model can now instantiate with the correct values
Feb 22, 2020
90acde0
connected signals and improved tests
Feb 23, 2020
97efe1a
got test_model.py for widget working by upgrading pyviz_comms to 0.7.3
Feb 23, 2020
3675051
added many tests. added readonly and regex to Location class
Feb 24, 2020
8e5e7bd
improved regex and associated testing
Feb 24, 2020
f47226d
added regex and tests
Feb 24, 2020
0414232
more work
Feb 25, 2020
72ce33b
fixed regex error on pathname
Feb 25, 2020
0867b01
fixed init and readonly problems
Feb 25, 2020
8f4723f
renamed refresh to reload
Feb 25, 2020
17aaabd
Cleaned up Location model
philippjfr Mar 12, 2020
fe2f9ad
Refactored Location component
philippjfr Mar 13, 2020
79b08d6
Minor cleanup
philippjfr Mar 13, 2020
57580c9
Add Location section to docs
philippjfr Mar 13, 2020
a7ba958
Fixed flakes
philippjfr Mar 30, 2020
d3833cc
Fixed flakes
philippjfr Mar 31, 2020
56f3908
Split viewable and reactive modules
philippjfr Apr 15, 2020
9b4241f
Add Syncable location component
philippjfr Apr 15, 2020
2dfe85b
Fixed merge conflict
philippjfr Apr 15, 2020
ec0617b
Add ability to sync parameters
philippjfr Apr 15, 2020
aab5ae5
Fixed flakes
philippjfr Apr 15, 2020
15624d9
Improved support for syncing query params
philippjfr Apr 15, 2020
9a0205b
Allow syncing Parameterized with query string
philippjfr Apr 15, 2020
87f4f96
Refactored location tests
philippjfr Apr 15, 2020
1f645cf
Fixed issues in reactive module
philippjfr Apr 15, 2020
ce93bf0
Fixed undefined variable
philippjfr Apr 15, 2020
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: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -118,4 +118,4 @@ builtdocs/
# vscode settings
.vscode/
.vscode/panel.code-workspace
discover/
discover/
philippjfr marked this conversation as resolved.
Show resolved Hide resolved
45 changes: 38 additions & 7 deletions examples/user_guide/Deploy_and_Export.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -229,13 +229,8 @@
"pn.serve({'markdown': '# This is a Panel app', 'json': pn.pane.JSON({'abc': 123})})\n",
"```\n",
"\n",
"The ``pn.serve`` function accepts the same arguments as the `show` method."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The ``pn.serve`` function accepts the same arguments as the `show` method.\n",
"\n",
"\n",
"\n",
"## Launching a server on the commandline\n",
Expand Down Expand Up @@ -332,6 +327,42 @@
"\n",
"This mechanism may be used to modify the behavior of an app dependending on parameters provided in the URL. \n",
philippjfr marked this conversation as resolved.
Show resolved Hide resolved
"\n",
"#### Cookies\n",
"\n",
"The `panel.state.cookies` will allow accessing the cookies stored in the browser and on the bokeh server.\n",
philippjfr marked this conversation as resolved.
Show resolved Hide resolved
"\n",
"#### Headers\n",
"\n",
"The `panel.state.headers` will allow accessing the HTTP headers stored in the browser and on the bokeh server.\n",
philippjfr marked this conversation as resolved.
Show resolved Hide resolved
"\n",
"#### Location\n",
"\n",
"When starting a server session Panel will attach a `Location` component which can be accessed using `pn.state.location`. The `Location` component servers a number of functions:\n",
philippjfr marked this conversation as resolved.
Show resolved Hide resolved
"\n",
"- Navigation between pages via ``pathname``\n",
"- Sharing (parts of) the page state in the url as ``search`` parameters for bookmarking and sharing.\n",
"- Navigating to subsections of the page via the ``hash_`` parameter.\n",
"\n",
"##### Core\n",
"\n",
"* **``pathname``** (string): pathname part of the url, e.g. '/user_guide/Interact.html'.\n",
"* **``search``** (string): search part of the url e.g. '?color=blue'.\n",
philippjfr marked this conversation as resolved.
Show resolved Hide resolved
"* **``hash_``** (string): hash part of the url e.g. '#interact'.\n",
"* **``reload``** (bool): Whether or not to reload the page when the url is updated.\n",
" - For independent apps this should be set to True. \n",
" - For integrated or single page apps this should be set to False.\n",
"\n",
"##### Readonly\n",
"\n",
"* **``href``** (string): The full url, e.g. 'https://panel.holoviz.org/user_guide/Interact.html:80?color=blue#interact'.\n",
"* **``protocol``** (string): protocol part of the url, e.g. 'http:' or 'https:'\n",
"* **``port``** (string): port number, e.g. '80'"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Accessing the Bokeh model\n",
"\n",
"Since Panel is built on top of Bokeh, all Panel objects can easily be converted to a Bokeh model. The ``get_root`` method returns a model representing the contents of a Panel:"
Expand Down
11 changes: 10 additions & 1 deletion examples/user_guide/Overview.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,16 @@
"\n",
"> - `cache`: A global cache which can be used to share data between different processes.\n",
"> - `cookies`: HTTP request cookies for the current session.\n",
"> - `curdoc`: When running a server session this property holds the current bokeh Document. \n",
"> - `curdoc`: When running a server session this property holds the current bokeh Document.\n",
"> - `location`: In a server context this provides read and write access to the URL:\n",
" * `hash`: hash in window.location e.g. '#interact'\n",
" * `pathname`: pathname in window.location e.g. '/user_guide/Interact.html'\n",
" * `search`: search in window.location e.g. '?color=blue'\n",
" * `reload`: Reloads the page when the location is updated.\n",
" * `href` (readonly): The full url, e.g. 'https://localhost:80?color=blue#interact'\n",
" * `hostname` (readonly): hostname in window.location e.g. 'panel.holoviz.org'\n",
" * `protocol` (readonly): protocol in window.location e.g. 'http:' or 'https:'\n",
" * `port` (readonly): port in window.location e.g. '80'\n",
"> - `headers`: HTTP request headers for the current session.\n",
"> - `session_args`: When running a server session this return the request arguments.\n",
"> - `webdriver`: Caches the current webdriver to speed up export of bokeh models to PNGs.\n",
Expand Down
77 changes: 74 additions & 3 deletions examples/user_guide/Param.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@
"source": [
"Panel supports using parameters and dependencies between parameters as expressed by ``param`` in a simple way to encapsulate dashboards as declarative, self-contained classes.\n",
"\n",
"Parameters are Python attributes extended using the [Param library](https://github.com/ioam/param) to support types, ranges, and documentation, which turns out to be just the information you need to automatically create widgets for each parameter. \n",
"Parameters are Python attributes extended using the [Param library](https://github.com/holoviz/param) to support types, ranges, and documentation, which turns out to be just the information you need to automatically create widgets for each parameter.\n",
"\n",
"Additionally Panel provides support for linking parameters to the URL query string to allow parameterizing an app very easily.\n",
"\n",
"# Parameters and widgets\n",
"\n",
Expand Down Expand Up @@ -466,6 +468,62 @@
" viewer.panel())"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Syncing query parameters\n",
"\n",
"By default the current [query parameters](https://en.wikipedia.org/wiki/Query_string) in the URL (specified as a URL suffix such as `?color=blue`) are made available on `pn.state.location.query_params`. To make working with query parameters straightforward the `Location` object also provides a `sync` method which allows syncing query parameters with the parameters on a `Parameterized` object.\n",
"\n",
"We will start by defining a `Parameterized` class:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"class QueryExample(param.Parameterized):\n",
" \n",
" integer = param.Integer(default=None, bounds=(0, 10))\n",
" \n",
" string = param.String(default='A string')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Now we will use the `pn.state.location` object to sync it with the URL query string (note that in a notebook environment `pn.state.location` is not initialized until the first plot has been displayed). The sync method takes the Parameterized object or instance to sync with as the first argument and a list or dictionary of the parameters as the second argument. If a dictionary is provided it should map from the Parameterized object's parameters to the query parameter name in the URL:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"pn.state.location.sync(QueryExample, {'integer': 'int', 'string': 'str'})"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Now the Parameterized object is bi-directionally linked to the URL query parameter, if we set a query parameter in Python it will update the URL bar and when we specify a URL with a query parameter that will be set on the Parameterized, e.g. let us set the 'integer' parameter and watch the URL in your browser update:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"QueryExample.integer = 5"
]
},
{
"cell_type": "markdown",
"metadata": {},
Expand All @@ -475,11 +533,24 @@
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"pygments_lexer": "ipython3"
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.5"
}
},
"nbformat": 4,
"nbformat_minor": 2
"nbformat_minor": 4
}
4 changes: 2 additions & 2 deletions panel/io/embed.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ def param_to_jslink(model, widget):
"""
Converts Param pane widget links into JS links if possible.
"""
from ..viewable import Reactive
from ..reactive import Reactive
from ..widgets import Widget, LiteralInput

param_pane = widget._param_pane
Expand Down Expand Up @@ -188,7 +188,7 @@ def embed_state(panel, model, doc, max_states=1000, max_opts=3,

Arguments
---------
panel: panel.viewable.Reactive
panel: panel.reactive.Reactive
The Reactive component being exported
model: bokeh.model.Model
The bokeh model being exported
Expand Down
120 changes: 120 additions & 0 deletions panel/io/location.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
"""
Defines the Location widget which allows changing the href of the window.
"""

import urllib.parse as urlparse

import param

from ..models.location import Location as _BkLocation
from ..reactive import Syncable
from ..util import parse_query


class Location(Syncable):
"""
The Location component can be made available in a server context
to provide read and write access to the URL components in the
browser.
"""

href = param.String(readonly=True, doc="""
The full url, e.g. 'https://localhost:80?color=blue#interact'""")

hostname = param.String(readonly=True, doc="""
hostname in window.location e.g. 'panel.holoviz.org'""")

pathname = param.String(regex=r"^$|[\/].*$", doc="""
pathname in window.location e.g. '/user_guide/Interact.html'""")

protocol = param.String(readonly=True, doc="""
protocol in window.location e.g. 'http:' or 'https:'""")

port = param.String(readonly=True, doc="""
port in window.location e.g. '80'""")

search = param.String(regex=r"^$|\?", doc="""
search in window.location e.g. '?color=blue'""")

hash = param.String(regex=r"^$|#", doc="""
hash in window.location e.g. '#interact'""")

reload = param.Boolean(default=False, doc="""
Reload the page when the location is updated. For multipage
apps this should be set to True, For single page apps this
should be set to False""")

# Mapping from parameter name to bokeh model property name
_rename = {"name": None}

def __init__(self, **params):
super(Location, self).__init__(**params)
self._synced = []
self._syncing = False
self.param.watch(self._update_synced, ['search'])

def _get_model(self, doc, root=None, parent=None, comm=None):
model = _BkLocation(**self._process_param_change(self._init_properties()))
root = root or model
values = dict(self.param.get_param_values())
properties = list(self._process_param_change(values))
self._models[root.ref['id']] = (model, parent)
self._link_props(model, properties, doc, root, comm)
return model

def _update_synced(self, event=None):
if self._syncing:
return
query_params = self.query_params
for p, parameters in self._synced:
mapping = {v: k for k, v in parameters.items()}
p.param.set_param(**{mapping[k]: v for k, v in query_params.items()
if k in mapping})

def _update_query(self, *events, query=None):
if self._syncing:
return
query = query or {}
for e in events:
matches = [ps for o, ps in self._synced if o in (e.cls, e.obj)]
if not matches:
continue
query[matches[0][e.name]] = e.new
self._syncing = True
try:
self.update_query(**{k: v for k, v in query.items() if v is not None})
finally:
self._syncing = False

@property
def query_params(self):
return parse_query(self.search)

def update_query(self, **kwargs):
query = self.query_params
query.update(kwargs)
self.search = '?' + urlparse.urlencode(query)

def sync(self, parameterized, parameters=None):
"""
Syncs the parameters of a Parameterized object with the query
parameters in the URL.

Arguments
---------
parameterized (param.Parameterized):
The Paramterized object to sync query parameters with
parameters (list or dict):
A list or dictionary specifying parameters to sync.
If a dictionary is supplied it should define a mapping from
the Parameterized's parameteres to the names of the query
parameters.
"""
parameters = parameters or [p for p in parameterized.param if p != 'name']
if not isinstance(parameters, dict):
parameters = dict(zip(parameters, parameters))
self._synced.append((parameterized, parameters))
parameterized.param.watch(self._update_query, list(parameters))
self._update_synced()
self._update_query(query={v: getattr(parameterized, k)
for k, v in parameters.items()})
5 changes: 4 additions & 1 deletion panel/io/notebook.py
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ def render_model(model, comm=None):
{EXEC_MIME: {'id': target}})


def render_mimebundle(model, doc, comm, manager=None):
def render_mimebundle(model, doc, comm, manager=None, location=None):
"""
Displays bokeh output inside a notebook using the PyViz display
and comms machinery.
Expand All @@ -157,6 +157,9 @@ def render_mimebundle(model, doc, comm, manager=None):
add_to_doc(model, doc, True)
if manager is not None:
doc.add_root(manager)
if location is not None:
loc = location._get_model(doc, model, model, comm)
doc.add_root(loc)
return render_model(model, comm)


Expand Down
Loading