使用 Dash 组件嵌入 D3 + HTML
Embed D3 + HTML with Dash Components
我有 D3 + HTML 代码,我想将其嵌入到 Plotly Dash 中。有人可以帮忙吗?
例如,如果这是使用 D3 填充散点图的代码,我应该如何使用 plotly dash 进行回调?我已经有一个使用 plotly dash 构建的仪表板。因此,当单击应用按钮时,应调用此 javascript 并显示散点图。
<svg width="500" height="350"></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
var svg = d3.select("svg"),
margin = {top: 20, right: 20, bottom: 30, left: 50},
width = +svg.attr("width") - margin.left - margin.right,
height = +svg.attr("height") - margin.top - margin.bottom;
function make_x_axis() {
return d3.axisBottom(x)
// .scale(x)
// .orient("bottom")
.ticks(5)
}
function make_y_axis() {
return d3.axisLeft(y)
// .scale(y)
// .orient("left")
.ticks(5)
}
let points = d3.range(1, 10).map(function(i) {
return [i * width / 10, 50 + Math.random() * (height - 100)];
});
var x = d3.scaleLinear()
.rangeRound([0, width]);
var y = d3.scaleLinear()
.rangeRound([height, 0]);
var xAxis = d3.axisBottom(x),
yAxis = d3.axisLeft(y);
var line = d3.line()
.x(function(d) { return x(d[0]); })
.y(function(d) { return y(d[1]); });
let drag = d3.drag()
.on('start', dragstarted)
.on('drag', dragged)
.on('end', dragended);
svg.append('rect')
.attr('class', 'zoom')
.attr('cursor', 'move')
.attr('fill', 'none')
.attr('pointer-events', 'all')
.attr('width', width)
.attr('height', height)
.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')')
// svg.append("g")
// .attr("class", "grid")
// .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')')
// .call(make_x_axis()
// .tickSize(-height, 0, 0)
// .tickFormat("")
// )
//
// svg.append("g")
// .attr("class", "grid")
// .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')')
// // .attr("transform", "translate(0," + (height + margin.top) + ")")
// .call(make_y_axis()
// .tickSize(-width, 0, 0)
// .tickFormat("")
// )
svg.append("g")
.attr("class", "grid")
.attr("transform", `translate(${margin.left}, ${height + margin.top})`)
.call(make_x_axis()
.tickSize(-height)
.tickFormat("")
)
// add the Y gridlines
svg.append("g")
.attr("class", "grid")
.attr("transform", `translate(${margin.left}, ${margin.top})`)
.call(make_y_axis()
.tickSize(-width)
.tickFormat("")
)
var focus = svg.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
x.domain(d3.extent(points, function(d) { return d[0]; }));
y.domain(d3.extent(points, function(d) { return d[1]; }));
focus.append("path")
.datum(points)
.attr("fill", "none")
.attr("stroke", "white")
.attr("stroke-linejoin", "round")
.attr("stroke-linecap", "round")
.attr("stroke-width", 1.5)
.attr("d", line);
focus.selectAll('circle')
.data(points)
.enter()
.append('circle')
.attr('r', 5.0)
.attr('cx', function(d) { return x(d[0]); })
.attr('cy', function(d) { return y(d[1]); })
.style('cursor', 'pointer')
.style('fill', 'steelblue');
focus.selectAll('circle')
.call(drag);
focus.append('g')
.attr('class', 'axis axis--x')
.attr('transform', 'translate(0,' + height + ')')
.call(xAxis);
focus.append('g')
.attr('class', 'axis axis--y')
.call(yAxis);
function dragstarted(d) {
d3.select(this).raise().classed('active', true);
}
function dragged(d) {
//d[0] = x.invert(d3.event.x);
d[1] = y.invert(d3.event.y);
d3.select(this)
//.attr('cx', x(d[0]))
.attr('cy', y(d[1]))
focus.select('path').attr('d', line);
}
function dragended(d) {
d3.select(this).classed('active', false);
}
</script>
Usage.py
import dash
#import dash_alternative_viz as dav
import dash_html_components as html
from dash.dependencies import Input, Output
#import random
external_scripts = [
"https://d3js.org/d3.v4.min.js"
]
app = dash.Dash(external_scripts=external_scripts)
app.layout = html.Div(
[html.Div(id="content", className="app-header"),
html.Button(id="button", children="Button")]
)
@app.callback(
Output("content", "style"),
[dash.dependencies.Input("button", "n_clicks")],
)
def update_output(n_clicks):
if n_clicks:
return {"display": "block"}
return {"display": "no graph"}
if __name__ == "__main__":
app.run_server( port = 8052, debug=True)
header.css
app-header {
height: 350px;
width: 50%;
background-color: powderblue;
}
谢谢,
米拉
要在 Dash 应用程序中嵌入 Javascript,请参阅 this。包含 Javascript(和 CSS)的最简单方法是在应用程序的根目录中创建一个 assets
文件夹。
要在 Dash 应用程序中执行示例中的代码,您可以将 Javascript 代码放在 assets
文件夹中的 .js
文件中。
所以你可以有一个看起来像这样的 script.js
文件:
window.addEventListener("load", function () {
const svg = d3
.select("#content")
.append("svg")
.attr("width", "500")
.attr("height", "350");
const margin = { top: 20, right: 20, bottom: 30, left: 50 };
const width = +svg.attr("width") - margin.left - margin.right;
const height = +svg.attr("height") - margin.top - margin.bottom;
function make_x_axis() {
return (
d3
.axisBottom(x)
// .scale(x)
// .orient("bottom")
.ticks(5)
);
}
function make_y_axis() {
return (
d3
.axisLeft(y)
// .scale(y)
// .orient("left")
.ticks(5)
);
}
let points = d3.range(1, 10).map(function (i) {
return [(i * width) / 10, 50 + Math.random() * (height - 100)];
});
var x = d3.scaleLinear().rangeRound([0, width]);
var y = d3.scaleLinear().rangeRound([height, 0]);
var xAxis = d3.axisBottom(x),
yAxis = d3.axisLeft(y);
var line = d3
.line()
.x(function (d) {
return x(d[0]);
})
.y(function (d) {
return y(d[1]);
});
let drag = d3
.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended);
svg
.append("rect")
.attr("class", "zoom")
.attr("cursor", "move")
.attr("fill", "none")
.attr("pointer-events", "all")
.attr("width", width)
.attr("height", height)
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
svg
.append("g")
.attr("class", "grid")
.attr("transform", `translate(${margin.left}, ${height + margin.top})`)
.call(make_x_axis().tickSize(-height).tickFormat(""));
// add the Y gridlines
svg
.append("g")
.attr("class", "grid")
.attr("transform", `translate(${margin.left}, ${margin.top})`)
.call(make_y_axis().tickSize(-width).tickFormat(""));
var focus = svg
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
x.domain(
d3.extent(points, function (d) {
return d[0];
})
);
y.domain(
d3.extent(points, function (d) {
return d[1];
})
);
focus
.append("path")
.datum(points)
.attr("fill", "none")
.attr("stroke", "white")
.attr("stroke-linejoin", "round")
.attr("stroke-linecap", "round")
.attr("stroke-width", 1.5)
.attr("d", line);
focus
.selectAll("circle")
.data(points)
.enter()
.append("circle")
.attr("r", 5.0)
.attr("cx", function (d) {
return x(d[0]);
})
.attr("cy", function (d) {
return y(d[1]);
})
.style("cursor", "pointer")
.style("fill", "steelblue");
focus.selectAll("circle").call(drag);
focus
.append("g")
.attr("class", "axis axis--x")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
focus.append("g").attr("class", "axis axis--y").call(yAxis);
function dragstarted(d) {
d3.select(this).raise().classed("active", true);
}
function dragged(d) {
//d[0] = x.invert(d3.event.x);
d[1] = y.invert(d3.event.y);
d3.select(this)
//.attr('cx', x(d[0]))
.attr("cy", y(d[1]));
focus.select("path").attr("d", line);
}
function dragended(d) {
d3.select(this).classed("active", false);
}
document.querySelector("#content").style.display = "none" // Hide if button content if button has not yet been clicked.
});
我将您的代码放入 load
事件侦听器中,以确保 Javascript 仅在页面已加载时执行。
我做的另一项调整是向 Dash 布局添加一个容器 div(我给它的 ID 为 content
)并向该容器附加一个 svg
元素。您还可以 select body
或从 Dash 布局呈现的任何其他元素,并将 svg 附加到该元素。
据我所知,没有真正可以直接放入 Dash 布局的 Dash svg 组件。
所以想法是让 d3 为我们创建 svg 元素而不是 Dash。
显示图表的最小 Dash 布局如下所示:
external_scripts = [
"https://d3js.org/d3.v4.min.js",
]
app = dash.Dash(external_scripts=external_scripts)
app.layout = html.Div(
[html.Div(id="content"), html.Button(id="button", children="Button")]
)
d3 是使用 Dash
的 external_scripts
参数包含的。
为了在单击按钮时实际显示图表,我可以想到两种方法(略有不同)。
您使用 Dash 回调处理 Python 中的按钮点击:
app = dash.Dash(external_scripts=external_scripts)
app.layout = html.Div(
[
html.Div(id="content"),
html.Button(id="button", children="Button"),
]
)
@app.callback(
Output("content", "style"),
[dash.dependencies.Input("button", "n_clicks")],
)
def update_output(n_clicks):
if n_clicks:
return {"display": "block"}
return {}
如果未单击按钮至少一次未显示图表。如果单击该按钮,则显示图形。您可以更改行为以切换图形的可见性或在单击一次后隐藏按钮,根据您的需要进行调整。
另一种方法是添加一个click listener in your Javascript script。
一定要在assets
文件夹中也添加一个CSS文件,并在[=69]文件夹中添加一个width
和一个height
=] 如果你按照上面的例子,或者什么都不会出现。
另一种更复杂的方法是为您的图表创建一个 React
组件并将其转换为您可以在破折号布局中使用的破折号组件。请参阅我的回答 here and the official documentation here 以获取想法。如果您计划使用大量这些 Javascript 库来与 Dash 交互,我认为这种方法更可取。它为您提供了更多控制权,您可以以更具声明性的方式与这些 Javascript 库进行交互。
我有 D3 + HTML 代码,我想将其嵌入到 Plotly Dash 中。有人可以帮忙吗?
例如,如果这是使用 D3 填充散点图的代码,我应该如何使用 plotly dash 进行回调?我已经有一个使用 plotly dash 构建的仪表板。因此,当单击应用按钮时,应调用此 javascript 并显示散点图。
<svg width="500" height="350"></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
var svg = d3.select("svg"),
margin = {top: 20, right: 20, bottom: 30, left: 50},
width = +svg.attr("width") - margin.left - margin.right,
height = +svg.attr("height") - margin.top - margin.bottom;
function make_x_axis() {
return d3.axisBottom(x)
// .scale(x)
// .orient("bottom")
.ticks(5)
}
function make_y_axis() {
return d3.axisLeft(y)
// .scale(y)
// .orient("left")
.ticks(5)
}
let points = d3.range(1, 10).map(function(i) {
return [i * width / 10, 50 + Math.random() * (height - 100)];
});
var x = d3.scaleLinear()
.rangeRound([0, width]);
var y = d3.scaleLinear()
.rangeRound([height, 0]);
var xAxis = d3.axisBottom(x),
yAxis = d3.axisLeft(y);
var line = d3.line()
.x(function(d) { return x(d[0]); })
.y(function(d) { return y(d[1]); });
let drag = d3.drag()
.on('start', dragstarted)
.on('drag', dragged)
.on('end', dragended);
svg.append('rect')
.attr('class', 'zoom')
.attr('cursor', 'move')
.attr('fill', 'none')
.attr('pointer-events', 'all')
.attr('width', width)
.attr('height', height)
.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')')
// svg.append("g")
// .attr("class", "grid")
// .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')')
// .call(make_x_axis()
// .tickSize(-height, 0, 0)
// .tickFormat("")
// )
//
// svg.append("g")
// .attr("class", "grid")
// .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')')
// // .attr("transform", "translate(0," + (height + margin.top) + ")")
// .call(make_y_axis()
// .tickSize(-width, 0, 0)
// .tickFormat("")
// )
svg.append("g")
.attr("class", "grid")
.attr("transform", `translate(${margin.left}, ${height + margin.top})`)
.call(make_x_axis()
.tickSize(-height)
.tickFormat("")
)
// add the Y gridlines
svg.append("g")
.attr("class", "grid")
.attr("transform", `translate(${margin.left}, ${margin.top})`)
.call(make_y_axis()
.tickSize(-width)
.tickFormat("")
)
var focus = svg.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
x.domain(d3.extent(points, function(d) { return d[0]; }));
y.domain(d3.extent(points, function(d) { return d[1]; }));
focus.append("path")
.datum(points)
.attr("fill", "none")
.attr("stroke", "white")
.attr("stroke-linejoin", "round")
.attr("stroke-linecap", "round")
.attr("stroke-width", 1.5)
.attr("d", line);
focus.selectAll('circle')
.data(points)
.enter()
.append('circle')
.attr('r', 5.0)
.attr('cx', function(d) { return x(d[0]); })
.attr('cy', function(d) { return y(d[1]); })
.style('cursor', 'pointer')
.style('fill', 'steelblue');
focus.selectAll('circle')
.call(drag);
focus.append('g')
.attr('class', 'axis axis--x')
.attr('transform', 'translate(0,' + height + ')')
.call(xAxis);
focus.append('g')
.attr('class', 'axis axis--y')
.call(yAxis);
function dragstarted(d) {
d3.select(this).raise().classed('active', true);
}
function dragged(d) {
//d[0] = x.invert(d3.event.x);
d[1] = y.invert(d3.event.y);
d3.select(this)
//.attr('cx', x(d[0]))
.attr('cy', y(d[1]))
focus.select('path').attr('d', line);
}
function dragended(d) {
d3.select(this).classed('active', false);
}
</script>
Usage.py
import dash
#import dash_alternative_viz as dav
import dash_html_components as html
from dash.dependencies import Input, Output
#import random
external_scripts = [
"https://d3js.org/d3.v4.min.js"
]
app = dash.Dash(external_scripts=external_scripts)
app.layout = html.Div(
[html.Div(id="content", className="app-header"),
html.Button(id="button", children="Button")]
)
@app.callback(
Output("content", "style"),
[dash.dependencies.Input("button", "n_clicks")],
)
def update_output(n_clicks):
if n_clicks:
return {"display": "block"}
return {"display": "no graph"}
if __name__ == "__main__":
app.run_server( port = 8052, debug=True)
header.css
app-header {
height: 350px;
width: 50%;
background-color: powderblue;
}
谢谢, 米拉
要在 Dash 应用程序中嵌入 Javascript,请参阅 this。包含 Javascript(和 CSS)的最简单方法是在应用程序的根目录中创建一个 assets
文件夹。
要在 Dash 应用程序中执行示例中的代码,您可以将 Javascript 代码放在 assets
文件夹中的 .js
文件中。
所以你可以有一个看起来像这样的 script.js
文件:
window.addEventListener("load", function () {
const svg = d3
.select("#content")
.append("svg")
.attr("width", "500")
.attr("height", "350");
const margin = { top: 20, right: 20, bottom: 30, left: 50 };
const width = +svg.attr("width") - margin.left - margin.right;
const height = +svg.attr("height") - margin.top - margin.bottom;
function make_x_axis() {
return (
d3
.axisBottom(x)
// .scale(x)
// .orient("bottom")
.ticks(5)
);
}
function make_y_axis() {
return (
d3
.axisLeft(y)
// .scale(y)
// .orient("left")
.ticks(5)
);
}
let points = d3.range(1, 10).map(function (i) {
return [(i * width) / 10, 50 + Math.random() * (height - 100)];
});
var x = d3.scaleLinear().rangeRound([0, width]);
var y = d3.scaleLinear().rangeRound([height, 0]);
var xAxis = d3.axisBottom(x),
yAxis = d3.axisLeft(y);
var line = d3
.line()
.x(function (d) {
return x(d[0]);
})
.y(function (d) {
return y(d[1]);
});
let drag = d3
.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended);
svg
.append("rect")
.attr("class", "zoom")
.attr("cursor", "move")
.attr("fill", "none")
.attr("pointer-events", "all")
.attr("width", width)
.attr("height", height)
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
svg
.append("g")
.attr("class", "grid")
.attr("transform", `translate(${margin.left}, ${height + margin.top})`)
.call(make_x_axis().tickSize(-height).tickFormat(""));
// add the Y gridlines
svg
.append("g")
.attr("class", "grid")
.attr("transform", `translate(${margin.left}, ${margin.top})`)
.call(make_y_axis().tickSize(-width).tickFormat(""));
var focus = svg
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
x.domain(
d3.extent(points, function (d) {
return d[0];
})
);
y.domain(
d3.extent(points, function (d) {
return d[1];
})
);
focus
.append("path")
.datum(points)
.attr("fill", "none")
.attr("stroke", "white")
.attr("stroke-linejoin", "round")
.attr("stroke-linecap", "round")
.attr("stroke-width", 1.5)
.attr("d", line);
focus
.selectAll("circle")
.data(points)
.enter()
.append("circle")
.attr("r", 5.0)
.attr("cx", function (d) {
return x(d[0]);
})
.attr("cy", function (d) {
return y(d[1]);
})
.style("cursor", "pointer")
.style("fill", "steelblue");
focus.selectAll("circle").call(drag);
focus
.append("g")
.attr("class", "axis axis--x")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
focus.append("g").attr("class", "axis axis--y").call(yAxis);
function dragstarted(d) {
d3.select(this).raise().classed("active", true);
}
function dragged(d) {
//d[0] = x.invert(d3.event.x);
d[1] = y.invert(d3.event.y);
d3.select(this)
//.attr('cx', x(d[0]))
.attr("cy", y(d[1]));
focus.select("path").attr("d", line);
}
function dragended(d) {
d3.select(this).classed("active", false);
}
document.querySelector("#content").style.display = "none" // Hide if button content if button has not yet been clicked.
});
我将您的代码放入 load
事件侦听器中,以确保 Javascript 仅在页面已加载时执行。
我做的另一项调整是向 Dash 布局添加一个容器 div(我给它的 ID 为 content
)并向该容器附加一个 svg
元素。您还可以 select body
或从 Dash 布局呈现的任何其他元素,并将 svg 附加到该元素。
据我所知,没有真正可以直接放入 Dash 布局的 Dash svg 组件。
所以想法是让 d3 为我们创建 svg 元素而不是 Dash。
显示图表的最小 Dash 布局如下所示:
external_scripts = [
"https://d3js.org/d3.v4.min.js",
]
app = dash.Dash(external_scripts=external_scripts)
app.layout = html.Div(
[html.Div(id="content"), html.Button(id="button", children="Button")]
)
d3 是使用 Dash
的 external_scripts
参数包含的。
为了在单击按钮时实际显示图表,我可以想到两种方法(略有不同)。
您使用 Dash 回调处理 Python 中的按钮点击:
app = dash.Dash(external_scripts=external_scripts)
app.layout = html.Div(
[
html.Div(id="content"),
html.Button(id="button", children="Button"),
]
)
@app.callback(
Output("content", "style"),
[dash.dependencies.Input("button", "n_clicks")],
)
def update_output(n_clicks):
if n_clicks:
return {"display": "block"}
return {}
如果未单击按钮至少一次未显示图表。如果单击该按钮,则显示图形。您可以更改行为以切换图形的可见性或在单击一次后隐藏按钮,根据您的需要进行调整。
另一种方法是添加一个click listener in your Javascript script。
一定要在assets
文件夹中也添加一个CSS文件,并在[=69]文件夹中添加一个width
和一个height
=] 如果你按照上面的例子,或者什么都不会出现。
另一种更复杂的方法是为您的图表创建一个 React
组件并将其转换为您可以在破折号布局中使用的破折号组件。请参阅我的回答 here and the official documentation here 以获取想法。如果您计划使用大量这些 Javascript 库来与 Dash 交互,我认为这种方法更可取。它为您提供了更多控制权,您可以以更具声明性的方式与这些 Javascript 库进行交互。