Repository: zauberzeug/nicegui
Pull request #669 (2023-03-30 00:20:17)

Plotly orjson support numpy arrays of dtype=object
See [https://github.com/zauberzeug/nicegui/issues/601](https://github.com/zauberzeug/nicegui/issues/601).

* Add additional Plotly example utilizing dictionary interface
* Extend json serializer to support numpy arrays of `dtype=object`


Patch:
diff --git a/nicegui/json/orjson_wrapper.py b/nicegui/json/orjson_wrapper.py
index b56f92ab..5bea767f 100644
--- a/nicegui/json/orjson_wrapper.py
+++ b/nicegui/json/orjson_wrapper.py
@@ -1,5 +1,6 @@
 from typing import Any, Optional, Tuple
 
+import numpy as np
 import orjson
 from fastapi import Response
 
@@ -26,7 +27,7 @@ def dumps(obj: Any, sort_keys: bool = False, separators: Optional[Tuple[str, str
     if sort_keys:
         opts |= orjson.OPT_SORT_KEYS
 
-    return orjson.dumps(obj, option=opts).decode('utf-8')
+    return orjson.dumps(obj, option=opts, default=_orjson_converter).decode('utf-8')
 
 
 def loads(value: str) -> Any:
@@ -37,6 +38,12 @@ def loads(value: str) -> Any:
     return orjson.loads(value)
 
 
+def _orjson_converter(obj):
+    """Custom serializer/converter, e.g. for numpy object arrays."""
+    if isinstance(obj, np.ndarray) and obj.dtype == np.object_:
+        return obj.tolist()
+
+
 class NiceGUIJSONResponse(Response):
     """FastAPI response class to support our custom json serializer implementation.
 
@@ -45,4 +52,4 @@ class NiceGUIJSONResponse(Response):
     media_type = 'application/json'
 
     def render(self, content: Any) -> bytes:
-        return orjson.dumps(content, option=ORJSON_OPTS)
+        return orjson.dumps(content, option=ORJSON_OPTS, default=_orjson_converter)
diff --git a/website/more_documentation/plotly_documentation.py b/website/more_documentation/plotly_documentation.py
index 9bfc0924..c4c18ac0 100644
--- a/website/more_documentation/plotly_documentation.py
+++ b/website/more_documentation/plotly_documentation.py
@@ -1,3 +1,5 @@
+import numpy as np
+
 from nicegui import ui
 
 from ..documentation_tools import text_demo
@@ -12,6 +14,39 @@ def main_demo() -> None:
 
 
 def more() -> None:
+    @text_demo('Dictionary interface', '''
+        This demo shows how to use the declarative dictionary interface to create a plot.
+        For plots with many traces and data points, this is more efficient than the object-oriented interface.
+        The definition corresponds to the [JavaScript Plotly API](https://plotly.com/javascript/).
+        Due to different defaults, the resulting plot may look slightly different from the same plot created with the object-oriented interface,
+        but the functionality is the same.
+    ''')
+    def plot_dict_interface():
+        fig = {
+            'data': [
+                {
+                    'type': 'scatter',
+                    'name': 'Trace 1',
+                    'x': [1, 2, 3, 4],
+                    'y': [1, 2, 3, 2.5],
+                },
+                {
+                    'type': 'scatter',
+                    'name': 'Trace 2',
+                    'x': [1, 2, 3, 4],
+                    'y': np.array([1.4, 1.8, 3.8, 3.2]),
+                    'line': {'dash': 'dot', 'width': 3},
+                },
+            ],
+            'layout': {
+                'margin': {'l': 15, 'r': 0, 't': 0, 'b': 15},
+                'plot_bgcolor': '#E5ECF6',
+                'xaxis': {'gridcolor': 'white'},
+                'yaxis': {'gridcolor': 'white'},
+            },
+        }
+        ui.plotly(fig).classes('w-full h-40')
+
     @text_demo('Plot updates', '''
         This demo shows how to update the plot in real time.
         Click the button to add a new trace to the plot.


Test patch:
diff --git a/tests/test_json.py b/tests/test_json.py
index 5a434e06..5812eb03 100644
--- a/tests/test_json.py
+++ b/tests/test_json.py
@@ -43,6 +43,7 @@ def test_json():
         np.array([1.0, 0]),
         np.array([0, False, np.pi]),
         np.array(['2010-10-17 07:15:30', '2011-05-13 08:20:35', '2013-01-15 09:09:09'], dtype=np.datetime64),
+        np.array([1.0, None, 'test'], dtype=np.object_)
     ]
 
     for test in tests: