param icon indicating copy to clipboard operation
param copied to clipboard

Provide better ansi color codes. __doc__ strings are hard to read and not very "modern".

Open MarcSkovMadsen opened this issue 2 years ago • 9 comments

The param __doc__ string is powerful. But it is hard to read due to the colors chosen.

The blue on a black background is especially hard to read. But also the red can also be hard to read. And the green just does not look modern.

As a consequence I am not really finding my self using the __doc__ string. Which makes it harder to use the HoloViz ecosystem. Especially hvplot and HoloViews where a lot of needed information is in the __doc__ string and not on their web sites or something you can find via intellisense in your editor.

Can you read the blue below? 😄

image

from panel.template.fast.list import FastListTemplate
import param
import panel as pn
import ansiconv

ACCENT_COLOR = "#0072B5"
pn.state.sizing_mode="stretch_width"

class ClassificationPlot(param.Parameterized):
    """The ClassificationPlot plots the output of a classification, i.e. the *labels*
    and their *score*.
    """
    output_json = param.List(doc="""
    The ouput of the classification""")
    options = param.Dict(constant=True, doc="""
    The configuration used to construct the plot""")
    color = param.Color(ACCENT_COLOR, doc="""
    The colors of the bars""")
    theme = param.Selector(default="default", objects=["default", "dark"], doc="""
    The theme, i.e. 'default' or 'dark'. This is automatically set from pn.state.session_args""")

plot = ClassificationPlot()
def to_html(txt):
    html = ansiconv.to_html(txt)
    css = ansiconv.base_css()

    html = f"""
    <style>{css}</style>
    <pre class="ansi_fore ansi_back">{html}</pre>
    """
    return pn.Column(pn.pane.HTML(html, width=1200, sizing_mode="fixed"), height=500, scroll=True)

card = pn.layout.Card(
    to_html(plot.__doc__), header="ClassificationPlot", sizing_mode="stretch_width", collapsed=False
)

FastListTemplate(
    title="ClassificationPlot", main=[card], accent_base_color=ACCENT_COLOR, header_background=ACCENT_COLOR
).servable()

Additional Context

We should be able to get a lot of inspiration from Rich. That output is very readable.

image

MarcSkovMadsen avatar Nov 17 '21 06:11 MarcSkovMadsen

Proof of Concept

Ok. I guess the colors chosen have been chosen for a light background? I would still say they are not good.

This below is not great. But for me its better.

image

image

from panel.template.fast.list import FastListTemplate
import param
import panel as pn
import ansiconv

ACCENT_COLOR = "#0072B5"
pn.state.sizing_mode="stretch_width"

class ClassificationPlot(param.Parameterized):
    """The ClassificationPlot plots the output of a classification, i.e. the *labels*
    and their *score*.
    """
    output_json = param.List(doc="""
    The ouput of the classification""")
    options = param.Dict(constant=True, doc="""
    The configuration used to construct the plot""")
    color = param.Color(ACCENT_COLOR, doc="""
    The colors of the bars""")
    theme = param.Selector(default="default", objects=["default", "dark"], doc="""
    The theme, i.e. 'default' or 'dark'. This is automatically set from pn.state.session_args""")

plot = ClassificationPlot()

def get_css(background="#000000", color="#FFFFFF", red="#FF0000", green="#00FF00", blue="#0000FF", cyan="#00FFFF"):
    return f"""
.ansi_fore {{ color: {color}; }}
.ansi_back {{ background-color: {background}; }}
.ansi1 {{ font-weight: bold; }}
.ansi3 {{ font-weight: italic; }}
.ansi4 {{ text-decoration: underline; }}
.ansi9 {{ text-decoration: line-through; }}
.ansi30 {{ color: {background}; }}
.ansi31 {{ color: {red}; }}
.ansi32 {{ color: {green}; }}
.ansi33 {{ color: #FFFF00; }}
.ansi34 {{ color: {blue}; }}
.ansi35 {{ color: #FF00FF; }}
.ansi36 {{ color: {cyan}; }}
.ansi37 {{ color: {color}; }}
.ansi40 {{ background-color: {background}; }}
.ansi41 {{ background-color: {red}; }}
.ansi42 {{ background-color: {green}; }}
.ansi43 {{ background-color: #FFFF00; }}
.ansi44 {{ background-color: {blue}; }}
.ansi45 {{ background-color: #FF00FF; }}
.ansi46 {{ background-color: {cyan}; }}
.ansi47 {{ background-color: {color}; }}
"""

def to_html(txt, theme):
    html = ansiconv.to_html(txt)
    css = ansiconv.base_css()
    if theme=="dark":
        css = get_css(red="#ff8b8b", green="#8bFF8b", blue="#66b3ff")
    else:
        css = get_css(background="#FFFFFF", color="#000000", red="#8E0500", green="#19AF22", blue="#00008b", cyan="#17625F")

    html = f"""
    <style>{css}</style>
    <pre class="ansi_fore ansi_back">{html}</pre>
    """
    print(html)
    return pn.Column(pn.pane.HTML(html, width=1400, sizing_mode="fixed"), height=500, scroll=True)

try:
    theme = pn.state.session_args.get("theme")[0].decode()
except:
    theme = "default"
card = pn.layout.Card(
    to_html(plot.__doc__, theme=theme), header="ClassificationPlot", sizing_mode="stretch_width", collapsed=False
)

FastListTemplate(
    title="ClassificationPlot", main=[card], accent_base_color=ACCENT_COLOR, header_background=ACCENT_COLOR
).servable()

MarcSkovMadsen avatar Nov 17 '21 07:11 MarcSkovMadsen

Yes, the colors were originally chosen for visibility in a Jupyter notebook, defaulting to a white background. I'd be happy to accept a PR to update the colors in param/ipython.py#L32 to values that work well on both light and dark backgrounds. Unfortunately we can't even tell if a terminal supports ANSI, let alone what the background color might be (https://github.com/holoviz/param/pull/421).

To address the other issues you raise,

  • The contents of the help text should be generated and put on each project's website's reference gallery; that's a to-do item we have never taken care of and would be a great benefit to everyone!
  • Both for that purpose and simply for interactive use in Jupyter, I think param/holoviews/panel objects should have a full-featured HTML table representation like xarray's, which could then be embedded into the websites but also act as a much more usable representation interactively (e.g. allowing the verbose text for a given parameter to be shown or hidden on demand). I made a very basic start in https://github.com/holoviz/param/pull/425 but quickly ran into the problem of HoloViews and Panel objects already having an HTML representation.

I think moving forward on either or both of these fronts would be a great help to users.

jbednar avatar Nov 17 '21 19:11 jbednar

Might be a separate discussion, but I'd also be happy if the Parameters were ordered alphabetically by default, the help of Parameterized classes with lots of Parameters are difficult to parse.

maximlt avatar Nov 17 '21 19:11 maximlt

Agree on alphabetic. I have a function that reorders the output from pn.Param. It's so much less strain on your brain when parameters are sorted alphabetically.

MarcSkovMadsen avatar Nov 17 '21 19:11 MarcSkovMadsen

(Was editing my message but posting here now since you've added a comment Marc!)

+1 on improving the help!

Look how rich highlights alternating rows, I quite like that.

And as a comment here, I think that when I look at the help of a Parameterized object I most often go after a docstring (either I know there's a parameter and forgot its name or I'm looking for one that does what I want), and then maybe I have a look at the table. Somehow I almost never remember what the acronyms in the Mode column mean.

maximlt avatar Nov 17 '21 19:11 maximlt

+1 on moving the docstrings above the table. It is also most often the docstrings I am searching for. The table is nice to have.

One argument is also that if you are new to the framework seeing that table can be overwhelming. It takes time to understand and is not something you see in a normal docstring.

MarcSkovMadsen avatar Nov 18 '21 07:11 MarcSkovMadsen

Here's something from the web that suggests why we need to put the contents of hv.help on each gallery page:

https://stackoverflow.com/questions/70009010/customizing-hvplot-box

jbednar avatar Nov 18 '21 19:11 jbednar

I would also say the parameters are described in 3 places. That is too much. Personally I would prefer to just remove the initial ones.

image

(Solarized Theme)

MarcSkovMadsen avatar Nov 19 '21 06:11 MarcSkovMadsen

Agree. But we need to keep the init signature entry, since users can initialize a Parameterized class with non-param parameters:

class P(param.Parameterized):
    x = param.Number(default=10)

    def __init__(self, y, **params):
        x = y * 2
        super().__init__(x=x, **params)

I have the feeling the Parameter docstrings section could integrate all the info that is in the table above, which could then be removed entirely. It's already common usage to add the type and the default value next to the parameter name. image

maximlt avatar Nov 19 '21 13:11 maximlt