Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

No pn.help #3406

Open
MarcSkovMadsen opened this issue Apr 16, 2022 · 3 comments
Open

No pn.help #3406

MarcSkovMadsen opened this issue Apr 16, 2022 · 3 comments
Labels
type: docs Related to the Panel documentation and examples type: enhancement Minor feature or improvement to an existing feature
Milestone

Comments

@MarcSkovMadsen
Copy link
Collaborator

MarcSkovMadsen commented Apr 16, 2022

The VTK docstring mentions pn.help.

image

But there is no pn.help.

image

I think it would be a very good idea though and make it easier to use Panel. It would be similar to hv.help.

My suggestion though would be to make pn.help a Pane that can be included nicely into a data app, where you want to add documentation for some Parameterized Class/ Model you have created.

@MarcSkovMadsen MarcSkovMadsen added type: enhancement Minor feature or improvement to an existing feature type: docs Related to the Panel documentation and examples labels Apr 16, 2022
@MarcSkovMadsen MarcSkovMadsen added this to the Wishlist milestone Apr 16, 2022
@maximlt
Copy link
Member

maximlt commented Apr 19, 2022

Good catch! I guess this should be replaced by 'help(...)'?

The help pane is a good idea! It somehow overlaps with the idea of implementing a richer and more interactive help repr for Parameterized classes directly in Param. Except that Param's implementation shouldn't rely on Panel itself, but be pure front-end trickery.

@jbednar
Copy link
Member

jbednar commented Apr 19, 2022

Right. Once Datashader finally gets released (this week, I hope!), I'd like to revisit Param's stalled HTML repr PR (holoviz/param#425), as I think that will be really useful in docs and apps.

@MarcSkovMadsen
Copy link
Collaborator Author

MarcSkovMadsen commented May 6, 2022

An example that makes it easy to add the documentation of your model to your Panel app is shown below. You will need to install ansiconv.

I think its only inspiration. The real pn.help should be built from the ground up and not converting the ansi docs. That would make it possible to give the docs content, layout and style a make over.

panel-docstring-viewer.mp4
"""This module contains functionality to view docstrings of Parameterized Classes
"""
from typing import Dict, Tuple

import ansiconv
import panel as pn
import param

# Inspiration at https://iterm2colorschemes.com/
ANSI_THEMES = {
    "Solarized": {  # https://ethanschoonover.com/solarized/
        "default": {
            "background": "#fdf6e3",  # Background
            "color": "#657b83",  # Text
            "red": "#cb4b16",  # Parameters changed, 2nd row in table
            "green": "#859900",  # heading
            "blue": "#268bd2",  # Table Header, 1st row in table
            "cyan": "#2aa198",  # soft bound values
        },
        "dark": {
            "background": "#002b36",
            "color": "#839496",
            "red": "#cb4b16",
            "green": "#859900",
            "blue": "#268bd2",
            "cyan": "#2aa198",  # soft bound values
        },
    },
    "Tomorrow": {  # https://github.com/chriskempson/tomorrow-theme
        "default": {
            "background": "inherit",  # "#ffffff",
            "color": "#4d4d4c",  # Foreground
            "red": "#c82829",
            "green": "#718c00",
            "blue": "#4271ae",
            "cyan": "#3e999f",  # aqua
        },
        "dark": {
            "background": "inherit",  # "#1d1f21",
            "color": "#c5c8c6",
            "red": "#cc6666",
            "green": "#b5bd68",
            "blue": "#81a2be",
            "cyan": "#2aa198",
        },
    },
}

LAYOUT_PARAMETERS = {
    "background",
    "height",
    "width",
    "sizing_mode",
    "scroll",
    "min_height",
    "max_height",
    "min_width",
    "max_width",
}

def get_theme() -> str:
    """Returns the current theme: 'default' or 'dark'

    Returns:
        str: The current theme
    """
    args = pn.state.session_args
    if "theme" in args and args["theme"][0] == b"dark":
        return "dark"
    return "default"

def extract_layout_parameters(params: Dict) -> Tuple[Dict, Dict]:
    """Returns params, layout_params

    Args:
        params (Dict): Parameters provided to component

    Returns:
        Tuple[Dict, Dict]: params, layout_params
    """
    layout_params = {}
    non_layout_params = {}
    for key, val in params.items():
        if key in LAYOUT_PARAMETERS:
            layout_params[key] = val
        else:
            non_layout_params[key] = val
    if "name" in params:
        non_layout_params["name"] = layout_params["name"] = params["name"]
    return non_layout_params, layout_params


class DocStringViewer(pn.viewable.Viewer):
    """The DocStringViewer makes viewing the docstring of a Parameterized class easy and
    beautiful."""

    object = param.ClassSelector(
        class_=param.Parameterized,
        doc="""
    The Parameterized class to view
    """,
    )
    theme = param.Selector(
        default="default",
        objects=["default", "dark"],
        constant=True,
        doc="""
    The theme of the component: 'default' or 'dark.""",
    )
    palette = param.Selector(
        default="Tomorrow",
        objects=ANSI_THEMES.keys(),
        doc="""
    For example `Tomorrow` or `Solarized`.
    """,
    )
    _html = param.String(
        constant=True,
        doc="""
    The html representation of the doc string
    """,
    )

    def __init__(self, object=None, **params):  # pylint: disable=abstract-method, redefined-builtin
        params, layout_params = extract_layout_parameters(params)
        if "theme" not in params:
            params["theme"] = get_theme()
        if object:
            params["object"] = object
        super().__init__(**params)
        self._html_pane = pn.pane.HTML(sizing_mode="stretch_both")
        if "scroll" not in layout_params:
            layout_params["scroll"] = True
        self.layout = pn.Column(self._html_pane, **layout_params)
        self._update_html()

    def __panel__(self):
        return self.layout

    @param.depends("object", "theme", "palette", watch=True)
    def _update_html(self):
        with param.edit_constant(self):
            doc = self.object.__doc__
            if doc:
                doc = "\n".join(doc.split("\n")[1:])
            else:
                doc = ""
            self._html = self._to_html(doc, self.theme, self.palette)

    @param.depends("_html", watch=True)
    def _update_html_pane(self):
        self._html_pane.object = self._html

    @classmethod
    def _to_html(cls, txt, theme, palette):
        if not txt:
            return ""
        html = ansiconv.to_html(txt)
        css = cls._get_css(**ANSI_THEMES[palette][theme])
        html = f"""
        <style>{css}</style>
        <pre class="ansi_fore ansi_back">{html}</pre>
        """
        return html

    @staticmethod
    def _get_css(  # pylint: disable=too-many-arguments
        background="#000000",
        color="#FFFFFF",
        red="#FF0000",
        green="#00FF00",
        blue="#0000FF",
        cyan="#00FFFF",
    ):
        return f"""
    .ansi_fore {{ color: {color}; }}
    .ansi_back {{ background-color: {background}; padding: 20px; calc(100% - 60px);; border-radius: 4px; opacity: 0.8;font: 1rem Inconsolata, monospace; }}
    .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 test_app() -> DocStringViewer:
    """Returns a DocStringViewer for manual testing.

    Returns:
        DocStringViewer: [description]
    """
    


if __name__.startswith("bokeh"):
    pn.extension(sizing_mode="stretch_width")
    some_parameterized = DocStringViewer()
    docs = DocStringViewer(
        some_parameterized, sizing_mode="stretch_width", palette="Tomorrow", scroll=False
    )
    pn.state.location.sync(docs, {"palette": "palette"})
    controls = pn.Column(
        pn.pane.Markdown("**Palette**", margin=0),
        pn.widgets.RadioBoxGroup.from_param(docs.param.palette),
    )
    pn.template.FastListTemplate(
        title="DocStringViewer",
        sidebar=[controls],
        main=[docs],
    ).servable()

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: docs Related to the Panel documentation and examples type: enhancement Minor feature or improvement to an existing feature
Projects
None yet
Development

No branches or pull requests

3 participants