diff --git a/GettingStarted-01-NarrativeAndCode.ipynb b/GettingStarted-01-NarrativeAndCode.ipynb index fbe13fa..6e38194 100644 --- a/GettingStarted-01-NarrativeAndCode.ipynb +++ b/GettingStarted-01-NarrativeAndCode.ipynb @@ -1,271 +1,271 @@ { "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "\n", - "# Workshop \"Teaching Sciences and Engineering with Jupyter Notebooks\" 2022\n", + "# Workshop \"Teaching Sciences and Engineering with Jupyter Notebooks\" 2023\n", "\n", - "© Cécile Hardebolle, 2022
\n", + "Notebook by Cécile Hardebolle, 2023
\n", " Except where otherwise noted, the content of this notebook is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.
\n", "\"Creative \n", "\n", "
\n", " How to use this notebook?
\n", " This notebook is made of text cells and code cells. The code cells have to be executed to see the result of the program.
To execute a cell, simply select it and click on the \"play\" button () in the tool bar just above the notebook, or type shift + enter.
It is important to execute the code cells in their order of appearance in the notebook.\n", "
\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Getting started: narrative and code\n", "\n", "A notebook is made of text cells (\"Markdown\") and code cells.\n", "\n", "## Text cells\n", "\n", "The text you are reading is contained in a text cell, which can be edited by *double clicking* on it.\n", "\n", "You can **format the text** in a notebook using the Markdown syntax. For instance, you can put a sentence in bold by adding double stars (\\*\\*) before and after, **like this.** \n", "The double stars are actually not visible, what you see is the result of the formatting once the text cell has been **rendered**. \n", "To render a text cell, simply select it and click on the \"play\" button () in the tool bar just above the notebook, or type shift + enter.\n", "\n", "Here is an [amazing cheat sheet about the Markdown syntax in Jupyter notebooks](https://medium.com/analytics-vidhya/the-ultimate-markdown-guide-for-jupyter-notebook-d5e5abf728fd) and you can also find the [full reference of the Mardown syntax on John Gruber's website](https://daringfireball.net/projects/markdown/syntax).\n", "\n", "To **add a text cell** to a notebook, click on the + icon in the toolbar at the top, then change the type of the cell to \"Markdown\" using the dropdown menu in the same toolbar.\n", "\n", "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Code cells\n", "\n", "Code cells make it possible to **embed a program** or pieces of program into a notebook, to execute them and **see the results** of its execution right away directly in the notebook.\n", "\n", "The code cell below contains a line of Python code that displays the text \"Hello!\". \n", "To **execute this cell**, simply select it and click on the \"play\" button () in the tool bar just above the notebook, or type shift + enter.
\n", "You should then see the text \"Hello!\" appear just below the code cell." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print(\"Hello!\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You might have noticed that a *number between square brackets* has appeared at the left of the code cell after its execution. \n", "This number indicates the **order in which the code cell has been executed**, with respect to the other code cells or to previous executions of the same code cell.\n", "\n", "The order in which different pieces of code are executed affects the execution results, which is why these numbers are important information to consider.\n", "\n", "To **add a code cell** to a notebook, simply click on the + icon in the toolbar at the top.\n", "\n", "
" ] }, { "cell_type": "markdown", "metadata": { "tags": [] }, "source": [ "---\n", "\n", "# Useful text formatting tools\n", "\n", "Below, we discuss some additional useful tools to format your narrative in notebooks.\n", "\n", "
\n", "\n", "## Spacing / structuring text\n", "\n", "You can space out your text in your notebooks using blank lines. \n", "Unfortunately Markdown has no syntax for blank lines therefore you have to use the HTML syntax `
` each time you want to introduce one.\n", "\n", "To separate paragraphs you can also use horizontal lines, which are easily generated in Markdown with the syntax `---` (triple minus). \n", "The HMTL equivalent is `
`.\n", "\n", "**Example:** Edit the current cell to see how the following blank line is generated, and find the symbols that generate the horizontal line at the top of the section.\n", "\n", "
" ] }, { "cell_type": "markdown", "metadata": { "tags": [] }, "source": [ "## Hyperlinks\n", "\n", "To create any type of link in Markdown, the syntax is the following: `[text that will appear](URL)` \n", "If you wish to create a link to a local file in the current folder, for instance another notebook, simply replace the URL with the path of the file, relative to the notebook in which the link is: `[text that will appear](subfolder/file.extension)`\n", "\n", "**Example:** Here is a link to our [Getting Started notebook on interactive visualizations](GettingStarted-02-InteractiveVisualization.ipynb).\n", "\n", "
" ] }, { "cell_type": "markdown", "metadata": { "tags": [] }, "source": [ "## Highlighting important information\n", "\n", "Text cells also support more elaborate [HTML](https://html.com/) and [CSS](https://html.com/css/) formatting, for instance to style paragraphs or titles with colors and backgrounds.\n", "\n", "**Example:** The two cells below illustrate how you can highlight the instructions or solution of an activity. \n", "Feel free to reuse these examples (simply edit the text cell and copy-paste the content)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", " Activity
\n", "\n", "Add a line to the plot that draws a door to the house. \n", "Then create a slider that will let the user choose the width of the door. \n", " \n", "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "\n", "**Solution** - You check your answer with the solution by clicking on the \"...\" below.\n", " \n", "
\n", "\n", "

" ] }, { "cell_type": "markdown", "metadata": { "tags": [] }, "source": [ "## Hiding cells\n", "\n", "Hidding cells can be used to make a notebook more readable, to hide supplementary content or to hide exercise solutions.\n", "\n", "To hide a cell, click on the blue bar that appears on its left when you select or edit it. \n", "A hidden cell appears as a series of 3 dots `...` on which you can simply click to show the cell again. \n", "If you save your notebook with a hidden cell, the cell will remain hidden when you re-open your notebook (therefore if you share a notebook with hidden cells to students, the cells will be hidden for them).\n", "\n", "\n", "\n", "\n", "\n", "
" ] }, { "attachments": { "a3a2f418-e391-442c-99b9-c118480d23e9.png": { "image/png": "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" } }, "cell_type": "markdown", "metadata": {}, "source": [ "## Images\n", "\n", "Images play an important role for understanding explanations, so don't be afraid to embed images (photos, diagrams, sketches, etc.) into your notebooks.\n", "\n", "A first option to include an image in a text cell is to use the Markdown syntax, where you indicate either the path to the image file in your workspace (relative to the notebook you are currently editing) or a URL if you use an online image: `![](relative/path/to/image/file.png)`\n", "\n", "One drawback of the Markdown syntax is that it does not allow you so specify the size of the image, which means your image appears *as is*. \n", "To include an image while modifying the size at which it is displayed, you can use the HTML syntax: ``. If you specify only the width or the height, the ratio of the image will be conserved. If you specify both values then the ratio will be adapted to what you specify.\n", "\n", "If you want to quickly include an image into a notebook (e.g. a screen capture) without going through the steps of saving the image into your workspace then writing the necessary code to embed it, then you can also simply drag-and-drop or copy-paste an image right onto your text cell and Jupyter will take care of everything! (Note: the image is then stored in the metadata of your notebook, therefore you will not be able to retrieve it afterwards, and it increases the size of your notebook...).\n", "\n", "**Examples:** Edit the cell to see the code of the different examples. \n", "![](figs/epfl-logo.png) ![main-logo.png](attachment:a3a2f418-e391-442c-99b9-c118480d23e9.png)\n", "\n", "
\n", "
" ] }, { "cell_type": "markdown", "metadata": { "tags": [] }, "source": [ "## Math equations\n", "\n", "You can use LaTeX to write mathematical equations in your Markdown cells, both inline like this: $\\lvert\\vec{T}\\rvert$ or in blocks like this:\n", "\n", "$$\n", "\\begin{align}\n", "\\lvert\\vec{T}\\rvert = \\frac{\\frac{1}{2}.m.g}{sin(\\alpha)}\n", "\\end{align}\n", "$$\n", "\n", "Of course you need to be familiar with the LaTeX syntax and symbols. \n", "You can find a quick intro and overview in the [LaTeX chapter](https://personal.math.ubc.ca/~pwalls/math-python/jupyter/latex/) of the online [\"Mathematical Python\"](https://personal.math.ubc.ca/~pwalls/math-python/) textbook (made with notebooks!).\n", "\n", "Note that notebooks support only a subset of the LaTeX mathematical syntax, more specifically it relies on the MathJax subset of LaTeX, more information in the [official documentation of MathJax](https://docs.mathjax.org/en/latest/input/tex/)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "---\n", "# References\n", "\n", "The full documentation of JupyterLab: \n", "https://jupyterlab.readthedocs.io/en/1.2.x/ \n", "\n", "A useful cheat sheet on the Mardown syntax: \n", "https://medium.com/analytics-vidhya/the-ultimate-markdown-guide-for-jupyter-notebook-d5e5abf728fd\n", "\n", "The full reference for the Markdown syntax in Jupyter notebooks: \n", "https://daringfireball.net/projects/markdown/syntax\n" ] } ], "metadata": { "kernelspec": { "display_name": "Python", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.8.10" } }, "nbformat": 4, "nbformat_minor": 4 } diff --git a/GettingStarted-02-InteractiveVisualization.ipynb b/GettingStarted-02-InteractiveVisualization.ipynb index 5965ca8..85e4a01 100644 --- a/GettingStarted-02-InteractiveVisualization.ipynb +++ b/GettingStarted-02-InteractiveVisualization.ipynb @@ -1,432 +1,488 @@ { "cells": [ { "cell_type": "markdown", "id": "42235b78-7365-4921-8034-a7139a839952", "metadata": { "toc-hr-collapsed": true }, "source": [ "# Workshop \"Teaching Sciences and Engineering with Jupyter Notebooks\" 2022\n", "\n", - "© Cécile Hardebolle, 2022
\n", - " Except where otherwise noted, the content of this notebook is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.
\n", + "Notebook by Cécile Hardebolle, 2023
\n", + "Except where otherwise noted, the content of this notebook is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.
\n", "\"Creative \n", "\n", "
\n", " How to use this notebook?
\n", " This notebook is made of text cells and code cells. The code cells have to be executed to see the result of the program.
To execute a cell, simply select it and click on the \"play\" button () in the tool bar just above the notebook, or type shift + enter.
It is important to execute the code cells in their order of appearance in the notebook.\n", "
" ] }, { "cell_type": "markdown", "id": "a2774ae7-2192-4acc-9b04-39a894706ab5", "metadata": {}, "source": [ "# Tutorial: creating interactive visualizations with Matplotlib\n", "\n", "This very short tutorial walks you through the steps for creating an interactive visualization with matplotlib. \n", - "Please note that there are *multiple ways* to achieve the same result. This tutorial is just one possible example." + "Please note that there are *multiple ways* to achieve the same result. This tutorial is just one possible example.\n", + "\n", + "Our goal in this tutorial is to obtain the interactive figure below, that represents a house (ok, it's a bit silly 😅 but it's just an example). \n", + "The two sliders change the height of the roof and the width of the door.\n", + "\n", + "" ] }, { "cell_type": "markdown", "id": "a7ecbc64-f2c7-4066-ab9c-eeabe7675d06", "metadata": {}, "source": [ "## Technical setup" ] }, { "cell_type": "markdown", "id": "2ca6f8ff-3012-47f2-8f91-f0c05c4ec0a7", "metadata": {}, "source": [ "We need to import two libraries: \n", "* matplotlib for plotting\n", "* ipywidgets for interaction elements such as sliders" ] }, { "cell_type": "code", - "execution_count": 1, + "execution_count": null, "id": "178a7a7c-86dd-4bad-80ee-70c250708b82", "metadata": {}, "outputs": [], "source": [ "import matplotlib.pyplot as plt\n", "import ipywidgets as widgets" ] }, { "cell_type": "markdown", "id": "8322fb56-37f2-45b0-9c40-2ddfa29eb4b6", "metadata": {}, "source": [ "Before starting to construct a figure, we need to tell matplotlib what we want to generate:\n", "* static images: `%matplotlib inline`\n", - "* or dynamic interactive visuals: `%matplotlib widget`" + "* or dynamic interactive visuals: `%matplotlib widget`\n", + "\n", + "This setting changes the type of `backend` that matplotlib will use. \n", + "With the static backend, matplotlib will generate a new image each time we make a modification. \n", + "With the interactive backend, only one image will be generated and all the actions and drawings will update the image - this is what we want to do in this tutorial." ] }, { "cell_type": "code", - "execution_count": 2, + "execution_count": null, "id": "2f42895d-8a0a-41b9-aca7-118ed0c476cd", "metadata": {}, "outputs": [], "source": [ "# In this tutorial, we will create an interactive figure\n", "%matplotlib widget" ] }, { "cell_type": "markdown", "id": "c7a1a710-02f9-4ce0-b546-6422f2db7549", "metadata": {}, "source": [ - "This setting changes the type of `backend` that matplotlib will use. \n", - "With the static backend, matplotlib will generate a new image each time we make a modification. \n", - "With the interactive backend, only one image will be generated and all the actions and drawings will update the image. \n", - "\n", "*Watchout: code written with one backend usually does not work with another backend!*" ] }, { "cell_type": "markdown", "id": "568a1cf2-9745-4396-8575-4d245dfa125a", "metadata": {}, "source": [ "## Data to generate the visualization\n", "Let's generate some data to plot. \n", - "In this example we will draw a house and its roof, using sets of x and y coordinates to define points connected by a line." + "To simplify, we model the house by a rectangle and its roof by a triangle. \n", + "These figures are drawn using lines defined by successive points represented by their x and y coordinates. \n", + "\n", + "For instance, to draw a rectangle, we plot a line defined by four points A, B, C and D, as in the figure below. \n", + "\n", + "\n", + "\n", + "To plot the line, we need to define two lists:\n", + "* the x coordinates of these four points: [$x_A$, $x_B$, $x_C$, $x_D$]\n", + "* the y coordinates of the same points: [$y_A$, $y_B$, $y_C$, $y_D$]" ] }, { "cell_type": "code", - "execution_count": 3, - "id": "29922ff8-37d4-4b1e-9cf6-9fa5d25975c1", + "execution_count": null, + "id": "b2ec3458-425c-4d66-9f25-239471e2764b", "metadata": {}, "outputs": [], "source": [ "# We will plot a rectangle to model a house\n", "house_x = [2, 2, 4, 4]\n", - "house_y = [0, 2, 2, 0]\n", - "\n", + "house_y = [0, 2, 2, 0]" + ] + }, + { + "cell_type": "markdown", + "id": "fe94291e-3ff6-4d5d-baff-2e052c198a95", + "metadata": {}, + "source": [ + "Then we do the same for the triangle that represents the roof." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c22828f5-8b07-48de-bdc8-25b78bc89dcb", + "metadata": {}, + "outputs": [], + "source": [ "# We will plot a triangle to model the roof\n", "roof_x = [2, 3, 4]\n", "roof_y = [2, 5, 2]" ] }, { "cell_type": "markdown", "id": "fea1da5d-f18d-48f4-8b4f-e24d3d1cb745", "metadata": {}, "source": [ "## Building the visualization" ] }, { "cell_type": "markdown", "id": "b0530b84-7d3d-4e65-9960-e222f434686f", "metadata": {}, "source": [ "Before starting to build the figure, we first tell matplolib to wait until we tell him to show the figure where we want in the notebook. \n", "If we don't do that, it will start to display the figure right after the first instructions we write." ] }, { "cell_type": "code", "execution_count": null, "id": "9d65ead3-efc7-40f5-823a-306980328261", "metadata": {}, "outputs": [], "source": [ "# Turn the output off for the moment\n", "plt.ioff();" ] }, { "cell_type": "markdown", "id": "22c696c5-f1bb-48cc-b68a-286bea11396e", "metadata": {}, "source": [ "Now let's build the plot. It has two main components:\n", - "* a figure, which is an overall container\n", + "* a figure, which is an overall container (usually \"invisible\")\n", "* one or more \"axes\" i.e. plots" ] }, { "cell_type": "code", "execution_count": null, "id": "f99f07ce-f750-493e-bac0-76427ac83ffb", "metadata": {}, "outputs": [], "source": [ "# Creation of the figure\n", "fig = plt.figure(num='Interactive figure', figsize=(6,4))\n", "\n", "# Creation of one subplot/axe - it will take position index number 1 in a grid of 1 row and 1 column, as described by (nrows, ncols, index)\n", "ax = fig.add_subplot(1,1,1)" ] }, { "cell_type": "markdown", "id": "19d36c78-ecb3-4f17-8199-a48437331805", "metadata": {}, "source": [ "Once we have created these components, we can plot our data. \n", "The `plot` method of the `axe` object plots y versus x as lines and/or markers. It returns the resulting `line` object(s)." ] }, { "cell_type": "code", "execution_count": null, "id": "a061a2e0-1775-4752-8884-88bf00639903", "metadata": {}, "outputs": [], "source": [ - "# Plot the house\n", + "# First let's plot the \"house\" i.e. the rectangle\n", "ax.plot(house_x, house_y)\n", "\n", - "# Plot the roof, and get the resulting line, on which we will add interactivity later - NOTICE the syntax with the comma \"roof_line, =\"\n", + "# Then plot the roof/triangle, and get the resulting line, on which we will add interactivity later - NOTICE the syntax with the comma \"roof_line, =\"\n", "roof_line, = ax.plot(roof_x, roof_y)" ] }, { "cell_type": "markdown", "id": "69412705-47f2-40b3-9cd0-95422a0b6309", "metadata": {}, "source": [ "Now let's show the resulting figure! \n", "For that we need to turn on again the interactive mode of matplotlib." ] }, { "cell_type": "code", "execution_count": null, "id": "d940f221-813c-4f52-8fa3-766fd0fa9b2f", "metadata": {}, "outputs": [], "source": [ "# Turn the output on\n", "plt.ion()\n", "\n", "# Show the figure\n", "display(fig.canvas)" ] }, { "cell_type": "markdown", "id": "425f9a44-f36b-417b-a9de-35e855bd5487", "metadata": {}, "source": [ "## Adding some interactivity \n", "Now we can add some interactivity to this plot. For that, we proceed in four steps:\n", "1. We create a slider" ] }, { "cell_type": "code", "execution_count": null, "id": "eadae473-19a9-4f18-b453-4aec54012015", "metadata": {}, "outputs": [], "source": [ - "# We create a slider with values ranging from 2 to 10 in steps of .5, by default on value 5\n", + "# We create a slider with values ranging from 2 to 10 in steps of .5, by default it will be set on value 5\n", "roof_widget = widgets.FloatSlider(min=2, max=10, step=0.5, value=5, description='Roof height:')" ] }, { "cell_type": "markdown", "id": "37549dfa-026c-4ee2-9c45-5e55b06f84b8", "metadata": {}, "source": [ - "2. We create a function that will be called when the slider is moved and will update the figure" + "2. We create a function that will be called when the slider is moved and will update the figure: \n", + " it will change the coordinates of the points in the line that represents the roof, using the value indicated by the slider" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 2, "id": "93c29bff-d13f-477f-ab8d-9723856472b1", "metadata": {}, "outputs": [], "source": [ "# This function will be called when the slider is moved\n", "def roof_event_handler(change):\n", " # It allows us to retrieve the new value of the slider\n", " newposition = change.new\n", "\n", - " # Then we can update the points of the roof line\n", + " # Then we can update the y coordinates of the points in the roof line - this is the important part of the function!!\n", + " # Note: to change the height of the roof, we only change the y coordinate of the middle point (top of the triangle), the first and last points do not change\n", " roof_line.set_ydata([2, newposition, 2])\n", " \n", " # Finally we tell the figure to draw the changed parts\n", " fig.canvas.draw_idle()" ] }, { "cell_type": "markdown", "id": "bfc19f67-fd2a-4d99-a138-3e84c25a962b", "metadata": {}, "source": [ "3. We link the slider to the callback function" ] }, { "cell_type": "code", "execution_count": null, "id": "c7155220-f5f2-40f4-aad6-38d055441646", "metadata": {}, "outputs": [], "source": [ "# Finally we link the widget to the callback function \n", "roof_widget.observe(roof_event_handler, names='value')" ] }, { "cell_type": "markdown", "id": "3242d42a-706b-4040-bf08-a2d118829ab4", "metadata": {}, "source": [ "4. And then we display both the figure and the slider" ] }, { "cell_type": "code", "execution_count": null, "id": "9ebd1106-589f-46ad-b7cd-bfce1db0744a", "metadata": {}, "outputs": [], "source": [ "# Display the figure again, and the widget below (no need to turn the interactive mode on again since we did it earlier)\n", "display(fig.canvas, roof_widget)" ] }, { "cell_type": "markdown", "id": "03b9a626-ee0a-4bec-ba57-d2691252d881", "metadata": {}, "source": [ "Note that the slider udpates both the figure here and also the figure above. \n", "This is the work of the interactive backend!" ] }, { "cell_type": "markdown", "id": "e6c31f99-06b2-42a5-988f-a8e81951b6e2", "metadata": {}, "source": [ "---\n", "\n", "# Your turn now!" ] }, { "cell_type": "markdown", "id": "b4c41719-fe79-4c76-ba62-68e5fbb3edc1", "metadata": {}, "source": [ "
\n", " Activity
\n", "\n", "Add a line to the plot that draws a door to the house. \n", "Then create a slider that will let the user choose the width of the door. \n", " \n", "
" ] }, + { + "cell_type": "markdown", + "id": "8a9f7b39-9bf7-48e4-aedf-a4583815794a", + "metadata": {}, + "source": [ + "Here are the steps you need to follow:\n", + "1. First define the x and y coordinates of the line that will represent the door\n", + "1. Plot the line, retrieve the result\n", + "1. Create a slider\n", + "1. Create a function that will update the coordinates of the points of the door line depending on the value of the slider\n", + "1. Link the slider to the function\n", + "1. Display the figure and the sliders" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "f0f60b17-d9cb-4b51-93b4-d472edf2cd2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Your code here...\n" + ] + }, { "cell_type": "markdown", "id": "206b7ae1-02b9-44d0-9af9-4472df30adbe", "metadata": {}, "source": [ "
\n", "\n", "**Solution** - You can see *one possible solution* by clicking on the \"...\" below.\n", " \n", "
" ] }, { "cell_type": "code", "execution_count": null, "id": "15d0e85e-abc0-4ff1-af59-47eb4c19869b", "metadata": { "jupyter": { "source_hidden": true }, "tags": [] }, "outputs": [], "source": [ "# First define the x and y coordinates of the door\n", "door_x = [2.25, 2.25, 2.50, 2.50]\n", "door_y = [0, 1, 1, 0]\n", "\n", "# Draw the door on the plot and get the resulting line (to be able to update it with the slider)\n", "door_line, = ax.plot(door_x, door_y)\n", "\n", "# Create a widget for the width of the door\n", "door_widget = widgets.FloatSlider(min=0.25, max=1.5, step=0.05, value=0.25, description='Door width:')\n", "\n", "# This function will be called when the door slider is moved\n", "def door_event_handler(change):\n", " # It allows us to retrieve the new value of the slider\n", " newwidth = change.new\n", "\n", " # Then we can change the points of the door line\n", " door_line.set_xdata([2.25, 2.25, 2.25+newwidth, 2.25+newwidth])\n", " \n", " # Finally we tell the figure to draw the changed parts\n", " fig.canvas.draw_idle()\n", " \n", "\n", "# Finally we link the widget to the callback function \n", "door_widget.observe(door_event_handler, names='value')\n", "\n", "# Let's display again the whole figure with the two sliders\n", "display(fig.canvas, roof_widget, door_widget)" ] }, { "cell_type": "markdown", "id": "9690331e-446d-4062-97d0-069276e335bf", "metadata": {}, "source": [ "---\n", "\n", "# Additional resources\n", "\n", "More on figures and axes of Matplotlib: \n", "https://medium.com/@kapil.mathur1987/matplotlib-an-introduction-to-its-object-oriented-interface-a318b1530aed\n", "\n", "Using the widgets to add interactivity: \n", "https://ipywidgets.readthedocs.io/en/latest/examples/Widget%20Events.html\n", "\n", "Details about the backends of matplotlib: \n", "https://matplotlib.org/3.4.3/users/interactive.html" ] } ], "metadata": { "kernelspec": { "display_name": "Python", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.8.10" } }, "nbformat": 4, "nbformat_minor": 5 } diff --git a/GettingStarted-03-InteractiveVisualizationHiddenCode.ipynb b/GettingStarted-03-InteractiveVisualizationHiddenCode.ipynb index 87f54c7..10e5f0a 100644 --- a/GettingStarted-03-InteractiveVisualizationHiddenCode.ipynb +++ b/GettingStarted-03-InteractiveVisualizationHiddenCode.ipynb @@ -1,174 +1,174 @@ { "cells": [ { "cell_type": "markdown", "id": "4a8ba901-956b-49da-88f3-7a41036c4dbc", "metadata": { "toc-hr-collapsed": true }, "source": [ "# Workshop \"Teaching Sciences and Engineering with Jupyter Notebooks\" 2022\n", "\n", - "© Cécile Hardebolle, 2022
\n", - " Except where otherwise noted, the content of this notebook is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.
\n", + "Notebook by Cécile Hardebolle, 2023
\n", + "Except where otherwise noted, the content of this notebook is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.
\n", "\"Creative \n", "\n", "
\n", " How to use this notebook?
\n", " This notebook is made of text cells and code cells. The code cells have to be executed to see the result of the program.
To execute a cell, simply select it and click on the \"play\" button () in the tool bar just above the notebook, or type shift + enter.
It is important to execute the code cells in their order of appearance in the notebook.\n", "
" ] }, { "cell_type": "markdown", "id": "37abfac0-d5d8-44be-8572-425abf875a19", "metadata": {}, "source": [ "# Demo: hiding the code of your interactive visualizations\n", "\n", "This notebook demonstrates how the code from an interactive visualization can be hidden from a notebook. \n", "Again there are different ways to achieve the same result, this is only one possible solution.\n", "\n", "In this demo, we use the same \"interactive house\" visualization example as [in this notebook](GettingStarted-02-InteractiveVisualization.ipynb)." ] }, { "cell_type": "markdown", "id": "c0c4751d-38b6-449b-8db2-8699e51a8eba", "metadata": {}, "source": [ "## Putting the code in an external Python file\n", "The main principle is to put the code that generates the interactive visualization into an external Python file. \n", "In such a file, code can take the form of functions or classes.\n", "\n", "Here is an exemple: [interactivevisualization.py (click here to open the file)](lib/interactivevisualization.py). \n", "In this demo, we have simply created a function that generates the interactive house visualization and displays it. " ] }, { "cell_type": "markdown", "id": "c0e26a53-bc03-429f-aeb0-71abb560f549", "metadata": { "tags": [] }, "source": [ "## Using the file\n", "To be able to use the code defined in our Python file, we simply use the built-in `import` feature of Python." ] }, { "cell_type": "code", "execution_count": null, "id": "8aa8717e-44e5-4df6-b79f-0f6e01488d9c", "metadata": {}, "outputs": [], "source": [ "# Let's import the content of our Python file\n", "from lib.interactivevisualization import *" ] }, { "cell_type": "markdown", "id": "b0c5c5ec-6a13-4bd7-aa39-d2531010c0bf", "metadata": {}, "source": [ "Then we can simply call the function we have defined." ] }, { "cell_type": "code", "execution_count": null, "id": "a15eab91-850b-4402-b8fa-a244fbce9f70", "metadata": {}, "outputs": [], "source": [ "# Now we can use the functions defined in the file\n", "displayInteractiveHouse()" ] }, { "cell_type": "markdown", "id": "1c44abd2-1641-42e7-b7eb-c5e14c77251c", "metadata": {}, "source": [ "---\n", "\n", "## Differences between the code in the notebook and in the external file\n", "\n", "### Choosing the matplotlib backend\n", "\n", "We cannot use the instruction `%matplotlib widget` to choose the backend of matplotlib in a Python file because it works only when in a [notebook](02-InteractiveVisualization.ipynb). \n", "Instead we need to use the following two lines, which do exactly the same thing:\n", "```python\n", "from IPython import get_ipython\n", "get_ipython().run_line_magic('matplotlib', 'widget')\n", "\n", "```\n", "\n", "### Turning the interactive mode on/off\n", "\n", "Since the code is in one piece only - compared to split over several cells in a [notebook](02-InteractiveVisualization.ipynb) - we don't need to switch the interactive mode of matplotlib off and back on again. \n", "Matplotlib will simply render the full visualization in one shot when the function is called.\n", "\n", "Therefore, we **don't need to**:\n", "- call `plt.ioff()` to turn the interactive mode off\n", "- call `plt.ion()` to turn it back on\n", "- display the figure explicitely (`display(fig.canvas)`) - matplotlib will display it automatically while it is drawn\n", "\n", "NB: try it out, if you display the figure, you will see the figure twice..." ] }, { "cell_type": "markdown", "id": "1bd36fc9-b9cd-464a-acd1-d2b574cc59a7", "metadata": {}, "source": [ "---\n", "\n", "## Function or class?\n", "\n", "Note that, in this design, the event handler function becomes a *nested* function, which some would argue is not a great design. \n", "Another way to achieve the same thing is to create a class with the different variables of the visualization as attributes and the event handler function as a method. \n", "For this, you need to be familiar with [object-oriented programming in Python](https://realpython.com/python3-object-oriented-programming/). \n", "\n", "Here is an example of a more complex visualization example implemented as a Python class: [SuspendedObject.py (click here to open the file)](SuspendedObject.py). \n", "\n", "Execute the code cell below to see the result. \n", "Note that once you have executed this cell, the previous visualization will not work anymore since the interactive backend renders only one visualization at a time. \n", "This is actually one limitation of the interactive backend." ] }, { "cell_type": "code", "execution_count": null, "id": "a7737bde-e3f6-4c33-8bf7-fe95e9382e26", "metadata": {}, "outputs": [], "source": [ "from lib.suspendedobjectinteractive import *\n", "SuspendedObjectLab();" ] } ], "metadata": { "kernelspec": { "display_name": "Python", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.8.10" } }, "nbformat": 4, "nbformat_minor": 5 }