Skip to content
Snippets Groups Projects
Commit 0089a3e5 authored by sheepmax's avatar sheepmax
Browse files

Improved CSS; fixed bugs with special tag init

parent de4be3ec
No related branches found
No related tags found
2 merge requests!37Publish current website,!33Thebe touchups
Pipeline #205491 passed
%% Cell type:markdown id: tags:
# Benchmarks: cell tags
This page includes benchmarks for the behaviour of different cell tags with Thebe.
%% Cell type:code id: tags:thebe-init
``` python
# Thebe init
print("This should show on startup")
```
%% Cell type:code id: tags:hide-input
``` python
# Hide input
print("You shouldn't be able to see this without expanding the dropdown.")
print("But the output should be shown outside of it!")
```
%% Cell type:code id: tags:thebe-remove-input
%% Cell type:code id: tags:thebe-remove-input-init
``` python
# Remove input
print("This should be shown despite the lack of input.")
```
%% Cell type:code id: tags:thebe-remove-input
%% Cell type:code id: tags:thebe-remove-input-init
``` python
# Remove input should preserve placeholder output
print("This output should still be shown until execution.")
```
%% Output
This output should still be shown until execution.
%% Cell type:code id: tags:
``` python
# Predefined output should be removed upon execution
print("Already ran this locally")
```
%% Output
Already ran this locally
%% Cell type:code id: tags:hide-input
``` python
# Hide input with predefined output should show predefined output until execution
print("I'm free from the dropdown!")
```
%% Output
I'm free from the dropdown!
%% Cell type:code id: tags:
``` python
```
......
%% Cell type:markdown id: tags:
# Widgets with active kernel
If you publish widgets in a jupyter book they might (depending on your implementation) require an active python kernel for the output to be interactive. Using the [thebe-integration](live_code.md), this is possible.
To make this possible, you have to install ipywidgets from within our cell, as this package is not part of the thebe environment from the start:
%% Cell type:code id: tags:thebe-init
``` python
%pip install ipywidgets
```
%% Cell type:markdown id: tags:
You can hide this cell by using the tag `thebe-remove-input`
Afterwards, you can enter your widget just like you would in a regular notebook.
Please note:
- You might want to hide the output before the thebe has been activated. Therefore, make sure you include '_dont_execute' in the filename.
- The widget is not interactive when the {fa}`rocket` --> {guilabel}`Live Code` is not clicked, you have to make that clear to the reader. You can make sure all relevant cells are executed upon starting the interactive mode by adding `thebe-init` as a cell tag.
- You can hide the code by adding a custom-made tag: `thebe-remove-input`. This tags also has the same effect as `thebe-init`: it runs the cells directly after the interactive mode is activated.
## Example
The code below shows an example of the strain energy in a structure and work done by a force for different trial functions. Because of the actual python code, the plot 'refreshes' upon updating a widgets. This can be avoided by updating the current figure instead of plotting a new figure. Remember, first press {fa}`rocket` --> {guilabel}`Live Code` and then wait until all cells are executed:
%% Cell type:code id: tags:thebe-remove-input
%% Cell type:code id: tags:thebe-remove-input-init
``` python
import sympy as sp
F = 10
EI = 20e3
L = 3
# Polynomial trial function
x, C1, C2, C3, C4, F, u0= sp.symbols('x, C1, C2, C3, C4, F, u0')
w = C1*x**3+C2*x**2+C3*x + C4
eq1 = sp.Eq(w.subs(x,0),0)
eq2 = sp.Eq(w.diff(x).subs(x,0),0)
eq3 = sp.Eq((-w.diff(x,2)*EI).subs(x,L),0)
eq4 = sp.Eq(w.subs(x,L),u0)
sol = sp.solve((eq1, eq2,eq3,eq4),(C1,C2,C3,C4))
w_sol = sp.nsimplify(w.subs(sol))
w_numpy_polynomial = sp.lambdify((u0,x),w_sol)
M_sol_polynomial = sp.nsimplify(EI * w_sol.diff(x,2))
Ev_polynomial = sp.integrate(sp.nsimplify(M_sol_polynomial**2/(EI*2)),(x,0,3))
Ev_numpy_polynomial = sp.lambdify(u0,Ev_polynomial)
V_polynomial = Ev_polynomial - 10 * u0
V_numpy_polynomial = sp.lambdify(u0,V_polynomial)
#cosine trial function
w_sol = u0*sp.sin(x/12*2*sp.pi-sp.pi/2)+u0
w_numpy_sin = sp.lambdify((u0,x),w_sol)
M_sol_sin = sp.nsimplify(EI * w_sol.diff(x,2))
Ev_sin = sp.integrate(sp.nsimplify(M_sol_sin**2/(EI*2)),(x,0,3))
Ev_numpy_sin = sp.lambdify(u0,Ev_sin)
V_sin = Ev_sin - 10 * u0
V_numpy_sin = sp.lambdify(u0,V_sin)
#sin trial function
w_sol = 2*u0*(sp.cosh(x/sp.pi))-2*u0
w_numpy_cosh = sp.lambdify((u0,x),w_sol)
M_sol_cosh = sp.nsimplify(EI * w_sol.diff(x,2))
Ev_cosh = sp.integrate(sp.nsimplify(M_sol_cosh**2/(EI*2)),(x,0,3))
Ev_numpy_cosh = sp.lambdify(u0,Ev_cosh)
V_cosh = Ev_cosh - 10 * u0
V_numpy_cosh = sp.lambdify(u0,V_cosh)
import matplotlib.pylab as plt
import numpy as np
from ipywidgets import widgets, interact
def func(u,trial_function):
fig, axs = plt.subplots(2, 2, figsize=(10, 6))
x = np.linspace(0,3,100)
if trial_function == 'Polynomial':
w_numpy = w_numpy_polynomial
Ev_numpy = Ev_numpy_polynomial
V_numpy = V_numpy_polynomial
elif trial_function == 'cos':
w_numpy = w_numpy_sin
Ev_numpy = Ev_numpy_sin
V_numpy = V_numpy_sin
elif trial_function == 'cosh':
w_numpy = w_numpy_cosh
Ev_numpy = Ev_numpy_cosh
V_numpy = V_numpy_cosh
axs[0,0].plot(x,w_numpy(u,x))
axs[0,0].set_xlim([-0.2,3.2])
axs[0,0].set_ylim([-0.003,0.01])
axs[0,0].invert_yaxis()
axs[0,0].annotate(text='', xy=(3,w_numpy(u,3)), xytext=(3,0), arrowprops=dict(arrowstyle='fancy'))
axs[0,0].text(3.1,w_numpy(u,3),'$u_0$')
axs[0,0].axis('off')
axs[0,1].axis('off')
axs[0,1].set_xlim([-0.2,3.2])
axs[0,1].set_ylim([-0.003,0.01])
axs[0,1].annotate(text='', xy=(1.5,w_numpy(u,3)), xytext=(1.5,w_numpy(u,3)-0.003), arrowprops=dict(arrowstyle='simple'))
axs[0,1].invert_yaxis()
x_axis= ['$E_v$','$A_F$','$E_v - A_F$']
y_axis = [Ev_numpy(u),w_numpy(u,3)*10,Ev_numpy(u)-w_numpy(u,3)*10]
axs[1,0].bar(x_axis,y_axis,color=('blue','green','orange'))
axs[1,0].set_ylim([-0.03,0.1])
axs[1,0].set_yticklabels([])
axs[1,0].set_yticks([])
u_range=np.linspace(-0.0015,0.01,100)
axs[1,1].plot(u_range,Ev_numpy(u_range),label='$E_v$',color='blue')
axs[1,1].plot(u_range,w_numpy(u_range,3)*10,label='$F_A$',color='green')
axs[1,1].plot(u_range,V_numpy(u_range),label='$E_v - A_F$',color='orange')
axs[1,1].legend()
axs[1,1].plot(u,Ev_numpy(u),'o',color='blue')
axs[1,1].plot(u,w_numpy(u,3)*10,'o',color='green')
axs[1,1].plot(u,Ev_numpy(u)-w_numpy(u,3)*10,'o',color='orange')
axs[1,1].set_ylim([-0.03,0.1])
axs[1,1].set_xlim([-0.0015,0.01])
axs[1,1].set_xlabel('$u_0$')
axs[1,1].set_xticks([])
axs[1,1].set_xticklabels([])
axs[1,1].set_yticks([])
plt.show()
```
%% Cell type:markdown id: tags:
The graph will appear below in interactive mode:
%% Cell type:code id: tags:thebe-remove-input
%% Cell type:code id: tags:thebe-remove-input-init
``` python
interact(func, u = widgets.FloatSlider(min=-0.001, max=0.01, value=0, step=0.0002, description="Displacement u_0",readout_format='.4f',style= {'description_width': '180px'},layout = {'width': '400px'}),
trial_function = widgets.ToggleButtons(options=['Polynomial', 'cos', 'cosh']));
```
%% Cell type:markdown id: tags:
......
......@@ -18,8 +18,8 @@
color: var(--pst-color-text-base);
}
.jp-OutputArea {
padding: 5px 0px 5px 0px;
.cell_output .jp-OutputArea-output pre {
border: var(--mystnb-source-border-width) var(--mystnb-source-border-color) solid;
}
.thebe-controls {
......@@ -53,6 +53,24 @@
height: 20px;
}
html[data-theme=dark] .tag_hide-input summary {
background: var(--pst-color-surface) !important;
}
.cell_output {
margin-top: 0.2em !important;
}
.cell_output .output.stream {
margin-top: 0.2em !important;
border: var(--mystnb-source-border-width) var(--mystnb-source-border-color) solid !important;
}
div.container.cell_output {
padding-left: 0;
margin-bottom: 1em;
}
/* These rules are used for the dark mode code styling */
/* Values taken from: https://codemirror.net/5/demo/theme.html#abcdef, with some minor tweaks to comments and gutter */
html[data-theme=dark] div.CodeMirror-selected { background: #515151; }
......
......@@ -183,21 +183,6 @@ var configureThebe = () => {
});
};
async function runInitCells() {
var thebeInitCells = document.querySelectorAll(
".thebe-init, .tag_thebe-init"
);
for (const cell of thebeInitCells) {
console.log("Initializing Thebe with cell: " + cell.id);
const notebookCell = getNotebookCellOfCodeCell(cell);
// Emulate clicking the run button, with more information about execution
thebe.setButtonsBusy(notebookCell.id);
await notebookCell.execute();
thebe.clearButtonsBusy(notebookCell.id);
}
}
/**
* Update the page DOM to use Thebe elements
*/
......@@ -385,17 +370,46 @@ function override_pyodide_lookup(fs, server_path) {
fs.lookupPath = new_lookup;
}
async function runInitCells() {
var thebeInitCells = document.querySelectorAll(
".thebe-init, .tag_thebe-init"
);
for (const cell of thebeInitCells) {
console.log("Initializing Thebe with cell: " + cell.id);
const notebookCell = getNotebookCellOfCodeCell(cell);
// Emulate clicking the run button, with more information about execution
thebe.setButtonsBusy(notebookCell.id);
await notebookCell.execute();
thebe.clearButtonsBusy(notebookCell.id);
}
}
function wrapNakedOutput(element) {
const wrapper = document.createElement("div");
wrapper.classList.add("cell_output", "container");
wrapper.appendChild(element);
return wrapper;
}
function setupSpecialTaggedElements() {
for (const taggedElement of window.specialTaggedElements) {
switch (taggedElement.tag) {
case "thebe-remove-input": {
case "thebe-remove-input-init": {
const { newCellInfo, newNotebookCell } = setupNewCell(
undefined,
undefined,
taggedElement.code
);
newNotebookCell.execute();
taggedElement.placeholder.before(newNotebookCell.area.node);
const wrappedOutput = wrapNakedOutput(newNotebookCell.area.node);
// The 4 following lines are an ugly hack to make sure we preserve init order
// Maybe improving runInitCells could circumvent this
wrappedOutput.classList.add("tag_thebe-init");
const idDiv = document.createElement("div");
idDiv.setAttribute("data-thebe-id", newNotebookCell.id);
wrappedOutput.appendChild(idDiv);
taggedElement.placeholder.before(wrappedOutput);
break;
}
default: {
......@@ -411,7 +425,7 @@ function moveHideInputOutput() {
const taggedCells = document.querySelectorAll(".tag_hide-input");
for (const cell of taggedCells) {
const outputArea = cell.querySelector(".jp-OutputArea");
cell.after(outputArea);
cell.appendChild(wrapNakedOutput(outputArea));
}
}
......@@ -420,15 +434,6 @@ var initThebe = async () => {
document.querySelector(".dropdown-launch-buttons").remove();
console.log("[sphinx-thebe]: Loading thebe...");
$(".thebe-launch-button ").css("display", "none");
// Provides nice things like a running animation and some padding
{
await loadStyleAsync("/thebe.css");
await loadStyleAsync("/_static/code.css");
}
$(".thebe-launch-button ").css("display", "block");
$(".thebe-launch-button ").text("Loading thebe...");
await loadScriptAsync("/thebe-lite.min.js");
......@@ -461,14 +466,13 @@ var initThebe = async () => {
code: `import ipykernel; ipykernel.version_info = (0,0)`,
}).done;
updateThebeButtonStatus("Running pre-intialized cells...");
setupSpecialTaggedElements();
await runInitCells();
updateThebeButtonStatus("Python interaction ready!", false);
moveHideInputOutput();
setupSpecialTaggedElements();
};
// Helper function to munge the language name
......@@ -486,7 +490,7 @@ function handleThebeRemoveInputTag(element) {
placeholder.style.display = "none";
window.specialTaggedElements.push({
tag: "thebe-remove-input",
tag: "thebe-remove-input-init",
placeholder: placeholder,
code: element.querySelector("pre").textContent?.trim() ?? "",
});
......@@ -500,10 +504,10 @@ function handleThebeRemoveInputTag(element) {
}
// Deal with custom-defined tags to properly prepare Thebe and DOM
// Current special tags: thebe-remove-input
// Current special tags: thebe-remove-input-init
function consumeSpecialTags() {
const specialTagsInfo = [
{ tag: "thebe-remove-input", handler: handleThebeRemoveInputTag },
{ tag: "thebe-remove-input-init", handler: handleThebeRemoveInputTag },
];
window.specialTaggedElements = [];
......@@ -524,3 +528,6 @@ if (document.readyState !== "loading") {
consumeSpecialTags();
});
}
loadStyleAsync("/thebe.css");
loadStyleAsync("/_static/code.css");
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment