Skip to content

template

GoldenElvis

Bases: object

Adaptation of Leon van Kouwen's elvis layout system https://github.com/LeonvanKouwen/elvis

Generates a jinja GoldenLayout Template based on panel's default GoldenLayout Template. This modification features a fixed sidebar with a main layout part which can be customized with columns/rows/stacks

Source code in pyhdx/web/template.py
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
class GoldenElvis(object):
    """
    Adaptation of Leon van Kouwen's elvis layout system
    https://github.com/LeonvanKouwen/elvis

    Generates a jinja GoldenLayout Template based on panel's default GoldenLayout Template. This modification features a
    fixed sidebar with a main layout part which can be customized with columns/rows/stacks

    """

    NESTABLE = """
        {
            type: '%s',
            content: [ %s ],
            %s
        },
        """

    VIEW = """
        {   
            type: 'component',
            componentName: 'view',
            componentState: 
            { 
                model: '{{ embed(roots.%s) }}',
                %s
            },
            isClosable: false,
        },
        """

    def __init__(self, main_controller, template, theme, title=None):
        self.main_controller = main_controller
        self.template_cls = template
        self.theme_cls = theme
        self.title = title

        self.panels = {}

    @property
    def jinja_base_string_template(self):
        _base = pathlib.Path(__file__).parent / "jinja_base.html"
        base_string_template = string.Template(_base.read_text())

        return base_string_template

    def compose(self, golden_layout_string, **kwargs):
        """
        Creates a servable template from a golden layout js code string.
        :param main_controller: Application main controller
        :param golden_layout_string: Result of nesting stacks, columns, rows, and panels
                                     using the methods in this class.
        """

        controllers = self.main_controller.control_panels.values()
        template_code = ReadString(
            self.jinja_base_string_template.substitute(main_body=golden_layout_string)
        )
        self.template_cls._template = template_code

        template = self.template_cls(title=self.title, theme=self.theme_cls, **kwargs)
        controls = pn.Accordion(
            *[controller.panel for controller in controllers],
            toggle=True,
            sizing_mode="fixed",
            width=SIDEBAR_WIDTH,
        )

        template.sidebar.append(controls)

        for panel_ID, panel in self.panels.items():
            template._render_items[panel_ID] = (panel, ["main"])

        return template

    def view(self, view, title=None, width=None, height=None, scrollable=True):
        """
        Adds a viewable panel.
        :param view: The panel to show in this golden layout sub section.
        :param title: The text to show at the top of the panel.
        :param width: Initial width.
        :param height: Initial height.
        """
        # pn.config.js_files.update(fig_panel.js_files)

        # We need to register every panel with a unique name such that after
        # composing the jinja2 template, we can add them (see compose function).

        # It seems that these unique names cannot start with a number or they cannot be referenced directly
        # Therefore, currently tmpl.main.append cannot be used as this generates ?
        if isinstance(view, str):
            fig_panel = self.main_controller.views[view]
            fig_panel.update()  # initialize (needed? probably not)

        else:
            fig_panel = view

        panel_ID = "ID" + str(id(fig_panel))
        title = title or default_label_formatter(getattr(fig_panel, "name", None))

        item = pn.Row(fig_panel.panel, sizing_mode="stretch_both")  # Place figure in layout
        self.panels[panel_ID] = item
        title_str = "title: '%s'," % str(title) if title is not None else "title: '',"
        width_str = "width: %s," % str(width) if width is not None else ""
        height_str = "height: %s," % str(height) if height is not None else ""
        scroll_str = "css_classes: ['lm_content_noscroll']" if not scrollable else ""

        # scroll_str = "css_classes: ['overflow-y: hidden !important']" # this doesnt work
        # scroll_str = "overflow: 'hidden'," #if not scrollable else ""
        settings = title_str + height_str + width_str + scroll_str
        return self.VIEW % (panel_ID, settings)

    def get_settings(self, **kwargs):
        settings = ""
        for name, val in kwargs.items():
            if isinstance(val, str):
                settings += f"{name}: '{val}'"
            elif isinstance(val, (float, int)):
                settings += f"{name}: {val}"

        return settings

    def _block(self, *args, container="stack", **kwargs):
        """
        Creates nestable js code strings. Note that 'stack', 'colum' and 'row' are the
        strings dictated by the golden layout js code.
        """
        content = "".join(arg for arg in args)
        settings = self.get_settings(**kwargs)
        return self.NESTABLE % (container, content, settings)

    def stack(self, *args, **kwargs):
        """Adds a 'tab' element."""
        return self._block(*args, container="stack", **kwargs)

    def column(self, *args, **kwargs):
        """Vertically aligned panels"""
        return self._block(*args, container="column", **kwargs)

    def row(self, *args, **kwargs):
        """Horizontally aligned panels"""
        return self._block(*args, container="row", **kwargs)

column(*args, **kwargs)

Vertically aligned panels

Source code in pyhdx/web/template.py
164
165
166
def column(self, *args, **kwargs):
    """Vertically aligned panels"""
    return self._block(*args, container="column", **kwargs)

compose(golden_layout_string, **kwargs)

Creates a servable template from a golden layout js code string. :param main_controller: Application main controller :param golden_layout_string: Result of nesting stacks, columns, rows, and panels using the methods in this class.

Source code in pyhdx/web/template.py
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
def compose(self, golden_layout_string, **kwargs):
    """
    Creates a servable template from a golden layout js code string.
    :param main_controller: Application main controller
    :param golden_layout_string: Result of nesting stacks, columns, rows, and panels
                                 using the methods in this class.
    """

    controllers = self.main_controller.control_panels.values()
    template_code = ReadString(
        self.jinja_base_string_template.substitute(main_body=golden_layout_string)
    )
    self.template_cls._template = template_code

    template = self.template_cls(title=self.title, theme=self.theme_cls, **kwargs)
    controls = pn.Accordion(
        *[controller.panel for controller in controllers],
        toggle=True,
        sizing_mode="fixed",
        width=SIDEBAR_WIDTH,
    )

    template.sidebar.append(controls)

    for panel_ID, panel in self.panels.items():
        template._render_items[panel_ID] = (panel, ["main"])

    return template

row(*args, **kwargs)

Horizontally aligned panels

Source code in pyhdx/web/template.py
168
169
170
def row(self, *args, **kwargs):
    """Horizontally aligned panels"""
    return self._block(*args, container="row", **kwargs)

stack(*args, **kwargs)

Adds a 'tab' element.

Source code in pyhdx/web/template.py
160
161
162
def stack(self, *args, **kwargs):
    """Adds a 'tab' element."""
    return self._block(*args, container="stack", **kwargs)

view(view, title=None, width=None, height=None, scrollable=True)

Adds a viewable panel. :param view: The panel to show in this golden layout sub section. :param title: The text to show at the top of the panel. :param width: Initial width. :param height: Initial height.

Source code in pyhdx/web/template.py
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
def view(self, view, title=None, width=None, height=None, scrollable=True):
    """
    Adds a viewable panel.
    :param view: The panel to show in this golden layout sub section.
    :param title: The text to show at the top of the panel.
    :param width: Initial width.
    :param height: Initial height.
    """
    # pn.config.js_files.update(fig_panel.js_files)

    # We need to register every panel with a unique name such that after
    # composing the jinja2 template, we can add them (see compose function).

    # It seems that these unique names cannot start with a number or they cannot be referenced directly
    # Therefore, currently tmpl.main.append cannot be used as this generates ?
    if isinstance(view, str):
        fig_panel = self.main_controller.views[view]
        fig_panel.update()  # initialize (needed? probably not)

    else:
        fig_panel = view

    panel_ID = "ID" + str(id(fig_panel))
    title = title or default_label_formatter(getattr(fig_panel, "name", None))

    item = pn.Row(fig_panel.panel, sizing_mode="stretch_both")  # Place figure in layout
    self.panels[panel_ID] = item
    title_str = "title: '%s'," % str(title) if title is not None else "title: '',"
    width_str = "width: %s," % str(width) if width is not None else ""
    height_str = "height: %s," % str(height) if height is not None else ""
    scroll_str = "css_classes: ['lm_content_noscroll']" if not scrollable else ""

    # scroll_str = "css_classes: ['overflow-y: hidden !important']" # this doesnt work
    # scroll_str = "overflow: 'hidden'," #if not scrollable else ""
    settings = title_str + height_str + width_str + scroll_str
    return self.VIEW % (panel_ID, settings)

ReadString

Bases: str

Extends the string class such that it can be used to monkey-patch the _template class attribute of GoldenTemplate

Source code in pyhdx/web/template.py
20
21
22
23
24
25
26
class ReadString(str):
    """
    Extends the `string` class such that it can be used to monkey-patch the _template class attribute of GoldenTemplate
    """

    def read_text(self):
        return str(self)