The following properties map to the values of the CSS properties of the same name (underscores being replaced with dashes), applied to the top DOM elements of the corresponding widget.
\n",
+ "\n",
+ "#### Sizes\n",
+ "* `height`\n",
+ "* `width`\n",
+ "* `max_height`\n",
+ "* `max_width`\n",
+ "* `min_height`\n",
+ "* `min_width`\n",
+ "\n",
+ "#### Display\n",
+ "* `visibility`\n",
+ "* `display`\n",
+ "* `overflow`\n",
+ "* `overflow_x`\n",
+ "* `overflow_y`\n",
+ "\n",
+ "#### Box model\n",
+ "* `border`\n",
+ "* `margin`\n",
+ "* `padding`\n",
+ "\n",
+ "#### Positioning\n",
+ "* `top`\n",
+ "* `left`\n",
+ "* `bottom`\n",
+ "* `right`\n",
+ "\n",
+ "#### Flexbox\n",
+ "* `order`\n",
+ "* `flex_flow`\n",
+ "* `align_items`\n",
+ "* `flex`\n",
+ "* `align_self`\n",
+ "* `align_content`\n",
+ "* `justify_content`\n",
+ "\n",
+ "### Shorthand CSS properties\n",
+ "\n",
+ "You may have noticed that certain CSS properties such as `margin-[top/right/bottom/left]` seem to be missing. The same holds for `padding-[top/right/bottom/left]` etc.\n",
+ "\n",
+ "In fact, you can atomically specify `[top/right/bottom/left]` margins via the `margin` attribute alone by passing the string `'100px 150px 100px 80px'` for a respectively `top`, `right`, `bottom` and `left` margins of `100`, `150`, `100` and `80` pixels.\n",
+ "\n",
+ "Similarly, the `flex` attribute can hold values for `flex-grow`, `flex-shrink` and `flex-basis`. The `border` attribute is a shorthand property for `border-width`, `border-style (required)`, and `border-color`."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 19,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import ipywidgets as widgets\n",
+ "from IPython.display import display"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Conclusion\n",
+ "\n",
+ "You should now have an understanding of how to style widgets!"
+ ]
+ }
+ ],
+ "metadata": {
+ "cell_tags": [
+ [
+ "",
+ null
+ ]
+ ],
+ "kernelspec": {
+ "display_name": "Python 3",
+ "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.6.2"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 1
+}
diff --git a/Complete-Python-3-Bootcamp-master/16-Bonus Material - Introduction to GUIs/01-Interact.ipynb b/Complete-Python-3-Bootcamp-master/16-Bonus Material - Introduction to GUIs/01-Interact.ipynb
new file mode 100644
index 0000000..051c543
--- /dev/null
+++ b/Complete-Python-3-Bootcamp-master/16-Bonus Material - Introduction to GUIs/01-Interact.ipynb
@@ -0,0 +1,1057 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Using Interact\n",
+ "\n",
+ "In this lecture we will begin to learn about creating dashboard-type GUI with iPython widgets!"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The `interact` function (`ipywidgets.interact`) automatically creates user interface (UI) controls for exploring code and data interactively. It is the easiest way to get started using IPython's widgets."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Start with some imports!\n",
+ "\n",
+ "from ipywidgets import interact, interactive, fixed\n",
+ "import ipywidgets as widgets"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "Please Note! The widgets in this notebook won't show up on NbViewer or GitHub renderings. To view the widgets and interact with them, you will need to download this notebook and run it with a Jupyter Notebook server.\n",
+ "\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Basic `interact`"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "At the most basic level, `interact` auto-generates UI controls for function arguments, and then calls the function with those arguments when you manipulate the controls interactively. To use `interact`, you need to define a function that you want to explore. Here is a function that prints its only argument `x`."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Very basic function\n",
+ "def f(x):\n",
+ " return x"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "When you pass this function as the first argument to `interact` along with an integer keyword argument (`x=10`), a slider is generated and bound to the function parameter. Note that the semicolon here just prevents an **out** cell from showing up."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "application/vnd.jupyter.widget-view+json": {
+ "model_id": "d19364a3984541918392df94acd74157",
+ "version_major": 2,
+ "version_minor": 0
+ },
+ "text/html": [
+ "Failed to display Jupyter Widget of type interactive.
\n",
+ "\n",
+ " If you're reading this message in the Jupyter Notebook or JupyterLab Notebook, it may mean\n",
+ " that the widgets JavaScript is still loading. If this message persists, it\n",
+ " likely means that the widgets JavaScript library is either not installed or\n",
+ " not enabled. See the Jupyter\n",
+ " Widgets Documentation for setup instructions.\n",
+ "
\n",
+ "\n",
+ " If you're reading this message in another frontend (for example, a static\n",
+ " rendering on GitHub or NBViewer),\n",
+ " it may mean that your frontend doesn't currently support widgets.\n",
+ "
\n"
+ ],
+ "text/plain": [
+ "interactive(children=(IntSlider(value=10, description='x', max=30, min=-10), Output()), _dom_classes=('widget-interact',))"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "# Generate a slider to interact with\n",
+ "interact(f, x=10,);"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "When you move the slider, the function is called, which prints the current value of `x`.\n",
+ "\n",
+ "If you pass `True` or `False`, `interact` will generate a check-box:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "metadata": {
+ "scrolled": true
+ },
+ "outputs": [
+ {
+ "data": {
+ "application/vnd.jupyter.widget-view+json": {
+ "model_id": "d56caf432729463dabc716ef65c437db",
+ "version_major": 2,
+ "version_minor": 0
+ },
+ "text/html": [
+ "Failed to display Jupyter Widget of type interactive.
\n",
+ "\n",
+ " If you're reading this message in the Jupyter Notebook or JupyterLab Notebook, it may mean\n",
+ " that the widgets JavaScript is still loading. If this message persists, it\n",
+ " likely means that the widgets JavaScript library is either not installed or\n",
+ " not enabled. See the Jupyter\n",
+ " Widgets Documentation for setup instructions.\n",
+ "
\n",
+ "\n",
+ " If you're reading this message in another frontend (for example, a static\n",
+ " rendering on GitHub or NBViewer),\n",
+ " it may mean that your frontend doesn't currently support widgets.\n",
+ "
\n"
+ ],
+ "text/plain": [
+ "interactive(children=(Checkbox(value=True, description='x'), Output()), _dom_classes=('widget-interact',))"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "# Booleans generate check-boxes\n",
+ "interact(f, x=True);"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "If you pass a string, `interact` will generate a text area."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "application/vnd.jupyter.widget-view+json": {
+ "model_id": "3ec606e93ced408992a5cb0f59903ed9",
+ "version_major": 2,
+ "version_minor": 0
+ },
+ "text/html": [
+ "Failed to display Jupyter Widget of type interactive.
\n",
+ "\n",
+ " If you're reading this message in the Jupyter Notebook or JupyterLab Notebook, it may mean\n",
+ " that the widgets JavaScript is still loading. If this message persists, it\n",
+ " likely means that the widgets JavaScript library is either not installed or\n",
+ " not enabled. See the Jupyter\n",
+ " Widgets Documentation for setup instructions.\n",
+ "
\n",
+ "\n",
+ " If you're reading this message in another frontend (for example, a static\n",
+ " rendering on GitHub or NBViewer),\n",
+ " it may mean that your frontend doesn't currently support widgets.\n",
+ "
\n"
+ ],
+ "text/plain": [
+ "interactive(children=(Text(value='Hi there!', description='x'), Output()), _dom_classes=('widget-interact',))"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "# Strings generate text areas\n",
+ "interact(f, x='Hi there!');"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "`interact` can also be used as a decorator. This allows you to define a function and interact with it in a single shot. As this example shows, `interact` also works with functions that have multiple arguments."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 6,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "application/vnd.jupyter.widget-view+json": {
+ "model_id": "4d87f586f60945c3ba0ed9dcc4e1874a",
+ "version_major": 2,
+ "version_minor": 0
+ },
+ "text/html": [
+ "Failed to display Jupyter Widget of type interactive.
\n",
+ "\n",
+ " If you're reading this message in the Jupyter Notebook or JupyterLab Notebook, it may mean\n",
+ " that the widgets JavaScript is still loading. If this message persists, it\n",
+ " likely means that the widgets JavaScript library is either not installed or\n",
+ " not enabled. See the Jupyter\n",
+ " Widgets Documentation for setup instructions.\n",
+ "
\n",
+ "\n",
+ " If you're reading this message in another frontend (for example, a static\n",
+ " rendering on GitHub or NBViewer),\n",
+ " it may mean that your frontend doesn't currently support widgets.\n",
+ "
\n"
+ ],
+ "text/plain": [
+ "interactive(children=(Checkbox(value=True, description='x'), FloatSlider(value=1.0, description='y', max=3.0, min=-1.0), Output()), _dom_classes=('widget-interact',))"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "# Using a decorator!\n",
+ "@interact(x=True, y=1.0)\n",
+ "def g(x, y):\n",
+ " return (x, y)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Fixing arguments using `fixed`"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "There are times when you may want to explore a function using `interact`, but fix one or more of its arguments to specific values. This can be accomplished by wrapping values with the `fixed` function."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 7,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Again, a simple function\n",
+ "def h(p, q):\n",
+ " return (p, q)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "When we call `interact`, we pass `fixed(20)` for q to hold it fixed at a value of `20`."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 8,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "application/vnd.jupyter.widget-view+json": {
+ "model_id": "1bcccacaa1cc43f9a1df1b4df87503ff",
+ "version_major": 2,
+ "version_minor": 0
+ },
+ "text/html": [
+ "Failed to display Jupyter Widget of type interactive.
\n",
+ "\n",
+ " If you're reading this message in the Jupyter Notebook or JupyterLab Notebook, it may mean\n",
+ " that the widgets JavaScript is still loading. If this message persists, it\n",
+ " likely means that the widgets JavaScript library is either not installed or\n",
+ " not enabled. See the Jupyter\n",
+ " Widgets Documentation for setup instructions.\n",
+ "
\n",
+ "\n",
+ " If you're reading this message in another frontend (for example, a static\n",
+ " rendering on GitHub or NBViewer),\n",
+ " it may mean that your frontend doesn't currently support widgets.\n",
+ "
\n"
+ ],
+ "text/plain": [
+ "interactive(children=(IntSlider(value=5, description='p', max=15, min=-5), Output()), _dom_classes=('widget-interact',))"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "interact(h, p=5, q=fixed(20));"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Notice that a slider is only produced for `p` as the value of `q` is fixed."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Widget abbreviations"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "When you pass an integer-valued keyword argument of `10` (`x=10`) to `interact`, it generates an integer-valued slider control with a range of `[-10,+3\\times10]`. In this case, `10` is an *abbreviation* for an actual slider widget:\n",
+ "\n",
+ "```python\n",
+ "IntSlider(min=-10,max=30,step=1,value=10)\n",
+ "```\n",
+ "\n",
+ "In fact, we can get the same result if we pass this `IntSlider` as the keyword argument for `x`:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 9,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "application/vnd.jupyter.widget-view+json": {
+ "model_id": "a251c042d8de4ecfbbfe2f2c4d65de55",
+ "version_major": 2,
+ "version_minor": 0
+ },
+ "text/html": [
+ "Failed to display Jupyter Widget of type interactive.
\n",
+ "\n",
+ " If you're reading this message in the Jupyter Notebook or JupyterLab Notebook, it may mean\n",
+ " that the widgets JavaScript is still loading. If this message persists, it\n",
+ " likely means that the widgets JavaScript library is either not installed or\n",
+ " not enabled. See the Jupyter\n",
+ " Widgets Documentation for setup instructions.\n",
+ "
\n",
+ "\n",
+ " If you're reading this message in another frontend (for example, a static\n",
+ " rendering on GitHub or NBViewer),\n",
+ " it may mean that your frontend doesn't currently support widgets.\n",
+ "
\n"
+ ],
+ "text/plain": [
+ "interactive(children=(IntSlider(value=10, description='x', max=30, min=-10), Output()), _dom_classes=('widget-interact',))"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "# Can call the IntSlider to get more specific\n",
+ "interact(f, x=widgets.IntSlider(min=-10,max=30,step=1,value=10));"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "This examples clarifies how `interact` processes its keyword arguments:\n",
+ "\n",
+ "1. If the keyword argument is a `Widget` instance with a `value` attribute, that widget is used. Any widget with a `value` attribute can be used, even custom ones.\n",
+ "2. Otherwise, the value is treated as a *widget abbreviation* that is converted to a widget before it is used.\n",
+ "\n",
+ "The following table gives an overview of different widget abbreviations:\n",
+ "\n",
+ "\n",
+ " | Keyword argument | Widget |
\n",
+ " | `True` or `False` | Checkbox |
\n",
+ " | `'Hi there'` | Text |
\n",
+ " | `value` or `(min,max)` or `(min,max,step)` if integers are passed | IntSlider |
\n",
+ " | `value` or `(min,max)` or `(min,max,step)` if floats are passed | FloatSlider |
\n",
+ " | `['orange','apple']` or `{'one':1,'two':2}` | Dropdown |
\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Note that a dropdown is used if a list or a dict is given (signifying discrete choices), and a slider is used if a tuple is given (signifying a range).\n",
+ "\n",
+ "You have seen how the checkbox and text area widgets work above. Here, more details about the different abbreviations for sliders and drop-downs are given.\n",
+ "\n",
+ "If a 2-tuple of integers is passed `(min,max)`, an integer-valued slider is produced with those minimum and maximum values (inclusively). In this case, the default step size of `1` is used."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 10,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "application/vnd.jupyter.widget-view+json": {
+ "model_id": "671e9e1043b44bbf905862440167bec4",
+ "version_major": 2,
+ "version_minor": 0
+ },
+ "text/html": [
+ "Failed to display Jupyter Widget of type interactive.
\n",
+ "\n",
+ " If you're reading this message in the Jupyter Notebook or JupyterLab Notebook, it may mean\n",
+ " that the widgets JavaScript is still loading. If this message persists, it\n",
+ " likely means that the widgets JavaScript library is either not installed or\n",
+ " not enabled. See the Jupyter\n",
+ " Widgets Documentation for setup instructions.\n",
+ "
\n",
+ "\n",
+ " If you're reading this message in another frontend (for example, a static\n",
+ " rendering on GitHub or NBViewer),\n",
+ " it may mean that your frontend doesn't currently support widgets.\n",
+ "
\n"
+ ],
+ "text/plain": [
+ "interactive(children=(IntSlider(value=2, description='x', max=4), Output()), _dom_classes=('widget-interact',))"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "# Min,Max slider with Tuples\n",
+ "interact(f, x=(0,4));"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "If a 3-tuple of integers is passed `(min,max,step)`, the step size can also be set."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 11,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "application/vnd.jupyter.widget-view+json": {
+ "model_id": "912b839b05ba488d819ff0c09c877a57",
+ "version_major": 2,
+ "version_minor": 0
+ },
+ "text/html": [
+ "Failed to display Jupyter Widget of type interactive.
\n",
+ "\n",
+ " If you're reading this message in the Jupyter Notebook or JupyterLab Notebook, it may mean\n",
+ " that the widgets JavaScript is still loading. If this message persists, it\n",
+ " likely means that the widgets JavaScript library is either not installed or\n",
+ " not enabled. See the Jupyter\n",
+ " Widgets Documentation for setup instructions.\n",
+ "
\n",
+ "\n",
+ " If you're reading this message in another frontend (for example, a static\n",
+ " rendering on GitHub or NBViewer),\n",
+ " it may mean that your frontend doesn't currently support widgets.\n",
+ "
\n"
+ ],
+ "text/plain": [
+ "interactive(children=(IntSlider(value=4, description='x', max=8, step=2), Output()), _dom_classes=('widget-interact',))"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "# (min, max, step)\n",
+ "interact(f, x=(0,8,2));"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "A float-valued slider is produced if the elements of the tuples are floats. Here the minimum is `0.0`, the maximum is `10.0` and step size is `0.1` (the default)."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 12,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "application/vnd.jupyter.widget-view+json": {
+ "model_id": "e7cbdf72471d406ca80bed9a232e6605",
+ "version_major": 2,
+ "version_minor": 0
+ },
+ "text/html": [
+ "Failed to display Jupyter Widget of type interactive.
\n",
+ "\n",
+ " If you're reading this message in the Jupyter Notebook or JupyterLab Notebook, it may mean\n",
+ " that the widgets JavaScript is still loading. If this message persists, it\n",
+ " likely means that the widgets JavaScript library is either not installed or\n",
+ " not enabled. See the Jupyter\n",
+ " Widgets Documentation for setup instructions.\n",
+ "
\n",
+ "\n",
+ " If you're reading this message in another frontend (for example, a static\n",
+ " rendering on GitHub or NBViewer),\n",
+ " it may mean that your frontend doesn't currently support widgets.\n",
+ "
\n"
+ ],
+ "text/plain": [
+ "interactive(children=(FloatSlider(value=5.0, description='x', max=10.0), Output()), _dom_classes=('widget-interact',))"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "interact(f, x=(0.0,10.0));"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The step size can be changed by passing a third element in the tuple."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 13,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "application/vnd.jupyter.widget-view+json": {
+ "model_id": "239573e3448a44179962dccfb9fe15cf",
+ "version_major": 2,
+ "version_minor": 0
+ },
+ "text/html": [
+ "Failed to display Jupyter Widget of type interactive.
\n",
+ "\n",
+ " If you're reading this message in the Jupyter Notebook or JupyterLab Notebook, it may mean\n",
+ " that the widgets JavaScript is still loading. If this message persists, it\n",
+ " likely means that the widgets JavaScript library is either not installed or\n",
+ " not enabled. See the Jupyter\n",
+ " Widgets Documentation for setup instructions.\n",
+ "
\n",
+ "\n",
+ " If you're reading this message in another frontend (for example, a static\n",
+ " rendering on GitHub or NBViewer),\n",
+ " it may mean that your frontend doesn't currently support widgets.\n",
+ "
\n"
+ ],
+ "text/plain": [
+ "interactive(children=(FloatSlider(value=5.0, description='x', max=10.0, step=0.01), Output()), _dom_classes=('widget-interact',))"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "interact(f, x=(0.0,10.0,0.01));"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "For both integer and float-valued sliders, you can pick the initial value of the widget by passing a default keyword argument to the underlying Python function. Here we set the initial value of a float slider to `5.5`."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 14,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "application/vnd.jupyter.widget-view+json": {
+ "model_id": "fd58de461e564c38b0a12def19ccfd1b",
+ "version_major": 2,
+ "version_minor": 0
+ },
+ "text/html": [
+ "Failed to display Jupyter Widget of type interactive.
\n",
+ "\n",
+ " If you're reading this message in the Jupyter Notebook or JupyterLab Notebook, it may mean\n",
+ " that the widgets JavaScript is still loading. If this message persists, it\n",
+ " likely means that the widgets JavaScript library is either not installed or\n",
+ " not enabled. See the Jupyter\n",
+ " Widgets Documentation for setup instructions.\n",
+ "
\n",
+ "\n",
+ " If you're reading this message in another frontend (for example, a static\n",
+ " rendering on GitHub or NBViewer),\n",
+ " it may mean that your frontend doesn't currently support widgets.\n",
+ "
\n"
+ ],
+ "text/plain": [
+ "interactive(children=(FloatSlider(value=5.5, description='x', max=20.0, step=0.5), Output()), _dom_classes=('widget-interact',))"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "@interact(x=(0.0,20.0,0.5))\n",
+ "def h(x=5.5):\n",
+ " return x"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Dropdown menus are constructed by passing a list of strings. In this case, the strings are both used as the names in the drop-down menu UI and passed to the underlying Python function."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 15,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "application/vnd.jupyter.widget-view+json": {
+ "model_id": "88ab10cb594d46b2ade99079f5087f12",
+ "version_major": 2,
+ "version_minor": 0
+ },
+ "text/html": [
+ "Failed to display Jupyter Widget of type interactive.
\n",
+ "\n",
+ " If you're reading this message in the Jupyter Notebook or JupyterLab Notebook, it may mean\n",
+ " that the widgets JavaScript is still loading. If this message persists, it\n",
+ " likely means that the widgets JavaScript library is either not installed or\n",
+ " not enabled. See the Jupyter\n",
+ " Widgets Documentation for setup instructions.\n",
+ "
\n",
+ "\n",
+ " If you're reading this message in another frontend (for example, a static\n",
+ " rendering on GitHub or NBViewer),\n",
+ " it may mean that your frontend doesn't currently support widgets.\n",
+ "
\n"
+ ],
+ "text/plain": [
+ "interactive(children=(Dropdown(description='x', options=('apples', 'oranges'), value='apples'), Output()), _dom_classes=('widget-interact',))"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "interact(f, x=['apples','oranges']);"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "If you want a drop-down menu that passes non-string values to the Python function, you can pass a dictionary. The keys in the dictionary are used for the names in the drop-down menu UI and the values are the arguments that are passed to the underlying Python function."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 16,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "application/vnd.jupyter.widget-view+json": {
+ "model_id": "d006408fb16f457b81eb604f5bf18220",
+ "version_major": 2,
+ "version_minor": 0
+ },
+ "text/html": [
+ "Failed to display Jupyter Widget of type interactive.
\n",
+ "\n",
+ " If you're reading this message in the Jupyter Notebook or JupyterLab Notebook, it may mean\n",
+ " that the widgets JavaScript is still loading. If this message persists, it\n",
+ " likely means that the widgets JavaScript library is either not installed or\n",
+ " not enabled. See the Jupyter\n",
+ " Widgets Documentation for setup instructions.\n",
+ "
\n",
+ "\n",
+ " If you're reading this message in another frontend (for example, a static\n",
+ " rendering on GitHub or NBViewer),\n",
+ " it may mean that your frontend doesn't currently support widgets.\n",
+ "
\n"
+ ],
+ "text/plain": [
+ "interactive(children=(Dropdown(description='x', options={'one': 10, 'two': 20}, value=10), Output()), _dom_classes=('widget-interact',))"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "interact(f, x={'one': 10, 'two': 20});"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Using function annotations with `interact`"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "You can also specify widget abbreviations using [function annotations](https://docs.python.org/3/tutorial/controlflow.html#function-annotations).\n",
+ "\n",
+ "Define a function with a checkbox widget abbreviation for the argument `x`."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 17,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def f(x:True): # Python 3 only\n",
+ " return x"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Then, because the widget abbreviation has already been defined, you can call `interact` with a single argument."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 18,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "application/vnd.jupyter.widget-view+json": {
+ "model_id": "70f34f96f12143b492fc3160d089f854",
+ "version_major": 2,
+ "version_minor": 0
+ },
+ "text/html": [
+ "Failed to display Jupyter Widget of type interactive.
\n",
+ "\n",
+ " If you're reading this message in the Jupyter Notebook or JupyterLab Notebook, it may mean\n",
+ " that the widgets JavaScript is still loading. If this message persists, it\n",
+ " likely means that the widgets JavaScript library is either not installed or\n",
+ " not enabled. See the Jupyter\n",
+ " Widgets Documentation for setup instructions.\n",
+ "
\n",
+ "\n",
+ " If you're reading this message in another frontend (for example, a static\n",
+ " rendering on GitHub or NBViewer),\n",
+ " it may mean that your frontend doesn't currently support widgets.\n",
+ "
\n"
+ ],
+ "text/plain": [
+ "interactive(children=(Checkbox(value=True, description='x'), Output()), _dom_classes=('widget-interact',))"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "interact(f);"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## interactive"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "In addition to `interact`, IPython provides another function, `interactive`, that is useful when you want to reuse the widgets that are produced or access the data that is bound to the UI controls.\n",
+ "\n",
+ "Note that unlike `interact`, the return value of the function will not be displayed automatically, but you can display a value inside the function with `IPython.display.display`.\n",
+ "\n",
+ "Here is a function that returns the sum of its two arguments and displays them. The display line may be omitted if you don’t want to show the result of the function."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 22,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from IPython.display import display\n",
+ "\n",
+ "def f(a, b):\n",
+ " display(a + b)\n",
+ " return a+b"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Unlike `interact`, `interactive` returns a `Widget` instance rather than immediately displaying the widget."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 23,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "w = interactive(f, a=10, b=20)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The widget is an `interactive`, a subclass of `VBox`, which is a container for other widgets."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 24,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "ipywidgets.widgets.interaction.interactive"
+ ]
+ },
+ "execution_count": 24,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "type(w)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The children of the `interactive` are two integer-valued sliders and an output widget, produced by the widget abbreviations above."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 25,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "(IntSlider(value=10, description='a', max=30, min=-10),\n",
+ " IntSlider(value=20, description='b', max=60, min=-20),\n",
+ " Output())"
+ ]
+ },
+ "execution_count": 25,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "w.children"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "To actually display the widgets, you can use IPython's `display` function."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 26,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "application/vnd.jupyter.widget-view+json": {
+ "model_id": "0da895902d664d5086ed00d535d36ef3",
+ "version_major": 2,
+ "version_minor": 0
+ },
+ "text/html": [
+ "Failed to display Jupyter Widget of type interactive.
\n",
+ "\n",
+ " If you're reading this message in the Jupyter Notebook or JupyterLab Notebook, it may mean\n",
+ " that the widgets JavaScript is still loading. If this message persists, it\n",
+ " likely means that the widgets JavaScript library is either not installed or\n",
+ " not enabled. See the Jupyter\n",
+ " Widgets Documentation for setup instructions.\n",
+ "
\n",
+ "\n",
+ " If you're reading this message in another frontend (for example, a static\n",
+ " rendering on GitHub or NBViewer),\n",
+ " it may mean that your frontend doesn't currently support widgets.\n",
+ "
\n"
+ ],
+ "text/plain": [
+ "interactive(children=(IntSlider(value=10, description='a', max=30, min=-10), IntSlider(value=20, description='b', max=60, min=-20), Output()), _dom_classes=('widget-interact',))"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "display(w)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "At this point, the UI controls work just like they would if `interact` had been used. You can manipulate them interactively and the function will be called. However, the widget instance returned by `interactive` also give you access to the current keyword arguments and return value of the underlying Python function.\n",
+ "\n",
+ "Here are the current keyword arguments. If you rerun this cell after manipulating the sliders, the values will have changed."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 27,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "{'a': 10, 'b': 20}"
+ ]
+ },
+ "execution_count": 27,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "w.kwargs"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Here is the current return value of the function."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 28,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "30"
+ ]
+ },
+ "execution_count": 28,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "w.result"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Conclusion\n",
+ "\n",
+ "You should now have a basic understanding of how to use Interact in Jupyter Notebooks!"
+ ]
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "Python 3",
+ "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.6.2"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 1
+}
diff --git a/Complete-Python-3-Bootcamp-master/16-Bonus Material - Introduction to GUIs/02-Widget Basics.ipynb b/Complete-Python-3-Bootcamp-master/16-Bonus Material - Introduction to GUIs/02-Widget Basics.ipynb
new file mode 100644
index 0000000..ca3246b
--- /dev/null
+++ b/Complete-Python-3-Bootcamp-master/16-Bonus Material - Introduction to GUIs/02-Widget Basics.ipynb
@@ -0,0 +1,673 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Widget Basics\n",
+ "\n",
+ "In this lecture we will continue to build off our understanding of **interact** and **interactive** to begin using full widgets!"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## What are widgets?"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "source": [
+ "Widgets are eventful python objects that have a representation in the browser, often as a control like a slider, textbox, etc."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## What can they be used for?"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "source": [
+ "You can use widgets to build **interactive GUIs** for your notebooks. \n",
+ "You can also use widgets to **synchronize stateful and stateless information** between Python and JavaScript."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Using widgets "
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "source": [
+ "To use the widget framework, you need to import `ipywidgets`."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import ipywidgets as widgets"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "source": [
+ "### repr"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Widgets have their own display `repr` which allows them to be displayed using IPython's display framework. Constructing and returning an `IntSlider` automatically displays the widget (as seen below). Widgets are displayed inside the output area below the code cell. Clearing cell output will also remove the widget."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "metadata": {
+ "scrolled": true
+ },
+ "outputs": [
+ {
+ "data": {
+ "application/vnd.jupyter.widget-view+json": {
+ "model_id": "e1feeb5d164345929849b3775b347ad7",
+ "version_major": 2,
+ "version_minor": 0
+ },
+ "text/html": [
+ "Failed to display Jupyter Widget of type IntSlider.
\n",
+ "\n",
+ " If you're reading this message in the Jupyter Notebook or JupyterLab Notebook, it may mean\n",
+ " that the widgets JavaScript is still loading. If this message persists, it\n",
+ " likely means that the widgets JavaScript library is either not installed or\n",
+ " not enabled. See the Jupyter\n",
+ " Widgets Documentation for setup instructions.\n",
+ "
\n",
+ "\n",
+ " If you're reading this message in another frontend (for example, a static\n",
+ " rendering on GitHub or NBViewer),\n",
+ " it may mean that your frontend doesn't currently support widgets.\n",
+ "
\n"
+ ],
+ "text/plain": [
+ "IntSlider(value=0)"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "widgets.IntSlider()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "source": [
+ "### display()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "You can also explicitly display the widget using `display(...)`."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "application/vnd.jupyter.widget-view+json": {
+ "model_id": "75f00040661845228532350c310c4bc6",
+ "version_major": 2,
+ "version_minor": 0
+ },
+ "text/html": [
+ "Failed to display Jupyter Widget of type IntSlider.
\n",
+ "\n",
+ " If you're reading this message in the Jupyter Notebook or JupyterLab Notebook, it may mean\n",
+ " that the widgets JavaScript is still loading. If this message persists, it\n",
+ " likely means that the widgets JavaScript library is either not installed or\n",
+ " not enabled. See the Jupyter\n",
+ " Widgets Documentation for setup instructions.\n",
+ "
\n",
+ "\n",
+ " If you're reading this message in another frontend (for example, a static\n",
+ " rendering on GitHub or NBViewer),\n",
+ " it may mean that your frontend doesn't currently support widgets.\n",
+ "
\n"
+ ],
+ "text/plain": [
+ "IntSlider(value=0)"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "from IPython.display import display\n",
+ "w = widgets.IntSlider()\n",
+ "display(w)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "source": [
+ "### Multiple display() calls"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "If you display the same widget twice, the displayed instances in the front-end will remain in sync with each other. Try dragging the slider below and watch the slider above."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "application/vnd.jupyter.widget-view+json": {
+ "model_id": "75f00040661845228532350c310c4bc6",
+ "version_major": 2,
+ "version_minor": 0
+ },
+ "text/html": [
+ "Failed to display Jupyter Widget of type IntSlider.
\n",
+ "\n",
+ " If you're reading this message in the Jupyter Notebook or JupyterLab Notebook, it may mean\n",
+ " that the widgets JavaScript is still loading. If this message persists, it\n",
+ " likely means that the widgets JavaScript library is either not installed or\n",
+ " not enabled. See the Jupyter\n",
+ " Widgets Documentation for setup instructions.\n",
+ "
\n",
+ "\n",
+ " If you're reading this message in another frontend (for example, a static\n",
+ " rendering on GitHub or NBViewer),\n",
+ " it may mean that your frontend doesn't currently support widgets.\n",
+ "
\n"
+ ],
+ "text/plain": [
+ "IntSlider(value=0)"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "display(w)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "source": [
+ "### Closing widgets"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "You can close a widget by calling its `close()` method."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "application/vnd.jupyter.widget-view+json": {
+ "model_id": "75f00040661845228532350c310c4bc6",
+ "version_major": 2,
+ "version_minor": 0
+ },
+ "text/html": [
+ "Failed to display Jupyter Widget of type IntSlider.
\n",
+ "\n",
+ " If you're reading this message in the Jupyter Notebook or JupyterLab Notebook, it may mean\n",
+ " that the widgets JavaScript is still loading. If this message persists, it\n",
+ " likely means that the widgets JavaScript library is either not installed or\n",
+ " not enabled. See the Jupyter\n",
+ " Widgets Documentation for setup instructions.\n",
+ "
\n",
+ "\n",
+ " If you're reading this message in another frontend (for example, a static\n",
+ " rendering on GitHub or NBViewer),\n",
+ " it may mean that your frontend doesn't currently support widgets.\n",
+ "
\n"
+ ],
+ "text/plain": [
+ "IntSlider(value=0)"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "display(w)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 6,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "w.close()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Widget properties"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "source": [
+ "All of the IPython widgets share a similar naming scheme. To read the value of a widget, you can query its `value` property."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 7,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "application/vnd.jupyter.widget-view+json": {
+ "model_id": "a5ab821e8906437d99a86664021856d3",
+ "version_major": 2,
+ "version_minor": 0
+ },
+ "text/html": [
+ "Failed to display Jupyter Widget of type IntSlider.
\n",
+ "\n",
+ " If you're reading this message in the Jupyter Notebook or JupyterLab Notebook, it may mean\n",
+ " that the widgets JavaScript is still loading. If this message persists, it\n",
+ " likely means that the widgets JavaScript library is either not installed or\n",
+ " not enabled. See the Jupyter\n",
+ " Widgets Documentation for setup instructions.\n",
+ "
\n",
+ "\n",
+ " If you're reading this message in another frontend (for example, a static\n",
+ " rendering on GitHub or NBViewer),\n",
+ " it may mean that your frontend doesn't currently support widgets.\n",
+ "
\n"
+ ],
+ "text/plain": [
+ "IntSlider(value=0)"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "w = widgets.IntSlider()\n",
+ "display(w)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 8,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "0"
+ ]
+ },
+ "execution_count": 8,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "w.value"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Similarly, to set a widget's value, you can set its `value` property."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 9,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "w.value = 100"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "source": [
+ "### Keys"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "In addition to `value`, most widgets share `keys`, `description`, and `disabled`. To see the entire list of synchronized, stateful properties of any specific widget, you can query the `keys` property."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 10,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "['_dom_classes',\n",
+ " '_model_module',\n",
+ " '_model_module_version',\n",
+ " '_model_name',\n",
+ " '_view_count',\n",
+ " '_view_module',\n",
+ " '_view_module_version',\n",
+ " '_view_name',\n",
+ " 'continuous_update',\n",
+ " 'description',\n",
+ " 'disabled',\n",
+ " 'layout',\n",
+ " 'max',\n",
+ " 'min',\n",
+ " 'orientation',\n",
+ " 'readout',\n",
+ " 'readout_format',\n",
+ " 'step',\n",
+ " 'style',\n",
+ " 'value']"
+ ]
+ },
+ "execution_count": 10,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "w.keys"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Shorthand for setting the initial values of widget properties"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "source": [
+ "While creating a widget, you can set some or all of the initial values of that widget by defining them as keyword arguments in the widget's constructor (as seen below)."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 12,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "application/vnd.jupyter.widget-view+json": {
+ "model_id": "b35936af3f2741ae823d7d2fd4c7fcda",
+ "version_major": 2,
+ "version_minor": 0
+ },
+ "text/html": [
+ "Failed to display Jupyter Widget of type Text.
\n",
+ "\n",
+ " If you're reading this message in the Jupyter Notebook or JupyterLab Notebook, it may mean\n",
+ " that the widgets JavaScript is still loading. If this message persists, it\n",
+ " likely means that the widgets JavaScript library is either not installed or\n",
+ " not enabled. See the Jupyter\n",
+ " Widgets Documentation for setup instructions.\n",
+ "
\n",
+ "\n",
+ " If you're reading this message in another frontend (for example, a static\n",
+ " rendering on GitHub or NBViewer),\n",
+ " it may mean that your frontend doesn't currently support widgets.\n",
+ "
\n"
+ ],
+ "text/plain": [
+ "Text(value='Hello World!', disabled=True)"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "widgets.Text(value='Hello World!', disabled=True)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Linking two similar widgets"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "source": [
+ "If you need to display the same value two different ways, you'll have to use two different widgets. Instead of attempting to manually synchronize the values of the two widgets, you can use the `link` or `jslink` function to link two properties together (the difference between these is discussed in Widget Events). Below, the values of two widgets are linked together."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 13,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "application/vnd.jupyter.widget-view+json": {
+ "model_id": "2013fbc8bf434a2b8d8d8ebe8f06c89a",
+ "version_major": 2,
+ "version_minor": 0
+ },
+ "text/html": [
+ "Failed to display Jupyter Widget of type FloatText.
\n",
+ "\n",
+ " If you're reading this message in the Jupyter Notebook or JupyterLab Notebook, it may mean\n",
+ " that the widgets JavaScript is still loading. If this message persists, it\n",
+ " likely means that the widgets JavaScript library is either not installed or\n",
+ " not enabled. See the Jupyter\n",
+ " Widgets Documentation for setup instructions.\n",
+ "
\n",
+ "\n",
+ " If you're reading this message in another frontend (for example, a static\n",
+ " rendering on GitHub or NBViewer),\n",
+ " it may mean that your frontend doesn't currently support widgets.\n",
+ "
\n"
+ ],
+ "text/plain": [
+ "FloatText(value=0.0)"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "application/vnd.jupyter.widget-view+json": {
+ "model_id": "edacad1b8c34472c92ac5052449cba52",
+ "version_major": 2,
+ "version_minor": 0
+ },
+ "text/html": [
+ "Failed to display Jupyter Widget of type FloatSlider.
\n",
+ "\n",
+ " If you're reading this message in the Jupyter Notebook or JupyterLab Notebook, it may mean\n",
+ " that the widgets JavaScript is still loading. If this message persists, it\n",
+ " likely means that the widgets JavaScript library is either not installed or\n",
+ " not enabled. See the Jupyter\n",
+ " Widgets Documentation for setup instructions.\n",
+ "
\n",
+ "\n",
+ " If you're reading this message in another frontend (for example, a static\n",
+ " rendering on GitHub or NBViewer),\n",
+ " it may mean that your frontend doesn't currently support widgets.\n",
+ "
\n"
+ ],
+ "text/plain": [
+ "FloatSlider(value=0.0)"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "a = widgets.FloatText()\n",
+ "b = widgets.FloatSlider()\n",
+ "display(a,b)\n",
+ "\n",
+ "mylink = widgets.jslink((a, 'value'), (b, 'value'))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Unlinking widgets"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "source": [
+ "Unlinking the widgets is simple. All you have to do is call `.unlink` on the link object. Try changing one of the widgets above after unlinking to see that they can be independently changed."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 14,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "mylink.unlink()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Conclusion\n",
+ "\n",
+ "You should now be beginning to have an understanding of how Widgets can interact with each other and how you can begin to specify widget details."
+ ]
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "Python 3",
+ "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.6.2"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 1
+}
diff --git a/Complete-Python-3-Bootcamp-master/16-Bonus Material - Introduction to GUIs/03-Widget List.ipynb b/Complete-Python-3-Bootcamp-master/16-Bonus Material - Introduction to GUIs/03-Widget List.ipynb
new file mode 100644
index 0000000..26d98ab
--- /dev/null
+++ b/Complete-Python-3-Bootcamp-master/16-Bonus Material - Introduction to GUIs/03-Widget List.ipynb
@@ -0,0 +1,859 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Widget List\n",
+ "\n",
+ "This lecture will serve as a reference for widgets, providing a list of the GUI widgets available!"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Complete list"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "source": [
+ "For a complete list of the GUI widgets available to you, you can list the registered widget types. `Widget` is the base class."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import ipywidgets as widgets\n",
+ "\n",
+ "# Show all available widgets!\n",
+ "for item in widgets.Widget.widget_types.items():\n",
+ " print(item[0][2][:-5])"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "source": [
+ "## Numeric widgets"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "There are 10 widgets distributed with IPython that are designed to display numeric values. Widgets exist for displaying integers and floats, both bounded and unbounded. The integer widgets share a similar naming scheme to their floating point counterparts. By replacing `Float` with `Int` in the widget name, you can find the Integer equivalent."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "source": [
+ "### IntSlider"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "widgets.IntSlider(\n",
+ " value=7,\n",
+ " min=0,\n",
+ " max=10,\n",
+ " step=1,\n",
+ " description='Test:',\n",
+ " disabled=False,\n",
+ " continuous_update=False,\n",
+ " orientation='horizontal',\n",
+ " readout=True,\n",
+ " readout_format='d'\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "source": [
+ "### FloatSlider"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "widgets.FloatSlider(\n",
+ " value=7.5,\n",
+ " min=0,\n",
+ " max=10.0,\n",
+ " step=0.1,\n",
+ " description='Test:',\n",
+ " disabled=False,\n",
+ " continuous_update=False,\n",
+ " orientation='horizontal',\n",
+ " readout=True,\n",
+ " readout_format='.1f',\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Sliders can also be **displayed vertically**."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "widgets.FloatSlider(\n",
+ " value=7.5,\n",
+ " min=0,\n",
+ " max=10.0,\n",
+ " step=0.1,\n",
+ " description='Test:',\n",
+ " disabled=False,\n",
+ " continuous_update=False,\n",
+ " orientation='vertical',\n",
+ " readout=True,\n",
+ " readout_format='.1f',\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### IntRangeSlider"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "widgets.IntRangeSlider(\n",
+ " value=[5, 7],\n",
+ " min=0,\n",
+ " max=10,\n",
+ " step=1,\n",
+ " description='Test:',\n",
+ " disabled=False,\n",
+ " continuous_update=False,\n",
+ " orientation='horizontal',\n",
+ " readout=True,\n",
+ " readout_format='d',\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### FloatRangeSlider"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "widgets.FloatRangeSlider(\n",
+ " value=[5, 7.5],\n",
+ " min=0,\n",
+ " max=10.0,\n",
+ " step=0.1,\n",
+ " description='Test:',\n",
+ " disabled=False,\n",
+ " continuous_update=False,\n",
+ " orientation='horizontal',\n",
+ " readout=True,\n",
+ " readout_format='.1f',\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### IntProgress"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "widgets.IntProgress(\n",
+ " value=7,\n",
+ " min=0,\n",
+ " max=10,\n",
+ " step=1,\n",
+ " description='Loading:',\n",
+ " bar_style='', # 'success', 'info', 'warning', 'danger' or ''\n",
+ " orientation='horizontal'\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "source": [
+ "### FloatProgress"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "widgets.FloatProgress(\n",
+ " value=7.5,\n",
+ " min=0,\n",
+ " max=10.0,\n",
+ " step=0.1,\n",
+ " description='Loading:',\n",
+ " bar_style='info',\n",
+ " orientation='horizontal'\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The numerical text boxes that impose some limit on the data (range, integer-only) impose that restriction when the user presses enter."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "source": [
+ "### BoundedIntText"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "widgets.BoundedIntText(\n",
+ " value=7,\n",
+ " min=0,\n",
+ " max=10,\n",
+ " step=1,\n",
+ " description='Text:',\n",
+ " disabled=False\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "source": [
+ "### BoundedFloatText"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "widgets.BoundedFloatText(\n",
+ " value=7.5,\n",
+ " min=0,\n",
+ " max=10.0,\n",
+ " step=0.1,\n",
+ " description='Text:',\n",
+ " disabled=False\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### IntText"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "widgets.IntText(\n",
+ " value=7,\n",
+ " description='Any:',\n",
+ " disabled=False\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "source": [
+ "### FloatText"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "widgets.FloatText(\n",
+ " value=7.5,\n",
+ " description='Any:',\n",
+ " disabled=False\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "source": [
+ "## Boolean widgets"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "There are three widgets that are designed to display a boolean value."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### ToggleButton"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "widgets.ToggleButton(\n",
+ " value=False,\n",
+ " description='Click me',\n",
+ " disabled=False,\n",
+ " button_style='', # 'success', 'info', 'warning', 'danger' or ''\n",
+ " tooltip='Description',\n",
+ " icon='check'\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "source": [
+ "### Checkbox"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "widgets.Checkbox(\n",
+ " value=False,\n",
+ " description='Check me',\n",
+ " disabled=False\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Valid\n",
+ "\n",
+ "The valid widget provides a read-only indicator."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "widgets.Valid(\n",
+ " value=False,\n",
+ " description='Valid!',\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "source": [
+ "## Selection widgets"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "There are several widgets that can be used to display single selection lists, and two that can be used to select multiple values. All inherit from the same base class. You can specify the **enumeration of selectable options by passing a list** (options are either (label, value) pairs, or simply values for which the labels are derived by calling `str`). You can **also specify the enumeration as a dictionary**, in which case the **keys will be used as the item displayed** in the list and the corresponding **value will be used** when an item is selected (in this case, since dictionaries are unordered, the displayed order of items in the widget is unspecified)."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "source": [
+ "### Dropdown"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "widgets.Dropdown(\n",
+ " options=['1', '2', '3'],\n",
+ " value='2',\n",
+ " description='Number:',\n",
+ " disabled=False,\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The following is also valid:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "widgets.Dropdown(\n",
+ " options={'One': 1, 'Two': 2, 'Three': 3},\n",
+ " value=2,\n",
+ " description='Number:',\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "source": [
+ "### RadioButtons"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "widgets.RadioButtons(\n",
+ " options=['pepperoni', 'pineapple', 'anchovies'],\n",
+ " # value='pineapple',\n",
+ " description='Pizza topping:',\n",
+ " disabled=False\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "source": [
+ "### Select"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "widgets.Select(\n",
+ " options=['Linux', 'Windows', 'OSX'],\n",
+ " value='OSX',\n",
+ " # rows=10,\n",
+ " description='OS:',\n",
+ " disabled=False\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### SelectionSlider"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "widgets.SelectionSlider(\n",
+ " options=['scrambled', 'sunny side up', 'poached', 'over easy'],\n",
+ " value='sunny side up',\n",
+ " description='I like my eggs ...',\n",
+ " disabled=False,\n",
+ " continuous_update=False,\n",
+ " orientation='horizontal',\n",
+ " readout=True\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### SelectionRangeSlider\n",
+ "The value, index, and label keys are 2-tuples of the min and max values selected. The options must be nonempty."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import datetime\n",
+ "dates = [datetime.date(2015,i,1) for i in range(1,13)]\n",
+ "options = [(i.strftime('%b'), i) for i in dates]\n",
+ "widgets.SelectionRangeSlider(\n",
+ " options=options,\n",
+ " index=(0,11),\n",
+ " description='Months (2015)',\n",
+ " disabled=False\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "source": [
+ "### ToggleButtons"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "widgets.ToggleButtons(\n",
+ " options=['Slow', 'Regular', 'Fast'],\n",
+ " description='Speed:',\n",
+ " disabled=False,\n",
+ " button_style='', # 'success', 'info', 'warning', 'danger' or ''\n",
+ " tooltips=['Description of slow', 'Description of regular', 'Description of fast'],\n",
+ " # icons=['check'] * 3\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### SelectMultiple\n",
+ "Multiple values can be selected with shift and/or ctrl (or command) pressed and mouse clicks or arrow keys."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "widgets.SelectMultiple(\n",
+ " options=['Apples', 'Oranges', 'Pears'],\n",
+ " value=['Oranges'],\n",
+ " # rows=10,\n",
+ " description='Fruits',\n",
+ " disabled=False\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "source": [
+ "## String widgets\n",
+ "There are several widgets that can be used to display a string value. The `Text` and `Textarea` widgets accept input. The `HTML` and `HTMLMath` widgets display a string as HTML (`HTMLMath` also renders math). The `Label` widget can be used to construct a custom control label."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "source": [
+ "### Text"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "widgets.Text(\n",
+ " value='Hello World',\n",
+ " placeholder='Type something',\n",
+ " description='String:',\n",
+ " disabled=False\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Textarea"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "widgets.Textarea(\n",
+ " value='Hello World',\n",
+ " placeholder='Type something',\n",
+ " description='String:',\n",
+ " disabled=False\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "source": [
+ "### Label\n",
+ "The `Label` widget is useful if you need to build a custom description next to a control using similar styling to the built-in control descriptions."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "widgets.HBox([widgets.Label(value=\"The $m$ in $E=mc^2$:\"), widgets.FloatSlider()])\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### HTML"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "widgets.HTML(\n",
+ " value=\"Hello World\",\n",
+ " placeholder='Some HTML',\n",
+ " description='Some HTML',\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### HTML Math"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "widgets.HTMLMath(\n",
+ " value=r\"Some math and HTML: \\(x^2\\) and $$\\frac{x+1}{x-1}$$\",\n",
+ " placeholder='Some HTML',\n",
+ " description='Some HTML',\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Image"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "file = open(\"images/WidgetArch.png\", \"rb\")\n",
+ "image = file.read()\n",
+ "widgets.Image(\n",
+ " value=image,\n",
+ " format='png',\n",
+ " width=300,\n",
+ " height=400,\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "source": [
+ "## Button"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "widgets.Button(\n",
+ " description='Click me',\n",
+ " disabled=False,\n",
+ " button_style='', # 'success', 'info', 'warning', 'danger' or ''\n",
+ " tooltip='Click me',\n",
+ " icon='check'\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Conclusion\n",
+ "\n",
+ "Even more widgets are described in the notebook **Widget List - Advanced**. Use these as a future reference for yourself!"
+ ]
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "Python 3",
+ "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.6.2"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 1
+}
diff --git a/Complete-Python-3-Bootcamp-master/16-Bonus Material - Introduction to GUIs/04-Widget Events.ipynb b/Complete-Python-3-Bootcamp-master/16-Bonus Material - Introduction to GUIs/04-Widget Events.ipynb
new file mode 100644
index 0000000..4aa124a
--- /dev/null
+++ b/Complete-Python-3-Bootcamp-master/16-Bonus Material - Introduction to GUIs/04-Widget Events.ipynb
@@ -0,0 +1,419 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "source": [
+ "# Widget Events\n",
+ "\n",
+ "In this lecture we will discuss widget events, such as button clicks!"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Special events"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The `Button` is not used to represent a data type. Instead the button widget is used to handle mouse clicks. The `on_click` method of the `Button` can be used to register a function to be called when the button is clicked. The docstring of the `on_click` can be seen below."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import ipywidgets as widgets\n",
+ "\n",
+ "print(widgets.Button.on_click.__doc__)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "source": [
+ "### Example #1 - on_click"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Since button clicks are stateless, they are transmitted from the front-end to the back-end using custom messages. By using the `on_click` method, a button that prints a message when it has been clicked is shown below."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from IPython.display import display\n",
+ "button = widgets.Button(description=\"Click Me!\")\n",
+ "display(button)\n",
+ "\n",
+ "def on_button_clicked(b):\n",
+ " print(\"Button clicked.\")\n",
+ "\n",
+ "button.on_click(on_button_clicked)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "source": [
+ "### Example #2 - on_submit"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The `Text` widget also has a special `on_submit` event. The `on_submit` event fires when the user hits enter."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "text = widgets.Text()\n",
+ "display(text)\n",
+ "\n",
+ "def handle_submit(sender):\n",
+ " print(text.value)\n",
+ "\n",
+ "text.on_submit(handle_submit)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "source": [
+ "## Traitlet events\n",
+ "Widget properties are IPython traitlets and traitlets are eventful. To handle changes, the `observe` method of the widget can be used to register a callback. The docstring for `observe` can be seen below."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "print(widgets.Widget.observe.__doc__)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "source": [
+ "### Signatures\n",
+ "Mentioned in the docstring, the callback registered must have the signature `handler(change)` where `change` is a dictionary holding the information about the change.\n",
+ "\n",
+ "Using this method, an example of how to output an `IntSlider`’s value as it is changed can be seen below.\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "int_range = widgets.IntSlider()\n",
+ "display(int_range)\n",
+ "\n",
+ "def on_value_change(change):\n",
+ " print(change['new'])\n",
+ "\n",
+ "int_range.observe(on_value_change, names='value')"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Linking Widgets\n",
+ "Often, you may want to simply link widget attributes together. Synchronization of attributes can be done in a simpler way than by using bare traitlets events."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Linking traitlets attributes in the kernel¶\n",
+ "\n",
+ "The first method is to use the `link` and `dlink` functions from the `traitlets` module. This only works if we are interacting with a live kernel.\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import traitlets"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Create Caption\n",
+ "caption = widgets.Label(value = 'The values of slider1 and slider2 are synchronized')\n",
+ "\n",
+ "# Create IntSliders\n",
+ "slider1 = widgets.IntSlider(description='Slider 1')\n",
+ "slider2 = widgets.IntSlider(description='Slider 2')\n",
+ "\n",
+ "# Use trailets to link\n",
+ "l = traitlets.link((slider1, 'value'), (slider2, 'value'))\n",
+ "\n",
+ "# Display!\n",
+ "display(caption, slider1, slider2)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Create Caption\n",
+ "caption = widgets.Label(value='Changes in source values are reflected in target1')\n",
+ "\n",
+ "# Create Sliders\n",
+ "source = widgets.IntSlider(description='Source')\n",
+ "target1 = widgets.IntSlider(description='Target 1')\n",
+ "\n",
+ "# Use dlink\n",
+ "dl = traitlets.dlink((source, 'value'), (target1, 'value'))\n",
+ "display(caption, source, target1)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Function `traitlets.link` and `traitlets.dlink` return a `Link` or `DLink` object. The link can be broken by calling the `unlink` method."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# May get an error depending on order of cells being run!\n",
+ "l.unlink()\n",
+ "dl.unlink()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Registering callbacks to trait changes in the kernel\n",
+ "\n",
+ "Since attributes of widgets on the Python side are traitlets, you can register handlers to the change events whenever the model gets updates from the front-end.\n",
+ "\n",
+ "The handler passed to observe will be called with one change argument. The change object holds at least a `type` key and a `name` key, corresponding respectively to the type of notification and the name of the attribute that triggered the notification.\n",
+ "\n",
+ "Other keys may be passed depending on the value of `type`. In the case where type is `change`, we also have the following keys:\n",
+ "* `owner` : the HasTraits instance\n",
+ "* `old` : the old value of the modified trait attribute\n",
+ "* `new` : the new value of the modified trait attribute\n",
+ "* `name` : the name of the modified trait attribute.\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "caption = widgets.Label(value='The values of range1 and range2 are synchronized')\n",
+ "slider = widgets.IntSlider(min=-5, max=5, value=1, description='Slider')\n",
+ "\n",
+ "def handle_slider_change(change):\n",
+ " caption.value = 'The slider value is ' + (\n",
+ " 'negative' if change.new < 0 else 'nonnegative'\n",
+ " )\n",
+ "\n",
+ "slider.observe(handle_slider_change, names='value')\n",
+ "\n",
+ "display(caption, slider)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Linking widgets attributes from the client side\n",
+ "\n",
+ "When synchronizing traitlets attributes, you may experience a lag because of the latency due to the roundtrip to the server side. You can also directly link widget attributes in the browser using the link widgets, in either a unidirectional or a bidirectional fashion.\n",
+ "\n",
+ "Javascript links persist when embedding widgets in html web pages without a kernel."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# NO LAG VERSION\n",
+ "caption = widgets.Label(value = 'The values of range1 and range2 are synchronized')\n",
+ "\n",
+ "range1 = widgets.IntSlider(description='Range 1')\n",
+ "range2 = widgets.IntSlider(description='Range 2')\n",
+ "\n",
+ "l = widgets.jslink((range1, 'value'), (range2, 'value'))\n",
+ "display(caption, range1, range2)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# NO LAG VERSION\n",
+ "caption = widgets.Label(value = 'Changes in source_range values are reflected in target_range')\n",
+ "\n",
+ "source_range = widgets.IntSlider(description='Source range')\n",
+ "target_range = widgets.IntSlider(description='Target range')\n",
+ "\n",
+ "dl = widgets.jsdlink((source_range, 'value'), (target_range, 'value'))\n",
+ "display(caption, source_range, target_range)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Function `widgets.jslink` returns a `Link` widget. The link can be broken by calling the `unlink` method."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "l.unlink()\n",
+ "dl.unlink()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### The difference between linking in the kernel and linking in the client\n",
+ "\n",
+ "Linking in the kernel means linking via python. If two sliders are linked in the kernel, when one slider is changed the browser sends a message to the kernel (python in this case) updating the changed slider, the link widget in the kernel then propagates the change to the other slider object in the kernel, and then the other slider’s kernel object sends a message to the browser to update the other slider’s views in the browser. If the kernel is not running (as in a static web page), then the controls will not be linked.\n",
+ "\n",
+ "Linking using jslink (i.e., on the browser side) means contructing the link in Javascript. When one slider is changed, Javascript running in the browser changes the value of the other slider in the browser, without needing to communicate with the kernel at all. If the sliders are attached to kernel objects, each slider will update their kernel-side objects independently.\n",
+ "\n",
+ "To see the difference between the two, go to the [ipywidgets documentation](http://ipywidgets.readthedocs.io/en/latest/examples/Widget%20Events.html) and try out the sliders near the bottom. The ones linked in the kernel with `link` and `dlink` are no longer linked, but the ones linked in the browser with `jslink` and `jsdlink` are still linked.\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Continuous updates\n",
+ "\n",
+ "Some widgets offer a choice with their `continuous_update` attribute between continually updating values or only updating values when a user submits the value (for example, by pressing Enter or navigating away from the control). In the next example, we see the “Delayed” controls only transmit their value after the user finishes dragging the slider or submitting the textbox. The “Continuous” controls continually transmit their values as they are changed. Try typing a two-digit number into each of the text boxes, or dragging each of the sliders, to see the difference.\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import traitlets\n",
+ "a = widgets.IntSlider(description=\"Delayed\", continuous_update=False)\n",
+ "b = widgets.IntText(description=\"Delayed\", continuous_update=False)\n",
+ "c = widgets.IntSlider(description=\"Continuous\", continuous_update=True)\n",
+ "d = widgets.IntText(description=\"Continuous\", continuous_update=True)\n",
+ "\n",
+ "traitlets.link((a, 'value'), (b, 'value'))\n",
+ "traitlets.link((a, 'value'), (c, 'value'))\n",
+ "traitlets.link((a, 'value'), (d, 'value'))\n",
+ "widgets.VBox([a,b,c,d])"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Sliders, `Text`, and `Textarea` controls default to `continuous_update=True`. `IntText` and other text boxes for entering integer or float numbers default to `continuous_update=False` (since often you’ll want to type an entire number before submitting the value by pressing enter or navigating out of the box)."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Conclusion\n",
+ "You should now feel comfortable linking Widget events!"
+ ]
+ }
+ ],
+ "metadata": {
+ "cell_tags": [
+ [
+ "",
+ null
+ ]
+ ],
+ "kernelspec": {
+ "display_name": "Python 3",
+ "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.6.2"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 1
+}
diff --git a/Complete-Python-3-Bootcamp-master/16-Bonus Material - Introduction to GUIs/05-Widget Styling.ipynb b/Complete-Python-3-Bootcamp-master/16-Bonus Material - Introduction to GUIs/05-Widget Styling.ipynb
new file mode 100644
index 0000000..7886628
--- /dev/null
+++ b/Complete-Python-3-Bootcamp-master/16-Bonus Material - Introduction to GUIs/05-Widget Styling.ipynb
@@ -0,0 +1,349 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Widget Styling\n",
+ "\n",
+ "In this lecture we will learn about the various ways to style widgets!"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## `style` vs. `layout`\n",
+ "\n",
+ "There are two ways to change the appearance of widgets in the browser. The first is through the `layout` attribute which exposes layout-related CSS properties for the top-level DOM element of widgets, such as margins and positioning. The second is through the `style` attribute which exposes non-layout related attributes like button color and font weight. While `layout` is general to all widgets and containers of widgets, `style` offers tools specific to each type of widget.\n",
+ "\n",
+ "Thorough understanding of all that `layout` has to offer requires knowledge of front-end web development, including HTML and CSS. This section provides a brief overview of things that can be adjusted using `layout`. However, the full set of tools are provided in the separate notebook **Advanced Widget Styling with Layout**.\n",
+ "\n",
+ "To learn more about web development, including HTML and CSS, check out the course [\n",
+ "Python and Django Full Stack Web Developer Bootcamp](https://www.udemy.com/python-and-django-full-stack-web-developer-bootcamp/)\n",
+ "\n",
+ "Basic styling is more intuitive as it relates directly to each type of widget. Here we provide a set of helpful examples of the `style` attribute.\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## The `layout` attribute\n",
+ "Jupyter interactive widgets have a `layout` attribute exposing a number of CSS properties that impact how widgets are laid out. These properties map to the values of the CSS properties of the same name (underscores being replaced with dashes), applied to the top DOM elements of the corresponding widget.\n",
+ "\n",
+ "#### Sizes\n",
+ "* `height`\n",
+ "* `width`\n",
+ "* `max_height`\n",
+ "* `max_width`\n",
+ "* `min_height`\n",
+ "* `min_width`\n",
+ "\n",
+ "#### Display\n",
+ "* `visibility`\n",
+ "* `display`\n",
+ "* `overflow`\n",
+ "* `overflow_x`\n",
+ "* `overflow_y`\n",
+ "\n",
+ "#### Box model\n",
+ "* `border`\n",
+ "* `margin`\n",
+ "* `padding`\n",
+ "\n",
+ "#### Positioning\n",
+ "* `top`\n",
+ "* `left`\n",
+ "* `bottom`\n",
+ "* `right`\n",
+ "\n",
+ "#### Flexbox\n",
+ "* `order`\n",
+ "* `flex_flow`\n",
+ "* `align_items`\n",
+ "* `flex`\n",
+ "* `align_self`\n",
+ "* `align_content`\n",
+ "* `justify_content`"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## A quick example of `layout`\n",
+ "\n",
+ "We've already seen what a slider looks like without any layout adjustments:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import ipywidgets as widgets\n",
+ "from IPython.display import display\n",
+ "\n",
+ "w = widgets.IntSlider()\n",
+ "display(w)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Let's say we wanted to change two of the properties of this widget: `margin` and `height`. We want to center the slider in the output area and increase its height. This can be done by adding `layout` attributes to **w**"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "w.layout.margin = 'auto'\n",
+ "w.layout.height = '75px'"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Notice that the slider changed positions on the page immediately!\n",
+ "\n",
+ "\n",
+ "Layout settings can be passed from one widget to another widget of the same type. Let's first create a new IntSlider:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "x = widgets.IntSlider(value=15,description='New slider')\n",
+ "display(x)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Now assign **w**'s layout settings to **x**:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "x.layout = w.layout"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "That's it! For a complete set of instructions on using `layout`, visit the **Advanced Widget Styling - Layout** notebook."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Predefined styles\n",
+ "\n",
+ "Before we investigate the `style` attribute, it should be noted that many widgets offer a list of pre-defined styles that can be passed as arguments during creation.\n",
+ "\n",
+ "For example, the `Button` widget has a `button_style` attribute that may take 5 different values:\n",
+ "\n",
+ "* `'primary'`\n",
+ "* `'success'`\n",
+ "* `'info'`\n",
+ "* `'warning'`\n",
+ "* `'danger'`\n",
+ "\n",
+ "besides the default empty string `''`."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import ipywidgets as widgets\n",
+ "\n",
+ "widgets.Button(description='Ordinary Button', button_style='')"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "widgets.Button(description='Danger Button', button_style='danger')"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## The `style` attribute"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "While the `layout` attribute only exposes layout-related CSS properties for the top-level DOM element of widgets, the\n",
+ "`style` attribute is used to expose non-layout related styling attributes of widgets.\n",
+ "\n",
+ "However, the properties of the `style` atribute are specific to each widget type."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "b1 = widgets.Button(description='Custom color')\n",
+ "b1.style.button_color = 'lightgreen'\n",
+ "b1"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "You can get a list of the style attributes for a widget with the `keys` property."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "b1.style.keys"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Note that `widgets.Button().style.keys` also works."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Just like the `layout` attribute, widget styles can be assigned to other widgets."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "b2 = widgets.Button()\n",
+ "b2.style = b1.style\n",
+ "b2"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Note that only the style was picked up by **b2**, not any other parameters like `description`."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Widget styling attributes are specific to each widget type."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "s1 = widgets.IntSlider(description='Blue handle')\n",
+ "s1.style.handle_color = 'lightblue'\n",
+ "s1"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Widget style traits\n",
+ "\n",
+ "These are traits that belong to some of the more common widgets:\n",
+ "\n",
+ "#### Button\n",
+ "\n",
+ "- `button_color`\n",
+ "- `font_weight`\n",
+ "\n",
+ "#### IntSlider, FloatSlider, IntRangeSlider, FloatRangeSlider\n",
+ "\n",
+ "- `description_width`\n",
+ "- `handle_color`\n",
+ "\n",
+ "#### IntProgress, FloatProgress\n",
+ "\n",
+ "- `bar_color`\n",
+ "- `description_width`\n",
+ "\n",
+ "Most others such as `ToggleButton`, `Checkbox`, `Dropdown`, `RadioButtons`, `Select` and `Text` only have `description_width` as an adjustable trait."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Conclusion\n",
+ "\n",
+ "You should now have an understanding of how to style widgets!"
+ ]
+ }
+ ],
+ "metadata": {
+ "cell_tags": [
+ [
+ "",
+ null
+ ]
+ ],
+ "kernelspec": {
+ "display_name": "Python 3",
+ "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.6.2"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 1
+}
diff --git a/Complete-Python-3-Bootcamp-master/16-Bonus Material - Introduction to GUIs/06-Custom Widget.ipynb b/Complete-Python-3-Bootcamp-master/16-Bonus Material - Introduction to GUIs/06-Custom Widget.ipynb
new file mode 100644
index 0000000..964d205
--- /dev/null
+++ b/Complete-Python-3-Bootcamp-master/16-Bonus Material - Introduction to GUIs/06-Custom Widget.ipynb
@@ -0,0 +1,368 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Custom Widget\n",
+ "## Exploring the Lorenz System of Differential Equations"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "In this Notebook we explore the Lorenz system of differential equations:\n",
+ "\n",
+ "$$\n",
+ "\\begin{aligned}\n",
+ "\\dot{x} & = \\sigma(y-x) \\\\\n",
+ "\\dot{y} & = \\rho x - y - xz \\\\\n",
+ "\\dot{z} & = -\\beta z + xy\n",
+ "\\end{aligned}\n",
+ "$$\n",
+ "\n",
+ "This is one of the classic systems in non-linear differential equations. It exhibits a range of different behaviors as the parameters ($\\sigma$, $\\beta$, $\\rho$) are varied."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Imports"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "First, we import the needed things from IPython, [NumPy](http://www.numpy.org/), [Matplotlib](http://matplotlib.org/index.html) and [SciPy](http://www.scipy.org/). Check out the class [Python for Data Science and Machine Learning Bootcamp](https://www.udemy.com/python-for-data-science-and-machine-learning-bootcamp/) if you're interested in learning more about this part of Python!"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "%matplotlib inline"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from ipywidgets import interact, interactive\n",
+ "from IPython.display import clear_output, display, HTML"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import numpy as np\n",
+ "from scipy import integrate\n",
+ "\n",
+ "from matplotlib import pyplot as plt\n",
+ "from mpl_toolkits.mplot3d import Axes3D\n",
+ "from matplotlib.colors import cnames\n",
+ "from matplotlib import animation"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Computing the trajectories and plotting the result"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "We define a function that can integrate the differential equations numerically and then plot the solutions. This function has arguments that control the parameters of the differential equation ($\\sigma$, $\\beta$, $\\rho$), the numerical integration (`N`, `max_time`) and the visualization (`angle`)."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def solve_lorenz(N=10, angle=0.0, max_time=4.0, sigma=10.0, beta=8./3, rho=28.0):\n",
+ "\n",
+ " fig = plt.figure();\n",
+ " ax = fig.add_axes([0, 0, 1, 1], projection='3d');\n",
+ " ax.axis('off')\n",
+ "\n",
+ " # prepare the axes limits\n",
+ " ax.set_xlim((-25, 25))\n",
+ " ax.set_ylim((-35, 35))\n",
+ " ax.set_zlim((5, 55))\n",
+ " \n",
+ " def lorenz_deriv(x_y_z, t0, sigma=sigma, beta=beta, rho=rho):\n",
+ " \"\"\"Compute the time-derivative of a Lorenz system.\"\"\"\n",
+ " x, y, z = x_y_z\n",
+ " return [sigma * (y - x), x * (rho - z) - y, x * y - beta * z]\n",
+ "\n",
+ " # Choose random starting points, uniformly distributed from -15 to 15\n",
+ " np.random.seed(1)\n",
+ " x0 = -15 + 30 * np.random.random((N, 3))\n",
+ "\n",
+ " # Solve for the trajectories\n",
+ " t = np.linspace(0, max_time, int(250*max_time))\n",
+ " x_t = np.asarray([integrate.odeint(lorenz_deriv, x0i, t)\n",
+ " for x0i in x0])\n",
+ " \n",
+ " # choose a different color for each trajectory\n",
+ " colors = plt.cm.jet(np.linspace(0, 1, N));\n",
+ "\n",
+ " for i in range(N):\n",
+ " x, y, z = x_t[i,:,:].T\n",
+ " lines = ax.plot(x, y, z, '-', c=colors[i])\n",
+ " _ = plt.setp(lines, linewidth=2);\n",
+ "\n",
+ " ax.view_init(30, angle)\n",
+ " _ = plt.show();\n",
+ "\n",
+ " return t, x_t"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Let's call the function once to view the solutions. For this set of parameters, we see the trajectories swirling around two points, called attractors. "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "image/png": "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\n",
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "t, x_t = solve_lorenz(angle=0, N=10)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Using IPython's `interactive` function, we can explore how the trajectories behave as we change the various parameters."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 6,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "application/vnd.jupyter.widget-view+json": {
+ "model_id": "8c6fcac683e54aebb53559937774065e",
+ "version_major": 2,
+ "version_minor": 0
+ },
+ "text/html": [
+ "Failed to display Jupyter Widget of type interactive.
\n",
+ "\n",
+ " If you're reading this message in the Jupyter Notebook or JupyterLab Notebook, it may mean\n",
+ " that the widgets JavaScript is still loading. If this message persists, it\n",
+ " likely means that the widgets JavaScript library is either not installed or\n",
+ " not enabled. See the Jupyter\n",
+ " Widgets Documentation for setup instructions.\n",
+ "
\n",
+ "\n",
+ " If you're reading this message in another frontend (for example, a static\n",
+ " rendering on GitHub or NBViewer),\n",
+ " it may mean that your frontend doesn't currently support widgets.\n",
+ "
\n"
+ ],
+ "text/plain": [
+ "interactive(children=(IntSlider(value=10, description='N', max=50), FloatSlider(value=0.0, description='angle', max=360.0), FloatSlider(value=4.0, description='max_time', max=12.0, min=-4.0), FloatSlider(value=10.0, description='sigma', max=50.0), FloatSlider(value=2.6666666666666665, description='beta', max=8.0, min=-2.6666666666666665), FloatSlider(value=28.0, description='rho', max=50.0), Output()), _dom_classes=('widget-interact',))"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "w = interactive(solve_lorenz, angle=(0.,360.), N=(0,50), sigma=(0.0,50.0), rho=(0.0,50.0))\n",
+ "display(w);"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The object returned by `interactive` is a `Widget` object and it has attributes that contain the current result and arguments:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 7,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "t, x_t = w.result"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 8,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "{'N': 10,\n",
+ " 'angle': 0.0,\n",
+ " 'beta': 2.6666666666666665,\n",
+ " 'max_time': 4.0,\n",
+ " 'rho': 28.0,\n",
+ " 'sigma': 10.0}"
+ ]
+ },
+ "execution_count": 8,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "w.kwargs"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "After interacting with the system, we can take the result and perform further computations. In this case, we compute the average positions in $x$, $y$ and $z$."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 9,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "xyz_avg = x_t.mean(axis=1)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 10,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "(10, 3)"
+ ]
+ },
+ "execution_count": 10,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "xyz_avg.shape"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Creating histograms of the average positions (across different trajectories) show that on average the trajectories swirl about the attractors.\n",
+ "\n",
+ "*NOTE: These will look different from the lecture version if you adjusted any of the sliders in the* `interactive` *widget and changed the parameters.*"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 11,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "image/png": "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\n",
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "plt.hist(xyz_avg[:,0])\n",
+ "plt.title('Average $x(t)$');"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 12,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "image/png": "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\n",
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "plt.hist(xyz_avg[:,1])\n",
+ "plt.title('Average $y(t)$');"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Conclusion\n",
+ "\n",
+ "Hopefully you've enjoyed using widgets in the Jupyter Notebook system and have begun to explore the other GUI possibilities for Python!"
+ ]
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "Python 3",
+ "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.6.2"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 1
+}
diff --git a/Complete-Python-3-Bootcamp-master/16-Bonus Material - Introduction to GUIs/07-Advanced Widget List.ipynb b/Complete-Python-3-Bootcamp-master/16-Bonus Material - Introduction to GUIs/07-Advanced Widget List.ipynb
new file mode 100644
index 0000000..a4c3d1d
--- /dev/null
+++ b/Complete-Python-3-Bootcamp-master/16-Bonus Material - Introduction to GUIs/07-Advanced Widget List.ipynb
@@ -0,0 +1,382 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Advanced Widget List\n",
+ "\n",
+ "This notebook is an extension of **Widget List**, describing even more of the GUI widgets available!"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import ipywidgets as widgets"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "source": [
+ "### Output\n",
+ "The `Output` widget can capture and display stdout, stderr and [rich output generated by IPython](http://ipython.readthedocs.io/en/stable/api/generated/IPython.display.html#module-IPython.display). After the widget is created, direct output to it using a context manager."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "out = widgets.Output()\n",
+ "out"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "source": [
+ "You can print text to the output area as shown below."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "with out:\n",
+ " for i in range(10):\n",
+ " print(i, 'Hello world!')"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Rich material can also be directed to the output area. Anything which displays nicely in a Jupyter notebook will also display well in the `Output` widget."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from IPython.display import YouTubeVideo\n",
+ "with out:\n",
+ " display(YouTubeVideo('eWzY2nGfkXk'))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Play (Animation) widget\n",
+ "The `Play` widget is useful to perform animations by iterating on a sequence of integers with a certain speed. The value of the slider below is linked to the player.\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "play = widgets.Play(\n",
+ " # interval=10,\n",
+ " value=50,\n",
+ " min=0,\n",
+ " max=100,\n",
+ " step=1,\n",
+ " description=\"Press play\",\n",
+ " disabled=False\n",
+ ")\n",
+ "slider = widgets.IntSlider()\n",
+ "widgets.jslink((play, 'value'), (slider, 'value'))\n",
+ "widgets.HBox([play, slider])"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Date picker\n",
+ "The date picker widget works in Chrome and IE Edge, but does not currently work in Firefox or Safari because they do not support the HTML date input field."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "widgets.DatePicker(\n",
+ " description='Pick a Date',\n",
+ " disabled=False\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Color picker"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "widgets.ColorPicker(\n",
+ " concise=False,\n",
+ " description='Pick a color',\n",
+ " value='blue',\n",
+ " disabled=False\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "source": [
+ "### Controller\n",
+ "The `Controller` allows a game controller to be used as an input device."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "widgets.Controller(\n",
+ " index=0,\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Container/Layout widgets\n",
+ "\n",
+ "These widgets are used to hold other widgets, called children. Each has a `children` property that may be set either when the widget is created or later.\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "source": [
+ "### Box"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "items = [widgets.Label(str(i)) for i in range(4)]\n",
+ "widgets.Box(items)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "source": [
+ "### HBox"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "items = [widgets.Label(str(i)) for i in range(4)]\n",
+ "widgets.HBox(items)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### VBox"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "items = [widgets.Label(str(i)) for i in range(4)]\n",
+ "left_box = widgets.VBox([items[0], items[1]])\n",
+ "right_box = widgets.VBox([items[2], items[3]])\n",
+ "widgets.HBox([left_box, right_box])"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "source": [
+ "### Accordion"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "accordion = widgets.Accordion(children=[widgets.IntSlider(), widgets.Text()])\n",
+ "accordion.set_title(0, 'Slider')\n",
+ "accordion.set_title(1, 'Text')\n",
+ "accordion"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Tabs\n",
+ "\n",
+ "In this example the children are set after the tab is created. Titles for the tabes are set in the same way they are for `Accordion`.\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "tab_contents = ['P0', 'P1', 'P2', 'P3', 'P4']\n",
+ "children = [widgets.Text(description=name) for name in tab_contents]\n",
+ "tab = widgets.Tab()\n",
+ "tab.children = children\n",
+ "for i in range(len(children)):\n",
+ " tab.set_title(i, str(i))\n",
+ "tab"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "source": [
+ "### Accordion and Tab use `selected_index`, not value\n",
+ "\n",
+ "Unlike the rest of the widgets discussed earlier, the container widgets `Accordion` and `Tab` update their `selected_index` attribute when the user changes which accordion or tab is selected. That means that you can both see what the user is doing *and* programmatically set what the user sees by setting the value of `selected_index`.\n",
+ "\n",
+ "Setting `selected_index = None` closes all of the accordions or deselects all tabs.\n",
+ "\n",
+ "In the cells below try displaying or setting the `selected_index` of the `tab` and/or `accordion`.\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "tab.selected_index = 3"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "accordion.selected_index = None"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Nesting tabs and accordions\n",
+ "\n",
+ "Tabs and accordions can be nested as deeply as you want. If you have a few minutes, try nesting a few accordions or putting an accordion inside a tab or a tab inside an accordion.\n",
+ "\n",
+ "The example below makes a couple of tabs with an accordion children in one of them"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "tab_nest = widgets.Tab()\n",
+ "tab_nest.children = [accordion, accordion]\n",
+ "tab_nest.set_title(0, 'An accordion')\n",
+ "tab_nest.set_title(1, 'Copy of the accordion')\n",
+ "tab_nest"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Conclusion\n",
+ "\n",
+ "Use this as a further reference for yourself!"
+ ]
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "Python 3",
+ "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.6.2"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 1
+}
diff --git a/Complete-Python-3-Bootcamp-master/16-Bonus Material - Introduction to GUIs/08-Advanced Widget Styling with Layout.ipynb b/Complete-Python-3-Bootcamp-master/16-Bonus Material - Introduction to GUIs/08-Advanced Widget Styling with Layout.ipynb
new file mode 100644
index 0000000..4d61edc
--- /dev/null
+++ b/Complete-Python-3-Bootcamp-master/16-Bonus Material - Introduction to GUIs/08-Advanced Widget Styling with Layout.ipynb
@@ -0,0 +1,113 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Advanced Widget Styling with Layout\n",
+ "\n",
+ "This notebook expands on the **Widget Styling** lecture by describing the various HTML and CSS adjustments that can be made through the `layout` attribute."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## The `layout` attribute\n",
+ "Jupyter interactive widgets have a `layout` attribute exposing a number of CSS properties that impact how widgets are laid out.\n",
+ "\n",
+ "### Exposed CSS properties\n",
+ "The following properties map to the values of the CSS properties of the same name (underscores being replaced with dashes), applied to the top DOM elements of the corresponding widget.
\n",
+ "\n",
+ "#### Sizes\n",
+ "* `height`\n",
+ "* `width`\n",
+ "* `max_height`\n",
+ "* `max_width`\n",
+ "* `min_height`\n",
+ "* `min_width`\n",
+ "\n",
+ "#### Display\n",
+ "* `visibility`\n",
+ "* `display`\n",
+ "* `overflow`\n",
+ "* `overflow_x`\n",
+ "* `overflow_y`\n",
+ "\n",
+ "#### Box model\n",
+ "* `border`\n",
+ "* `margin`\n",
+ "* `padding`\n",
+ "\n",
+ "#### Positioning\n",
+ "* `top`\n",
+ "* `left`\n",
+ "* `bottom`\n",
+ "* `right`\n",
+ "\n",
+ "#### Flexbox\n",
+ "* `order`\n",
+ "* `flex_flow`\n",
+ "* `align_items`\n",
+ "* `flex`\n",
+ "* `align_self`\n",
+ "* `align_content`\n",
+ "* `justify_content`\n",
+ "\n",
+ "### Shorthand CSS properties\n",
+ "\n",
+ "You may have noticed that certain CSS properties such as `margin-[top/right/bottom/left]` seem to be missing. The same holds for `padding-[top/right/bottom/left]` etc.\n",
+ "\n",
+ "In fact, you can atomically specify `[top/right/bottom/left]` margins via the `margin` attribute alone by passing the string `'100px 150px 100px 80px'` for a respectively `top`, `right`, `bottom` and `left` margins of `100`, `150`, `100` and `80` pixels.\n",
+ "\n",
+ "Similarly, the `flex` attribute can hold values for `flex-grow`, `flex-shrink` and `flex-basis`. The `border` attribute is a shorthand property for `border-width`, `border-style (required)`, and `border-color`."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 19,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import ipywidgets as widgets\n",
+ "from IPython.display import display"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Conclusion\n",
+ "\n",
+ "You should now have an understanding of how to style widgets!"
+ ]
+ }
+ ],
+ "metadata": {
+ "cell_tags": [
+ [
+ "",
+ null
+ ]
+ ],
+ "kernelspec": {
+ "display_name": "Python 3",
+ "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.6.2"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 1
+}
diff --git a/Complete-Python-3-Bootcamp-master/17-Parallel Processing/.ipynb_checkpoints/01-Multithreading and Multiprocessing-checkpoint.ipynb b/Complete-Python-3-Bootcamp-master/17-Parallel Processing/.ipynb_checkpoints/01-Multithreading and Multiprocessing-checkpoint.ipynb
new file mode 100644
index 0000000..b7550a4
--- /dev/null
+++ b/Complete-Python-3-Bootcamp-master/17-Parallel Processing/.ipynb_checkpoints/01-Multithreading and Multiprocessing-checkpoint.ipynb
@@ -0,0 +1,552 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Multithreading and Multiprocessing\n",
+ "\n",
+ "Recall the phrase \"many hands make light work\". This is as true in programming as anywhere else.\n",
+ "\n",
+ "What if you could engineer your Python program to do four things at once? What would normally take an hour could (almost) take one fourth the time.\\*\n",
+ "\n",
+ "This is the idea behind parallel processing, or the ability to set up and run multiple tasks concurrently.\n",
+ "\n",
+ "\n",
+ "
\\* *We say almost, because you do have to take time setting up four processors, and it may take time to pass information between them.*"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Threading vs. Processing\n",
+ "\n",
+ "A good illustration of threading vs. processing would be to download an image file and turn it into a thumbnail.\n",
+ "\n",
+ "The first part, communicating with an outside source to download a file, involves a thread. Once the file is obtained, the work of converting it involves a process. Essentially, two factors determine how long this will take; the input/output speed of the network communication, or I/O, and the available processor, or CPU.\n",
+ "\n",
+ "#### I/O-intensive processes improved with multithreading:\n",
+ "* webscraping\n",
+ "* reading and writing to files\n",
+ "* sharing data between programs\n",
+ "* network communications\n",
+ "\n",
+ "\n",
+ "#### CPU-intensive processes improved with multiprocessing:\n",
+ "* computations\n",
+ "* text formatting\n",
+ "* image rescaling\n",
+ "* data analysis"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Multithreading Example: Webscraping\n",
+ "\n",
+ "Historically, the programming knowledge required to set up multithreading was beyond the scope of this course, as it involved a good understanding of Python's Global Interpreter Lock (the GIL prevents multiple threads from running the same Python code at once). Also, you had to set up special classes that behave like Producers to divvy up the work, Consumers (aka \"workers\") to perform the work, and a Queue to hold tasks and provide communcations. And that was just the beginning.\n",
+ "\n",
+ "Fortunately, we've already learned one of the most valuable tools we'll need – the `map()` function. When we apply it using two standard libraries, *multiprocessing* and *multiprocessing.dummy*, setting up parallel processes and threads becomes fairly straightforward.\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Here's a classic multithreading example provided by [IBM](http://www.ibm.com/developerworks/aix/library/au-threadingpython/) and adapted by [Chris Kiehl](http://chriskiehl.com/article/parallelism-in-one-line/) where you divide the task of retrieving web pages across multiple threads:\n",
+ "\n",
+ "\n",
+ " import time \n",
+ " import threading \n",
+ " import Queue \n",
+ " import urllib2 \n",
+ "\n",
+ " class Consumer(threading.Thread): \n",
+ " def __init__(self, queue): \n",
+ " threading.Thread.__init__(self)\n",
+ " self._queue = queue \n",
+ "\n",
+ " def run(self):\n",
+ " while True: \n",
+ " content = self._queue.get() \n",
+ " if isinstance(content, str) and content == 'quit':\n",
+ " break\n",
+ " response = urllib2.urlopen(content)\n",
+ " print 'Thanks!'\n",
+ "\n",
+ "\n",
+ " def Producer():\n",
+ " urls = [\n",
+ " 'http://www.python.org', 'http://www.yahoo.com'\n",
+ " 'http://www.scala.org', 'http://www.google.com'\n",
+ " # etc.. \n",
+ " ]\n",
+ " queue = Queue.Queue()\n",
+ " worker_threads = build_worker_pool(queue, 4)\n",
+ " start_time = time.time()\n",
+ "\n",
+ " # Add the urls to process\n",
+ " for url in urls: \n",
+ " queue.put(url) \n",
+ " # Add the poison pill\n",
+ " for worker in worker_threads:\n",
+ " queue.put('quit')\n",
+ " for worker in worker_threads:\n",
+ " worker.join()\n",
+ "\n",
+ " print 'Done! Time taken: {}'.format(time.time() - start_time)\n",
+ "\n",
+ " def build_worker_pool(queue, size):\n",
+ " workers = []\n",
+ " for _ in range(size):\n",
+ " worker = Consumer(queue)\n",
+ " worker.start() \n",
+ " workers.append(worker)\n",
+ " return workers\n",
+ "\n",
+ " if __name__ == '__main__':\n",
+ " Producer()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Using the multithreading library provided by the *multiprocessing.dummy* module and `map()` all of this becomes:\n",
+ "\n",
+ " import urllib2\n",
+ " from multiprocessing.dummy import Pool as ThreadPool\n",
+ " \n",
+ " pool = ThreadPool(4) # choose a number of workers\n",
+ " \n",
+ " urls = [\n",
+ " 'http://www.python.org', 'http://www.yahoo.com'\n",
+ " 'http://www.scala.org', 'http://www.google.com'\n",
+ " # etc.. \n",
+ " ]\n",
+ " \n",
+ " results = pool.map(urllib2.urlopen, urls)\n",
+ " pool.close() \n",
+ " pool.join()\n",
+ " \n",
+ "In the above code, the *multiprocessing.dummy* module provides the parallel threads, and `map(urllib2.urlopen, urls)` assigns the labor!"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Multiprocessing Example: Monte Carlo\n",
+ "\n",
+ "Let's code out an example to see how the parts fit together. We can time our results using the *timeit* module to measure any performance gains. Our task is to apply the Monte Carlo Method to estimate the value of Pi."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Monte Carle Method and Estimating Pi\n",
+ "\n",
+ "If you draw a circle of radius 1 (a unit circle) and enclose it in a square, the areas of the two shapes are given as\n",
+ "\n",
+ "\n",
+ " Area Formulas\n",
+ " | circle | $$πr^2$$ |
\n",
+ " | square | $$4 r^2$$ |
\n",
+ "
\n",
+ "\n",
+ "\n",
+ "Therefore, the ratio of the volume of the circle to the volume of the square is $$\\frac{π}{4}$$\n",
+ "\n",
+ "The Monte Carlo Method plots a series of random points inside the square. By comparing the number that fall within the circle to those that fall outside, with a large enough sample we should have a good approximation of Pi. You can see a good demonstration of this [here](https://academo.org/demos/estimating-pi-monte-carlo/) (Hit the **Animate** button on the page).\n",
+ "\n",
+ "For a given number of points *n*, we have $$π = \\frac{4 \\cdot points\\ inside\\ circle}{total\\ points\\ n}$$\n",
+ "\n",
+ "To set up our multiprocessing program, we first derive a function for finding Pi that we can pass to `map()`:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from random import random # perform this import outside the function\n",
+ "\n",
+ "def find_pi(n):\n",
+ " \"\"\"\n",
+ " Function to estimate the value of Pi\n",
+ " \"\"\"\n",
+ " inside=0\n",
+ "\n",
+ " for i in range(0,n):\n",
+ " x=random()\n",
+ " y=random()\n",
+ " if (x*x+y*y)**(0.5)<=1: # if i falls inside the circle\n",
+ " inside+=1\n",
+ "\n",
+ " pi=4*inside/n\n",
+ " return pi"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Let's test `find_pi` on 5,000 points:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "3.1064"
+ ]
+ },
+ "execution_count": 2,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "find_pi(5000)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "This ran very quickly, but the results are not very accurate!\n",
+ "\n",
+ "Next we'll write a script that sets up a pool of workers, and lets us time the results against varying sized pools. We'll set up two arguments to represent *processes* and *total_iterations*. Inside the script, we'll break *total_iterations* down into the number of iterations passed to each process, by making a processes-sized list.
For example:\n",
+ "\n",
+ " total_iterations = 1000\n",
+ " processes = 5\n",
+ " iterations = [total_iterations//processes]*processes\n",
+ " iterations\n",
+ " # Output: [200, 200, 200, 200, 200]\n",
+ " \n",
+ "This list will be passed to our `map()` function along with `find_pi()`"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Writing test.py\n"
+ ]
+ }
+ ],
+ "source": [
+ "%%writefile test.py\n",
+ "from random import random\n",
+ "from multiprocessing import Pool\n",
+ "import timeit\n",
+ "\n",
+ "def find_pi(n):\n",
+ " \"\"\"\n",
+ " Function to estimate the value of Pi\n",
+ " \"\"\"\n",
+ " inside=0\n",
+ "\n",
+ " for i in range(0,n):\n",
+ " x=random()\n",
+ " y=random()\n",
+ " if (x*x+y*y)**(0.5)<=1: # if i falls inside the circle\n",
+ " inside+=1\n",
+ "\n",
+ " pi=4*inside/n\n",
+ " return pi\n",
+ "\n",
+ "if __name__ == '__main__':\n",
+ " N = 10**5 # total iterations\n",
+ " P = 5 # number of processes\n",
+ " \n",
+ " p = Pool(P)\n",
+ " print(timeit.timeit(lambda: print(f'{sum(p.map(find_pi, [N//P]*P))/P:0.7f}'), number=10))\n",
+ " p.close()\n",
+ " p.join()\n",
+ " print(f'{N} total iterations with {P} processes')"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "3.1466800\n",
+ "3.1364400\n",
+ "3.1470400\n",
+ "3.1370400\n",
+ "3.1256400\n",
+ "3.1398400\n",
+ "3.1395200\n",
+ "3.1363600\n",
+ "3.1437200\n",
+ "3.1334400\n",
+ "0.2370227286270967\n",
+ "100000 total iterations with 5 processes\n"
+ ]
+ }
+ ],
+ "source": [
+ "! python test.py"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Great! The above test took under a second on our computer.\n",
+ "\n",
+ "Now that we know our script works, let's increase the number of iterations, and compare two different pools. Sit back, this may take awhile!"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Overwriting test.py\n"
+ ]
+ }
+ ],
+ "source": [
+ "%%writefile test.py\n",
+ "from random import random\n",
+ "from multiprocessing import Pool\n",
+ "import timeit\n",
+ "\n",
+ "def find_pi(n):\n",
+ " \"\"\"\n",
+ " Function to estimate the value of Pi\n",
+ " \"\"\"\n",
+ " inside=0\n",
+ "\n",
+ " for i in range(0,n):\n",
+ " x=random()\n",
+ " y=random()\n",
+ " if (x*x+y*y)**(0.5)<=1: # if i falls inside the circle\n",
+ " inside+=1\n",
+ "\n",
+ " pi=4*inside/n\n",
+ " return pi\n",
+ "\n",
+ "if __name__ == '__main__':\n",
+ " N = 10**7 # total iterations\n",
+ " \n",
+ " P = 1 # number of processes\n",
+ " p = Pool(P)\n",
+ " print(timeit.timeit(lambda: print(f'{sum(p.map(find_pi, [N//P]*P))/P:0.7f}'), number=10))\n",
+ " p.close()\n",
+ " p.join()\n",
+ " print(f'{N} total iterations with {P} processes')\n",
+ " \n",
+ " P = 5 # number of processes\n",
+ " p = Pool(P)\n",
+ " print(timeit.timeit(lambda: print(f'{sum(p.map(find_pi, [N//P]*P))/P:0.7f}'), number=10))\n",
+ " p.close()\n",
+ " p.join()\n",
+ " print(f'{N} total iterations with {P} processes')"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 6,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "3.1420964\n",
+ "3.1417412\n",
+ "3.1411108\n",
+ "3.1408184\n",
+ "3.1414204\n",
+ "3.1417656\n",
+ "3.1408324\n",
+ "3.1418828\n",
+ "3.1420492\n",
+ "3.1412804\n",
+ "36.03526345242264\n",
+ "10000000 total iterations with 1 processes\n",
+ "3.1424524\n",
+ "3.1418376\n",
+ "3.1415292\n",
+ "3.1410344\n",
+ "3.1422376\n",
+ "3.1418736\n",
+ "3.1420540\n",
+ "3.1411452\n",
+ "3.1421652\n",
+ "3.1410672\n",
+ "17.300921846344366\n",
+ "10000000 total iterations with 5 processes\n",
+ "\n"
+ ]
+ }
+ ],
+ "source": [
+ "! python test.py"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Hopefully you saw that with 5 processes our script ran faster!"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## More is Better ...to a point.\n",
+ "\n",
+ "The gain in speed as you add more parallel processes tends to flatten out at some point. In any collection of tasks, there are going to be one or two that take longer than average, and no amount of added processing can speed them up. This is best described in [Amdahl's Law](https://en.wikipedia.org/wiki/Amdahl%27s_law)."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Advanced Script\n",
+ "\n",
+ "In the example below, we'll add a context manager to shrink these three lines\n",
+ "\n",
+ " p = Pool(P)\n",
+ " ...\n",
+ " p.close()\n",
+ " p.join()\n",
+ " \n",
+ "to one line:\n",
+ "\n",
+ " with Pool(P) as p:\n",
+ " \n",
+ "And we'll accept command line arguments using the *sys* module.\n",
+ " "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 7,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Writing test2.py\n"
+ ]
+ }
+ ],
+ "source": [
+ "%%writefile test2.py\n",
+ "from random import random\n",
+ "from multiprocessing import Pool\n",
+ "import timeit\n",
+ "import sys\n",
+ "\n",
+ "N = int(sys.argv[1]) # these arguments are passed in from the command line\n",
+ "P = int(sys.argv[2])\n",
+ "\n",
+ "def find_pi(n):\n",
+ " \"\"\"\n",
+ " Function to estimate the value of Pi\n",
+ " \"\"\"\n",
+ " inside=0\n",
+ "\n",
+ " for i in range(0,n):\n",
+ " x=random()\n",
+ " y=random()\n",
+ " if (x*x+y*y)**(0.5)<=1: # if i falls inside the circle\n",
+ " inside+=1\n",
+ "\n",
+ " pi=4*inside/n\n",
+ " return pi\n",
+ "\n",
+ "if __name__ == '__main__':\n",
+ " \n",
+ " with Pool(P) as p:\n",
+ " print(timeit.timeit(lambda: print(f'{sum(p.map(find_pi, [N//P]*P))/P:0.5f}'), number=10))\n",
+ " print(f'{N} total iterations with {P} processes')"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 8,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "3.14121\n",
+ "3.14145\n",
+ "3.14178\n",
+ "3.14194\n",
+ "3.14109\n",
+ "3.14201\n",
+ "3.14243\n",
+ "3.14150\n",
+ "3.14203\n",
+ "3.14116\n",
+ "16.871822701405073\n",
+ "10000000 total iterations with 500 processes\n"
+ ]
+ }
+ ],
+ "source": [
+ "! python test2.py 10000000 500"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Great! Now you should have a good understanding of multithreading and multiprocessing!"
+ ]
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "Python 3",
+ "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.6.2"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 2
+}
diff --git a/Complete-Python-3-Bootcamp-master/17-Parallel Processing/01-Multithreading and Multiprocessing.ipynb b/Complete-Python-3-Bootcamp-master/17-Parallel Processing/01-Multithreading and Multiprocessing.ipynb
new file mode 100644
index 0000000..b7550a4
--- /dev/null
+++ b/Complete-Python-3-Bootcamp-master/17-Parallel Processing/01-Multithreading and Multiprocessing.ipynb
@@ -0,0 +1,552 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Multithreading and Multiprocessing\n",
+ "\n",
+ "Recall the phrase \"many hands make light work\". This is as true in programming as anywhere else.\n",
+ "\n",
+ "What if you could engineer your Python program to do four things at once? What would normally take an hour could (almost) take one fourth the time.\\*\n",
+ "\n",
+ "This is the idea behind parallel processing, or the ability to set up and run multiple tasks concurrently.\n",
+ "\n",
+ "\n",
+ "
\\* *We say almost, because you do have to take time setting up four processors, and it may take time to pass information between them.*"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Threading vs. Processing\n",
+ "\n",
+ "A good illustration of threading vs. processing would be to download an image file and turn it into a thumbnail.\n",
+ "\n",
+ "The first part, communicating with an outside source to download a file, involves a thread. Once the file is obtained, the work of converting it involves a process. Essentially, two factors determine how long this will take; the input/output speed of the network communication, or I/O, and the available processor, or CPU.\n",
+ "\n",
+ "#### I/O-intensive processes improved with multithreading:\n",
+ "* webscraping\n",
+ "* reading and writing to files\n",
+ "* sharing data between programs\n",
+ "* network communications\n",
+ "\n",
+ "\n",
+ "#### CPU-intensive processes improved with multiprocessing:\n",
+ "* computations\n",
+ "* text formatting\n",
+ "* image rescaling\n",
+ "* data analysis"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Multithreading Example: Webscraping\n",
+ "\n",
+ "Historically, the programming knowledge required to set up multithreading was beyond the scope of this course, as it involved a good understanding of Python's Global Interpreter Lock (the GIL prevents multiple threads from running the same Python code at once). Also, you had to set up special classes that behave like Producers to divvy up the work, Consumers (aka \"workers\") to perform the work, and a Queue to hold tasks and provide communcations. And that was just the beginning.\n",
+ "\n",
+ "Fortunately, we've already learned one of the most valuable tools we'll need – the `map()` function. When we apply it using two standard libraries, *multiprocessing* and *multiprocessing.dummy*, setting up parallel processes and threads becomes fairly straightforward.\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Here's a classic multithreading example provided by [IBM](http://www.ibm.com/developerworks/aix/library/au-threadingpython/) and adapted by [Chris Kiehl](http://chriskiehl.com/article/parallelism-in-one-line/) where you divide the task of retrieving web pages across multiple threads:\n",
+ "\n",
+ "\n",
+ " import time \n",
+ " import threading \n",
+ " import Queue \n",
+ " import urllib2 \n",
+ "\n",
+ " class Consumer(threading.Thread): \n",
+ " def __init__(self, queue): \n",
+ " threading.Thread.__init__(self)\n",
+ " self._queue = queue \n",
+ "\n",
+ " def run(self):\n",
+ " while True: \n",
+ " content = self._queue.get() \n",
+ " if isinstance(content, str) and content == 'quit':\n",
+ " break\n",
+ " response = urllib2.urlopen(content)\n",
+ " print 'Thanks!'\n",
+ "\n",
+ "\n",
+ " def Producer():\n",
+ " urls = [\n",
+ " 'http://www.python.org', 'http://www.yahoo.com'\n",
+ " 'http://www.scala.org', 'http://www.google.com'\n",
+ " # etc.. \n",
+ " ]\n",
+ " queue = Queue.Queue()\n",
+ " worker_threads = build_worker_pool(queue, 4)\n",
+ " start_time = time.time()\n",
+ "\n",
+ " # Add the urls to process\n",
+ " for url in urls: \n",
+ " queue.put(url) \n",
+ " # Add the poison pill\n",
+ " for worker in worker_threads:\n",
+ " queue.put('quit')\n",
+ " for worker in worker_threads:\n",
+ " worker.join()\n",
+ "\n",
+ " print 'Done! Time taken: {}'.format(time.time() - start_time)\n",
+ "\n",
+ " def build_worker_pool(queue, size):\n",
+ " workers = []\n",
+ " for _ in range(size):\n",
+ " worker = Consumer(queue)\n",
+ " worker.start() \n",
+ " workers.append(worker)\n",
+ " return workers\n",
+ "\n",
+ " if __name__ == '__main__':\n",
+ " Producer()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Using the multithreading library provided by the *multiprocessing.dummy* module and `map()` all of this becomes:\n",
+ "\n",
+ " import urllib2\n",
+ " from multiprocessing.dummy import Pool as ThreadPool\n",
+ " \n",
+ " pool = ThreadPool(4) # choose a number of workers\n",
+ " \n",
+ " urls = [\n",
+ " 'http://www.python.org', 'http://www.yahoo.com'\n",
+ " 'http://www.scala.org', 'http://www.google.com'\n",
+ " # etc.. \n",
+ " ]\n",
+ " \n",
+ " results = pool.map(urllib2.urlopen, urls)\n",
+ " pool.close() \n",
+ " pool.join()\n",
+ " \n",
+ "In the above code, the *multiprocessing.dummy* module provides the parallel threads, and `map(urllib2.urlopen, urls)` assigns the labor!"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Multiprocessing Example: Monte Carlo\n",
+ "\n",
+ "Let's code out an example to see how the parts fit together. We can time our results using the *timeit* module to measure any performance gains. Our task is to apply the Monte Carlo Method to estimate the value of Pi."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Monte Carle Method and Estimating Pi\n",
+ "\n",
+ "If you draw a circle of radius 1 (a unit circle) and enclose it in a square, the areas of the two shapes are given as\n",
+ "\n",
+ "\n",
+ " Area Formulas\n",
+ " | circle | $$πr^2$$ |
\n",
+ " | square | $$4 r^2$$ |
\n",
+ "
\n",
+ "\n",
+ "\n",
+ "Therefore, the ratio of the volume of the circle to the volume of the square is $$\\frac{π}{4}$$\n",
+ "\n",
+ "The Monte Carlo Method plots a series of random points inside the square. By comparing the number that fall within the circle to those that fall outside, with a large enough sample we should have a good approximation of Pi. You can see a good demonstration of this [here](https://academo.org/demos/estimating-pi-monte-carlo/) (Hit the **Animate** button on the page).\n",
+ "\n",
+ "For a given number of points *n*, we have $$π = \\frac{4 \\cdot points\\ inside\\ circle}{total\\ points\\ n}$$\n",
+ "\n",
+ "To set up our multiprocessing program, we first derive a function for finding Pi that we can pass to `map()`:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from random import random # perform this import outside the function\n",
+ "\n",
+ "def find_pi(n):\n",
+ " \"\"\"\n",
+ " Function to estimate the value of Pi\n",
+ " \"\"\"\n",
+ " inside=0\n",
+ "\n",
+ " for i in range(0,n):\n",
+ " x=random()\n",
+ " y=random()\n",
+ " if (x*x+y*y)**(0.5)<=1: # if i falls inside the circle\n",
+ " inside+=1\n",
+ "\n",
+ " pi=4*inside/n\n",
+ " return pi"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Let's test `find_pi` on 5,000 points:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "3.1064"
+ ]
+ },
+ "execution_count": 2,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "find_pi(5000)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "This ran very quickly, but the results are not very accurate!\n",
+ "\n",
+ "Next we'll write a script that sets up a pool of workers, and lets us time the results against varying sized pools. We'll set up two arguments to represent *processes* and *total_iterations*. Inside the script, we'll break *total_iterations* down into the number of iterations passed to each process, by making a processes-sized list.
For example:\n",
+ "\n",
+ " total_iterations = 1000\n",
+ " processes = 5\n",
+ " iterations = [total_iterations//processes]*processes\n",
+ " iterations\n",
+ " # Output: [200, 200, 200, 200, 200]\n",
+ " \n",
+ "This list will be passed to our `map()` function along with `find_pi()`"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Writing test.py\n"
+ ]
+ }
+ ],
+ "source": [
+ "%%writefile test.py\n",
+ "from random import random\n",
+ "from multiprocessing import Pool\n",
+ "import timeit\n",
+ "\n",
+ "def find_pi(n):\n",
+ " \"\"\"\n",
+ " Function to estimate the value of Pi\n",
+ " \"\"\"\n",
+ " inside=0\n",
+ "\n",
+ " for i in range(0,n):\n",
+ " x=random()\n",
+ " y=random()\n",
+ " if (x*x+y*y)**(0.5)<=1: # if i falls inside the circle\n",
+ " inside+=1\n",
+ "\n",
+ " pi=4*inside/n\n",
+ " return pi\n",
+ "\n",
+ "if __name__ == '__main__':\n",
+ " N = 10**5 # total iterations\n",
+ " P = 5 # number of processes\n",
+ " \n",
+ " p = Pool(P)\n",
+ " print(timeit.timeit(lambda: print(f'{sum(p.map(find_pi, [N//P]*P))/P:0.7f}'), number=10))\n",
+ " p.close()\n",
+ " p.join()\n",
+ " print(f'{N} total iterations with {P} processes')"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "3.1466800\n",
+ "3.1364400\n",
+ "3.1470400\n",
+ "3.1370400\n",
+ "3.1256400\n",
+ "3.1398400\n",
+ "3.1395200\n",
+ "3.1363600\n",
+ "3.1437200\n",
+ "3.1334400\n",
+ "0.2370227286270967\n",
+ "100000 total iterations with 5 processes\n"
+ ]
+ }
+ ],
+ "source": [
+ "! python test.py"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Great! The above test took under a second on our computer.\n",
+ "\n",
+ "Now that we know our script works, let's increase the number of iterations, and compare two different pools. Sit back, this may take awhile!"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Overwriting test.py\n"
+ ]
+ }
+ ],
+ "source": [
+ "%%writefile test.py\n",
+ "from random import random\n",
+ "from multiprocessing import Pool\n",
+ "import timeit\n",
+ "\n",
+ "def find_pi(n):\n",
+ " \"\"\"\n",
+ " Function to estimate the value of Pi\n",
+ " \"\"\"\n",
+ " inside=0\n",
+ "\n",
+ " for i in range(0,n):\n",
+ " x=random()\n",
+ " y=random()\n",
+ " if (x*x+y*y)**(0.5)<=1: # if i falls inside the circle\n",
+ " inside+=1\n",
+ "\n",
+ " pi=4*inside/n\n",
+ " return pi\n",
+ "\n",
+ "if __name__ == '__main__':\n",
+ " N = 10**7 # total iterations\n",
+ " \n",
+ " P = 1 # number of processes\n",
+ " p = Pool(P)\n",
+ " print(timeit.timeit(lambda: print(f'{sum(p.map(find_pi, [N//P]*P))/P:0.7f}'), number=10))\n",
+ " p.close()\n",
+ " p.join()\n",
+ " print(f'{N} total iterations with {P} processes')\n",
+ " \n",
+ " P = 5 # number of processes\n",
+ " p = Pool(P)\n",
+ " print(timeit.timeit(lambda: print(f'{sum(p.map(find_pi, [N//P]*P))/P:0.7f}'), number=10))\n",
+ " p.close()\n",
+ " p.join()\n",
+ " print(f'{N} total iterations with {P} processes')"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 6,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "3.1420964\n",
+ "3.1417412\n",
+ "3.1411108\n",
+ "3.1408184\n",
+ "3.1414204\n",
+ "3.1417656\n",
+ "3.1408324\n",
+ "3.1418828\n",
+ "3.1420492\n",
+ "3.1412804\n",
+ "36.03526345242264\n",
+ "10000000 total iterations with 1 processes\n",
+ "3.1424524\n",
+ "3.1418376\n",
+ "3.1415292\n",
+ "3.1410344\n",
+ "3.1422376\n",
+ "3.1418736\n",
+ "3.1420540\n",
+ "3.1411452\n",
+ "3.1421652\n",
+ "3.1410672\n",
+ "17.300921846344366\n",
+ "10000000 total iterations with 5 processes\n",
+ "\n"
+ ]
+ }
+ ],
+ "source": [
+ "! python test.py"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Hopefully you saw that with 5 processes our script ran faster!"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## More is Better ...to a point.\n",
+ "\n",
+ "The gain in speed as you add more parallel processes tends to flatten out at some point. In any collection of tasks, there are going to be one or two that take longer than average, and no amount of added processing can speed them up. This is best described in [Amdahl's Law](https://en.wikipedia.org/wiki/Amdahl%27s_law)."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Advanced Script\n",
+ "\n",
+ "In the example below, we'll add a context manager to shrink these three lines\n",
+ "\n",
+ " p = Pool(P)\n",
+ " ...\n",
+ " p.close()\n",
+ " p.join()\n",
+ " \n",
+ "to one line:\n",
+ "\n",
+ " with Pool(P) as p:\n",
+ " \n",
+ "And we'll accept command line arguments using the *sys* module.\n",
+ " "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 7,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Writing test2.py\n"
+ ]
+ }
+ ],
+ "source": [
+ "%%writefile test2.py\n",
+ "from random import random\n",
+ "from multiprocessing import Pool\n",
+ "import timeit\n",
+ "import sys\n",
+ "\n",
+ "N = int(sys.argv[1]) # these arguments are passed in from the command line\n",
+ "P = int(sys.argv[2])\n",
+ "\n",
+ "def find_pi(n):\n",
+ " \"\"\"\n",
+ " Function to estimate the value of Pi\n",
+ " \"\"\"\n",
+ " inside=0\n",
+ "\n",
+ " for i in range(0,n):\n",
+ " x=random()\n",
+ " y=random()\n",
+ " if (x*x+y*y)**(0.5)<=1: # if i falls inside the circle\n",
+ " inside+=1\n",
+ "\n",
+ " pi=4*inside/n\n",
+ " return pi\n",
+ "\n",
+ "if __name__ == '__main__':\n",
+ " \n",
+ " with Pool(P) as p:\n",
+ " print(timeit.timeit(lambda: print(f'{sum(p.map(find_pi, [N//P]*P))/P:0.5f}'), number=10))\n",
+ " print(f'{N} total iterations with {P} processes')"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 8,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "3.14121\n",
+ "3.14145\n",
+ "3.14178\n",
+ "3.14194\n",
+ "3.14109\n",
+ "3.14201\n",
+ "3.14243\n",
+ "3.14150\n",
+ "3.14203\n",
+ "3.14116\n",
+ "16.871822701405073\n",
+ "10000000 total iterations with 500 processes\n"
+ ]
+ }
+ ],
+ "source": [
+ "! python test2.py 10000000 500"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Great! Now you should have a good understanding of multithreading and multiprocessing!"
+ ]
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "Python 3",
+ "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.6.2"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 2
+}
diff --git a/Complete-Python-3-Bootcamp-master/17-Parallel Processing/test.py b/Complete-Python-3-Bootcamp-master/17-Parallel Processing/test.py
new file mode 100644
index 0000000..a75551c
--- /dev/null
+++ b/Complete-Python-3-Bootcamp-master/17-Parallel Processing/test.py
@@ -0,0 +1,35 @@
+from random import random
+from multiprocessing import Pool
+import timeit
+
+def find_pi(n):
+ """
+ Function to estimate the value of Pi
+ """
+ inside=0
+
+ for i in range(0,n):
+ x=random()
+ y=random()
+ if (x*x+y*y)**(0.5)<=1: # if i falls inside the circle
+ inside+=1
+
+ pi=4*inside/n
+ return pi
+
+if __name__ == '__main__':
+ N = 10**7 # total iterations
+
+ P = 1 # number of processes
+ p = Pool(P)
+ print(timeit.timeit(lambda: print(f'{sum(p.map(find_pi, [N//P]*P))/P:0.7f}'), number=10))
+ p.close()
+ p.join()
+ print(f'{N} total iterations with {P} processes')
+
+ P = 5 # number of processes
+ p = Pool(P)
+ print(timeit.timeit(lambda: print(f'{sum(p.map(find_pi, [N//P]*P))/P:0.7f}'), number=10))
+ p.close()
+ p.join()
+ print(f'{N} total iterations with {P} processes\n')
\ No newline at end of file
diff --git a/Complete-Python-3-Bootcamp-master/17-Parallel Processing/test2.py b/Complete-Python-3-Bootcamp-master/17-Parallel Processing/test2.py
new file mode 100644
index 0000000..15d87c2
--- /dev/null
+++ b/Complete-Python-3-Bootcamp-master/17-Parallel Processing/test2.py
@@ -0,0 +1,28 @@
+from random import random
+from multiprocessing import Pool
+import timeit
+import sys
+
+N = int(sys.argv[1]) # these arguments are passed in from the command line
+P = int(sys.argv[2])
+
+def find_pi(n):
+ """
+ Function to estimate the value of Pi
+ """
+ inside=0
+
+ for i in range(0,n):
+ x=random()
+ y=random()
+ if (x*x+y*y)**(0.5)<=1: # if i falls inside the circle
+ inside+=1
+
+ pi=4*inside/n
+ return pi
+
+if __name__ == '__main__':
+
+ with Pool(P) as p:
+ print(timeit.timeit(lambda: print(f'{sum(p.map(find_pi, [N//P]*P))/P:0.5f}'), number=10))
+ print(f'{N} total iterations with {P} processes')
\ No newline at end of file
diff --git a/Complete-Python-3-Bootcamp-master/FAQ.ipynb b/Complete-Python-3-Bootcamp-master/FAQ.ipynb
new file mode 100644
index 0000000..3ccb212
--- /dev/null
+++ b/Complete-Python-3-Bootcamp-master/FAQ.ipynb
@@ -0,0 +1,60 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "#FAQ\n",
+ "This Notebook will be updated with Frequently Asked Questions for the Course."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "**How do you see the hints? How do you see the DocStrings for functions? etc...**\n",
+ "\n",
+ "You can use Shift+Tab when you cursor is placed after an object or function for iPython to reveal the Docstring or more information."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "**How do you auto-complete?**\n",
+ "\n",
+ "Use tab to autocomplete methods,objects, or functions. If there is more than one option available, multiple options appear."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": true
+ },
+ "outputs": [],
+ "source": []
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "Python 2",
+ "language": "python",
+ "name": "python2"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 2
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython2",
+ "version": "2.7.10"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/Complete-Python-3-Bootcamp-master/Jupyter (iPython) Notebooks Guide.ipynb b/Complete-Python-3-Bootcamp-master/Jupyter (iPython) Notebooks Guide.ipynb
new file mode 100644
index 0000000..e88ee03
--- /dev/null
+++ b/Complete-Python-3-Bootcamp-master/Jupyter (iPython) Notebooks Guide.ipynb
@@ -0,0 +1,37 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Guide to Using Jupyter Notebooks\n",
+ "In this lecture we will be going over the basics of the Jupyter (previously called iPython Notebooks).\n",
+ "\n",
+ "For a complete User Manual check out the [Bryn Mawr College Computer Science Guide](https://jupyter.brynmawr.edu/services/public/dblank/Jupyter%20Notebook%20Users%20Manual.ipynb).\n",
+ "\n",
+ "Most of the breakdown will actually occur in the presentation corresponding to this Notebook. So please refer to either the presentation or the full User Manual linked above."
+ ]
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "Python 3",
+ "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.6.2"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 1
+}
diff --git a/Complete-Python-3-Bootcamp-master/README.md b/Complete-Python-3-Bootcamp-master/README.md
new file mode 100644
index 0000000..de18f6a
--- /dev/null
+++ b/Complete-Python-3-Bootcamp-master/README.md
@@ -0,0 +1,8 @@
+# Complete-Python-3-Bootcamp
+Course Files for Complete Python 3 Bootcamp Course on Udemy
+
+
+Get it now for 95% off with the link:
+https://www.udemy.com/complete-python-bootcamp/?couponCode=COMPLETE_GITHUB
+
+Thanks!
diff --git a/firstpythoncodes.txt b/firstpythoncodes.txt
new file mode 100644
index 0000000..430a21b
--- /dev/null
+++ b/firstpythoncodes.txt
@@ -0,0 +1 @@
+Here I'll keep track of random codes,strings and functions for learning purposes.
\ No newline at end of file
diff --git a/notes_links.txt b/notes_links.txt
new file mode 100644
index 0000000..b9ff2fd
--- /dev/null
+++ b/notes_links.txt
@@ -0,0 +1 @@
+Notes taken and other interesting learning resources.
\ No newline at end of file
diff --git a/project_ideas.txt b/project_ideas.txt
new file mode 100644
index 0000000..f01f12e
--- /dev/null
+++ b/project_ideas.txt
@@ -0,0 +1 @@
+project ideas documentation
\ No newline at end of file