Browse Source

simplification and cleanup

Falko Schindler 1 year ago
parent
commit
866de092df
2 changed files with 26 additions and 48 deletions
  1. 25 48
      nicegui/elements/echart.py
  2. 1 0
      nicegui/optional_features.py

+ 25 - 48
nicegui/elements/echart.py

@@ -2,10 +2,21 @@ from typing import Callable, Dict, Optional
 
 from typing_extensions import Self
 
+from .. import optional_features
 from ..awaitable_response import AwaitableResponse
 from ..element import Element
 from ..events import EChartPointClickEventArguments, GenericEventArguments, handle_event
 
+try:
+    import simplejson as json  # TODO: replace library or add to requirements or optional features
+    from pyecharts.charts.base import default
+    from pyecharts.charts.chart import Base as Chart
+    from pyecharts.commons.utils import JsCode
+    JS_CODE_PREFIX = JsCode('⬌').js_code.split('⬌')[0]
+    optional_features.register('pyecharts')
+except ImportError:
+    pass
+
 
 class EChart(Element, component='echart.js', libraries=['lib/echarts/echarts.min.js']):
 
@@ -51,15 +62,26 @@ class EChart(Element, component='echart.js', libraries=['lib/echarts/echarts.min
             ])
 
     @classmethod
-    def from_pyecharts(cls, chart_object: object, on_point_click: Optional[Callable] = None) -> Self:
+    def from_pyecharts(cls, chart: 'Chart', on_point_click: Optional[Callable] = None) -> Self:
         """Create an echart element from a pyecharts object.
 
-        :param chart_object: pyecharts chart object
+        :param chart: pyecharts chart object
         :param on_click_point: callback function that is called when a point is clicked
 
         :return: echart element
         """
-        options = _PyechartsUtils.chart2options(chart_object)
+        options = json.loads(json.dumps(chart.get_options(), default=default, ignore_nan=True))
+        stack = [options]
+        while stack:
+            current = stack.pop()
+            if isinstance(current, list):
+                stack.extend(current)
+            elif isinstance(current, dict):
+                for key, value in tuple(current.items()):
+                    if isinstance(value, str) and value.startswith(JS_CODE_PREFIX) and value.endswith(JS_CODE_PREFIX):
+                        current[f':{key}'] = current.pop(key)[len(JS_CODE_PREFIX):-len(JS_CODE_PREFIX)]
+                    else:
+                        stack.append(value)
         return cls(options, on_point_click)
 
     @property
@@ -88,48 +110,3 @@ class EChart(Element, component='echart.js', libraries=['lib/echarts/echarts.min
         :return: AwaitableResponse that can be awaited to get the result of the method call
         """
         return self.run_method('run_chart_method', name, *args, timeout=timeout, check_interval=check_interval)
-
-
-class _PyechartsUtils:
-    # pyecharts is not defined as a constant.
-    # https://github.com/pyecharts/pyecharts/blob/f92c839a51d3878eeb24504ad191706c9db2c2ed/pyecharts/commons/utils.py#L8
-    JS_CODE_PREFIX = '--x_x--0_0--'
-
-    @classmethod
-    def chart2options(cls, chart) -> Dict:
-        """Convert pyecharts chart object to options dictionary."""
-        # pylint: disable=import-outside-toplevel
-        import simplejson as json
-        from pyecharts.charts.base import default
-        from pyecharts.charts.chart import Base
-
-        assert isinstance(chart, Base), 'must be a pyecharts chart object'
-
-        dumps_str = json.dumps(chart.get_options(), default=default, ignore_nan=True)
-        opts = json.loads(dumps_str)
-        cls._replace_key_name_of_js_code(opts)
-        return opts
-
-    @classmethod
-    def _replace_key_name_of_js_code(cls, opts: Dict) -> None:
-        stack = [opts]
-        while stack:
-            cur = stack.pop()
-            if isinstance(cur, list):
-                stack.extend(cur)
-            elif isinstance(cur, dict):
-                for key, value in tuple(cur.items()):
-                    if isinstance(value, str) and cls._is_js_code_str(value):
-                        cur[f':{key}'] = cls._replace_js_code_fix(value)
-                        del cur[key]
-                    else:
-                        stack.append(value)
-
-    @classmethod
-    def _is_js_code_str(cls, text: str) -> bool:
-        return text[:len(cls.JS_CODE_PREFIX)] == cls.JS_CODE_PREFIX \
-            and text[-len(cls.JS_CODE_PREFIX):] == cls.JS_CODE_PREFIX
-
-    @classmethod
-    def _replace_js_code_fix(cls, text: str) -> str:
-        return text[len(cls.JS_CODE_PREFIX):-len(cls.JS_CODE_PREFIX)]

+ 1 - 0
nicegui/optional_features.py

@@ -8,6 +8,7 @@ FEATURE = Literal[
     'pandas',
     'pillow',
     'plotly',
+    'pyecharts',
     'webview',
 ]