736 lines
23 KiB
Python
736 lines
23 KiB
Python
"""
|
|
Classes for drawing maps.
|
|
|
|
"""
|
|
|
|
import warnings
|
|
from collections import OrderedDict
|
|
from typing import Dict, List, Optional, Sequence, Tuple, Type, Union
|
|
|
|
from branca.element import Element, Figure, Html, MacroElement
|
|
from jinja2 import Template
|
|
|
|
from folium.elements import ElementAddToElement, EventHandler
|
|
from folium.utilities import (
|
|
JsCode,
|
|
TypeBounds,
|
|
TypeJsonValue,
|
|
camelize,
|
|
escape_backticks,
|
|
parse_options,
|
|
validate_location,
|
|
)
|
|
|
|
|
|
class Evented(MacroElement):
|
|
"""The base class for Layer and Map
|
|
|
|
Adds the `on` method for event handling capabilities.
|
|
|
|
See https://leafletjs.com/reference.html#evented for
|
|
more in depth documentation. Please note that we have
|
|
only added the `on(<Object> eventMap)` variant of this
|
|
method using python keyword arguments.
|
|
"""
|
|
|
|
def on(self, **event_map: JsCode):
|
|
for event_type, handler in event_map.items():
|
|
self.add_child(EventHandler(event_type, handler))
|
|
|
|
|
|
class Layer(Evented):
|
|
"""An abstract class for everything that is a Layer on the map.
|
|
It will be used to define whether an object will be included in
|
|
LayerControls.
|
|
|
|
Parameters
|
|
----------
|
|
name : string, default None
|
|
The name of the Layer, as it will appear in LayerControls
|
|
overlay : bool, default False
|
|
Adds the layer as an optional overlay (True) or the base layer (False).
|
|
control : bool, default True
|
|
Whether the Layer will be included in LayerControls.
|
|
show: bool, default True
|
|
Whether the layer will be shown on opening.
|
|
"""
|
|
|
|
def __init__(
|
|
self,
|
|
name: Optional[str] = None,
|
|
overlay: bool = False,
|
|
control: bool = True,
|
|
show: bool = True,
|
|
):
|
|
super().__init__()
|
|
self.layer_name = name if name is not None else self.get_name()
|
|
self.overlay = overlay
|
|
self.control = control
|
|
self.show = show
|
|
|
|
def render(self, **kwargs):
|
|
if self.show:
|
|
self.add_child(
|
|
ElementAddToElement(
|
|
element_name=self.get_name(),
|
|
element_parent_name=self._parent.get_name(),
|
|
),
|
|
name=self.get_name() + "_add",
|
|
)
|
|
super().render(**kwargs)
|
|
|
|
|
|
class FeatureGroup(Layer):
|
|
"""
|
|
Create a FeatureGroup layer ; you can put things in it and handle them
|
|
as a single layer. For example, you can add a LayerControl to
|
|
tick/untick the whole group.
|
|
|
|
Parameters
|
|
----------
|
|
name : str, default None
|
|
The name of the featureGroup layer.
|
|
It will be displayed in the LayerControl.
|
|
If None get_name() will be called to get the technical (ugly) name.
|
|
overlay : bool, default True
|
|
Whether your layer will be an overlay (ticked with a check box in
|
|
LayerControls) or a base layer (ticked with a radio button).
|
|
control: bool, default True
|
|
Whether the layer will be included in LayerControls.
|
|
show: bool, default True
|
|
Whether the layer will be shown on opening.
|
|
**kwargs
|
|
Additional (possibly inherited) options. See
|
|
https://leafletjs.com/reference.html#featuregroup
|
|
|
|
"""
|
|
|
|
_template = Template(
|
|
"""
|
|
{% macro script(this, kwargs) %}
|
|
var {{ this.get_name() }} = L.featureGroup(
|
|
{{ this.options|tojson }}
|
|
);
|
|
{% endmacro %}
|
|
"""
|
|
)
|
|
|
|
def __init__(
|
|
self,
|
|
name: Optional[str] = None,
|
|
overlay: bool = True,
|
|
control: bool = True,
|
|
show: bool = True,
|
|
**kwargs: TypeJsonValue,
|
|
):
|
|
super().__init__(name=name, overlay=overlay, control=control, show=show)
|
|
self._name = "FeatureGroup"
|
|
self.tile_name = name if name is not None else self.get_name()
|
|
self.options = parse_options(**kwargs)
|
|
|
|
|
|
class LayerControl(MacroElement):
|
|
"""
|
|
Creates a LayerControl object to be added on a folium map.
|
|
|
|
This object should be added to a Map object. Only Layer children
|
|
of Map are included in the layer control.
|
|
|
|
Note
|
|
----
|
|
The LayerControl should be added last to the map.
|
|
Otherwise, the LayerControl and/or the controlled layers may not appear.
|
|
|
|
Parameters
|
|
----------
|
|
position : str
|
|
The position of the control (one of the map corners), can be
|
|
'topleft', 'topright', 'bottomleft' or 'bottomright'
|
|
default: 'topright'
|
|
collapsed : bool, default True
|
|
If true the control will be collapsed into an icon and expanded on
|
|
mouse hover or touch.
|
|
autoZIndex : bool, default True
|
|
If true the control assigns zIndexes in increasing order to all of
|
|
its layers so that the order is preserved when switching them on/off.
|
|
draggable: bool, default False
|
|
By default the layer control has a fixed position. Set this argument
|
|
to True to allow dragging the control around.
|
|
**kwargs
|
|
Additional (possibly inherited) options. See
|
|
https://leafletjs.com/reference.html#control-layers
|
|
|
|
"""
|
|
|
|
_template = Template(
|
|
"""
|
|
{% macro script(this,kwargs) %}
|
|
var {{ this.get_name() }}_layers = {
|
|
base_layers : {
|
|
{%- for key, val in this.base_layers.items() %}
|
|
{{ key|tojson }} : {{val}},
|
|
{%- endfor %}
|
|
},
|
|
overlays : {
|
|
{%- for key, val in this.overlays.items() %}
|
|
{{ key|tojson }} : {{val}},
|
|
{%- endfor %}
|
|
},
|
|
};
|
|
let {{ this.get_name() }} = L.control.layers(
|
|
{{ this.get_name() }}_layers.base_layers,
|
|
{{ this.get_name() }}_layers.overlays,
|
|
{{ this.options|tojson }}
|
|
).addTo({{this._parent.get_name()}});
|
|
|
|
{%- if this.draggable %}
|
|
new L.Draggable({{ this.get_name() }}.getContainer()).enable();
|
|
{%- endif %}
|
|
|
|
{% endmacro %}
|
|
"""
|
|
)
|
|
|
|
def __init__(
|
|
self,
|
|
position: str = "topright",
|
|
collapsed: bool = True,
|
|
autoZIndex: bool = True,
|
|
draggable: bool = False,
|
|
**kwargs: TypeJsonValue,
|
|
):
|
|
super().__init__()
|
|
self._name = "LayerControl"
|
|
self.options = parse_options(
|
|
position=position, collapsed=collapsed, autoZIndex=autoZIndex, **kwargs
|
|
)
|
|
self.draggable = draggable
|
|
self.base_layers: OrderedDict[str, str] = OrderedDict()
|
|
self.overlays: OrderedDict[str, str] = OrderedDict()
|
|
|
|
def reset(self) -> None:
|
|
self.base_layers = OrderedDict()
|
|
self.overlays = OrderedDict()
|
|
|
|
def render(self, **kwargs) -> None:
|
|
"""Renders the HTML representation of the element."""
|
|
self.reset()
|
|
for item in self._parent._children.values():
|
|
if not isinstance(item, Layer) or not item.control:
|
|
continue
|
|
key = item.layer_name
|
|
if not item.overlay:
|
|
self.base_layers[key] = item.get_name()
|
|
else:
|
|
self.overlays[key] = item.get_name()
|
|
super().render()
|
|
|
|
|
|
class Icon(MacroElement):
|
|
"""
|
|
Creates an Icon object that will be rendered
|
|
using Leaflet.awesome-markers.
|
|
|
|
Parameters
|
|
----------
|
|
color : str, default 'blue'
|
|
The color of the marker. You can use:
|
|
|
|
['red', 'blue', 'green', 'purple', 'orange', 'darkred',
|
|
'lightred', 'beige', 'darkblue', 'darkgreen', 'cadetblue',
|
|
'darkpurple', 'white', 'pink', 'lightblue', 'lightgreen',
|
|
'gray', 'black', 'lightgray']
|
|
|
|
icon_color : str, default 'white'
|
|
The color of the drawing on the marker. You can use colors above,
|
|
or an html color code.
|
|
icon : str, default 'info-sign'
|
|
The name of the marker sign.
|
|
See Font-Awesome website to choose yours.
|
|
Warning : depending on the icon you choose you may need to adapt
|
|
the `prefix` as well.
|
|
angle : int, default 0
|
|
The icon will be rotated by this amount of degrees.
|
|
prefix : str, default 'glyphicon'
|
|
The prefix states the source of the icon. 'fa' for font-awesome or
|
|
'glyphicon' for bootstrap 3.
|
|
|
|
https://github.com/lvoogdt/Leaflet.awesome-markers
|
|
|
|
"""
|
|
|
|
_template = Template(
|
|
"""
|
|
{% macro script(this, kwargs) %}
|
|
var {{ this.get_name() }} = L.AwesomeMarkers.icon(
|
|
{{ this.options|tojson }}
|
|
);
|
|
{{ this._parent.get_name() }}.setIcon({{ this.get_name() }});
|
|
{% endmacro %}
|
|
"""
|
|
)
|
|
color_options = {
|
|
"red",
|
|
"darkred",
|
|
"lightred",
|
|
"orange",
|
|
"beige",
|
|
"green",
|
|
"darkgreen",
|
|
"lightgreen",
|
|
"blue",
|
|
"darkblue",
|
|
"cadetblue",
|
|
"lightblue",
|
|
"purple",
|
|
"darkpurple",
|
|
"pink",
|
|
"white",
|
|
"gray",
|
|
"lightgray",
|
|
"black",
|
|
}
|
|
|
|
def __init__(
|
|
self,
|
|
color: str = "blue",
|
|
icon_color: str = "white",
|
|
icon: str = "info-sign",
|
|
angle: int = 0,
|
|
prefix: str = "glyphicon",
|
|
**kwargs: TypeJsonValue,
|
|
):
|
|
super().__init__()
|
|
self._name = "Icon"
|
|
if color not in self.color_options:
|
|
warnings.warn(
|
|
f"color argument of Icon should be one of: {self.color_options}.",
|
|
stacklevel=2,
|
|
)
|
|
self.options = parse_options(
|
|
marker_color=color,
|
|
icon_color=icon_color,
|
|
icon=icon,
|
|
prefix=prefix,
|
|
extra_classes=f"fa-rotate-{angle}",
|
|
**kwargs,
|
|
)
|
|
|
|
|
|
class Marker(MacroElement):
|
|
"""
|
|
Create a simple stock Leaflet marker on the map, with optional
|
|
popup text or Vincent visualization.
|
|
|
|
Parameters
|
|
----------
|
|
location: tuple or list
|
|
Latitude and Longitude of Marker (Northing, Easting)
|
|
popup: string or folium.Popup, default None
|
|
Label for the Marker; either an escaped HTML string to initialize
|
|
folium.Popup or a folium.Popup instance.
|
|
tooltip: str or folium.Tooltip, default None
|
|
Display a text when hovering over the object.
|
|
icon: Icon plugin
|
|
the Icon plugin to use to render the marker.
|
|
draggable: bool, default False
|
|
Set to True to be able to drag the marker around the map.
|
|
|
|
Returns
|
|
-------
|
|
Marker names and HTML in obj.template_vars
|
|
|
|
Examples
|
|
--------
|
|
>>> Marker(location=[45.5, -122.3], popup="Portland, OR")
|
|
>>> Marker(location=[45.5, -122.3], popup=Popup("Portland, OR"))
|
|
# If the popup label has characters that need to be escaped in HTML
|
|
>>> Marker(
|
|
... location=[45.5, -122.3],
|
|
... popup=Popup("Mom & Pop Arrow Shop >>", parse_html=True),
|
|
... )
|
|
"""
|
|
|
|
_template = Template(
|
|
"""
|
|
{% macro script(this, kwargs) %}
|
|
var {{ this.get_name() }} = L.marker(
|
|
{{ this.location|tojson }},
|
|
{{ this.options|tojson }}
|
|
).addTo({{ this._parent.get_name() }});
|
|
{% endmacro %}
|
|
"""
|
|
)
|
|
|
|
def __init__(
|
|
self,
|
|
location: Optional[Sequence[float]] = None,
|
|
popup: Union["Popup", str, None] = None,
|
|
tooltip: Union["Tooltip", str, None] = None,
|
|
icon: Optional[Icon] = None,
|
|
draggable: bool = False,
|
|
**kwargs: TypeJsonValue,
|
|
):
|
|
super().__init__()
|
|
self._name = "Marker"
|
|
self.location = validate_location(location) if location is not None else None
|
|
self.options = parse_options(
|
|
draggable=draggable or None, autoPan=draggable or None, **kwargs
|
|
)
|
|
if icon is not None:
|
|
self.add_child(icon)
|
|
self.icon = icon
|
|
if popup is not None:
|
|
self.add_child(popup if isinstance(popup, Popup) else Popup(str(popup)))
|
|
if tooltip is not None:
|
|
self.add_child(
|
|
tooltip if isinstance(tooltip, Tooltip) else Tooltip(str(tooltip))
|
|
)
|
|
|
|
def _get_self_bounds(self) -> List[List[float]]:
|
|
"""Computes the bounds of the object itself.
|
|
|
|
Because a marker has only single coordinates, we repeat them.
|
|
"""
|
|
assert self.location is not None
|
|
return [self.location, self.location]
|
|
|
|
def render(self) -> None:
|
|
if self.location is None:
|
|
raise ValueError(
|
|
f"{self._name} location must be assigned when added directly to map."
|
|
)
|
|
super().render()
|
|
|
|
|
|
class Popup(Element):
|
|
"""Create a Popup instance that can be linked to a Layer.
|
|
|
|
Parameters
|
|
----------
|
|
html: string or Element
|
|
Content of the Popup.
|
|
parse_html: bool, default False
|
|
True if the popup is a template that needs to the rendered first.
|
|
max_width: int for pixels or text for percentages, default '100%'
|
|
The maximal width of the popup.
|
|
show: bool, default False
|
|
True renders the popup open on page load.
|
|
sticky: bool, default False
|
|
True prevents map and other popup clicks from closing.
|
|
lazy: bool, default False
|
|
True only loads the Popup content when clicking on the Marker.
|
|
"""
|
|
|
|
_template = Template(
|
|
"""
|
|
var {{this.get_name()}} = L.popup({{ this.options|tojson }});
|
|
|
|
{% for name, element in this.html._children.items() %}
|
|
{% if this.lazy %}
|
|
{{ this._parent.get_name() }}.once('click', function() {
|
|
{{ this.get_name() }}.setContent($(`{{ element.render(**kwargs).replace('\\n',' ') }}`)[0]);
|
|
});
|
|
{% else %}
|
|
var {{ name }} = $(`{{ element.render(**kwargs).replace('\\n',' ') }}`)[0];
|
|
{{ this.get_name() }}.setContent({{ name }});
|
|
{% endif %}
|
|
{% endfor %}
|
|
|
|
{{ this._parent.get_name() }}.bindPopup({{ this.get_name() }})
|
|
{% if this.show %}.openPopup(){% endif %};
|
|
|
|
{% for name, element in this.script._children.items() %}
|
|
{{element.render()}}
|
|
{% endfor %}
|
|
"""
|
|
) # noqa
|
|
|
|
def __init__(
|
|
self,
|
|
html: Union[str, Element, None] = None,
|
|
parse_html: bool = False,
|
|
max_width: Union[str, int] = "100%",
|
|
show: bool = False,
|
|
sticky: bool = False,
|
|
lazy: bool = False,
|
|
**kwargs: TypeJsonValue,
|
|
):
|
|
super().__init__()
|
|
self._name = "Popup"
|
|
self.header = Element()
|
|
self.html = Element()
|
|
self.script = Element()
|
|
|
|
self.header._parent = self
|
|
self.html._parent = self
|
|
self.script._parent = self
|
|
|
|
script = not parse_html
|
|
|
|
if isinstance(html, Element):
|
|
self.html.add_child(html)
|
|
elif isinstance(html, str):
|
|
html = escape_backticks(html)
|
|
self.html.add_child(Html(html, script=script))
|
|
|
|
self.show = show
|
|
self.lazy = lazy
|
|
self.options = parse_options(
|
|
max_width=max_width,
|
|
autoClose=False if show or sticky else None,
|
|
closeOnClick=False if sticky else None,
|
|
**kwargs,
|
|
)
|
|
|
|
def render(self, **kwargs) -> None:
|
|
"""Renders the HTML representation of the element."""
|
|
for name, child in self._children.items():
|
|
child.render(**kwargs)
|
|
|
|
figure = self.get_root()
|
|
assert isinstance(
|
|
figure, Figure
|
|
), "You cannot render this Element if it is not in a Figure."
|
|
|
|
figure.script.add_child(
|
|
Element(self._template.render(this=self, kwargs=kwargs)),
|
|
name=self.get_name(),
|
|
)
|
|
|
|
|
|
class Tooltip(MacroElement):
|
|
"""
|
|
Create a tooltip that shows text when hovering over its parent object.
|
|
|
|
Parameters
|
|
----------
|
|
text: str
|
|
String to display as a tooltip on the object. If the argument is of a
|
|
different type it will be converted to str.
|
|
style: str, default None.
|
|
HTML inline style properties like font and colors. Will be applied to
|
|
a div with the text in it.
|
|
sticky: bool, default True
|
|
Whether the tooltip should follow the mouse.
|
|
**kwargs
|
|
These values will map directly to the Leaflet Options. More info
|
|
available here: https://leafletjs.com/reference.html#tooltip
|
|
|
|
"""
|
|
|
|
_template = Template(
|
|
"""
|
|
{% macro script(this, kwargs) %}
|
|
{{ this._parent.get_name() }}.bindTooltip(
|
|
`<div{% if this.style %} style={{ this.style|tojson }}{% endif %}>
|
|
{{ this.text }}
|
|
</div>`,
|
|
{{ this.options|tojson }}
|
|
);
|
|
{% endmacro %}
|
|
"""
|
|
)
|
|
valid_options: Dict[str, Tuple[Type, ...]] = {
|
|
"pane": (str,),
|
|
"offset": (tuple,),
|
|
"direction": (str,),
|
|
"permanent": (bool,),
|
|
"sticky": (bool,),
|
|
"interactive": (bool,),
|
|
"opacity": (float, int),
|
|
"attribution": (str,),
|
|
"className": (str,),
|
|
}
|
|
|
|
def __init__(
|
|
self,
|
|
text: str,
|
|
style: Optional[str] = None,
|
|
sticky: bool = True,
|
|
**kwargs: TypeJsonValue,
|
|
):
|
|
super().__init__()
|
|
self._name = "Tooltip"
|
|
|
|
self.text = str(text)
|
|
|
|
kwargs.update({"sticky": sticky})
|
|
self.options = self.parse_options(kwargs)
|
|
|
|
if style:
|
|
assert isinstance(
|
|
style, str
|
|
), "Pass a valid inline HTML style property string to style."
|
|
# noqa outside of type checking.
|
|
self.style = style
|
|
|
|
def parse_options(
|
|
self,
|
|
kwargs: Dict[str, TypeJsonValue],
|
|
) -> Dict[str, TypeJsonValue]:
|
|
"""Validate the provided kwargs and return options as json string."""
|
|
kwargs = {camelize(key): value for key, value in kwargs.items()}
|
|
for key in kwargs.keys():
|
|
assert (
|
|
key in self.valid_options
|
|
), "The option {} is not in the available options: {}.".format(
|
|
key, ", ".join(self.valid_options)
|
|
)
|
|
assert isinstance(
|
|
kwargs[key], self.valid_options[key]
|
|
), f"The option {key} must be one of the following types: {self.valid_options[key]}."
|
|
return kwargs
|
|
|
|
|
|
class FitBounds(MacroElement):
|
|
"""Fit the map to contain a bounding box with the
|
|
maximum zoom level possible.
|
|
|
|
Parameters
|
|
----------
|
|
bounds: list of (latitude, longitude) points
|
|
Bounding box specified as two points [southwest, northeast]
|
|
padding_top_left: (x, y) point, default None
|
|
Padding in the top left corner. Useful if some elements in
|
|
the corner, such as controls, might obscure objects you're zooming
|
|
to.
|
|
padding_bottom_right: (x, y) point, default None
|
|
Padding in the bottom right corner.
|
|
padding: (x, y) point, default None
|
|
Equivalent to setting both top left and bottom right padding to
|
|
the same value.
|
|
max_zoom: int, default None
|
|
Maximum zoom to be used.
|
|
"""
|
|
|
|
_template = Template(
|
|
"""
|
|
{% macro script(this, kwargs) %}
|
|
{{ this._parent.get_name() }}.fitBounds(
|
|
{{ this.bounds|tojson }},
|
|
{{ this.options|tojson }}
|
|
);
|
|
{% endmacro %}
|
|
"""
|
|
)
|
|
|
|
def __init__(
|
|
self,
|
|
bounds: TypeBounds,
|
|
padding_top_left: Optional[Sequence[float]] = None,
|
|
padding_bottom_right: Optional[Sequence[float]] = None,
|
|
padding: Optional[Sequence[float]] = None,
|
|
max_zoom: Optional[int] = None,
|
|
):
|
|
super().__init__()
|
|
self._name = "FitBounds"
|
|
self.bounds = bounds
|
|
self.options = parse_options(
|
|
max_zoom=max_zoom,
|
|
padding_top_left=padding_top_left,
|
|
padding_bottom_right=padding_bottom_right,
|
|
padding=padding,
|
|
)
|
|
|
|
|
|
class FitOverlays(MacroElement):
|
|
"""Fit the bounds of the maps to the enabled overlays.
|
|
|
|
Parameters
|
|
----------
|
|
padding: int, default 0
|
|
Amount of padding in pixels applied in the corners.
|
|
max_zoom: int, optional
|
|
The maximum possible zoom to use when fitting to the bounds.
|
|
fly: bool, default False
|
|
Use a smoother, longer animation.
|
|
fit_on_map_load: bool, default True
|
|
Apply the fit when initially loading the map.
|
|
"""
|
|
|
|
_template = Template(
|
|
"""
|
|
{% macro script(this, kwargs) %}
|
|
function customFlyToBounds() {
|
|
let bounds = L.latLngBounds([]);
|
|
{{ this._parent.get_name() }}.eachLayer(function(layer) {
|
|
if (typeof layer.getBounds === 'function') {
|
|
bounds.extend(layer.getBounds());
|
|
}
|
|
});
|
|
if (bounds.isValid()) {
|
|
{{ this._parent.get_name() }}.{{ this.method }}(bounds, {{ this.options|tojson }});
|
|
}
|
|
}
|
|
{{ this._parent.get_name() }}.on('overlayadd', customFlyToBounds);
|
|
{%- if this.fit_on_map_load %}
|
|
customFlyToBounds();
|
|
{%- endif %}
|
|
{% endmacro %}
|
|
"""
|
|
)
|
|
|
|
def __init__(
|
|
self,
|
|
padding: int = 0,
|
|
max_zoom: Optional[int] = None,
|
|
fly: bool = False,
|
|
fit_on_map_load: bool = True,
|
|
):
|
|
super().__init__()
|
|
self._name = "FitOverlays"
|
|
self.method = "flyToBounds" if fly else "fitBounds"
|
|
self.fit_on_map_load = fit_on_map_load
|
|
self.options = parse_options(padding=(padding, padding), max_zoom=max_zoom)
|
|
|
|
|
|
class CustomPane(MacroElement):
|
|
"""
|
|
Creates a custom pane to hold map elements.
|
|
|
|
Behavior is as in https://leafletjs.com/examples/map-panes/
|
|
|
|
Parameters
|
|
----------
|
|
name: string
|
|
Name of the custom pane. Other map elements can be added
|
|
to the pane by specifying the 'pane' kwarg when constructing
|
|
them.
|
|
z_index: int or string, default 625
|
|
The z-index that will be associated with the pane, and will
|
|
determine which map elements lie over/under it. The default
|
|
(625) corresponds to between markers and tooltips. Default
|
|
panes and z-indexes can be found at
|
|
https://leafletjs.com/reference.html#map-pane
|
|
pointer_events: bool, default False
|
|
Whether or not layers in the pane should interact with the
|
|
cursor. Setting to False will prevent interfering with
|
|
pointer events associated with lower layers.
|
|
"""
|
|
|
|
_template = Template(
|
|
"""
|
|
{% macro script(this, kwargs) %}
|
|
var {{ this.get_name() }} = {{ this._parent.get_name() }}.createPane(
|
|
{{ this.name|tojson }});
|
|
{{ this.get_name() }}.style.zIndex = {{ this.z_index|tojson }};
|
|
{% if not this.pointer_events %}
|
|
{{ this.get_name() }}.style.pointerEvents = 'none';
|
|
{% endif %}
|
|
{% endmacro %}
|
|
"""
|
|
)
|
|
|
|
def __init__(
|
|
self,
|
|
name: str,
|
|
z_index: Union[int, str] = 625,
|
|
pointer_events: bool = False,
|
|
):
|
|
super().__init__()
|
|
self._name = "Pane"
|
|
self.name = name
|
|
self.z_index = z_index
|
|
self.pointer_events = pointer_events
|