From c13bb19f97142ac34980bef03b22d2b413378489 Mon Sep 17 00:00:00 2001 From: Marian Ivanov Date: Tue, 28 Feb 2023 16:22:12 +0100 Subject: [PATCH 1/2] First implementation of div widget added --- .../InteractiveDrawing/bokeh/bokehTools.py | 21 ++++++++++++------- .../bokeh/test_bokehDrawSA.py | 8 ++++--- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/RootInteractive/InteractiveDrawing/bokeh/bokehTools.py b/RootInteractive/InteractiveDrawing/bokeh/bokehTools.py index 1f7c93d7..44068997 100644 --- a/RootInteractive/InteractiveDrawing/bokeh/bokehTools.py +++ b/RootInteractive/InteractiveDrawing/bokeh/bokehTools.py @@ -3,6 +3,7 @@ from bokeh.models.transforms import CustomJSTransform from bokeh.models.mappers import LinearColorMapper from bokeh.models.widgets.tables import ScientificFormatter, DataTable +from bokeh.models.widgets.markups import Div from bokeh.models.plots import Plot from bokeh.transform import * from RootInteractive.InteractiveDrawing.bokeh.ConcatenatedString import ConcatenatedString @@ -509,6 +510,19 @@ def bokehDrawArray(dataFrame, query, figureArray, histogramArray=[], parameterAr else: optionGroup = options continue + optionLocal = optionGroup.copy() + nvars = len(variables) + if isinstance(variables[-1], dict): + logging.info("Option %s", variables[-1]) + optionLocal.update(variables[-1]) + nvars -= 1 + if variables[0] == 'div': + text_content = variables[-1].get("text", variables[1]) + widget = Div(text=text_content) + plotArray.append(widget) + if "name" in optionLocal: + plotDict[optionLocal["name"]] = widget + continue if variables[0] == 'table': TOptions = { 'include': '', @@ -708,13 +722,6 @@ def bokehDrawArray(dataFrame, query, figureArray, histogramArray=[], parameterAr widgetParams.append(variables) continue - optionLocal = optionGroup.copy() - nvars = len(variables) - if isinstance(variables[-1], dict): - logging.info("Option %s", variables[-1]) - optionLocal.update(variables[-1]) - nvars -= 1 - x_transform = optionLocal.get("x_transform", None) x_transform_parsed, x_transform_customjs = make_transform(x_transform, paramDict, aliasDict, cdsDict, jsFunctionDict) y_transform = optionLocal.get("y_transform", None) diff --git a/RootInteractive/InteractiveDrawing/bokeh/test_bokehDrawSA.py b/RootInteractive/InteractiveDrawing/bokeh/test_bokehDrawSA.py index 3f69cd14..d44ce66b 100644 --- a/RootInteractive/InteractiveDrawing/bokeh/test_bokehDrawSA.py +++ b/RootInteractive/InteractiveDrawing/bokeh/test_bokehDrawSA.py @@ -91,7 +91,7 @@ ['slider', ['AA'], {'bins': 10, "toggleable":True, "name": "widgetAA"}], ['multiSelect', ["DDC", "A2", "A3", "A4", "A0", "A1"]], ['multiSelectBitmask', ["maskAC"], {"mapping": {"A": 2, "C": 1}, "how":"all", "title": "maskAC(all)"}], - ['select',["CC", 0, 1, 2, 3], {"default": 1, "toggleable":True}], + ['select',["CC", 0, 1, 2, 3], {"default": 1}], ['multiSelect',["BoolB"]], ['textQuery', {"title": "selection", "name":"selectionText"}], #['slider','F', ['@min()','@max()','@med','@min()','@median()+3*#tlm()']], # to be implmneted @@ -103,7 +103,8 @@ ['spinner', ['nPoints'], {"name": "nPointsRender"}], ['select', ['transformX'], {"name": "transformX"}], ['select', ['transformY'], {"name": "transformY"}], - ['slider', ['paramX'], {"name":"paramX"}] + ['slider', ['paramX'], {"name":"paramX"}], + ['div', "Test description, insert description here", {"name":"description"}] ] widgetLayoutDesc={ @@ -119,7 +120,8 @@ "B": [ [3, 4, {'commonX': 1, 'y_visible': 3, 'x_visible':1, 'plot_height': 100}], {'plot_height': 100, 'sizing_mode': 'scale_width', 'y_visible' : 2} - ] + ], + "Description": ["description"] } def test_record(record_property: ty.Callable[[str, ty.Any], None]): From 0a075869e280046a9ee4932687423fd98fa6520d Mon Sep 17 00:00:00 2001 From: Marian Ivanov Date: Fri, 3 Mar 2023 14:06:29 +0100 Subject: [PATCH 2/2] Added automatically generated description table from metadata --- .../InteractiveDrawing/bokeh/bokehTools.py | 34 +++++++++++++++++-- .../bokeh/test_bokehDrawSA.py | 6 ++-- 2 files changed, 36 insertions(+), 4 deletions(-) diff --git a/RootInteractive/InteractiveDrawing/bokeh/bokehTools.py b/RootInteractive/InteractiveDrawing/bokeh/bokehTools.py index 44068997..5e04c3ea 100644 --- a/RootInteractive/InteractiveDrawing/bokeh/bokehTools.py +++ b/RootInteractive/InteractiveDrawing/bokeh/bokehTools.py @@ -517,7 +517,21 @@ def bokehDrawArray(dataFrame, query, figureArray, histogramArray=[], parameterAr optionLocal.update(variables[-1]) nvars -= 1 if variables[0] == 'div': - text_content = variables[-1].get("text", variables[1]) + text_content = optionLocal.get("text", variables[1]) + widget = Div(text=text_content) + plotArray.append(widget) + if "name" in optionLocal: + plotDict[optionLocal["name"]] = widget + continue + if variables[0] == 'descriptionTable': + data_source = optionLocal.get("source", None) + meta_fields = optionLocal.get("meta_fields", ["AxisTitle", "Description"]) + if "variables" in optionLocal: + used_variables = optionLocal["variables"] + else: + meta = cdsDict[data_source]["meta"] + used_variables = [*{i.split(".")[0] for i in meta.keys()}] + text_content = makeDescriptionTable(cdsDict, data_source, used_variables, meta_fields) widget = Div(text=text_content) plotArray.append(widget) if "name" in optionLocal: @@ -1615,6 +1629,7 @@ def makeCDSDict(sourceArray, paramDict, options={}): if cds_name in cdsDict: raise ValueError("Column data source IDs must be unique. Multiple data sources with name: "+ str(cds_name)+ " detected.") cdsDict[cds_name] = iSource + iSource["meta"] = {} # Detect the type if "type" not in iSource: @@ -1645,6 +1660,7 @@ def makeCDSDict(sourceArray, paramDict, options={}): # Create cdsOrig if cdsType == "source": + iSource["meta"] = iSource["data"].meta.metaData.copy() if "arrayCompression" in iSource and iSource["arrayCompression"] is not None: iSource["cdsOrig"] = CDSCompress(name=name_orig) else: @@ -1891,4 +1907,18 @@ def makeCdsSel(cdsDict, paramDict, key): downsampler.update() """)]) cds_used["cdsSel"] = cdsSel - return cdsSel \ No newline at end of file + return cdsSel + +def makeDescriptionTable(cdsDict, cdsName, fields, meta_fields): + th = '\n'.join(meta_fields) + rows = [] + for i in fields: + current_row = [] + for j in meta_fields: + value = cdsDict[cdsName]["meta"].get(f"{i}.{j}", "") + current_row.append(f"{value}") + current_row = '\t'.join(current_row) + rows.append(f"{i}{current_row}") + rows = '\n'.join(rows) + text = f" { rows}
{th}
" + return text \ No newline at end of file diff --git a/RootInteractive/InteractiveDrawing/bokeh/test_bokehDrawSA.py b/RootInteractive/InteractiveDrawing/bokeh/test_bokehDrawSA.py index d44ce66b..e8ede1be 100644 --- a/RootInteractive/InteractiveDrawing/bokeh/test_bokehDrawSA.py +++ b/RootInteractive/InteractiveDrawing/bokeh/test_bokehDrawSA.py @@ -24,6 +24,8 @@ AddMetadata(tree, "B.AxisTitle", "B (cm/s)") AddMetadata(tree, "C.AxisTitle", "B (s)") AddMetadata(tree, "D.AxisTitle", "D (a.u.)") + AddMetadata(tree, "A.Description", "Lorem ipsum") + AddMetadata(tree, "B.Description", "The velocity B") except: pass @@ -52,7 +54,7 @@ df['errY']=df.A*0.02+0.02 df['maskAC']=2*(df['A']>.5)|1*(df['C']>.5) df.head(10) -df.meta.metaData = {'A.AxisTitle': "A (cm)", 'B.AxisTitle': "B (cm/s)", 'C.AxisTitle': "C (s)", 'D.AxisTitle': "D (a.u.)", 'Bool.AxisTitle': "A>half", 'E.AxisTitle': "Category"} +df.meta.metaData = {'A.AxisTitle': "A (cm)", 'B.AxisTitle': "B (cm/s)", 'C.AxisTitle': "C (s)", 'D.AxisTitle': "D (a.u.)", 'E.AxisTitle': "Category", "A.Description": "The distance A"} parameterArray = [ {"name": "colorZ", "value":"A", "options":["A", "B", "EE"]}, @@ -104,7 +106,7 @@ ['select', ['transformX'], {"name": "transformX"}], ['select', ['transformY'], {"name": "transformY"}], ['slider', ['paramX'], {"name":"paramX"}], - ['div', "Test description, insert description here", {"name":"description"}] + ['descriptionTable', {"name":"description"}] ] widgetLayoutDesc={