-
-
Notifications
You must be signed in to change notification settings - Fork 525
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
Support assignment operators in ReactiveHTML scripts #2718
Conversation
Codecov Report
@@ Coverage Diff @@
## master #2718 +/- ##
==========================================
+ Coverage 81.95% 81.97% +0.01%
==========================================
Files 187 187
Lines 23959 23967 +8
==========================================
+ Hits 19636 19647 +11
+ Misses 4323 4320 -3
Continue to review full report at Codecov.
|
Just for the record. The below script did not work with Panel 0.12.1. But it works on the current master branch with this included. import panel as pn
import pathlib
import param
pn.extension(sizing_mode="stretch_width")
RENDER = """
function map_node(target_id){
if (target_id===null | !target_id.includes("-")){ return "" }
target_id = target_id.split("-")[0]
if (Object.keys(data.node_map).length === 0){ return target_id }
if (target_id in data.node_map){
return data.node_map[target_id]
} else {
return ""
}
}
function map_target_ids(node){
if (node===null){ return "" }
if (Object.keys(data.node_map).length === 0){ return node }
var lst = []
Object.keys(data.node_map).forEach(key => {
var node_map=data.node_map
if ( node_map[key] === node ){
lst.push(key)
}
})
return lst
}
function getTarget(targetId){
targetId = targetId + "-" + interactiveSVG.id.split("-")[1]
return document.getElementById(targetId)
}
function addClassName(targetId, className){
getTarget(targetId).classList.add(className)
}
function removeClassName(targetId, className){
getTarget(targetId).classList.remove(className)
}
state.mouseoverTargetIds=[];
state.activeTargetIds=[];
if (interactiveSVG !== null){
interactiveSVG.addEventListener('click', (e) => {
data.node_clicks=data.node_clicks + 1;
data.node_click=map_node(e.target.id);
if (data.value===data.node_click){
data.value=""
} else {
data.value=data.node_click
}
state.activeTargetIds.forEach(targetId => {
removeClassName(targetId, "interactiveSVG-active")
})
state.activeTargetIds = map_target_ids(data.value)
state.activeTargetIds.forEach(targetId => {
addClassName(targetId, "interactiveSVG-active")
})
});
interactiveSVG.addEventListener('mouseover', (e) => {
data.node_mouseover = map_node(e.target.id);
state.mouseoverTargetIds.forEach(targetId => {
removeClassName(targetId, "interactiveSVG-mouseover")
})
state.mouseoverTargetIds = map_target_ids(data.node_mouseover)
state.mouseoverTargetIds.forEach(targetId => {
addClassName(targetId, "interactiveSVG-mouseover")
})
})
}
"""
class SVGInteractive(pn.reactive.ReactiveHTML):
svg = param.String(doc="""
A SVG text string. The elements you want to be able to hover or click
needs to have an id. For example <path ... id="path5645" />""")
value = param.String(doc="""The node on the svg currently selected""")
node_clicks = param.Integer(doc="""The total number of clicks on the svg""")
node_click = param.String(doc="""The node last clicked or ''""")
node_mouseover = param.String(doc="""The node the mouse is currently over or ''""")
node_mouseleave = param.String(doc="""The node the mouse was previously over or ''""")
node_map = param.Dict(allow_None=False, doc="""
Used to combine multiple elements (key) into one node (value).""")
_template = """<div id="interactiveSVG" style="height:100%">{{svg}}</div>"""
_scripts = {
"render": RENDER,
}
def __init__(self, **params):
params["node_map"] = params.get("node_map", {})
super().__init__(**params)
self.param.watch(self._handle_node_mouseover, "node_mouseover")
def _handle_node_mouseover(self, event):
self.node_mouseleave = event.old WIND_TURBINE_PATH = "wind_turbine_int.svg"
WIND_TURBINE_SVG = pathlib.Path(WIND_TURBINE_PATH).read_text()
NODE_MAP = {
"path3437": "Foundation",
"path5624": "Foundation",
"path4721": "Connection to the electric grid",
"path5627": "Connection to the electric grid",
"path5642": "Tower",
"path4727": "Tower",
"path4733": "Access ladder",
"path5639": "Access ladder",
"path4739": "Yaw control",
"path5636": "Yaw control",
"path5633": "Nacelle",
"path4745": "Nacelle",
"path4751": "Generator",
"path5630": "Generator",
"path3445": "Anemomenter",
"path5653": "Anemomenter",
"path4757": "Brake",
"path5650": "Brake",
"path5645": "Gearbox",
"path5647": "Gearbox",
"path4763": "Gearbox",
"path4769": "Rotor blade",
"path5668": "Rotor blade",
"path5666": "Rotor blade",
"path4775": "Blade pitch control",
"path5661": "Blade pitch control",
"path5663": "Blade pitch control",
"path4781": "Rotor hub",
"path5658": "Rotor hub",
"path5656": "Rotor hub",
}
UNDER_CONSTRUCTION = "https://www.seekpng.com/png/full/66-668827_jbvgodih4wzfrk-bob-the-builder-under-construction.png"
NODE_IMAGE = {
"Gearbox": "https://3ohkdk3zdzcq1dul50oqjvvf-wpengine.netdna-ssl.com/wp-content/uploads/2018/02/1a_SKF_Windturbine_with_gearbox_3point_suspension_04-smaller.jpg",
"Yaw control": "https://upload.wikimedia.org/wikipedia/en/thumb/2/21/Wind.turbine.components.and.coordinates.svg/1200px-Wind.turbine.components.and.coordinates.svg.png",
"Generator": "http://www.fiddlersgreen.net/miscellanous/Wind-Turbine/IMAGES/Wind-Turbine-Cutaway.jpg?w=640",
"Rotor hub": "https://d2t1xqejof9utc.cloudfront.net/screenshots/pics/d380d23e7ae374af96cc732bad701c34/large.png",
"Access ladder": "https://cdn.offshorewind.biz/wp-content/uploads/sites/2/2019/09/23085556/%C3%98rsted-Buys-Into-Pict-Offshore-Develops-New-OW-Access-System2.jpg",
"Connection to the electric grid": "https://www.turbinas-eolicas.com/2000w.files/on-grid.gif",
"Blade pitch control": "https://media.springernature.com/lw685/springer-static/image/art%3A10.1007%2Fs40313-020-00584-x/MediaObjects/40313_2020_584_Fig1_HTML.png",
"Tower": "http://xn--drmstrre-64ad.dk/wp-content/wind/miller/windpower%20web/res/twrssk.jpg",
"Rotor blade": "https://cached.imagescaler.hbpl.co.uk/resize/scaleWidth/882/cached.offlinehbpl.hbpl.co.uk/news/OPW/5BC5CE1D-B770-CD9B-E2A992251B7F901B.gif",
"Foundation": "https://upload.wikimedia.org/wikipedia/commons/thumb/b/be/Concrete_base_for_turbine_23_-_geograph.org.uk_-_517353.jpg/220px-Concrete_base_for_turbine_23_-_geograph.org.uk_-_517353.jpg",
"Anemomenter": "http://www.windturbinestar.com/uploads/images/Anemometer/Anemoscope-2.jpg",
"Brake": "https://app01.balluff.com/image-fit/471-0/fileadmin/user_upload/industries/energie/wind-energy/appl_energy_wind-energy_better-maintenance.jpg",
"Nacelle": "https://upload.wikimedia.org/wikipedia/commons/d/de/Scout_moor_gearbox%2C_rotor_shaft_and_brake_assembly.jpg",
}
wind_turbine = SVGInteractive(svg=WIND_TURBINE_SVG, node_map=NODE_MAP, height=600)
# , node_map=NODE_MAP
ACCENT_BASE_COLOR = "#926AA6"
DESCRIPTION = """# ReactiveHTML Makes It Easy to Make SVGs Interactive
## The `ReactiveHTML` component of Panel makes it much easier to make SVGs interactive.
I am not an expert on wind turbine design and do not know if the information is meaningful. It's used
here to illustrate the interactivity of a SVG image. Not wind turbine design.
You can learn more about [Wind Turbine Design](https://en.wikipedia.org/wiki/Wind_turbine_design) on Wikipedia
and find the [Wind_turbine_int.svg](https://upload.wikimedia.org/wikipedia/commons/a/ac/Wind_turbine_int.svg) on
Wikimedia.
"""
def show_info(node, no_text):
print(node)
if node in NODE_IMAGE:
image = NODE_IMAGE[node]
return f"""
# {node}
<img alt="{node}" src="{image}" height="400px">
"""
return no_text
show_info_mouseover = pn.bind(show_info, node=wind_turbine.param.node_mouseover, no_text="Hover over a node to show image")
show_info_value = pn.bind(show_info, node=wind_turbine.param.value, no_text="Click a node to show image")
component = pn.Row(
wind_turbine,
pn.panel(show_info_mouseover, height=100),
pn.panel(show_info_value, height=100),
)
template = pn.template.FastListTemplate(
site="Awesome Panel",
title="Interactive Wind Turbine with Panel",
logo="https://panel.holoviz.org/_static/logo_stacked.png",
header_background=ACCENT_BASE_COLOR,
accent_base_color=ACCENT_BASE_COLOR,
sidebar=[
pn.Param(
wind_turbine,
parameters=["node_clicks", "node_click", "node_mouseover", "node_mouseleave", "value"],
)
],
main=[DESCRIPTION, component],
).servable() wind_turbine_int.csv (rename from .csv to .svg). |
* Support assignment operators in ReactiveHTML scripts * Add test
* Fix #2631 - handle RuntimeError: dictionary changed size during iteration (#2632) * Fix #2631 - handle RuntimeError: dictionary changed size during iteration * Update panel/reactive.py Co-authored-by: Marc Skov Madsen <masma@orsted.dk> Co-authored-by: Philipp Rudiger <prudiger@anaconda.com> * Ensure tests pass in packaged version (#2636) * Ensure tests pass in packaged version * Update plotly test * Add option to hide constant parameters (#2637) * Add option to hide constant parameters * Add test * Add support for bokeh 2.4 (#2644) * Ensure sessions get distinct files in config (#2646) * Fix bug when updating Trend data (#2647) * Enhance templates docs (#2658) * clarifiy a sentence in the intro * add a short definition for modal * update the number of areas * add links to template reference * add an image of the 4 template areas * add a modal section * add link to the Golden framework * clarify theming * Added on_session_destroyed callback (#2659) * Cleanup * Ensure sorters are applied correctly after updating Tabulator value (#2639) * Ensure sorters are applied correctly after updating Tabulator value * Fix indents * Add Folium reference notebook (#2672) * add Folium reference * clean up notebook * Fix typo * clear notebook * Fix compatibility with bokeh 2.4 DocumentCallbackManager (#2687) * Fix compatibility with bokeh 2.4 DocumentCallbackManager * Fix flake * correctly accessing the filtered dataframe for selection of tabulator… (#2676) * correctly accessing the filtered dataframe for selection of tabulator #2642 * removing unused fixture * Ensure threaded servers are killed after test failures (#2688) * Unpin xarray * Unescape child literal HTML in ReactiveHTML (#2690) * Stricter validation for linking syntax in ReactiveHTML._template (#2689) * Stricter validation for linking syntax in ReactiveHTML._template * Add tests * Update docs * Clarify child templates * fix-reloading (#2692) Co-authored-by: Marc Skov Madsen <masma@orsted.dk> * Ensure Trend indicator can be rendered in layout (#2694) * Resolve remaining compatibility issues with bokeh 2.4 (#2696) * resize plot when window resizes (#2704) Co-authored-by: Marc Skov Madsen <masma@orsted.dk> * Editable sliders' `name` can be changed (#2678) * add tests for editable int and float sliders * add failing tests when updating their name * prevent the composite layout from using name The Column/Row wrapping the composite widget inherited the name param and watched it. This is no longer true, which allows the title of some composite widgets to be updated. * Switch binder links to latest version (#2705) * fix-plotly-mapbox-relayout (#2717) Co-authored-by: Marc Skov Madsen <masma@orsted.dk> * Add the version number in the binder badge (#2711) * Add the version number in the binder badge Which should help us remember that the link has to be updated after a new release. * Support assignment operators in ReactiveHTML scripts (#2718) * Support assignment operators in ReactiveHTML scripts * Add test * Fix support for async functions on pn.state.add_periodic_callback (#2737) * Upgrade to bokeh 2.4 and drop compatibility for older versions (#2739) * Update changelog * Fix rebase error * Fix flake * Bump panel.js version * Fix rc version * Update bokeh versions Co-authored-by: Marc Skov Madsen <marc.skov.madsen@gmail.com> Co-authored-by: Marc Skov Madsen <masma@orsted.dk> Co-authored-by: Maxime Liquet <35924738+maximlt@users.noreply.github.com> Co-authored-by: Nestor Ghenzi <46707985+nghenzi@users.noreply.github.com> Co-authored-by: Simon <simonparlow@gmx.net> Co-authored-by: maximlt <mliquet@anaconda.com>
Fixes #2715