Coder Social home page Coder Social logo

holoviz / panel Goto Github PK

View Code? Open in Web Editor NEW
4.2K 55.0 458.0 122.62 MB

Panel: The powerful data exploration & web app framework for Python

Home Page: https://panel.holoviz.org

License: BSD 3-Clause "New" or "Revised" License

Python 82.99% TypeScript 10.44% JavaScript 0.82% CSS 2.35% HTML 2.57% Shell 0.02% Jupyter Notebook 0.18% Less 0.63%
holoviz panel dashboards control-panels gui dataapp dataviz bokeh datascience holoviews

panel's Introduction



HoloViz: High-level tools to simplify visualization in Python

Test suite status Status
Site/Content version Github tag
Docs gh-pages site dev-site
Status Dashboard
Binder Binder
Code of Conduct Based on Contributor Covenant
Fiscal Sponsor Powered by NumFOCUS

What is HoloViz?

HoloViz provides high-level Python tools that are designed to work together to solve the entire problem of visualization, from conducting exploratory data analysis to deploying complex dashboards.

The core HoloViz projects are:

  • Panel: Create interactive dashboards in Jupyter notebooks or standalone apps
  • hvPlot: Quickly and interactively explore data with a familiar API
  • HoloViews: Visualize while you analyze by declaring data properties
  • GeoViews: Extend HoloViews for geographic data
  • Datashader: Render big data images in a browser
  • Lumen: Construct no-code dashboards from simple YAML specifications
  • Colorcet: Plot with perceptually based colormaps
  • Param: Declaratively code in Python
Panel Logo hvPlot Logo HoloViews Logo GeoViews Logo
Datashader Logo Lumen Logo Colorcet Logo Param Logo

All HoloViz projects are freely available for commercial or non-commercial use according to a permissive open-source license as described in each project's website.

HoloViz uses a custom open governance model and is fiscally sponsored by NumFOCUS. Consider making a tax-deductible donation to help the project pay for developer time, professional services, travel, workshops, and a variety of other needs.

NumFOCUS donation details

NumFOCUS is a 501(c)(3) non-profit charity in the United States; as such, donations to NumFOCUS are tax-deductible as allowed by law. As with any donation, you should consult with your personal tax adviser or the IRS about your particular tax situation.


What is the purpose of this specific repository?

This repository provides an entry point for the HoloViz ecosystem. The best way to experience this repository is on HoloViz.org.

This website provides:

  1. Introduction to the HoloViz organization and its projects
  2. Guidance for which HoloViz tools to start with given different use cases
  3. Demonstrations of using multiple HoloViz tools in a single workflow
  4. Communication channels and FAQ for HoloViz community members
  5. Governance, roadmap, and contributing guide for HoloViz developers

Getting Started

We recommend starting with holoviz.org/tutorial. If you already have a problem to solve that involves a particular data type, check out holoviz.org/background for guidance on which HoloViz tools to focus on.

panel's People

Contributors

a-recknagel avatar ahuang11 avatar cdeil avatar ceball avatar dependabot[bot] avatar droumis avatar fohrloop avatar govinda18 avatar hoxbro avatar hyamanieu avatar jbednar avatar jlstevens avatar joelostblom avatar jonmmease avatar jrycw avatar jsignell avatar kebowen730 avatar keul avatar marcskovmadsen avatar mattpap avatar maximlt avatar philippjfr avatar pre-commit-ci[bot] avatar raybellwaves avatar sdc50 avatar stubatiger avatar tbym avatar theomathurin avatar thuydotm avatar xavartley avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

panel's Issues

Pane example with plotly

Iยดve tried to do the pane example, and while it works for the bokeh and matplotlib plots, the plotly part displays just the top part of the output (the toolbar):
image

The plot seems to be there and saving it as png works:
image

Supported object types and libraries

Panel is designed to be very inclusive, providing basic support for a large number of different types of viewable objects. In categories ordered by decreasing richness of the representation, the supported types are:

  • Interactive plots -- implemented specifically for each library:
    • Bokeh plots
    • Plotly plots
    • Altair or Vega plots
    • VTK plots
    • HoloViews plots
      • hvPlot plots for Pandas, XArray, Dask, Streamz, Intake, and GeoPandas data (via Bokeh+HoloViews)
  • Image-based plots -- implemented specifically for each library, but trivial to add more:
    • Matplotlib figures (as PNG)
      • Seaborn plots (generates Matplotlib figures)
      • plotnine plots (generates Matplotlib figures; superseding Python ggpy/ggplot2)
    • R ggplot2 plots (via rpy2, as PNG)
    • yt plots (as Matplotlib PNG)
  • Images -- PNG, JPG, SVG, or animated GIF:
    • anything with a _repr_svg_ rich display method
      • Shapely images
      • Dot/Graphviz Graph/DiGraph objects
    • anything with a _repr_png_ rich display method, including:
      • IPython.display.Image
      • PIL/Pillow images
      • Datashader images
      • NetworkX graph, plotted using dot/graphviz using nxpd
    • anything with a _repr_jpg_ rich display method
    • files on the local filesystem ending in .png, .jpg, .svg, or .gif
    • image URLs ending in .png, .jpg, .svg, or .gif.
  • HTML -- anything with a _repr_html_ rich display method, including:
    • HTML code as Python strings
    • Pandas dataframes (as a table; requires height specification)
  • Markdown -- anything with a _repr_markdown_ rich display method, including:
    • Markdown text as Python strings
  • Equations -- via PNGs rendered using matplotlib and Pillow:
    • Anything with a _repr_latex_ rich display method, including IPython.display.Latex objects
    • LaTeX equations as Python strings
    • Sympy Expr objects
  • Widgets -- based on Bokeh widgets:
    • Bokeh widgets (wrapped as panel.widgets)
    • Parameterized objects (parameters from Param automatically mapped to panel.widgets)
  • Code
    • Editable code, in the ACE editor
  • Str -- preformatted text representing anything displayable using Python's str() function:
    • any Python object

The library selects the richest representation available (generally those higher in this list) unless explicitly requested. If you want a rich (non-plaintext) representation for a given object not already supported, you could consider adding one of the various _repr_X_ methods that allow rich display in IPython/Jupyter. That way, the object will have a rich representation in a Jupyter notebook or Jupyter Lab session, as well as being usable in Panel. If you only want Panel support, you could consider adding a suitable Pane class to Panel, accepting an object of that type and returning something displayable in a Panel.

Implementing Pane classes should be straightforward for most object types, with the specific exception that Jupyter-based interactivity provided by ipywidgets is more difficult. We are currently (7/2019) working on ipywidgets integration, and expect that it will be available in late summer 2019, allowing Panel to support the interactive features from:

  • bqplot
  • ipyvolume
  • plotly 3.0-specific features
  • streamz objects

Add linked plots example

Current Panel examples show off various Panel widgets and various plotting libraries, but we don't currently have a dashboard example where each plot is linked so that selected items in one pane affect each of the others. A typical example is to "drill down" on some overview plot, expanding selected item(s) in a different plot, and in turn allowing some subset of those values to be displayed as a table. None of this functionality requires Panel, but it is important to demonstrate that it can be done in Panel, so that one can provide full-featured, deeply interactive dashboards.

TypeError: unhashable type: 'list'

Hi,
I tested panel with version 0.1.0a11 without issue
image
But I tried to update version and I get this error
image
version: panel-0.1.0a14.post4+gec6e3d1

Add subpanel expansion options

Right now, when a Parameterized object with a Parameter obj that is also a Parameterized is displayed in a Param pane, Panel will create an extra widget ... that when clicked will open a separate set of widgets to edit that subobject's parameters. This is useful behavior for property sheets, which need to handle any arbitrary collections of nested objects, but when designing a dashboard with a fixed layout it's difficult to handle the dynamic resizing involved in this approach. I propose that we add two Boolean flags to control whether we want the user to be able to control the subobject's widgets visibility, and whether we want them visible at first:

  • expandable=False, expand_at_first=True: Start out expanded; provide no widget to unexpand
  • expandable=False, expand_at_first=False: Start out unexpanded; provide no widget to expand
  • expandable=True, expand_at_first=True: Start out expanded, but allow unexpanding
  • expandable=True, expand_at_first=False: Start out unexpanded, but allow expanding (current default)

This way people could have a fixed layout if they wish, either with or without the ability to edit subobject parameters.

Find better name for the Panel classes

After discussing the organization of the panel library we decided that the PanelBase class and its subclass are misnamed. This is because we all agreed that the concept of a panel more accurately describes the combination of several components, e.g. a set of widgets with some associated plot.

The class name for the classes that currently inherit from PanelBase should convey the idea that it is a viewable and reactive object that wraps some other object and displays it as part of some larger panel.

Names that we have considered (and so far rejected):

  • SubPanel

CC: @jbednar @ceball @jlstevens

Overriding the names of widgets

I know it inherits from variable name and titlelizes, removing the underscore, but if I wanted a different, longer name, I don't want my code to be cluttered with a long variable name.

image

Philipp suggested panel.Pane(Example.param, rename={'unbounded_float': 'My float variable'})

Allow Param panes to select specific Parameters

Right now, a Param panel will display all the Parameters of the Parameterized that it is given, as long as they are over the precedence threshold. This is a good default, but in many cases one wants to display only a subset of them, either because only those Parameters are relevant, or because one wants to put some subgroup of the Parameters in one spot on the page, and the others in another.

To allow displaying subsets, we could add an argument to pane.Param to whitelist or blacklist a given set of Parameters in a particular instantiation, then could have multiple instantiations per Parameterized at different spots on the page. A whitelist would presumably be provided as a Python list, in which case the order of Parameter names would be respected as the order of the widgets to instantiate.

Supply compositional reprs

One issue that cropped up discussing #124 is that panel does not currently supply compositional reprs allowing you to see the structure of the object. These probably can't be fully declarative (i.e evalable reprs) but some representation of the composition would be very useful (the obvious example of such reprs are those I am familiar with in holoviews).

Panel requires bounds to be set

As discussed at our meeting...

minimum_size = param.Integer(default=10)

Will produce an un-editable widget (just some text on the screen). This also happens with param.Number. I believe it has something to do with None bounds.

panel server failure

The following works in the notebook:

import numpy as np
import holoviews as hv
hv.notebook_extension('bokeh','matplotlib')
from holoviews.streams import SingleTap

import param, panel as pn
pn.extension()

class SelectionFail(param.Parameterized):
    typ = param.Parameter(default='curve')

    @param.depends('typ', watch=True)
    def view(self):
        tap      = SingleTap(transient=True)
        
        def react_to_tap(x,y):
            if x is not None and y is not None:
                if   self.typ == 'curve':   h = hv.Curve  ( ((x,0),(y,0)) )
                elif self.typ == 'scatter': h = hv.Scatter( ((x,0),(y,0)) )
                return h.redim.range(x=(-1,1), y=(-1,1)).relabel('{},{}'.format(x,y)).options(show_grid=True)
            else:
                if   self.typ == 'curve':   h = hv.Curve  ( ((0,1),(0,1)) )
                elif self.typ == 'scatter': h = hv.Scatter( ((0,1),(0,1)) )
                return h.redim.range(x=(-1,1), y=(-1,1)).relabel('{},{}'.format(x,y)).options(show_grid=True)

        return hv.DynamicMap( react_to_tap, streams=[ tap ])

widgets  = pn.widgets.Select(options=['curve','scatter'])
fail     = SelectionFail(name="Ouch")
widgets.link(fail, value='typ')
pn.Column( 'Click on the plot or change plot type', widgets, fail.view ).servable()

when served with panel serve --show Fail.ipynb however,
the app fails as soon as the typ selection is changed

Unreliable ggplot support

The current support for R ggplot visualizations uses tempfile.mktemp(), which is subject to race conditions because it uses a filename rather than a file handle. Perhaps as a result, the plots currently work fine for a few widget clicks, but then begin to fail. Maybe the ggplot interface can be changed to write directly to an in-memory object, as in this code?

from rpy2.robjects.packages import importr, data
import rpy2.robjects.lib.ggplot2 as g2
from rpy2.ipython.ggplot import image_png

datasets = importr('datasets')
mtcars = data(datasets).fetch('mtcars')['mtcars']
gp = g2.ggplot(mtcars)
pp = (gp + g2.aes_string(x='wt', y='mpg')
         + g2.geom_point(g2.aes_string(colour='qsec'))
         + g2.scale_colour_gradient(low="yellow", high="red") 
         + g2.geom_smooth(method='loess') 
         + g2.labs(title="mtcars", x='wt', y='mpg'))

image_png(pp, width=800, height=300)

From what I could see, this version doesn't use a temporary file; see lib/python3.6/site-packages/rpy2/ipython/ggplot.py (seems based on BytesIO). Can we use this approach instead?

Add examples of Graph/Network rendering

As of PRs #85 and #88, dot-based graph plots are now supported in Panel, either as SVG representations of GraphViz Graph/DiGraph objects, or as PNG representations of NetworkX objects created by nxpd.draw():

from graphviz import Graph
hw = Graph('hello', format='svg')
hw.edge('Hello', 'World')

import networkx as nx, nxpd
G = nx.cycle_graph(4, create_using=nx.DiGraph())
d = nxpd.draw(G, show='ipynb')

import panel as pn
pn.Row(hw, "Text", d, "Text")

image

Remaining issues:

  • Spacing for these objects is not currently automatically determined
  • It would be nice to provide graph examples in the User Guide

Errors in watch handlers are swallowed, and are hard to debug

I stumbled upon an issue that the watch handler for a param attribute is properly called, but swallows exceptions (when using panel inside a Jupyter Lab notebook). This makes it hard to see where a code errors, since there is no indication for where or what went wrong.

image

Is there a way make those errors bubble or to ease the debugging experience here?

Cannot read property 'hasOwnProperty' of undefined

import param, panel as pn
pn.extension()

class A(param.Parameterized):
    a = param.Number(7, bounds=(0,10))
    b = param.Boolean(True)
    c = param.String("Yes")

class B(param.Parameterized):
    p = param.ObjectSelector(A())

    @param.depends("p.param", watch=True)
    def fn(self):
        print("fn")
        pass
    
b=B()
pn.Row(b)

image

b.p.a=8

image

pip install fails

"pip install panel" completes with "Successfully installed panel-0.0.1 param-1.8.1", but the package can not be import afterwards. Notably, '.../site-packages/panel-0.0.1.dist-info' directory does get created, but the actual package directory '.../site-packages/panel' does not. This is from python3.6 virtual environment.

Feature Request: support param.DataFrame

I'd like to have a larger tabular configuration inside a Dashboard, so param.DataFrame seems to match my requirements quite well, but currently panel does not support showing DataFrame params:

import param
import panel as pn
import pandas as pd

pn.extension()

class Config(param.Parameterized):
    tabular = param.DataFrame(pd.DataFrame({'a': [1,2,3], 
                                            'b': ["foo", "bar", "baz"]}), 
                              columns={'a', 'b'})
    
pn.Pane(Config)
TypeError: 'DataFrame' objects are mutable, thus they cannot be hashed

I'm currently using hv.Table(..).options(editable=True) and an Apply Action as a work-around,
but that break a clean design.

As I'm not familiar with javascript it's hard for me to develop a feature like this myself.

Proposal: Make Panel objects explicitly compositional

Panel has been very useful for me in my work already, making it easy to compose displayable items into an overall app or dashboard arrangement. However, the ability to decompose and recompose these objects has some obvious limits that I have found very frustrating. These limitations make it very hard to follow a code-development process of getting something working and adapting it into whatever final form I want, without having to go backwards and start over. To me, the compositional nature of Panel is the whole point of the library, and so I want to work to eliminate any of these limitations on composition, decomposition, and recomposition.

For instance, in my trivial clifford interact example, I can call pn.interact(clifford_plot, n=(1,20000000), colormap=ps) to get an app like:

image

It's great that I can do that in one line, and often that's all I'll need. But let's say I want to go ahead and publish that as a dashboard -- now I want to swap the order to be left to right, add some instructions, and so on. If I look at what's displayed, I see a composite object of two items (a box of widgets, and a plot), and that's what I want to be able to work with now that I have them. (I may even want to pull some of the widgets out individually, move some up to the top, some to the right, throw away others, etc., but that issue can be discussed later even though it's probably covered under the same principles listed here.)

When I look, I see that the object returned in that call is <interactive interactive00116>, which is opaque but turns out to be a type of Pane. Unlike Layouts, Panes are not inherently a container or compositional object, yet this one and several others (yt and HoloViews, at least) return something that looks like it would be a container, either with multiple plots or with a plot and some widgets. If I study the code for the implementation of this particular pane, I can find that it has a layout attribute with the two items on it (widgets and plot) that I can see, and it turns out that I can indeed pull out those items and build a new layout:

i=pp.interact(clifford_plot, a=(-2,2), b=(-2,2), c=(-2,2), d=(-2,2), 
              n=(1,20000000), colormap=ps, panel_layout=pp.Row)
logo = "https://tinyurl.com/y9c2zn65/logo_stacked_s.png"
text = """Try out the widgets!"""

i.layout[0] = pp.Column(logo, text, i.layout[0])
i.servable()

image

It's great that that works, but discovering that took some great leaps of faith and violated my intuitions about how things should work. I was expecting the object returned to be explicitly compositional already, so that I could simply decompose it and recompose it as I like, without having to figure out the special layout object inside it. Otherwise, how would I know if recomposing it that way is ok, or if the interact object had some special properties at the combined level that will now no longer work? Both of those issues (the lack of discoverability and the lack of knowing what might be lost when unpacking) strongly discourage such unpacking, and thus limit my ability as a user to exploit the object that I created. Those forces would push me to avoid interact and instead build up my object compositionally, so that I can have control over how it is recomposed, making me abandon the convenience of interact -- I'd treat interact as a dead end rather than as a quick start.

What I would like is for Panel to be explicitly compositional wherever it appears to be compositional. For this to work, magic convenience functions like interact() would need to constrain their magic (wherever possible) to the construction, not the operation, of a composite object, and then return something that is clearly and explicitly a composite. Here, I think what's returned should just be a container of the two items I can see (widgets and plots), ready for me to do whatever I like with it. That's the only way I can see that a Panel user will be able to naturally and easily go on a trajectory from seeing some initial thing to arranging precisely the dashboard they want to have. The same issue applies to the HoloViews pane and any other pane that returns objects that a user might want to rearrange or control (multiple plots, plots with widgets, multiple widgets, etc.) -- if it's something the user could want to rearrange and recompose, then it should be explicitly a container object of reusable objects from the start, not a pane that inside includes some container but also adds some magic (if possible!).

Allow editing slider values as text, at least for DynamicMap

For a bounded DynamicMap, e.g. this cell from the DynamicMap tutorial:

image

the slider values can be set only to the precision supported by the slider, which is a couple of digits when dragging the slider, and (for this example at least) a precision of 0.001 when using the left and right arrow keys to adjust the value more precisely. It would be great if we could click in the text box and type an arbitrarily precise value, since the underlying object supports any such value.

Obviously, such a feature would require error checking, such as cropping the value to the bounds of the range, but that doesn't seem onerous to implement. It makes the most sense for a DynamicMap, because it's the only way I can see to provide arbitrarily precise values, but it could also be implemented for a regular HoloMap by snapping the value to the nearest defined value.

Batch update for parameter values

The reactivity that comes from watching parameter values is really helpful, but it causes problems when changing a lot of parameter values at once. E.g. for this code:

from time import sleep
import param, panel as pp
pp.extension()

defaults = dict(a=0.2, b=0.4, c=0.6, d=0.8)

class C(param.Parameterized):
    a = param.Magnitude(defaults["a"])
    b = param.Magnitude(defaults["b"])
    c = param.Magnitude(defaults["c"])
    d = param.Magnitude(defaults["d"])
    
    reset_ = param.Action(lambda x: x.reset())
    
    @param.depends('a', 'b', 'c', 'd')
    def view(self):
        sleep(1)
        return "<H2>"+str((self.a, self.b, self.c, self.d))+"</H2>"

    def reset(self):
        self.set_param(**defaults)

c=C(name="")
pp.Row(c,pp.Column("<H1>Current&nbsp;values:</H2>", c.view))

image

If I press the "Reset" button, it changes the four parameters one... by... one..., calling the view() function each time. Here view() is just a sleep call, but in practice in my real code it's a lengthy computation to plot something complicated, which is where I noticed the issue. Oddly, in my real code I also was able to watch the widget values update one by one, but here the widgets are failing to be updated by the reset call even though the parameter values have changed:

image

Note: this code depends on #21 so that the view() return type is representable, but I saw the problems with other Pane types before that PR.

Add container option for subpanel_layout

Right now, the Param pane supports an option subpanel_layout, which can be set to a Row, Column, or Tabs layout, which determines what happens when someone clicks on '..." to edit the subobject's parameters (i.e. either opening up a new set of widgets in a row, in a column, or in a different tab). There's a fourth useful option, which is to open up those widgets in a specific container provided explicitly, so that users can put that container anywhere on the visible page. There would need to be a way to pass in that container, and there might need to be a way to specify which parameter this container applies to (either all subobjects, or maybe just one specific subobject, with another container provided for each other subobject).

BUG: pip install panel version doesn't import

I'm running through the Scipy 2018 PyViz tutorial and command 16 on the 01_Workflow_Introduction notebook uses panel.

When I use a pip install version of panel, I get:

import panel
[Out]:ModuleNotFoundError: No module named 'panel'

The pip install version is panel-0.0.1 and I confirmed that it was installed in my environment with conda list and pip freeze.
panel

However, when I install with conda using conda install panel -c pyviz, the import works with no issues.

panel_conda

Bokeh widgets extend outside of the space reserved for them

As shown in #50 and #42, a Bokeh widget gets some space reserved for it by Panel, but it doesn't respect that space:

import param

class C(param.Parameterized):
    a = param.Number(1.7, bounds=(-2, 2))
    cmap = param.ObjectSelector(kbc,[kbc,fire])

    def view(self):
        return self.a, self.cmap[0]
c=C()
pp.Row(c.param,c.view)

image

Is there some flag we can pass to Bokeh that will make it truncate the representation ( before "(1.7," in this case), or else resize the box to fit it (not appropriate in this case, but appropriate for some of the cases in #42)?

Add panel entry point

Passing along a suggestion that I think is a good one: so that Panel users won't be affected if we eventually do split out the parts of Bokeh that Panel uses, we should add an entry point for launching servers like panel serve file.py. For now that would just delegate to bokeh serve file.py, but we could then change that to whatever the underlying Bokeh infrastructure becomes, which could even be part of Panel itself.

Cursor keys in notebooks

In Chrome, clicking on a Panel slider widget will turn the outline blue, and then the slider can be controlled by the left and right arrow keys:
image
This is very convenient, but the cursor keys are also used to scroll horizontally in a Jupyter notebook output cell, whenever the output is larger than the area of that cell. In that case, the output scrolls whenever the widget is adjusted using the arrow keys, which is very confusing and distracting. Is there some way for the widget to swallow the keypress events once it uses them so that they aren't also available to Jupyter to control the horizontal scrolling of the output area? If one does want to scroll that area, one can simply not have a widget highlighted, and it seems like once the widget is highlighted only that widget should be processing the keypress events.

Creating a dynamic map with datetime range

import holoviews as hv
import pandas as pd
hv.extension('bokeh')

def timeline(time):
    return hv.VLine(time)

vline = hv.DynamicMap(timeline, kdims=['time']).redim.range(time=(pd.datetime(1999, 5, 1), pd.datetime(1999, 6, 1)))
vline

doesn't seem to work

but if I change it from datetime to integer
vline = hv.DynamicMap(timeline, kdims=['time']).redim.range(time=(0, 50))
That works.

Cannot modify some parameters in Tabs

If using the Tabs layout, some parameters (param.Number and param.String, possibly others) will add an (invalid) to the param name if a user changes the value. Querying the param in a separate cell will give the original value, not the user-modified value.

import panel
import param
from panel.layout import *
panel.extension()

class BaseClass(param.Parameterized):
    x                       = param.Parameter(default=3.14,doc="X position")
    y                       = param.Parameter(default="Not editable",constant=True)
    string_value            = param.String(default="str",doc="A string")
    num_int                 = param.Integer(50000,bounds=(-200,100000))
    unbounded_int           = param.Integer(23)
class foo(param.Parameterized):
    float_with_hard_bounds  = param.Number(8.2,bounds=(7.5,10))
    float_with_soft_bounds  = param.Number(0.5,bounds=(0,None),softbounds=(0,2))
    unbounded_float         = param.Number(30.01,precedence=0)
    hidden_parameter        = param.Number(2.718,precedence=-1)
    integer_range           = param.Range(default=(3,7),bounds=(0, 10))
    float_range             = param.Range(default=(0,1.57),bounds=(0, 3.145))

tabs = Tabs(('Line', foo), ('Scatter', BaseClass), width=350)
tabs

Release checklist

It's time to release this package! Before we do, there are a few more tasks left:

  • Add more content to the website:
    • Introduction (adapt from README.md)
    • Installation
    • Link in the FAQ
    • Add screenshots
    • Jim: review user guide
    • Add a gallery? (Maybe too ambitious for right now)
  • Add website theme (colors, fixing logo spacing)
  • Add interact() function
  • Finalize the name (it's now or never!)

Either before or soon after release, we should also:

  • Add links from parambokeh and paramnb pointing to Panel
  • Replace parambokeh and paramnb examples on holoviews.org and pyviz.org

Please edit these lists to add anything I missed.

Dashboard issues

I'm developing a couple of Datashader-based Panel apps/dashboards as self-contained examples and to work out some of the issues that arise in more complex settings, without getting into the added complexity of intra-plot linkages that comes with HoloViews/Bokeh plots and streams.

The first, simple app works fine with conda install -c pyviz pyviz:

https://anaconda.org/jbednar/datashaderclifford

It has no fatal flaws and works well in both a notebook and Bokeh Server context, with simple, clean, and clear code. It does have some non-fatal issues that I've worked around:

  • Non-class version: It doesn't seem like a class should be needed for such a simple case, and I suspect that many potential users will get put off by having to write a class from the very start. So it would be good to have a version of this notebook that doesn't require writing a class, at least as a comparison. Using something like ipywidgets' interact function might be a good approach.
  • Image sizing: I used a URL-based image (logo = "https://tinyurl.com/y9c2zn65/logo_stacked_s.png") because it works well in a notebook (avoiding issues finding a file on the filesystem) and on the server. However, I had to put an image of the correct final size at that URL, because if I try reducing the displayed size of a larger image by embedding it in a explicit pane.PNG(url, width=..., height=...), it was resized only in the notebook but it instead covered up the rest of the page on Bokeh Server. Not sure if that's a Bokeh or Panel issue.
  • Secifying a widget pane as .param: Right now, a Panel will show widgets for a Parameterized object c supplied to it (as in pp.Row(pp.Column(logo, c), c.view)), but I found that to be quite unintuitive -- objects can have many different things one might want to use as the representation, and it's surprising that I get a set of widgets by default for object c. I think it would be much clearer and more explicit if the user typed pp.Row(pp.Column(logo, c.param), c.view) instead, clearly laying out the widgets in the column and the graphical representation of the object in the row.

The second app needs Param and Panel master (in addition to pyviz as above), and is available at

https://anaconda.org/jbednar/datashaderattractors

This more complex dashboard mostly works, but there are still some fatal problems:

  • No object selection: param.ObjectSelector is meant for selecting an object from a list of allowed objects (here attractors), but it currently provides just one buttton, which when clicked pops up a widget box with the parameters of the existing object:
class Attractors(param.Parameterized):
    attractor = param.ObjectSelector(attractors["Clifford"], attractors)


The subobject's parameters can be then edited, but the object itself cannot be selected. Moreover, if the object is selected manually in a different notebook cell (as e.g. ats.attractor=attractors["Svensson"]) the button text never updates to reflect that change (always saying "Clifford Attractor").

  • No param-change batching: When selecting different sets of known-good parameters using the current widget, if the new attractor object happens to be the same one that is currently being displayed, the view gets redrawn once for every parameter that is changed, which is very slow and shows bogus intermediate patterns, making the app seem buggy. Instead there needs to be a mechanism for batching changes and updating the view only on completion.

There are also several non-fatal problems that have been worked around but which shouldn't have had to be worked around:

  • Subobject widgets overwrite other panes: I've worked around this by wrapping the ats parameters in a pane and explicitly selecting column-based layout, because if I don't (pp.Row(pp.Column(logo, ats), ats.view)), the subobject parameters overwrite the next item in the column:
    image
    Similar issues occur in the column layout if there is anything below the main app's widgets in the column (pp.Row(pp.Column(logo, widgets, params), ats.view)):

    Ideally the params object wouldn't have needed to be a parameter in Attractors at all, just provided to Panel to get some widgets, but I had to make it a Parameter so that the column layout would respect it.

  • Having to use bogus parameters to force updates: Maybe someone can think of a better way to do this, either with current Panel and Param or by adding some clean mechanism, but I found I had to add a bogus hidden parameter parameters_changed to achieve the behavior I wanted, which was for the display to update whenever a new current parameter set was changed with a widget, independently of the display updating whenever the parameters of the current attractor object were modified with widgets. I.e., I wanted the contents of _update_from_parameters() to be executed when (and only when) the ParameterSet's parameters were changed, and then for view() to be triggered. I couldn't see any direct way to trigger view(), so I added the dummy parameter and made view() depend on that, so that it would be triggered appropriately to get an update. Whereas if I had put the _update_from_parameters() code into view() itself, then I found no way to know whether view() was triggered by an update to params or by an update to one of the attractor parameters. (And the code needs to do different things in those two cases, i.e. either overwrite the attractor parameters or not.) So it's working, but it seems like a hack and I'm not sure how to avoid it. One could of course make _update_from_parameters() and view() both depend on params.param, but then there would be an ordering problem -- view must then always be called second, after the parameters have been updated. Jean-Luc suggested we could maybe specify a precedence for such dependencies, to control the order they are run in, and that does seem like it would work in this case, but I'd hope for some less drastic solution.

  • No subobject parameter editing for ClassSelector: My first version of this app used ClassSelector rather than ObjectSelector, which does allow the type to be selected, but did not allow the subobject parameters to be selected:
    image
    When is_instance=True, it behaves the same as above -- allows subobject parameters to be edited, but not allowing the class to be selected.

Pane vs Row/Column building

I'm struggling to understand why I would use Pane instead of just using Row and Column to build up a pane. The docs pages on the two are completely exclusive of any mention of one other.

  1. Can I mix and match?
  2. What benefit is one over the other?

DatetimeInput range checking is not working properly

    date_start = DatetimeInput(name='Start date', value=dt.datetime(2018, 9, 23),
                               start=dt.datetime(1900, 1, 1), end=dt.datetime(2050, 9, 30),
                               format='%Y-%m-%d %H:%M:%S')

Produces the error:

c:\projects\ers\github\es-workflows\updates\es-workflows\es_workflows\adh\model.py in model_times()
     36     date_start = DatetimeInput(name='Start date', value=dt.datetime(2018, 9, 23),
     37                                start=dt.datetime(1900, 1, 1), end=dt.datetime(2050, 9, 30),
---> 38                                format='%Y-%m-%d %H:%M:%S')
     39     date_stop = DatetimeInput(name='Stop date', value=dt.datetime(2018, 9, 24),
     40                               start=dt.datetime(1900, 1, 1), end=dt.datetime(2050, 9, 30),

c:\projects\ers\github\panel\master\panel\panel\widgets.py in __init__(self, **params)
    292             end = datetime.strftime(self.end, self.format)
    293             raise ValueError('DatetimeInput value must be between {start} and {end}, '
--> 294                              'supplied value is {value}'.format(start=start, end=end, value=value))
    295 
    296     def _process_property_change(self, msg):

ValueError: DatetimeInput value must be between 1900-01-01 00:00:00 and 2050-09-30 00:00:00, supplied value is 2018-09-23 00:00:00

"panel examples" command does not work

Following http://panel.pyviz.org/#usage, I couldn't get the examples:

$ panel --install-examples
usage: /home/ceball/mc3/envs/panel/bin/panel [-h] [-v] {html,info,json,png,sampledata,secret,serve,static,svg} ...
/home/ceball/mc3/envs/panel/bin/panel: error: unrecognized arguments: --install-examples

I was going to add pyct to make panel examples available (in common with other pyviz projects), but I'm not sure how I ought to do that, because there doesn't seem to be any existing argparse parser I can add to - bokeh's main() is just called.

Centering difference between panel and raw bokeh models

This issue seems to happen when using templates. Here is a small app composed of three files to demonstrate the issue by toggling self.use_panel in CenteringApp (a zip of the three files can be found here):

Run the app with python launch.py:

launch.py:

from dashboard import CenteringApp

if __name__ == '__main__':
    from bokeh.server.server import Server
    server = Server({
        '/center': CenteringApp(name = 'Weird centering')()})
    server.start()

    server.io_loop.add_callback(server.show, "/center")
    server.io_loop.start()

else:
    print("Run dashboard using 'python launch.py'")

dashboard.py (note holoviews isn't actually relevant here and could be stripped out too):

import panel
import os
import param

from jinja2 import Environment, FileSystemLoader

from bokeh.application.handlers import FunctionHandler
from bokeh.application import Application
from bokeh.models import Div

import holoviews as hv

renderer = hv.renderer('bokeh')
renderer.mode='server'

class App(object):
    """
    Mixin class to handle boilerplate necessary to turn holoviews
    objects into Bokeh applications.
    """

    name = 'Dashboard'

    def load_template(self, **params):
        appindex = os.path.join('.', 'index.html')
        env = Environment(loader=FileSystemLoader(os.path.dirname(appindex)))
        self.template = env.get_template('index.html')

    def apply_theming(self, doc):
        doc._template = self.template

    def doc_handler(self, doc):
        """
        Callback passed to the Bokeh FunctionHandler.
        """
        viewable = self.viewable(doc)
        if isinstance(viewable, hv.Dimensioned):
            doc = renderer.server_doc(viewable, doc=doc)
        else:
            doc.add_root(viewable)

        doc.title = self.name
        return doc

    def __call__(self):
        """
        Call to return a Bokeh Application to serve.
        """
        return Application(FunctionHandler(self.doc_handler))


class CenteringApp(param.Parameterized, App):

    def __init__(self, **params):
        super(CenteringApp, self).__init__(**params)
        self.load_template()

        self.use_panel = False # Change this
        text = "Should be in the center" * 8
        self.text = "<div style='text-align:left'>{text}.</div>".format(text=text)

    def viewable(self, doc=None):
        self.apply_theming(doc)
        if self.use_panel:
            obj = panel.Pane(Div(text=self.text, width=1300))
            return obj._get_root(doc)
        else:
            obj = Div(text=self.text, width=1300)
            return obj

And finally index.html:

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8">
        {{ bokeh_css }}
        {{ bokeh_js }}
        <style>
          html {
            width: 100%;
            height: 100%;
          }
          body {
            width: 100%;
            height: 100%;
            margin: auto;
          }
        </style>

        <style>
/* body background */
body {
  background-color: white;
  margin: 0px;
}

        </style>
    </head>

<body>
  <div title="alt text test" class="topnav" style='padding-top: 2em;padding-right: 2em'>
      <h1 id="title">&nbsp;Centering issue</h1>
  </div>
  <center>
    <div>
        {{ plot_div }}
        {{ plot_script }}
    </div>
  </center>
</body>

</html>

Using the raw bokeh model (self.use_panel=False):

image

Using panel (self.use_panel=True):

image

I would expect these two to look the same. I know I shouldn't be using the <center> tag (and will try specifying it with CSS shortly) in the template but I doubt that is the issue.

widget callback issue

print( pn.__name__, pn.__version__)   # prints:    panel 0.2.0a3.post4+g82e3f43
w = pn.widgets.TextInput(name='A widget', value='A string')
w.param.watch(print, 'value')
w

I can't come up with a reliable sequence, but the problem appears frequently:
alternate between

  • setting a string in the textinput widget
  • setting w.value to a different string

eventually, the textinput widget is not updated, although its value is indeed changed.
It may take setting w.value to a different string more than once before the widget text
updates again as before

One sequence that does seem to 'work':

  • set up w
  • change the widget text to '12'
  • set w.value='foo'
  • change the widget text back to '12'
  • set w.value='foo'
    at this point the widget does not update the display

Tabs index out of range when appending

I really can't explain this bug very well. Somehow in the server mode, when appending two panel elements to a tabs, it gives back a list index out of range. I will again show you a simple example:

jupyter_append_to_tabs
In Jupyter Notebook everything is working fine.

server_append_to_tabs
In the server mode, it appends the first tab, but for the second one following error is returned:

.
.
.
 File "c:\anaconda3\lib\site-packages\param\__init__.py", line 1220, in __set__
    super(List,self).__set__(obj,val)
  File "c:\anaconda3\lib\site-packages\param\parameterized.py", line 603, in __set__
    obj.param._call_watcher(s, event)
  File "c:\anaconda3\lib\site-packages\param\parameterized.py", line 1055, in _call_watcher
    watcher.fn(self_._update_event_type(watcher, event, self_.self_or_cls.param._TRIGGER))
  File "d:\bachelor_thesis\panel\panel\layout.py", line 66, in set_value
    msg['objects'] = self._get_objects(model, old, doc, root, comm)
  File "d:\bachelor_thesis\panel\panel\layout.py", line 295, in _get_objects
    child = old_children[old_objects.index(pane)]
IndexError: list index out of range

Again I am not sure, whether this is a bokeh bug or a panel bug, but it is really strange. I am currently working on a dashboard and I need to append elements to a tab dynamically. If any more info is needed pls tell me.

Why are my panes spaced so far apart?

In #21, the code:

import param, panel as pp, numpy as np
from panel import pane
pp.extension()

pp.Row("<i>string</i>",
          pane.Pane("<b>Pane</b>"), 
          pane.HTML("<u>HTML</u>"), 
          pane.Str( "<i>Str</i>"),
          pane.Str(np.zeros((5,3)), height=80))

is shown generating the output:

image

However, if I check out the panel commit corresponding to that revision (655b902), I get the same widely spaced output that I am getting for that code on current Panel master:

image

Can anyone think of why these results would be different, for what I think is the same panel code? What I'd like is something in the middle, i.e. with a parameter for inter-pane spacing that I could set to 20px or so, but otherwise like the old behavior.

Should Row and Column have width and height parameters?

Right now, Panes have width and height (most of them), but it looks like rows and columns only adapt to the size of the items within them. Can Row and Column support width and height (defaulting to None) so that people can reserve space for a variable number and size of thing that might be in them?

Of course, we'd immediately want control over what happens to the things inside relative to the space reserved (i.e. centered or justified in either direction, for both x and y), but we probably already need that anyway just because not everything in the same row or column will be the same width.

Behavior without @depends

If I use this library with Datashader, I can easily explore parameter values:

import numpy as np, pandas as pd, datashader as ds
from datashader import transfer_functions as tf
from datashader.colors import inferno, viridis
from numba import jit

@jit
def clifford_trajectory(a, b, c, d, x0, y0, n):
    xs, ys = np.zeros(n), np.zeros(n)
    xs[0], ys[0] = x0, y0
    for i in np.arange(n-1):
        xs[i+1] = np.sin(a * ys[i]) + c * np.cos(a * xs[i])
        ys[i+1] = np.sin(b * xs[i]) + d * np.cos(b * ys[i])
    return pd.DataFrame(dict(x=xs,y=ys))

def clifford_plot(a, b, c, d, n=10000000, cmap=inferno[::-1]):
    cvs = ds.Canvas(plot_width=400, plot_height=400)
    agg = cvs.points(clifford_trajectory(a, b, c, d, 0, 0, n), 'x', 'y')
    return tf.shade(agg, cmap=cmap)
from panel import Row
import param

class DatashaderPanel(param.Parameterized):
    a = param.Number(1.7, bounds=(-2, 2))
    b = param.Number(1.7, bounds=(-2, 2))
    c = param.Number(0.6, bounds=(-2, 2))
    d = param.Number(1.2, bounds=(-2, 2))

    @param.depends('a', 'b', 'c', 'd')
    def view(self):
        return clifford_plot(self.a, self.b, self.c, self.d)
    
panel = DatashaderPanel()
Row(panel, panel.view)

image

Here the plot depends on all the available Parameters, which I've declared with the new @param.depends decorator. I would have thought that the default behavior would have been to depend on all parameters already, though? Without any explicit declaration otherwise, it seems that any given method could have results that change when any parameter changes, so I would have thought it could infer what is declared explicitly here (that all parameters are depended on). However, if I comment out the @param.depends, I get a KeyError: 'dependencies', presumably indicating that there is code that requires @depends. Is there a clear reason that @depends is required here?

Subobjects overwrite other panes

When a Parameter has a Parameterized as a value, Panel will allow a separate pane to be opened to edit the parameters of that subobject. However, no space is created for the new pane, causing it to overwrite adjacent items:

import param, panel as pn
pn.extension()

class A(param.Parameterized):
    a = param.Number(7, bounds=(0,10))
    b = param.Boolean(True)
    c = param.String("Yes")

x = A(name="A")    

class B(param.Parameterized):
    sub = param.ObjectSelector(x)

y = B(name="B")

pn.Column(pn.Row(y,"https://panel.pyviz.org/_static/logo.png"))

image

pn.Column(pn.Param(y, subobject_layout=pn.Column),
          "https://panel.pyviz.org/_static/logo.png")

image

Overwriting like this could be appropriate in some cases, but in general it would be better if the other items moved aside to make room for the widgets. I believe this will have to wait on the Bokeh layout refactor.

TextInput stuck when running heavy calculations on server

When I am trying to run heavy calculations and change the value of the TextInput beforehand, the value change somehow seems to be stuck. I have prepared a small example:

pn.extension()

def heavy_calculation():
    epos_indexed = epos[0:10_000_000]
    cropped_index = np.where((epos_indexed['dx'] >= -5) & (epos_indexed['dx'] <= 10) 
                              & (epos_indexed['dy'] >= -10) & epos_indexed['dy'] <= 15)[0]
    # print(list(cropped_index))
    cropped_epos = epos.iloc[list(cropped_index)]

class Test:
    
    def __init__(self):
        self.button = pn.widgets.Button(name='Click Me!')
        self.txt = pn.widgets.TextInput(name='Misused as Log:')
        self.button.param.watch(self._button_click, 'clicks')
        
    def _button_click(self, clicks):
        self.txt.value = 'Start heavy calculation...'
        heavy_calculation()
        self.txt.value = 'Heavy calculation finished.'
    
    def view(self):
        return pn.Column(self.button, self.txt)

Test().view()

When I run this on a jupyter notebook, there is no problem:

jupyter_heavy_calc

But when I deploy it using panel serve --show, the first message on the TextInput widget does not appear:

server_heavy_calculation

For deploying I have downloaded the notebook as a python file and edited Test().view() to Test().view().server_doc().

epos is just a pandas DataFrame containing 36,000,000 elements.
I am not sure, whether this is a panel or bokeh problem.

A tooltip on javascript widgets could be useful

One static thing that could be mentioned on hover is that 'P' plays forward and 'R' plays in reverse.

This would already be useful, but if you want to be fancier you could generate dynamic tool tips that also supply some information about the dimensions that are varied over.

Edit: This isn't really specific to slider widgets really...

depends(obj.param) should not require obj to be expanded

Right now for the Param pane, if a Parameterized declares a wached dependency on a subobject's .param, the callback is triggered only if the subobject is expanded in the panel. E.g. for:

class A(param.Parameterized):
    p = param.ObjectSelector(P())

    @param.depends("p.param", watch=True)
    def fn(self):
        pass

if one expands p by pressing ... and edits some parameter of p, then fn() is called, but fn() is (erroneously) not called if p is not expanded and someone separately does something like p.a=2. Given that the dependency is declared independently of any GUI, I think fn() should be called whenever p's parameters change, whether or not the item is expanded in a GUI.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.