Source code for app

"""App class to easily define the structure of a typical Voilà dashboard."""
# Author(s): Davide.De-Marchi@ec.europa.eu
# Copyright © European Union 2022-2023
# 
# Licensed under the EUPL, Version 1.2 or as soon they will be approved by 
# the European Commission subsequent versions of the EUPL (the "Licence");
# 
# You may not use this work except in compliance with the Licence.
# 
# You may obtain a copy of the Licence at:
# https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12

# Unless required by applicable law or agreed to in writing, software
# distributed under the Licence is distributed on an "AS IS"
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
# express or implied.
# 
# See the Licence for the specific language governing permissions and
# limitations under the Licence.
from ipywidgets import widgets, Layout
from IPython.display import display, HTML
from IPython.core.display import HTML as ipyhtml   # V. I. for urlOpen to work!!!
import base64
import plotly.express as px
import plotly.graph_objects as go
import pandas as pd
from random import randrange
from datetime import datetime
import ipyvuetify as v

try:
    from . import settings
    from . import fontsettings
    from . import sidePanel
    from . import button
    from . import footer
    from . import snackbar
    from . import dialogMessage
    from . import dialogYesNo
    from . import dialogWait
    from . import dialogGeneric
    from . import fab
    from . import queryStrings
except:
    import settings
    import fontsettings
    import sidePanel
    import button
    import footer
    import snackbar
    import dialogMessage
    import dialogYesNo
    import dialogWait
    import dialogGeneric
    import fab
    import queryStrings


    
# Convert a measure in string (if int or float --> pixels units)
def measure2str(value):
    if isinstance(value, int):
        return '%dpx' % value
    elif isinstance(value, float):
        return '%fpx' % value
    else:
        return value

#####################################################################################################################################################
# A fully customizable App framework for Voilà
#####################################################################################################################################################
[docs]class app(): """ App class to easily define the basic structure of a typical Voilà dashboard. An app instance displays a series of ipywidgets.Output() widgets to ease the usage of messages/dialog-boxes/etc, through these methods: :func:`~app.snackbar` :func:`~app.dialogMessage` :func:`~app.dialogYesNo` :func:`~app.dialogGeneric` :func:`~app.dialogWaitOpen` :func:`~app.dialogWaitClose` :func:`~app.fab` :func:`~app.downloadText` :func:`~app.downloadBytes` :func:`~app.urlOpen` :func:`~app.urlParameter` :func:`~app.urlParameter` The main content of the dashboard should be displayed in the **app.outcontent** Output widget that completely fills the empty space of the dashboard between the title bar and the footer bar. Parameters ---------- title : str, optional Main title of the application to be displayed on the title bar (default is 'Title of the dashboard') titlesvg : str, optional SVG string to use as application title when the title string is empty (default is '') titlesvgclass : str, optional Class margins and padding to apply to the titlesvg drawing (default is 'pa-0 ma-0') titlecredits : str, optional Credits string to be displayed on the right side of the title bar (default is '') titlecredits2 : str, optional Secondary credits string to be displayed on the right side of the title bar (default is '') titlestyle : str, optional CSS style to apply to the main title of the dashboard (default is '', an example could be: 'font-family: "Times New Roman", Times, serif; font-weight: bold; font-size: 22px;') titlespacestyle : str, optional CSS style to apply to the space at the left of the title (default is 'width: 50px; min-width: 50px;'). It can be useful to move the title more on the centre of the title bar, by providing a titlespacestyle like 'width: 200px;' titleheight : int, optional Height of the title bar in pixels (default is 70 pixels) totalheight : int, optional Total height in pixels of the page (default is 985 pixels which is coherent with a FullHD screen dimension) dark : bool, optional If True the title text color is settings.textcolor_dark, if False it is settings.textcolor_nodark (default is settings.dark_mode) backcolor : str, optional Background color of the title bar (default is settings.color_second) titlewidth : str, optional Width of the part of the title bar that contains the main title of the dashboard (default is '600px', other values could be, for instance: '50%') footercolor : str, optional Background color to use for the footer bar displayed in the bottom part of the screen (default is 'lightgrey') backgroundimageurl : str, optional URL of the optional image to display as background of the title bar (default is None) backgroundimageposition : str, optional Defines the way the backgroundimage in the title is cropped (default is 'center center'). See `Vuetify.js v-img <https://vuetifyjs.com/en/api/v-img/#props-position>`_ and `CSS background-position documentation <https://developer.mozilla.org/en-US/docs/Web/CSS/background-position>`_ for help. logourl : str, optional URL of the image or SVG to use as a logo in the right end side of the title bar (default is European Commission logo) logomaxwidth : int, optional Maximum width in pixels of the logo image (default is 80 pixels) logomaxheight : int, optional Maximum height in pixels of the logo image (default is 60 pixels) titletabs : list of strings, optional List of tabs to be added to the title bar as the main visualization options of the dashboard (default is ['Tab 1', 'Tab 2', 'Tab 3']) titletabsactive : int, optional Index of the tab to activate at start (default is 0) titletabsstile : str, optional CSS style to apply to the tabs in the title bar (default is 'font-weight: bold;') titletabsactiveparameter : str, optional Name of the URL parameter to read for setting the activetab (default is None) titletabscolor : str, optional Color of the selected tab in the title tabs (default is settings.color_first) titletabsdark : bool, optional If True the text color of unselected tabs is settings.textcolor_dark, if False it is settings.textcolor_nodark (default is False) footertext : str, optional Text to display in the footer tab (default is '<current year> - Joint Research Centre') footerbuttons : list of strings, optional List of strings containing the caption of the buttons to display in the footer tab (default is ['Home', 'About Us', 'Team', 'Services', 'Blog', 'Contact Us']) footerheight : int or float or str optional Height of the footer bar. If an integer or a float is passed, the height is intended in pixels units, otherwise a string containing the units must be passed (example: '4vh'). Default is 68 for 68 pixels footercopyright : bool, optional If True adds the copyright symbol to the footer text (default is True) footerdark : bool, optional If True the footer text color is settings.textcolor_dark, if False it is settings.textcolor_nodark (default is settings.dark_mode) footercredits : str, optional Text for footer credits button (default is '') footercreditstooltip : str, optional Tooltip for the footer credits button (default is '') footercreditsurl : str, optional URL to open when the user clicks on the footer credits button (default is '') sidepaneltitle : str, optional Title of the left side panel (default is 'Settings') sidepanelwidth : int, optional Width in pixels of the left side panel (default is 400). If the size is 0, the icon to open the sidepanel will not be added to the title bar sidepaneltext : str, optional Text to display in the left side panel (default is '') sidepanelcontent : list of ipywidgets, optional List of ipywidgets object to display in the left side panel (default is []) sidepaneldark : bool, optional If True the title text color of the sidePanel is black, otehrwise is white (default is settings.dark_mode) sidepanelbackdark : bool, optional If True the background color of the sidePanel is black, otehrwise is white (default is settings.dark_mode) minipanelicons : list of strings, optional List of string containing the names of the icons to display in the minipanel that is displayed on the left side of the footer tab (default is []) minipaneltooltips : list of strings, optional List of tooltips to set for the icons of the minipanel (default is []) minipanelopen : bool, optional If True the minipanel is dopened on startup of the app (default is False) minipanellarge : bool, optional If True, the minipanel icons are displayed in the large version (default is True) minipanelbuttoncolor : str, optional Color of the 'three vertical points icon' that opens/closes the minipanel (default is the textcolor_notdark defined in the settings.py module) minipaneliconscolor : str, optional Color of the icons in the minipanel (default is the textcolor_notdark defined in the settings.py module) onclicktab : function, optional Python function to call when the user clicks on one of the tabs of the title bar. The function will receive a parameter of type string containing the text of the tab onclickcredits : function, optional Python function to call when the user clicks on the credits button on the title bar. The function will receive no parameters onclickcredits2 : function, optional Python function to call when the user clicks on the secondary credits button on the title bar. The function will receive no parameters onclicklogo : function, optional Python function to call when the user clicks on the logo image on the title bar. The function will receive no parameters onclickfooter : function, optional Python function to call when the user clicks on one of the buttons of the footer bar. The function will receive a parameter of type string containing the text of the button onclickminipanel : function, optional Python function to call when the user clicks on one of the icons of the minipanel in the footer bar. The function will receive a parameter of type int containing the index of the icon fullscreen : bool, optional If True the app will be displayed in fullscreen mode (default is False). In fullscreen mode the app will occupy all the available space on the web page (the titlebar will be aligned on top, the footer bar will be aligned on the bottom of the page, and the outcontent will occupy all the intermediate space between the title bar and the footer bar, irrescpective of the value passed in the totalheight parameter), and the positioning of the elements will be fully responsive. Attributes ---------- outcontent : ipywidgets.Output instance This is the output widget where the content of the application can be displayed To visually highlight the app.outcontent Output widget, this line of code can be executed:: g_app.outcontent.layout.border = '1px solid lightgrey' After the execution of that line, the border of the g_app.outcontent will be visible. To reset the border to the empty line, this line of code can be executed:: g_app.outcontent.layout.border = '' Example ------- Creation of an app class to define the structure of a Voilà dashboard:: from vois.vuetify import app, settings import ipyvuetify as v # Change global settings settings.dark_mode = False settings.color_second = '#68aad2' settings.color_first = '#1c4056' settings.button_rounded = False # Click on a tab of the title def on_click_tab(arg): g_app.snackbar(arg) # Click on the credits text def on_click_credits(): g_app.snackbar('CREDITS') # Click on the logo def on_click_logo(): g_app.snackbar('LOGO') # Click on the footer buttons def on_click_footer(arg): g_app.snackbar(arg) # Click on the footer minipanel def on_click_minipanel(index): g_app.snackbar(str(index)) g_app = app.app(title='Energy consumption example dashboard', titlecredits='Created by Unit I.3', titlewidth='60%', footercolor='#1c4056', titletabs=['Chart', 'Table', 'Static Map', 'Dynamic Map'], titletabscolor='#60b3e8', dark=True, footerdark=True, footercredits='Data credits', footercreditstooltip='Eurostat - European Commission', footercreditsurl='https://ec.europa.eu/eurostat/data/database', backcolor='#1c4056', sidepaneltitle='Help', sidepaneltext='Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.', sidepanelcontent=[v.Icon(class_='pa-0 ma-0 ml-2', children=['mdi-help'])], sidepaneldark=True, sidepanelbackdark=False, minipanelicons=['mdi-chart-bar', 'mdi-table-large', 'mdi-map-legend', 'mdi-file-chart-outline'], minipaneltooltips=['Download Chart', 'Download Table', 'Download Map', 'Generate Report in Word'], minipanelbuttoncolor='white', onclickminipanel=on_click_minipanel, onclicktab=on_click_tab, onclickcredits=on_click_credits, onclicklogo=on_click_logo, onclickfooter=on_click_footer) g_app.show() .. figure:: figures/app.png :scale: 100 % :alt: app widget App structure example """ def __init__(self, title='Title of the dashboard', titlesvg='', titlesvgclass='pa-0 ma-0', titlecredits='', titlecredits2='', titlestyle='', # 'font-family: "Times New Roman", Times, serif; font-weight: bold; font-size: 22px;' titlespacestyle='width: 50px; min-width: 50px;', titleheight=70, totalheight=985, dark=settings.dark_mode, backcolor=settings.color_second, titlewidth= '600px', # '50%' footercolor='lightgrey', backgroundimageurl=None, #'https://picsum.photos/id/1050/1920/1080?grayscale' backgroundimageposition='center center', logourl='https://jeodpp.jrc.ec.europa.eu/services/shared/Notebooks/images/European_Commission.svg', logomaxwidth = 80, logomaxheight = 60, titletabs=['Tab 1', 'Tab 2', 'Tab 3'], titletabsdark=False, titletabsactive=0, # Index of the tab to activate at start titletabsstile='font-weight: bold;', #'font-family: "Times New Roman", Times, serif; font-weight: bold;' titletabsactiveparameter=None, # Name of the URL parameter to read for setting the activetab titletabscolor=settings.color_first, # Color of the active tab in the title tabs footertext='%d - Joint Research Centre'%(datetime.now().year), footerbuttons=['Home', 'About Us', 'Team', 'Services', 'Blog', 'Contact Us'], footerheight=68, footercopyright=True, footerdark=False, footercredits='', # Text for footer credits button footercreditstooltip='', # Tooltip for the footer credits button footercreditsurl='', # URL to open when the user click on the footer credits button sidepaneltitle='Settings', sidepanelwidth=400, sidepaneltext='', sidepanelcontent=[], sidepaneldark=settings.dark_mode, sidepanelbackdark=settings.dark_mode, minipanelicons=[], # See footer.py minipaneltooltips=[], minipanelopen=False, minipanellarge=True, minipanelbuttoncolor=settings.textcolor_notdark, minipaneliconscolor=settings.textcolor_notdark, onclicktab=None, # Function with 1 string argument string containing the text of the tab onclickcredits=None, # Function with 0 arguments onclickcredits2=None, # Function with 0 arguments onclicklogo=None, # Function with 0 arguments onclickfooter=None, # Function with 1 string argument string containing the text of the button onclickminipanel=None, # Function with 1 string argument integer containing the index of the icon fullscreen=False): # If True the App will fill all the page! # Storing input parameters self.title = title self.titlecredits = titlecredits self.titlecredits2 = titlecredits2 self.titlestyle = titlestyle self.titleheight = measure2str(titleheight) self.totalheight = measure2str(totalheight) self.dark = dark self.backcolor = backcolor self.titlewidth = measure2str(titlewidth) self.footercolor = footercolor self.backgroundimageurl = backgroundimageurl self.backgroundimageposition = backgroundimageposition self.logourl = logourl self.logomaxwidth = logomaxwidth self.logomaxheight = logomaxheight self.titletabsstile = titletabsstile self.titletabs = titletabs self.titletabsactive = titletabsactive self.titletabsactiveparameter = titletabsactiveparameter self.titletabscolor = titletabscolor self.footertext = footertext self.footerbuttons = footerbuttons self.footerheight = measure2str(footerheight) self.footercopyright = footercopyright self.footerdark = footerdark self.footercredits=footercredits self.footercreditstooltip=footercreditstooltip self.footercreditsurl=footercreditsurl self.sidepaneltitle = sidepaneltitle self.sidepanelwidth = sidepanelwidth self.sidepaneltext = sidepaneltext self.sidepanelcontent = sidepanelcontent self.sidepaneldark = sidepaneldark self.sidepanelbackdark = sidepanelbackdark self.onclicktab = onclicktab self.onclickcredits = onclickcredits self.onclickcredits2 = onclickcredits2 self.onclicklogo = onclicklogo self.onclickfooter = onclickfooter self.fullscreen = fullscreen self.content_vbackground = None # v.Html object to display an image in the background self.content_panels_dict = {} # Dict to retrieve panels by name self.content_panels = [] # List of panels to overlay to the background if self.dark: buttontext = settings.textcolor_dark styletext = 'color: ' + settings.textcolor_dark + ';' else: buttontext = settings.textcolor_notdark styletext = 'color: ' + settings.textcolor_notdark + ';' self.contentheight = 'calc(calc(%s - %s) - %s)' % (self.totalheight, self.titleheight, self.footerheight) # Creation of Output widgets self.outservice = widgets.Output(layout=Layout(width='0px', height='0px')) # Available for the user self.outdialogs = widgets.Output(layout=Layout(width='0px', height='0px')) # Used for snackbars, fab and dialogs self.outdownload = widgets.Output(layout=Layout(width='0px', height='0px')) # Used for downloads self.outfabs = widgets.Output(layout=Layout(width='0px', height='0px')) # Used for fabs self.outurlopen = widgets.Output(layout=Layout(width='0px', height='0px')) # Used for urlOpen self.outurlupdate = widgets.Output(layout=Layout(width='0px', height='0px')) # Used for urlUpdate self.outplotly = widgets.Output(layout=Layout(width='0px', height='0px')) # Used for initializing plotly self.outpanel = widgets.Output(layout=Layout(width='0px', height='0px')) # Used for the side panel self.outpanels = widgets.Output(layout=Layout(width='0px', height='0px')) # used for the panels if self.fullscreen: self.outcontent = widgets.Output(layout=Layout(width='100vw', height='calc(calc(100vh - %s) - %s)' % ( self.titleheight, self.footerheight) ) ) else: self.outcontent = widgets.Output(layout=Layout(width='99.7%', height=self.contentheight)) self.outfooter = widgets.Output(layout=Layout(width='99.7%', height='calc(%s + 6px)' % self.footerheight)) # One instance of the dialogWait self.waitdlg = None # Read the parameters passed on the URL self.parameters = queryStrings.readParameters() if not self.titletabsactiveparameter is None: self.titletabsactive = int(self.urlParameter(self.titletabsactiveparameter, self.titletabsactive)) # Side panel self.panel = sidePanel.sidePanel(title=self.sidepaneltitle, text=self.sidepaneltext, content=self.sidepanelcontent, dark=self.sidepaneldark, backdark=self.sidepanelbackdark, width=self.sidepanelwidth, right=False, output=self.outpanel, zindex=9999, onclose=self.__internal_panelclosed) # App bar containing NavIcon, Title, Tabs, Credits and Logo if self.sidepanelwidth > 0: self.abi = v.AppBarNavIcon(style_=styletext) self.abi.on_event('click', self.__internal_onclickNavIcon) self.abih = v.Html(tag='div', class_="pa-0 ma-0 ml-2", children=[self.abi], style_='overflow: hidden; %s' % titlespacestyle) else: self.abih = v.Html(tag='div', class_="pa-0 ma-0", children=[' '], style_='overflow: hidden; %s' % titlespacestyle) if len(titlesvg) > 0: outsvg = widgets.Output() with outsvg: display(HTML(titlesvg)) self.abt = v.ToolbarTitle(children=[outsvg], class_=titlesvgclass, style_='%s %s' % (styletext, self.titlestyle) ) else: self.abt = v.ToolbarTitle(children=[self.title], style_='%s %s' % (styletext, self.titlestyle) ) if self.onclickcredits is None: self.credits = v.Btn(text=True, color=buttontext, dark=self.dark, children=[self.titlecredits], rounded=settings.button_rounded, style_='text-transform: none; cursor: initial;') else: self.credits = v.Btn(text=True, color=buttontext, dark=self.dark, children=[self.titlecredits], rounded=settings.button_rounded, style_='text-transform: none;') self.credits.on_event('click', self.__internal_onclickCredits) self.credits2 = v.Html(tag='div', children=['']) if len(self.titlecredits2) > 0: if self.onclickcredits2 is None: self.credits2 = v.Btn(text=True, color=buttontext, dark=self.dark, children=[self.titlecredits2], rounded=settings.button_rounded, style_='text-transform: none; cursor: initial;') else: self.credits2 = v.Btn(text=True, color=buttontext, dark=self.dark, children=[self.titlecredits2], rounded=settings.button_rounded, style_='text-transform: none;') self.credits2.on_event('click', self.__internal_onclickCredits2) self.tlist = [] for t in self.titletabs: telem = v.Tab(style_='%s %s' % ('', self.titletabsstile), children=[t]) telem.on_event('click', self.__internal_onclickTab) self.tlist.append(telem) self.tabs = v.Tabs(v_model=self.titletabsactive, dark=titletabsdark, color=self.titletabscolor, background_color='transparent', align_with_title=True, children=self.tlist, height=self.titleheight, min_height=self.titleheight, max_height=self.titleheight) s = v.Spacer() logostyle = '' if not self.onclicklogo is None: logostyle = 'cursor: pointer;' self.logoimg = v.Img(src=self.logourl, class_='pa-0 ma-0', style_=logostyle, contain=True, height=self.logomaxheight, width=self.logomaxwidth, max_height=self.logomaxheight, max_width=self.logomaxwidth) self.logoimg.on_event('click', self.__internal_onclickLogo) card1 = v.Card(height=titleheight, color='transparent', elevation=0, children=[self.abih], class_="d-flex align-center") card2 = v.Card(height=titleheight, color='transparent', elevation=0, children=[self.abt], class_="d-flex align-center", style_='overflow: hidden; width: %s;' % self.titlewidth) card3 = v.Card(height=titleheight, color='transparent', elevation=0, children=[self.tabs], class_="d-flex align-center") card4 = v.Card(height=titleheight, color='transparent', elevation=0, children=[self.credits, self.credits2], class_="d-flex align-center") card5 = v.Card(height=titleheight, color='transparent', elevation=0, children=[self.logoimg], class_="d-flex align-center mr-1") if len(self.titletabs) > 0: r = v.Row(justify="space-between", children=[card1,card2,card3,s,card4,card5], class_="pa-0 ma-0") else: r = v.Row(justify="space-between", no_gutters=True, children=[card1,card2,s,card4,card5], class_="pa-0 ma-0") if (not self.backgroundimageurl is None) and (len(self.backgroundimageurl) > 0): self.appbar = v.Img(src=self.backgroundimageurl, position=self.backgroundimageposition, children=[r], class_='pa-0 ma-0', style_='height: %s; overflow: hidden;' % self.titleheight, max_height=self.titleheight) else: self.appbar = v.Footer(color=backcolor, padless=True, children=[r], class_='pa-0 ma-0', style_='height: %s; overflow: hidden;' % self.titleheight, max_height=self.titleheight) # Footer if len(self.footerbuttons) > 0: self.f = footer.footer(text=self.footertext, color=self.footercolor, copyright=self.footercopyright, dark=self.footerdark, minipanelicons=minipanelicons, minipaneltooltips=minipaneltooltips, minipanelopen=minipanelopen, minipanellarge=minipanellarge, minipanelbuttoncolor=minipanelbuttoncolor, minipaneliconscolor=minipaneliconscolor, onclickminipanel=onclickminipanel, footercredits=self.footercredits, footercreditstooltip=self.footercreditstooltip, onclickcredits=self.__internal_onclickFooterCredits, buttons=self.footerbuttons, marginy=2, height=self.footerheight, onclick=self.onclickfooter) #, output=self.outfooter) else: self.f = footer.footer(text=self.footertext, color=self.footercolor, copyright=self.footercopyright, dark=self.footerdark, minipanelicons=minipanelicons, minipaneltooltips=minipaneltooltips, minipanelopen=minipanelopen, minipanellarge=minipanellarge, minipanelbuttoncolor=minipanelbuttoncolor, minipaneliconscolor=minipaneliconscolor, onclickminipanel=onclickminipanel, footercredits=self.footercredits, footercreditstooltip=self.footercreditstooltip, onclickcredits=self.__internal_onclickFooterCredits, marginy=2, height=self.footerheight, onclick=self.onclickfooter) #, output=self.outfooter) # Initialize Plotly with a 0x0 chart! with self.outplotly: data = {'name': ['mike', 'mike', 'cindy', 'cindy'], 'week': ['Week 1', 'Week 2', 'Week 1', 'Week 2'], 'perc': [0.45, 0.15, 0.25, 0.28]} df = pd.DataFrame(data) fig = px.bar(df, x="name", y="perc", color="week", title="", barmode='stack') fig.show() # Set the background image for the outcontent
[docs] def contentBackground(self, imageUrl=None): """ Sets the background image for the outcontent output widget Parameters ---------- imageUrl : str, optional URL of the optional image to display as background of the outcontent output widget (default is None) """ if imageUrl is None: self.content_vbackground = None self.outcontent.clear_output() else: self.content_vbackground = v.Html(tag='div', children=[''], class_="pa-0 ma-0", style_="width: 100vw; height: %s; border: 0px solid red; background-image: url('%s'); ; background-repeat: repeat;" % (self.outcontent.layout.height, imageUrl)) self.outcontent.clear_output() with self.outcontent: display(self.content_vbackground)
# Remove all panels
[docs] def contentResetPanels(self): """ Resets the list of panels to display on top of the outcontent output widget """ self.content_panels_dict = {} self.content_panels = [] self.outpanels.clear_output() if (not self.content_vbackground is None) : self.content_vbackground.children = []
# Adds a new panel
[docs] def contentAddPanel(self, width, height, left, top, border='1px solid black', backcolor='white', name='', number=None, title='', titleround=False, titlewidth='8vw', titleheight='2.4vh', titlefontsize='1.8vh', titlefontweight=500, icon='', icontooltip='', iconcolor='black', icononclick=None): """ Adds a new panel to overlay on top of the outcontent output widget. Parameters ---------- width : str Width of the panel (can be in any CSS coordinates, examples: '300px', '40vw' or 'calc(100vw - 600px)') height : str Height of the panel (can be in any CSS coordinates, examples: '300px', '40vh' or 'calc(100vh - 400px)') left : str Position of the panel from the left border of the outcontent output widget (can be in any CSS coordinates, examples: '300px' or '10vw') top : str Position of the panel from the top border of the outcontent output widget (can be in any CSS coordinates, examples: '300px' or '10vh') border : str, optional Border of the panel (default is '1px solid black') backcolor : str, optional Background color of the panel (default is 'white'). Please be aware that colors different from 'white' work badly if used for panels that will contain ipywidgets or ipyvuetify widgets. Transparent colors (for example: #ffffff00) or semi-transparent colors (for example: #ffffff55) work well if the panel contains only one of the SVG charts of the vois library (svhHeatmap, svgBubblesChart, etc.) name : str, optional Name of the panel (default is '') number : int, optional Number to assign to the panel, to generate unique CSS class names (default is None which means that the number is automatically generated) title : str, optional Title to show in the top-left border of the panel (default is '') titleround : bool, optional If True the sides of the title are rounded (default is False) titlewidth : str, optional Width of the title area (default is '8vw') titleheight : str, optional Height of the title area (default is '2.4vh') titlefontsize : str, optional Size of the font to use to display the title (default is '1.8vh') titlefontweight : int, optional Weight of the font to use to display the title (default is 500) icon : str, optional Name of an icon to display in the title bar of the panel (default is None) icontooltip : str, optional Tooltip to show when hover on the icon (default is '') iconcolor : str, optional Color of the icon (default is 'black') icononclick : function, optional Python function to call when the user clicks on the icon. The function will receive as parameter the name of the panel (default is None) """ out = widgets.Output(layout=Layout(width=width, height=height, border=border)) if number is None: num = randrange(999999) else: num = int(Number) classname = "%s_panel_out_%d" % (name,num) out.add_class(classname) out.clear_output() with self.outpanels: display(HTML('<style> .%s { background-color: %s !important; }</style>' % (classname, backcolor) )) panel = v.Html(tag='div', children=[out], class_="pa-0 ma-0", style_='width: %s; height: %s; border: none; position: absolute; top: %s; left: %s;' % (width, height, top, left)) self.content_panels.append(panel) if len(name) > 0: self.content_panels_dict[name] = [panel] if len(title) > 0 : radius = "0px 0px 0px 0px" if titleround : radius = "4px 4px 0px 0px" border = 'border-radius: %s; border: %s; border-style: solid solid none solid;' % (radius, border) position = 'position: absolute; top: calc(%s - %s); left: %s;' % (top, titleheight, left) children = [title] b = None if len(icon) > 0: b = button.button('', textcolor='#ffffff00', onclick=icononclick, argument=name, width=36, small=False, disabled=False, height=titleheight, outlined=False, tooltip=icontooltip, selected=False, icon=icon, iconleft=True, iconcolor=iconcolor, rounded=False) children = [v.Row(no_gutters=True, justify="space-between", children=[title,b.draw()])] titlecard = v.Card(width=titlewidth, elevation=0, color=backcolor, children=children, style_='font-family: %s; line-height: %s; font-size: %s; font-weight: %d; padding-left: 8px; text-align: start; %s %s' % (fontsettings.font_name, titleheight, titlefontsize, titlefontweight, border, position) ) self.content_panels.append(titlecard) if len(name) > 0: self.content_panels_dict[name].append(titlecard) return out
# Change settings of an existing panel given its name
[docs] def contentSetPanel(self, name, width, height, left, top, border='1px solid black', backcolor='white', titleround=False, titlewidth='8vw', titleheight='2.4vh', titlefontsize='1.8vh', titlefontweight=500, icon='', icontooltip='', iconcolor='black', icononclick=None): """ Change position, sizing and colors of a panel given its name. Parameters ---------- name : str Name of the panel to modify width : str Width of the panel (can be in any CSS coordinates, examples: '300px', '40vw' or 'calc(100vw - 600px)') height : str Height of the panel (can be in any CSS coordinates, examples: '300px', '40vh' or 'calc(100vh - 400px)') left : str Position of the panel from the left border of the outcontent output widget (can be in any CSS coordinates, examples: '300px' or '10vw') top : str Position of the panel from the top border of the outcontent output widget (can be in any CSS coordinates, examples: '300px' or '10vh') border : str, optional Border of the panel (default is '1px solid black') backcolor : str, optional Background color of the panel (default is 'white'). Please be aware that colors different from 'white' work badly if used for panels that will contain ipywidgets or ipyvuetify widgets. Transparent colors (for example: #ffffff00) or semi-transparent colors (for example: #ffffff55) work well if the panel contains only one of the SVG charts of the vois library (svhHeatmap, svgBubblesChart, etc.) titleround : bool, optional If True the sides of the title are rounded (default is False) titlewidth : str, optional Width of the title area (default is '8vw') titleheight : str, optional Height of the title area (default is '2.4vh') titlefontsize : str, optional Size of the font to use to display the title (default is '1.8vh') titlefontweight : int, optional Weight of the font to use to display the title (default is 500) icon : str, optional Name of an icon to display in the title bar of the panel (default is None) icontooltip : str, optional Tooltip to show when hover on the icon (default is '') iconcolor : str, optional Color of the icon (default is 'black') icononclick : function, optional Python function to call when the user clicks on the icon. The function will receive as parameter the name of the panel (default is None) """ if name in self.content_panels_dict: panellist = self.content_panels_dict[name] panel = panellist[0] titlecard = None if len(panellist) > 1: titlecard = panellist[1] if not titlecard is None and type(titlecard.children[0]) == v.generated.Row: title = titlecard.children[0].children[0] else: title = titlecard.children[0] if not titlecard is None: if len(icon) > 0: b = button.button('', textcolor='#ffffff00', onclick=icononclick, argument=name, width=36, small=False, disabled=False, height=titleheight, outlined=False, tooltip=icontooltip, selected=False, icon=icon, iconleft=True, iconcolor=iconcolor, rounded=False) titlecard.children = [v.Row(no_gutters=True, justify="space-between", children=[title,b.draw()])] else: titlecard.children = [title] classname = panel.children[0]._dom_classes[0] with self.outpanels: display(HTML('<style> .%s { background-color: %s !important; }</style>' % (classname, backcolor) )) panel.children[0].layout = Layout(width=width, height=height, border=border) panel.style_ = 'width: %s; height: %s; border: none; position: absolute; top: %s; left: %s;' % (width, height, top, left) panel.color = backcolor if not titlecard is None: radius = "0px 0px 0px 0px" if titleround : radius = "4px 4px 0px 0px" border = 'border-radius: %s; border: %s; border-style: solid solid none solid;' % (radius, border) position = 'position: absolute; top: calc(%s - %s); left: %s;' % (top, titleheight, left) titlecard.color = backcolor titlecard.width = titlewidth titlecard.style_ = 'font-family: %s; line-height: %s; font-size: %s; font-weight: %d; padding-left: 8px; text-align: start; %s %s' % (fontsettings.font_name, titleheight, titlefontsize, titlefontweight, border, position)
# Display the app
[docs] def show(self): """Display the app""" if (not self.content_vbackground is None) and (len(self.content_panels) > 0): self.content_vbackground.children = self.content_panels # Fix Firefox bug with white areas: with this fix all v.Img objects MUST declare the width and height to work correctly with self.outdialogs: display(HTML('<style>.v-responsive__sizer { padding-bottom: 0px !important; }</style>')) if self.fullscreen: outFullscreen = widgets.Output(layout=Layout(width='100vw', height='100vh')) #outFullscreen.add_class('box') with self.outdialogs: display(HTML('<style>.jp-OutputPrompt { min-width: 0px; border: 0px; }</style>')) with outFullscreen: display(self.appbar) display(widgets.HBox([self.outservice,self.outdialogs,self.outdownload,self.outfabs,self.outurlopen,self.outurlupdate,self.outplotly,self.outpanel,self.outpanels])) display(self.outcontent) display(self.f.draw()) run_in_voila = True try: from voila.utils import get_query_string except: run_in_voila = False transition = 'dialog-fade-transition' persistent = False if run_in_voila: persistent = True background = v.Card(children=[outFullscreen]) dialog = v.Dialog(v_model=True, fullscreen=True, transition=transition, persistent=persistent, no_click_animation=True, children=[background], style_='z-index:20001;') display(dialog) else: display(self.appbar) display(widgets.HBox([self.outservice,self.outdialogs,self.outdownload,self.outfabs,self.outurlopen,self.outurlupdate,self.outplotly,self.outpanel])) display(self.outcontent) #display(self.outfooter) display(self.f.draw())
# Sets the active tab
[docs] def setActiveTab(self, index): """Set the active tab of the title bar of the app""" if index >= 0 and index < len(self.tlist): self.titletabsactive = index self.tabs.v_model = self.titletabsactive
# Manage click on the navigation icon to open the side panel def __internal_onclickNavIcon(self, *args): self.panel.show() # Manage click on the logo def __internal_onclickLogo(self, *args): if not self.onclicklogo is None: self.onclicklogo() # Manage click on the title tabs def __internal_onclickTab(self, widget, event, data): if self.onclicktab: i = self.tlist.index(widget) self.onclicktab(self.titletabs[i]) # Manage click on the title credits button def __internal_onclickCredits(self, widget, event, data): if not self.onclickcredits is None: self.onclickcredits() # Manage click on the title credits2 button def __internal_onclickCredits2(self, widget, event, data): if not self.onclickcredits2 is None: self.onclickcredits2() # When the panel has bee closed def __internal_panelclosed(self): self.outpanel.clear_output() with self.outpanel: display(self.panel.nav) # Manage click on the footer credits button def __internal_onclickFooterCredits(self, *args): if len(self.footercreditsurl) > 0: self.urlOpen(self.footercreditsurl, target='_blank') # Display something in the service Output of this application
[docs] def display(self, arg): """Display something in the service Output of this application""" with self.outservice: display(arg)
# Display a message in a snackbar
[docs] def snackbar(self, *args, **kwargs): """Display a message in a snackbar. See :func:`~snackbar.snackbar` for the list of parameters.""" #if not 'color' in kwargs: kwargs['show'] = kwargs['show'] = True kwargs['output'] = self.outdialogs snackbar.snackbar(*args, **kwargs)
# Display a dialogMessage
[docs] def dialogMessage(self, *args, **kwargs): """Display a dialogMessage. See :func:`~dialogMessage.dialogMessage` for the list of parameters.""" kwargs['show'] = True kwargs['output'] = self.outdialogs dialogMessage.dialogMessage(*args, **kwargs)
# Display a dialogYesNo
[docs] def dialogYesNo(self, *args, **kwargs): """Display a dialogYesNo. See :func:`~dialogYesNo.dialogYesNo` for the list of parameters.""" kwargs['show'] = True kwargs['output'] = self.outdialogs dialogYesNo.dialogYesNo(*args, **kwargs)
# Display a dialogGeneric
[docs] def dialogGeneric(self, *args, **kwargs): """Display a dialogGeneric. See :func:`~dialogGeneric.dialogGeneric` for the list of parameters.""" kwargs['show'] = True kwargs['output'] = self.outdialogs dialogGeneric.dialogGeneric(*args, **kwargs)
# Open a dialogWait
[docs] def dialogWaitOpen(self, *args, **kwargs): """Open a dialogWait. See :func:`~dialogWait.dialogWait` for the list of parameters.""" kwargs['output'] = self.outdialogs self.waitdlg = dialogWait.dialogWait(*args, **kwargs)
# Close the dialogWait
[docs] def dialogWaitClose(self): """Close the dialogWait. See :func:`~dialogWait.dialogWait.close`.""" if not self.waitdlg is None: self.waitdlg.close() self.waitdlg = None
# Open a fab button
[docs] def fab(self, *args, **kwargs): """Open a fab button. See :func:`~fab.fab` for the list of parameters.""" kwargs['output'] = self.outfabs return fab.fab(*args, **kwargs)
# Direct download of a .txt file containing a string
[docs] def downloadText(self, textobj, fileName="download.txt"): """Direct download of a .txt file containing a string. Parameters ---------- textobj : str Text to write in the file to be downloaded fileName : str, optional Name of the file that is to be downloaded (default is 'download.txt') Example ------- If g_app is an instance of the app class, this code will download a text file containing the passed string:: g_app.downloadText('aaa bbb ccc') """ string_bytes = textobj.encode("ascii","ignore") base64_bytes = base64.b64encode(string_bytes) base64_string = base64_bytes.decode("ascii") self.outdownload.clear_output() with self.outdownload: display(HTML('<script>function downloadURI(uri, name) { var link = document.createElement("a"); link.download = name; link.href = uri; link.click();} downloadURI("data:application/octet-stream;charset=utf-8;base64,' + base64_string + '","' + fileName + '"); </script>')) self.outdownload.clear_output()
# Direct download of an array of bytes
[docs] def downloadBytes(self, bytesobj, fileName="download.bin"): """Direct download of an array of bytes. Parameters ---------- bytesobj : bytes-like object Bytes to write in the file to be downloaded fileName : str, optional Name of the file that is to be downloaded (default is 'download.bin') Example ------- If g_app is an instance of the app class, this code will download a binary file containing the passed bytes:: g_app.downloadBytes(b'ajgh lkjhl ') """ base64_bytes = base64.b64encode(bytesobj) base64_string = base64_bytes.decode("ascii") self.outdownload.clear_output() with self.outdownload: display(HTML('<script>function downloadURI(uri, name){ var link = document.createElement("a"); link.download = name; link.href = uri; link.click();} downloadURI("data:application/octet-stream;charset=utf-8;base64,''' + base64_string + '","' + fileName + '");</script>')) self.outdownload.clear_output()
# Open a web page in another tab
[docs] def urlOpen(self, url, target='_blank'): """Open a web page in another tab. Parameters ---------- url : str URL of the page to be opened target : str, optional Target of the open operation (default is '_blank' which means that the page will be opened in a new browser's tab) Example ------- If g_app is an instance of the app class, this code will open a new tab in the browser:: g_app.urlOpen('https://www.google.com') """ js = '<script type=\"text/javascript\">window.open("%s", "%s");</script>' % (url,target) with self.outurlopen: display(ipyhtml(js))
# Read the parameters passed on the URL
[docs] def urlParameter(self, parameterName, parameterDefaultValue=''): """Read the parameters passed on the URL. Parameters ---------- parameterName : str name of the parameter to read from the URL that launched the Voilà dashboard parameterDefaultValue : str, optional Default value of the parameter, in case it is not present in the URL that launched the Voilà dashboard (default is '') Example ------- If g_app is an instance of the app class, this code print the value of an URL parameter:: print(g_app.urlParameter('activetab')) """ return self.parameters.get(parameterName, [parameterDefaultValue])[0]
# Update the visualized URL in the browser
[docs] def urlUpdate(self, url): """Update the URL visualized in the top bar of the browser. Parameters ---------- url : str Partial url to add to the current browser's page key/values Example ------- If g_app is an instance of the app class, this code will add a key=value to the URL of the application:: g_app.urlUpdate('?test=3') """ js = "<script>window.history.replaceState({ additionalInformation: 'Updated the URL with JS' }, '', '%s');</script>" % url with self.outurlupdate: display(HTML(js))