Layout - grid of data tables

I have bokeh embedded in a Quasar page.

It’s essentially a grid of two rows, with the top row having two columns. I’d like the two main rows to be the same height. And the two columns in the top row to be the same width. And I’d like it all to respond to browser resize changes.

The main problem I’m seeing is with the data tables. Vertically, the one in the bottom row will go beyond the limits of the container. Horizontally, the one in the top row (on the right) will go beyond the limits of the container. The data tables also do not resize vertically or horizontally as the window gets smaller.

I’ve read about the different sizing_mode values and I’ve tried to use them, but I’m obviously misunderstanding them…

MIN_WIDTH = 100
MIN_HEIGHT = 100
SIZING_MODE = "stretch_both"
DEFAULT_NUM_FORMATTER = NumberFormatter(format='0,0[.]00',text_align='right')
DEFAULT_PERC_FORMATTER = NumberFormatter(format='0.0%', text_align='right')
DEFAULT_DATE_FORMATTER = DateFormatter(format='%Y-%m-%d')
COLUMNS_AVAILABLE = {
    "label": {
        "label": "Label",
        "formatter": None,
        "definition": TableColumn(field="label", title="Label"),
        "defaultColumn": True
    },
    "date": {
        "label": "Date",
        "formatter": DEFAULT_DATE_FORMATTER,
        "definition": TableColumn(field="date", title="Date", formatter=DEFAULT_DATE_FORMATTER),
        "defaultColumn": True
    },
    "totalChildren": {
        "label" : "Children",
        "formatter": None,
        "definition": TableColumn(field="totalChildren", title="Children"),
        "defaultColumn": True
    },
    "totalParents": {
        "label": "Parents",
        "formatter": None,
        "definition": TableColumn(field="totalParents", title="Parents"),
        "defaultColumn": True
    },
    "totalSiblings": {
        "label": "Siblings",
        "formatter": None,
        "definition": TableColumn(field="totalSiblings", title="Siblings"),
        "defaultColumn": True
    },
}
COLUMNS_ORDER = ["label",
    "date",
    "totalChildren",
    "totalParents",
    "totalSiblings"]

def createMap(wData, fData, mapOptions=None):
    x_range = (-12819494.949494913, -8470448.933782242)
    y_range = (2720808.080808082, 6578832.772166105)

    fig = figure(x_axis_type="mercator",
        y_axis_type="mercator",
        tools="pan,wheel_zoom,box_zoom,reset,tap,box_select,lasso_select",
        active_scroll="wheel_zoom",
        active_tap="tap",
        x_range=x_range, y_range=y_range,
        min_width=MIN_WIDTH,
        min_height=MIN_HEIGHT,
        sizing_mode=SIZING_MODE,)
    hover = HoverTool(tooltips=[("W", "@label"),])
    fig.add_tools(hover)
    
    fig.legend.click_policy = "hide"

    fig.legend.location = "top_left"
    
    fig.toolbar.active_inspect = None
    fig.toolbar.logo = None

    return fig

def createSummary(sources, ctx, info):
    figMap = createMap(sources['wData'], sources['fData'])
    modelPanel = createModelPanel(sources['wData'], ctx)
    fTable = createFTable(sources['fData'], ctx)
    
    gbox = grid(children=[[figMap, modelPanel],
            fTable])

    return gbox

def createModelPanel(data, ctx):
    selected_columns = []
    dataTableCols = []
    for key in COLUMNS_ORDER:
        column_def = COLUMNS_AVAILABLE[key]
        if "defaultColumn" in column_def.keys() and column_def["defaultColumn"] == True:
            dataTableCols.append(column_def["definition"])
            selected_columns.append(key)

    modelTable = DataTable(source=data, 
        columns=dataTableCols, 
        sizing_mode=SIZING_MODE, 
        width_policy="max")

    options = list()
    for key in COLUMNS_AVAILABLE.keys():
        option_tuple = (key, (COLUMNS_AVAILABLE[key])["label"])
        options.append(option_tuple)
    widget = MultiChoice(title="Select Columns",
        value=selected_columns,
        options=options,
        delete_button=True,
        height=100,
        sizing_mode="stretch_width")
    w_header = Div(text="""
            W Summary
        """,
        sizing_mode=SIZING_MODE)
    model_layout = layout(column(w_header, widget, modelTable), sizing_mode="scale_height")
    
    return model_layout

def createFTable(data, ctx):
    fheader = Div(text="""
            F Summary
        """,
        sizing_mode=SIZING_MODE)
    dataTableCols = []
    dateFormatter = DateFormatter(format='%Y-%m-%d')
    dataTableCols.append(TableColumn(field="aLabel", title="A-label"))
    dataTableCols.append(TableColumn(field="oLabel", title="O-label"))
    dataTableCols.append(TableColumn(field="date", title="Date", formatter=dateFormatter))
    dataTableCols.append(TableColumn(field="active", title="A"))
    dataTableCols.append(TableColumn(field="offset", title="O"))
    dataTableCols.append(TableColumn(field="name", title="Name"))
    dataTable = DataTable(source=data, 
        columns=dataTableCols, 
        sizing_mode=SIZING_MODE, 
        width_policy="max")

    table_layout = layout(column(fheader, dataTable), sizing_mode=SIZING_MODE)

    return table_layout

def createBokehChart(data, ctx):
    wCols = ['id', 'date', 'totalChildren', 'totalParents', 'totalSiblings']
    wData = createSource(data, wCols, 'wData')
    fCols = ['id', 'active', 'offset', 'aLabel', 'olabel', 'date', 'name']
    fData = createSource(data, fCols, 'fData')
    
    sources = dict(wData=wData, fData=fData)
    summary_view = createSummary(sources, ctx, dict())
    
    return summary_view

def createSource(data, cols, key):
    data_dict = dict([(key, []) for key in cols])

    if len(data) > 0 and key in data[0]:
        data_dict = dict([(col, data[0][key][col]) for col in cols])
        
    source = ColumnDataSource(data_dict, name=key)
    return source

cc @mateusz

@gregoryac Various layout issues have been an ongoing challenge for Bokeh, but improvements are continually made incrementally. My first suggestion is to try the latest stable release version, and see if that improves things. Judging by the old DataTable styling this is a least 2.1.x or earlier.

Yes, I forgot to mention that. We are currently using 2.0.2. I’d hoped there was an workaround, but maybe we’ll just have to upgrade ASAP.

Thanks for the reply.

Well, I would definitely suggest trying the latest version first, at least just to see whether it does in fact resolve the problem. I don’t know if there is any workaround, but if there is any fix, it would only be in a later release.