diff --git a/doc/user_guide/index.rst b/doc/user_guide/index.rst index 684d439dc1..f51c944509 100644 --- a/doc/user_guide/index.rst +++ b/doc/user_guide/index.rst @@ -5,21 +5,24 @@ User Guide * `Introduction `_ An overview of the capabilities of Panel. +* `Overview `_ + A high-level overview of the user guide and key concepts in Panel. + +* `Components `_ + An introduction to the three main component types: Widgets, Panes and Panel layouts. + +* `Customization `_ + How to customize the visual appearance, layout and size of Panel components. + * `Deploy & Export `_ Introduction to displaying, exporting and deploying panel apps * `Interact `_ Quickly making a panel using `interact()`. -* `Panes `_ - Adding visual components to your panel with panes. - * `Widgets `_ Declaring and working with Panel widgets. -* `Layouts `_ - Declaring and working with Panel layouts. - * `Parameters `_ Using Param to express panels in a self-contained class. @@ -43,12 +46,12 @@ Supplementary guides :maxdepth: 2 Introduction + Overview Components + Customization Deploy & Export - Interact - Panes Widgets - Layouts + Interact Parameters Linking Pipelines diff --git a/examples/user_guide/Components.ipynb b/examples/user_guide/Components.ipynb index f389c64e0f..42af8d2510 100644 --- a/examples/user_guide/Components.ipynb +++ b/examples/user_guide/Components.ipynb @@ -179,18 +179,22 @@ "\n", "## Panel layouts\n", "\n", - "The third type of component is are the Panel layout objects which allow arranging widget and pane objects into everything from a simple app to a complex dashboard. There are four main types of Layout containers:\n", + "The third type of component is are the Panel layout objects which allow arranging widget and pane objects into fixed size or responsively resizing layouts to build simply apps or complex dashboard. There are four main types of ``Panel`` objects:\n", "\n", "* **``Row``**: A ``Row`` arranges a list of components horizontally.\n", "* **``Column``**: A ``Column`` arranges a list of components vertically.\n", "* **``Tabs``**: ``Tabs`` lay out components in a list of selectable tabs. \n", "* **``GridSpec``**: A ``GridSpec`` lays out components on a grid.\n", "\n", - "In addition to these layout containers ``Spacer`` components allow controlling the spacing between components.\n", + "In addition to these layout containers the ``Spacer`` components allow controlling the spacing between other components.\n", "\n", "### Row & Column\n", "\n", - "The ``Row``, ``Column`` and ``Tabs`` layouts behave very similarly, they are list-like, which means they have many of the same methods as a simple Python list, making it easy to add, replace and remove components using ``append``, ``extend``, ``clear``, ``insert``, ``pop``, ``remove`` and ``__setitem__``." + "The ``Row``, ``Column`` and ``Tabs`` layouts behave very similarly, they are list-like, which means they have many of the same methods as a simple Python list, making it easy to add, replace and remove components interactively using ``append``, ``extend``, ``clear``, ``insert``, ``pop``, ``remove`` and ``__setitem__``. These methods making it possible to interactively configure and modify an arrangement of plots, making them an extremely powerful tool for building apps or dashboards.\n", + "\n", + "``Row`` and ``Column`` can be initialized as empty or with the objects to be displayed as arguments. If the object is not already a ``Widget``, ``Pane`` or ``Panel`` layout component the layout will internally use ``pn.panel`` function to convert the object to a displayable representation.\n", + "\n", + "To start with, we will declare a ``Column`` and populate it with a title and a widget:" ] }, { @@ -199,27 +203,73 @@ "metadata": {}, "outputs": [], "source": [ - "column = pn.Column()\n", - "\n", - "# Add an item\n", - "column.append('# A title')\n", - "\n", - "# Add multiple items\n", - "column.extend([pn.widgets.FloatSlider(), pn.widgets.TextInput()])\n", - "\n", - "# Replace the third item\n", - "column[2] = pn.widgets.Button(name='Click here')\n", + "column = pn.Column('# A title', pn.widgets.FloatSlider())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Next we add another bit of markdown:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "column.append('* Item 1\\n* Item 2')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Then we add a few more widgets:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "column.extend([pn.widgets.TextInput(), pn.widgets.Checkbox(name='Tick this!')])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "and finally we change our mind and replace the ``Checkbox`` with a button:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "column[3] = pn.widgets.Button(name='Click here')\n", "\n", "column" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The ability to add, remove and replace items opens up the possibility to building rich and responsive GUIs with the ease of manipulating a list." + ] + }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Tabs\n", "\n", - "The ``Tabs`` object shares all the same methods however, when adding or replacing items it is also possible to pass a tuple providing a custom title:" + "The ``Tabs`` layout allows is displaying multiple objects as individually toggleable tabs. Just like ``Column`` and ``Row`` it acts like a list, however, when adding or replacing items it is also possible to pass a tuple providing a custom title:" ] }, { @@ -230,11 +280,11 @@ "source": [ "from bokeh.plotting import figure\n", "\n", - "tabs = pn.Tabs()\n", - "\n", "p1 = figure(width=300, height=300)\n", "p1.line([1, 2, 3], [1, 2, 3])\n", "\n", + "tabs = pn.Tabs(p1)\n", + "\n", "# Add a tab\n", "tabs.append(('Slider', pn.widgets.FloatSlider()))\n", "\n", @@ -244,9 +294,6 @@ " ('Color', pn.widgets.ColorPicker())\n", "])\n", "\n", - "# Replace a tab\n", - "tabs[0] = ('Line Plot', p1)\n", - "\n", "tabs" ] }, @@ -285,11 +332,14 @@ "gspec = pn.GridSpec(sizing_mode='stretch_both', max_height=800)\n", "\n", "gspec[0, :3] = pn.pane.HTML(\"A\", background='#FF0000')\n", - "gspec[1:3, 0] = pn.pane.HTML(\"B\", background='#00FF00')\n", + "gspec[1:3, 0] = pn.pane.HTML(\"B\", background='#0000FF')\n", "gspec[1:3, 1:3] = fig\n", "gspec[3:5, 0] = hv.Curve([1, 2, 3])\n", "gspec[3:5, 1] = 'https://upload.wikimedia.org/wikipedia/commons/4/47/PNG_transparency_demonstration_1.png'\n", - "gspec[4:5, 2] = pn.Column(pn.widgets.FloatSlider(), pn.widgets.ColorPicker(), pn.widgets.Toggle(name='Toggle me'))\n", + "gspec[4:5, 2] = pn.Column(\n", + " pn.widgets.FloatSlider(),\n", + " pn.widgets.ColorPicker(),\n", + " pn.widgets.Toggle(name='Toggle Me!'))\n", "\n", "gspec" ] @@ -298,23 +348,51 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "When assigning to a grid cell that is already occupied ``GridSpec`` will generate a helpful warning which highlights which objects are overlapping an where:" + "When assigning to a grid cell that is already occupied ``GridSpec`` will generate a helpful warning which highlights which objects are overlapping and where:" ] }, { - "cell_type": "code", - "execution_count": null, + "cell_type": "markdown", "metadata": {}, - "outputs": [], "source": [ - "gspec[0:3, :2] = 'Some text'" + "```python\n", + "gspec[0:3, :2] = 'Some text'\n", + "```\n", + "\n", + "```bash\n", + "---------------------------------------------------------------------------\n", + "IndexError Traceback (most recent call last)\n", + " in \n", + "----> 1 gspec[0:3, :2] = 'Some text'\n", + "\n", + "~/development/panel/panel/layout.py in __setitem__(self, index, obj)\n", + " 640 'The following shows a view of the grid '\n", + " 641 '(empty: 0, occupied: 1, overlapping: 2):\\n\\n'+\n", + "--> 642 str(grid.astype('uint8')))\n", + " \n", + "IndexError: Specified region overlaps with the following existing object(s) in the grid:\n", + "\n", + " (0, 0): HTML(str, background='#FF0000', sizing_mode='stretch_both')\n", + "\n", + " (1, 0): HTML(str, background='#00FF00', sizing_mode='stretch_both')\n", + "\n", + " (1, 1): Str(ndarray, sizing_mode='stretch_both')\n", + "\n", + "The following shows a view of the grid (empty: 0, occupied: 1, overlapping: 2):\n", + "\n", + "[[2 2 1]\n", + " [2 2 1]\n", + " [2 2 1]\n", + " [1 1 0]\n", + " [1 1 1]]\n", + "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "In addition to assignment we can also slice and index the ``GridSpec`` to access an individual objects or a subregion of the grid, e.g. here we will access the last row and the last two columns:" + "In addition to assignment, we can also slice and index the ``GridSpec`` to access an individual object or a subregion of the grid, e.g. here we will access the last row and everything except the first column:" ] }, { @@ -328,22 +406,9 @@ } ], "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.8" + "pygments_lexer": "ipython3" } }, "nbformat": 4, diff --git a/examples/user_guide/Customization.ipynb b/examples/user_guide/Customization.ipynb new file mode 100644 index 0000000000..81ab40cd74 --- /dev/null +++ b/examples/user_guide/Customization.ipynb @@ -0,0 +1,323 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import panel as pn\n", + "\n", + "pn.extension()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Panel is built on top of [param](https://param.pyviz.org) and allows it to declare parameters which control the displayed output. In addition to parameters specific to \n", + "each component and class of components, all components define a shared set of parameters to control the size and style of the rendered views." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Styling Components" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### ``css_classes``\n", + "\n", + "The ``css_classes`` allows associating a CSS class with a component which allows referencing the component from a CSS style. CSS styles can be embedded in raw form or by referencing an external .css file by providing each to the panel extension using the ``raw_css`` and ``css_files`` arguments; both should be supplied as lists. Outside the notebook or if we want to add some CSS in an external module or library we can simply append to the ``pn.config.raw_css`` and ``pn.config.js_files`` config parameters.\n", + "\n", + "To demonstrate this let us define a CSS class called ``widget-box`` which we will give a background and a nice border:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "css = \"\"\"\n", + ".widget-box {\n", + " background: #f0f0f0;\n", + " border-radius: 5px;\n", + " border: 1px black solid;\n", + "}\n", + "\"\"\"\n", + "\n", + "pn.extension(raw_css=[css])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now that we have defined and loaded the CSS we will create a ``Column`` with the widget-box CSS class:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "pn.Column(\n", + " pn.widgets.FloatSlider(name='Number', margin=(10, 5, 5, 10)),\n", + " pn.widgets.Select(name='Fruit', options=['Apple', 'Orange', 'Pear'], margin=(0, 5, 5, 10)),\n", + " pn.widgets.Button(name='Run', margin=(5, 10, 10, 10)),\n", + "css_classes=['widget-box'])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### ``background``\n", + "\n", + "In case we simply want to give the component a background we can define one as a hex string:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "pn.Column(background='#f307eb', width=100, height=100)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### ``style``\n", + "\n", + "Certain components, specifically markup related panes, expose a ``style`` parameter which allows defining CSS styles which apply to the HTML container of the Pane's contents, e.g. the ``Markdown`` pane" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "pn.pane.Markdown('### A Markdown heading in Serif', style={'font-family': \"serif\"})" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Component Size and Layout\n", + "\n", + "The size of components and their spacing is also controlled through a set of parameters shared by all components." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### ``margin``\n", + "\n", + "The ``margin`` parameter can be used to create space around an element defined as the number of pixels at the (top, right, bottom, and left). The ``margin`` can be defined in one of three ways:\n", + "\n", + " margin=25\n", + " top, bottom, left and right margins are 25px\n", + "\n", + " margin=(25, 50)\n", + " top and bottom margins are 25px\n", + " right and left margins are 50px\n", + "\n", + " margin=(25, 50, 75, 100)\n", + " top margin is 25px\n", + " right margin is 50px\n", + " bottom margin is 75px\n", + " left margin is 100px\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "pn.Row(\n", + " pn.Column(pn.widgets.Button(name='Run', margin=25), background='#f0f0f0'),\n", + " pn.Column(pn.widgets.Button(name='Run', margin=(25, 50)), background='#f0f0f0'),\n", + " pn.Column(pn.widgets.Button(name='Run', margin=(25, 50, 75, 100)), background='#f0f0f0'))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Absolute sizing using ``width`` and ``height``\n", + "\n", + "By default all components use either auto-sizing or absolute sizing. Particularly layout components will generally take up as much space as the components within it and text or image based panes will adjust to the size of their contents. To set a fixed size on a component it is usually sufficient to set a width or height, but in certain cases setting ``sizing_mode='fixed'`` may also be required." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "pn.Row(\n", + " pn.pane.Markdown('ABCDE', background='#f0f0f0', width=200, height=200),\n", + " pn.pane.PNG('https://upload.wikimedia.org/wikipedia/commons/4/47/PNG_transparency_demonstration_1.png', width=200),\n", + " pn.widgets.FloatSlider(width=200))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Plots\n", + "\n", + "Unlike other components the size of a plot is usually determined by the underlying plotting library so it may be necessary to ensure that you set the size and aspect when declaring the plot." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Responsive sizing\n", + "\n", + "By default panel objects will use a fixed size if one is provided or adapt to the size of the content. However most panel objects also support reactive sizing which adjusts depending on the size of the viewport. These responsive sizing modes can be controlled using the ``sizing_mode`` parameter.\n", + "\n", + "#### ``sizing_mode``\n", + "\n", + "* **\"fixed\"**: Component is not responsive. It will retain its original width and height regardless of any subsequent browser window resize events.\n", + "\n", + "This is usually the default behavior and simply respects the provided width and height." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* **\"stretch_width\"**: Component will responsively resize to stretch to the available width, without maintaining any aspect ratio. The height of the component depends on the type of the component and may be fixed or fit to component's contents.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "pn.pane.Str(background='#f0f0f0', height=100, sizing_mode='stretch_width')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* **\"stretch_height\"**: Component will responsively resize to stretch to the available height, without maintaining any aspect ratio. The width of the component depends on the type of the component and may be fixed or fit to component's contents." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "To demonstrate the filling behavior in a document we declare a Column for the component to fill:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "pn.Column(\n", + " pn.pane.Str(background='#f0f0f0', sizing_mode='stretch_height'), height=200)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* **\"stretch_both\"**: Component is completely responsive, independently in width and height, and will occupy all the available horizontal and vertical space, even if this changes the aspect ratio of the component." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "pn.Column(\n", + " pn.pane.Str(background='#f0f0f0', sizing_mode='stretch_both'),\n", + " height=200)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* **\"scale_height\"**: Component will responsively resize to stretch to the available height, while maintaining the original or provided aspect ratio.\n", + "* **\"scale_width\"**: Component will responsively resize to stretch to the available width, while maintaining the original or provided aspect ratio.\n", + "* **\"scale_both\"**: Component will responsively resize to both the available width and height, while maintaining the original or provided aspect ratio." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "pn.Column(\n", + " pn.pane.PNG('https://upload.wikimedia.org/wikipedia/commons/4/47/PNG_transparency_demonstration_1.png', sizing_mode='scale_both'),\n", + " height=400, width=500, background='#3f3f3f')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Spacers\n", + "\n", + "Spacers are a very versatile component which makes it easy to put fixed or responsive spacing between objects. Like all other components it supports both absolute and responsive sizing modes:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "pn.Row(1, pn.Spacer(width=200), 2, pn.Spacer(width=100), 3, pn.Spacer(width=50), 4, pn.Spacer(width=25), 5)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The ``VSpacer``and ``HSpacer`` providing responsive horizontal and vertical spacing. Using these components we can space objects equidistantly in a layout and allow the empty space to shrink when the browser is resized." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "pn.Row(\n", + " pn.layout.HSpacer(), '* Item 1\\n* Item2', pn.layout.HSpacer(), '1. First\\n2. Second', pn.layout.HSpacer())" + ] + } + ], + "metadata": { + "language_info": { + "name": "python", + "pygments_lexer": "ipython3" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/examples/user_guide/Deploy_and_Export.ipynb b/examples/user_guide/Deploy_and_Export.ipynb index b33a2be7cb..64ee4a0b88 100644 --- a/examples/user_guide/Deploy_and_Export.ipynb +++ b/examples/user_guide/Deploy_and_Export.ipynb @@ -219,7 +219,7 @@ "\n", "To turn a notebook into a deployable app simply append ``.servable()`` to one or more panel objects, this will add the app to bokeh's ``curdoc`` ensuring it can be discovered by bokeh server on deployment. In this way it is trivial to build dashboards which can be used interactively in a notebook and then seamlessly deployed on bokeh server.\n", "\n", - "### Accessing state\n", + "### Accessing session state\n", "\n", "Whenever a Panel app is being served the ``panel.state`` object exposes some of the internal Bokeh server components to a user.\n", "\n", @@ -238,13 +238,8 @@ " phase = 1\n", "```\n", "\n", - "This may be used to modify the behavior of an app dependending on parameters provided in the URL. " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ + "This may be used to modify the behavior of an app dependending on parameters provided in the URL. \n", + "\n", "### Accessing the Bokeh model\n", "\n", "Since panel is built on top of Bokeh all panel objects can easily be converted to a bokeh model. The ``get_root`` method returns a model representing the contents of a Panel:" @@ -292,22 +287,9 @@ } ], "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.8" + "pygments_lexer": "ipython3" } }, "nbformat": 4, diff --git a/examples/user_guide/Django_Apps.ipynb b/examples/user_guide/Django_Apps.ipynb index 33ec5bb5ea..42622312ca 100644 --- a/examples/user_guide/Django_Apps.ipynb +++ b/examples/user_guide/Django_Apps.ipynb @@ -218,22 +218,9 @@ } ], "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.8" + "pygments_lexer": "ipython3" } }, "nbformat": 4, diff --git a/examples/user_guide/Layouts.ipynb b/examples/user_guide/Layouts.ipynb deleted file mode 100644 index 22c9e84f75..0000000000 --- a/examples/user_guide/Layouts.ipynb +++ /dev/null @@ -1,250 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import panel as pn\n", - "from bokeh.plotting import figure\n", - "\n", - "pn.extension()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Layout objects make it easy to compose ``Panel`` and ``Widget`` objects into complex dashboards." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Rows and Columns\n", - "\n", - "In the [Widgets user guide](Widgets.ipynb) we already discovered that the ``Row`` and ``Column`` layouts allow composing multiple widgets." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "w1 = pn.widgets.TextInput(name='Text:')\n", - "w2 = pn.widgets.FloatSlider(name='Slider')\n", - "widgets = pn.Column(w1, w2, width=400)\n", - "widgets" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Like all other objects layouts are reactive, i.e. if we change a parameter the corresponding views update automatically:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "widgets.objects = widgets.objects[::-1]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "To change a panel or add a new view layouts support item assignment and have an ``append`` method:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "w3 = pn.widgets.Select(options=['A', 'B', 'C'])\n", - "\n", - "widgets[1] = w3\n", - "widgets.append(w1)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "A ``Row`` or ``Column`` may contain any kind of object including plots:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "p1 = figure(width=300, height=300)\n", - "p2 = figure(width=300, height=300)\n", - "\n", - "p1.line([1, 2, 3], [1, 2, 3])\n", - "p2.circle([1, 2, 3], [1, 2, 3])\n", - "\n", - "row = pn.Row(p1, p2)\n", - "row" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This means layouts can be composed into complex dashboards:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "pn.Column(widgets, row)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And even after composing the components each individual component stays reactive, which makes it possible to rearrange the components interactively:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "row.objects = row.objects[::-1]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Or even to add new components:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "p3 = figure(width=300, height=300)\n", - "p3.square([1, 2, 3], [1, 2, 3], color='red')\n", - "row.append(p3)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Tabs" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Tabs allow switching between multiple objects by clicking on the corresponding tab. Tab labels may be defined as part of a tuple or by defining the ``name`` on the Pane." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "p1 = figure(width=300, height=300)\n", - "p2 = figure(width=300, height=300)\n", - "\n", - "p1.line([1, 2, 3], [1, 2, 3])\n", - "p2.circle([1, 2, 3], [1, 2, 3])\n", - "\n", - "tabs = pn.Tabs(('Line', p1), ('Scatter', pn.Column(p2, pn.widgets.TextInput(), pn.widgets.IntSlider())), width=350)\n", - "tabs" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "In addition to being able to modify the ``objects`` we can also get and set the currently ``active`` tab as an integer:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "print(tabs.active)\n", - "tabs.active = 1" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Responsive plots\n", - "\n", - "By default panel objects will use a fixed size if one is provided or a default size if none is provided. However most panel objects also support reactive sizing which adjusts depending on the size of the viewport. These responsive sizing modes can be controlled using the ``sizing_mode`` parameter:" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### Sizing mode\n", - "\n", - "* **\"fixed\"**: Component is not responsive. It will retain its original width and height regardless of any subsequent browser window resize events.\n", - "\n", - "* **\"stretch_width\"**: Component will responsively resize to stretch to the available width, without maintaining any aspect ratio. The height of the component depends on the type of the component and may be fixed or fit to component's contents.\n", - "\n", - "* **\"stretch_height\"**: Component will responsively resize to stretch to the available height, without maintaining any aspect ratio. The width of the component depends on the type of the component and may be fixed or fit to component's contents.\n", - "\n", - "* **\"stretch_both\"**: Component is completely responsive, independently in width and height, and will occupy all the available horizontal and vertical space, even if this changes the aspect ratio of the component.\n", - "\n", - "* **\"scale_width\"**: Component will responsively resize to stretch to the available width, while maintaining the original or provided aspect ratio.\n", - "\n", - "* **\"scale_height\"**: Component will responsively resize to stretch to the available height, while maintaining the original or provided aspect ratio.\n", - "\n", - "* **\"scale_both\"**: Component will responsively resize to both the available width and height, while maintaining the original or provided aspect ratio.\n" - ] - } - ], - "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.4" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/examples/user_guide/Overview.ipynb b/examples/user_guide/Overview.ipynb new file mode 100644 index 0000000000..34eedc5992 --- /dev/null +++ b/examples/user_guide/Overview.ipynb @@ -0,0 +1,127 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In order to get the best use out of this user guide it is important to have a grasp of some core concepts and ideas.\n", + "\n", + "### Components\n", + "\n", + "Component refers to one of the three types of objects you will ordinarily be working with a ``Pane``, ``Widget`` or ``Panel`` layout. To get an overview of the basic operation of different components see the [Components user guide](./Components.ipynb).\n", + "\n", + "\n", + "* **``Pane``**: A ``Pane`` object wraps a user supplied object and turns it into a renderable view. When the wrapped ``object`` changes or any other parameters changes a pane will update the view accordingly.\n", + "\n", + "* **``Panel``**: A ``Panel`` layout is a container to arrange multiple components into a visual layout, a Panel may therefore also refer not specifically to the object but the combination of multiple components into a simple app.\n", + "\n", + "* **``Widget``**: A ``Widget`` is a control component which allows users to provide input to your app or dashboard either in Python or directly in Javascript.\n", + "\n", + "---" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Display and rendering\n", + "\n", + "Throughout this user guide we will cover a number of ways to display panel objects, including display in the notebook, starting a server from the commandline, saving and embedding and more. For a detailed description see the [Deploy and Export user guide](./Deploy_and_Export.ipynb). \n", + "\n", + "#### Notebook\n", + "\n", + "The documentation is built using Jupyter notebooks and Panel objects display themselves in a notebook when placed on the last line of a notebook cell.\n", + "\n", + "##### ``pn.extension()``\n", + "\n", + ">The panel extension loads BokehJS, any custom models required and optionally custom JS and CSS in Jupyter notebook environments.\n", + "\n", + "##### ``.app()``\n", + "\n", + ">The ``.app()`` method present on all panel objects allows displaying an panel server inline in a notebook.\n", + "\n", + "#### Python REPL\n", + "\n", + "When working in a Python REPL which does not support rich-media output a panel can be still be launched.\n", + "\n", + "##### ``.show()``\n", + "\n", + ">The ``.show()`` method is present on all viewable panel objects and starts a server instance and opens a browser window to point to it. When working remotely a specific port to launch the app on can be supplied.\n", + "\n", + "#### Commandline\n", + "\n", + "Panel mirrors bokeh's commandline interface for launching and exporting apps and dashboards.\n", + "\n", + "##### ``panel serve app.py``\n", + "\n", + "> The ``panel serve`` command allows allows interactively displaying and deploying Panel apps from the commandline.\n", + "\n", + "#### Export\n", + "\n", + "When not working interactively a panel object can be exported to a static file.\n", + "\n", + "##### ``.save()``\n", + "\n", + "> The ``.save`` method present on all viewable panel objects allows saving panel objects to HTML or PNG files.\n", + "\n", + "##### Embedding\n", + "\n", + "> Embedding refers to the ability of a Panel object to serialize the widget state space into JSON which can be embedded in the notebook or as separate files and allow simple apps to be served without running a server.\n", + "\n", + "___" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Linking and callbacks\n", + "\n", + "One of the most important aspects of a general app and dashboarding framework is the ability to link different components in flexible ways and schedule callbacks in response to internal and external events. Panel provides convenient lower and higher-level APIs to achieve both for more details see the [Links](./Links.ipynb) and the [Callbacks](./Callbacks.ipynb) user guide. \n", + "\n", + "##### ``.param.watch``\n", + "\n", + "> The ``.param.watch`` method allows listening to parameter changes on an object using Python callbacks. It is the lowest level API and provides the most amount of control.\n", + "\n", + "##### ``.link()``\n", + "\n", + "> The ``.link()`` method present on all viewable panel objects is a convenient API to link the parameters of two objects together, uni- or bi-directionally.\n", + "\n", + "##### ``.jslink()``\n", + "\n", + "> The ``.jslink()`` method links the properties of the underlying bokeh models making it possible to define interactivity which works without a running server.\n", + "\n", + "___" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Interfaces\n", + "\n", + "``Panel`` provides three main interfaces for building GUIs and dashboards, each of which has different benefits and drawbacks. \n", + "\n", + "#### [Component API](./Widgets.ipynb)\n", + "\n", + "At the lowest level you can build interactive components entirely using ``Pane``, ``Widget`` and ``Panel`` components. By registering callbacks on components to modify other components provides a huge amount of flexibility in building interactive features but a lot of callbacks can often be difficult to keep track of. In other words this approach affords the most amount of flexibility but can easily grow in complexity. See the [Widgets user guide](./Widgets.ipynb)\n", + "\n", + "#### [``Param``](./Param.ipynb)\n", + "\n", + "``Panel`` itself is built on the [param](https://param.pyviz.org) library which allows expressing parameters on classes entirely independently of any GUI code. By using param to declare the parameters along with methods that depend on those parameters complex GUIs can be encapsulated in tidy, well-organized and declarative way. Panel will automatically convert parameter definition to corresponding widgets. This API requires the use of the param library to express the inputs and encapsulate the computations to be performed but once implemented this approach can lend itself to neat, well encapsulated code. See the [Param user guide](./Param.ipynb) for more detail.\n", + "\n", + "#### [``interact``](./Panel.ipynb)\n", + "\n", + "The ``interact`` API will be familiar to ipywidgets users, it provides a very simple API to define an interactive view which depends on a number of arguments. This approach works by declaring functions whose arguments will be inspected to infer a set widgets of widgets to control the display output. This approach makes it extremely easy to get started and even allows arranging the widgets and plots in different ways but may not be suited to more complex scenarios. See the [Param user guide](./Param.ipynb) for more detail." + ] + } + ], + "metadata": { + "language_info": { + "name": "python", + "pygments_lexer": "ipython3" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/examples/user_guide/Panes.ipynb b/examples/user_guide/Panes.ipynb deleted file mode 100644 index d3cb1aafcc..0000000000 --- a/examples/user_guide/Panes.ipynb +++ /dev/null @@ -1,353 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import panel as pn\n", - "\n", - "pn.extension()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "``Panel`` makes it possible to display and arrange a wide range of plots and other media on a page, including plots (matplotlib, bokeh, vega/altair, holoviews, and plotly), images (pngs, svgs, gifs, jpegs), and text (Markdown, HTML etc.).\n", - "\n", - "The ``pn.panel`` helper function returns a viewable representation of any object, e.g. a simple string will be interpreted as ``Markdown`` by default:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "layout = pn.panel('**Some text**')\n", - "layout" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The ``pn.panel`` will always return a Panel layout containing one or more views representing the supplied object. However we can also access the items of the ``Panel``, e.g. to confirm the type of the ``Pane`` that was created:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "print(layout)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This also makes it easy to manually compose the visible representation of an object. However in some cases it is necessary to manually choose the appropriate representation of an object, which we can do by manually constructing the desired ``Pane``, e.g. we may want to render the string from above without rendering the Markdown syntax. We can do so by manually constructing a ``pn.pane.Str``:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "pane = pn.pane.Str('*Some text*')\n", - "pane" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This also makes it easier to get a handle on the ``Pane`` object to update it. Since ``Pane`` objects are reactive updating the ``Pane.object`` will cause all existing views of the ``Pane`` to update in response:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "pane.object = 'Some updated text'" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "One thing to note is that since a ``Pane`` may define multiple views, when working with an explicitly constructed ``Pane`` care should be taken to compose the components of the ``Pane.layout`` manually." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Bokeh\n", - "\n", - "Since panel is built on top of bokeh it natively understand bokeh plots and models, which means we can easily mix and match bokeh and panel code:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from bokeh.plotting import figure\n", - "\n", - "fig = figure(width=300, height=300)\n", - "r = fig.line([1, 2, 3], [1, 2, 3])\n", - "\n", - "bk_layout = pn.panel(fig)\n", - "bk_layout" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "To update the plot we can modify the bokeh model and ``trigger`` an update to the model parameter:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "r.data_source.data['y'] = r.data_source.data['y'][::-1]\n", - "bk_layout.param.trigger('object')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This will efficiently sync just the changes we made to the model. Alternatively we can also replace the model entirely:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "new_fig = figure(width=300, height=300)\n", - "r = new_fig.scatter([1, 2, 3], [1, 2, 3])\n", - "\n", - "bk_layout.object = new_fig" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### HoloViews" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "HoloViews often provides a more concise way of declaring plots and HoloViews objects are also supported natively by panel." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import numpy as np\n", - "import holoviews as hv\n", - "\n", - "box = hv.BoxWhisker((np.random.randint(0, 10, 100), np.random.randn(100)), 'Group').sort()\n", - "\n", - "hv_layout = pn.panel(box)\n", - "hv_layout" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "A HoloViews panel can be updated in the same way as a matplotlib one:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "hv_layout.object = hv.Violin(box)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Additionally, since HoloViews is designed to make it easy to explore parameter spaces, the HoloViews pane will automatically add widgets when given a HoloMap or DynamicMap type. We can either display the default widget layout, or manually lay out the items in the layout:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "xs = np.linspace(0, np.pi*2)\n", - "hmap = hv.HoloMap({ph: hv.Curve((xs, np.sin(xs+ph))) for ph in np.linspace(0, np.pi*2, 11)}, 'Phase')\n", - "\n", - "widget_layout = pn.panel(hmap)\n", - "\n", - "pn.Column(widget_layout[1], widget_layout[0])" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Matplotlib" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import matplotlib.pyplot as plt\n", - "\n", - "fig = plt.figure()\n", - "ax = fig.add_subplot(111)\n", - "artist = ax.plot([1, 2, 3])[0]\n", - "\n", - "mpl_layout = pn.panel(fig)\n", - "mpl_layout" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "artist.set_ydata(artist.get_ydata()[::-1])\n", - "mpl_layout.param.trigger('object')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Altair/Vega\n", - "\n", - "Panel also provides a ``vega`` and ``vega-lite`` renderer which makes it possible to render ``altair`` plots. In addition to basic rendering the vega renderer will provide binary serialization for any array data sent to the browser, providing huge speedups over the standard JSON serialization employed by vega. This makes it possible to plot and interactively update much larger datasets." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import altair as alt\n", - "\n", - "from vega_datasets import data\n", - "\n", - "cars = data.cars()\n", - "\n", - "chart = alt.Chart(cars).mark_circle(size=60).encode(\n", - " x='Horsepower',\n", - " y='Miles_per_Gallon',\n", - " color='Origin',\n", - " tooltip=['Name', 'Origin', 'Horsepower', 'Miles_per_Gallon']\n", - ").interactive()\n", - "\n", - "vega_layout = pn.panel(chart)\n", - "vega_layout" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "As for all other ``Pane`` objects, we can update the chart by setting the ``object``. In case of altair/vega ``Panel`` will even cache the data, avoiding sending the data needlessly:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "vega_layout.object = chart.mark_circle(size=100)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Plotly Plots" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import numpy as np\n", - "import plotly.graph_objs as go\n", - "\n", - "xx=np.linspace(-3.5, 3.5, 100)\n", - "yy=np.linspace(-3.5, 3.5, 100)\n", - "x,y=np.meshgrid(xx, yy)\n", - "z=np.exp(-(x-1)**2-y**2)-(x**3+y**4-x/5)*np.exp(-(x**2+y**2))\n", - "\n", - "surface = go.Surface(z=z)\n", - "layout = go.Layout(\n", - " title='Plotly 3D Plot',\n", - " autosize=False,\n", - " width=500,\n", - " height=500,\n", - " margin=dict(t=50, b=50, r=50, l=50)\n", - ")\n", - "fig = go.Figure(data=[surface], layout=layout)\n", - "\n", - "plotly_layout = pn.panel(fig)\n", - "plotly_layout" - ] - } - ], - "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.4" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/examples/user_guide/Pipelines.ipynb b/examples/user_guide/Pipelines.ipynb index 4c7e314529..bba98750f0 100644 --- a/examples/user_guide/Pipelines.ipynb +++ b/examples/user_guide/Pipelines.ipynb @@ -216,22 +216,9 @@ } ], "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.6" + "pygments_lexer": "ipython3" } }, "nbformat": 4, diff --git a/examples/user_guide/Widgets.ipynb b/examples/user_guide/Widgets.ipynb index 584486c6ee..a3fcdef7cb 100644 --- a/examples/user_guide/Widgets.ipynb +++ b/examples/user_guide/Widgets.ipynb @@ -218,22 +218,9 @@ } ], "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.8" + "pygments_lexer": "ipython3" } }, "nbformat": 4,