1
0
Эх сурвалжийг харах

re-organize chart events; utilize NiceGUI's built-in event filter mechanism

Falko Schindler 1 жил өмнө
parent
commit
9d792b68f6

+ 2 - 2
.vscode/settings.json

@@ -3,13 +3,13 @@
   "editor.formatOnSave": true,
   "isort.args": ["--line-length", "120"],
   "prettier.printWidth": 120,
-  "python.formatting.provider": "none",
+  "python.formatting.provider": "autopep8",
   "python.formatting.autopep8Args": ["--max-line-length=120", "--experimental"],
   "python.testing.pytestArgs": ["."],
   "python.testing.pytestEnabled": true,
   "python.testing.unittestEnabled": false,
   "[python]": {
-    "editor.defaultFormatter": "ms-python.autopep8",
+    "editor.defaultFormatter": "ms-python.python",
     "editor.codeActionsOnSave": {
       "source.organizeImports": true
     }

+ 18 - 51
nicegui/elements/chart.js

@@ -5,57 +5,26 @@ export default {
       const imports = this.extras.map((extra) => import(window.path_prefix + extra));
       Promise.allSettled(imports).then(() => {
         this.seriesCount = this.options.series ? this.options.series.length : 0;
-        if (!this.options.plotOptions) {
-          this.options.plotOptions = {};
-        }
-        if (!this.options.plotOptions.series) {
-          this.options.plotOptions.series = {};
-        }
-        if (!this.options.plotOptions.series.point) {
-          this.options.plotOptions.series.point = {};
-        }
-        if (!this.options.plotOptions.series.point.events) {
-          this.options.plotOptions.series.point.events = {};
-        }
-        if (this.on_event_set === true) {
-          this.options.plotOptions.series.point.events.click = (e) => {
-            this.$emit("on_event", {
-              event_type: "point_click",
-              point_index: e.point.index,
-              point_x: e.point.x,
-              point_y: e.point.y,
-              series_index: e.point.series.index,
-            });
-          };
-        }
-        if (this.on_drag_drop_set === true) {
-          this.options.plotOptions.series.point.events.dragStart = (e) => {
-            this.$emit("on_point_drag_drop", {
-              event_type: "point_drag_start",
-            });
-          };
-          this.options.plotOptions.series.point.events.drag = (e) => {
-            this.$emit("on_point_drag_drop", {
-              event_type: "point_drag",
-              point_index: e.target.index,
-              point_x: e.target.x,
-              point_y: e.target.y,
-              series_index: e.target.series.index,
-            });
-          };
-          this.options.plotOptions.series.point.events.drop = (e) => {
-            this.$emit("on_point_drag_drop", {
-              event_type: "point_drop",
-              point_index: e.target.index,
-              point_x: e.target.x,
-              point_y: e.target.y,
-              series_name: e.target.series.name,
-              series_index: e.target.series.index,
-              series_x: e.target.series.xData,
-              series_y: e.target.series.yData,
-            });
+        this.options.plotOptions = this.options.plotOptions ?? {};
+        this.options.plotOptions.series = this.options.plotOptions.series ?? {};
+        this.options.plotOptions.series.point = this.options.plotOptions.series.point ?? {};
+        this.options.plotOptions.series.point.events = this.options.plotOptions.series.point.events ?? {};
+        function uncycle(e) {
+          // Highcharts events are cyclic, so we need to uncycle them
+          let { point, target, ...rest } = e;
+          point = point ?? target;
+          return {
+            ...rest,
+            point_index: point?.index,
+            point_x: point?.x,
+            point_y: point?.y,
+            series_index: point?.series?.index,
           };
         }
+        this.options.plotOptions.series.point.events.click = (e) => this.$emit("pointClick", uncycle(e));
+        this.options.plotOptions.series.point.events.dragStart = (e) => this.$emit("pointDragStart", uncycle(e));
+        this.options.plotOptions.series.point.events.drag = (e) => this.$emit("pointDrag", uncycle(e));
+        this.options.plotOptions.series.point.events.drop = (e) => this.$emit("pointDrop", uncycle(e));
         this.chart = Highcharts[this.type](this.$el, this.options);
         this.chart.reflow();
       });
@@ -91,7 +60,5 @@ export default {
     type: String,
     options: Object,
     extras: Array,
-    on_event_set: Boolean,
-    on_drag_drop_set: Boolean,
   },
 };

+ 56 - 65
nicegui/elements/chart.py

@@ -1,9 +1,8 @@
 from typing import Callable, Dict, List, Optional
 
 from ..element import Element
-from ..events import (ChartEventArguments, ChartPointEventArguments, ChartSeriesEventArguments, GenericEventArguments,
-                      handle_event)
-from ..helpers import KWONLY_SLOTS
+from ..events import (ChartPointClickEventArguments, ChartPointDragEventArguments, ChartPointDragStartEventArguments,
+                      ChartPointDropEventArguments, GenericEventArguments, handle_event)
 
 
 class Chart(Element,
@@ -13,8 +12,11 @@ class Chart(Element,
 
     def __init__(self, options: Dict, *,
                  type: str = 'chart', extras: List[str] = [],
-                 on_event: Optional[Callable] = None,
-                 on_drag_drop: Optional[Callable] = None) -> None:
+                 on_point_click: Optional[Callable] = None,
+                 on_point_drag_start: Optional[Callable] = None,
+                 on_point_drag: Optional[Callable] = None,
+                 on_point_drop: Optional[Callable] = None,
+                 ) -> None:
         """Chart
 
         An element to create a chart using `Highcharts <https://www.highcharts.com/>`_.
@@ -27,75 +29,64 @@ class Chart(Element,
         :param options: dictionary of Highcharts options
         :param type: chart type (e.g. "chart", "stockChart", "mapChart", ...; default: "chart")
         :param extras: list of extra dependencies to include (e.g. "annotations", "arc-diagram", "solid-gauge", ...)
-        :param on_change: callback to execute when value changes
+        :param on_point_click: callback function that is called when a point is clicked
+        :param on_point_drag_start: callback function that is called when a point drag starts
+        :param on_point_drag: callback function that is called when a point is dragged
+        :param on_point_drop: callback function that is called when a point is dropped
         """
         super().__init__()
         self._props['type'] = type
         self._props['options'] = options
         self._props['extras'] = extras
         self.libraries.extend(library for library in self.extra_libraries if library.path.stem in extras)
-        if on_event is not None:
-            self._props['on_event_set'] = True
-        if on_drag_drop is not None:
-            self._props['on_drag_drop_set'] = True
-        self._on_event = on_event
-        self._on_drag_drop = on_drag_drop
-        self.on('on_event', self.handle_event)
-        self.on('on_point_drag_drop', self.handle_drag_drop)
 
-    def handle_event(self, e: ChartEventArguments) -> None:
-        event_type = e.args.get('event_type', None)
-        if event_type == 'point_click':
-            arguments = ChartPointEventArguments(
-                sender=self,
-                client=self.client,
-                event_type=event_type,
-                point_index=e.args.get('point_index', None),
-                point_x=e.args.get('point_x', None),
-                point_y=e.args.get('point_y', None),
-                series_index=e.args.get('series_index', None),
-            )
-        else:
-            arguments = ChartEventArguments(
-                sender=self,
-                client=self.client,
-                event_type=event_type,
-            )
+        if on_point_click:
+            def handle_point_click(e: GenericEventArguments) -> None:
+                handle_event(on_point_click, ChartPointClickEventArguments(
+                    sender=self,
+                    client=self.client,
+                    event_type='point_click',
+                    point_index=e.args['point_index'],
+                    point_x=e.args['point_x'],
+                    point_y=e.args['point_y'],
+                    series_index=e.args['series_index'],
+                ))
+            self.on('pointClick', handle_point_click, ['point_index', 'point_x', 'point_y', 'series_index'])
 
-        handle_event(self._on_event, arguments)
+        if on_point_drag_start:
+            def handle_point_dragStart(_: GenericEventArguments) -> None:
+                handle_event(on_point_drag_start, ChartPointDragStartEventArguments(
+                    sender=self,
+                    client=self.client,
+                    event_type='point_drag_start',
+                ))
+            self.on('pointDragStart', handle_point_dragStart, [])
 
-    def handle_drag_drop(self, e: ChartEventArguments) -> None:
-        event_type = e.args.get('event_type', None)
-        if event_type == 'point_drag_start':
-            arguments = ChartEventArguments(
-                sender=self,
-                client=self.client,
-                event_type=event_type,
-            )
-        elif event_type == 'point_drag':
-            arguments = ChartPointEventArguments(
-                sender=self,
-                client=self.client,
-                event_type=event_type,
-                point_index=e.args.get('point_index', None),
-                point_x=e.args.get('point_x', None),
-                point_y=e.args.get('point_y', None),
-                series_index=e.args.get('series_index', None),
-            )
-        elif event_type == 'point_drop':
-            arguments = ChartSeriesEventArguments(
-                sender=self,
-                client=self.client,
-                event_type=event_type,
-                point_index=e.args.get('point_index', None),
-                point_x=e.args.get('point_x', None),
-                point_y=e.args.get('point_y', None),
-                series_index=e.args.get('series_index', None),
-                series_name=e.args.get('series_name', None),
-                series_x=e.args.get('series_x', None),
-                series_y=e.args.get('series_y', None),
-            )
-        handle_event(self._on_drag_drop, arguments)
+        if on_point_drag:
+            def handle_point_drag(e: GenericEventArguments) -> None:
+                handle_event(on_point_drag, ChartPointDragEventArguments(
+                    sender=self,
+                    client=self.client,
+                    event_type='point_drag',
+                    point_index=e.args['point_index'],
+                    point_x=e.args['point_x'],
+                    point_y=e.args['point_y'],
+                    series_index=e.args['series_index'],
+                ))
+            self.on('pointDrag', handle_point_drag, ['point_index', 'point_x', 'point_y', 'series_index'])
+
+        if on_point_drop:
+            def handle_point_drop(e: GenericEventArguments) -> None:
+                handle_event(on_point_drop, ChartPointDropEventArguments(
+                    sender=self,
+                    client=self.client,
+                    event_type='point_drop',
+                    point_index=e.args['point_index'],
+                    point_x=e.args['point_x'],
+                    point_y=e.args['point_y'],
+                    series_index=e.args['series_index'],
+                ))
+            self.on('pointDrop', handle_point_drop, ['point_index', 'point_x', 'point_y', 'series_index'])
 
     @property
     def options(self) -> Dict:

+ 19 - 5
nicegui/events.py

@@ -39,7 +39,7 @@ class ChartEventArguments(EventArguments):
 
 
 @dataclass(**KWONLY_SLOTS)
-class ChartPointEventArguments(ChartEventArguments):
+class ChartPointClickEventArguments(ChartEventArguments):
     series_index: int
     point_index: int
     point_x: float
@@ -47,10 +47,24 @@ class ChartPointEventArguments(ChartEventArguments):
 
 
 @dataclass(**KWONLY_SLOTS)
-class ChartSeriesEventArguments(ChartPointEventArguments):
-    series_name: str
-    series_x: List[float]
-    series_y: List[float]
+class ChartPointDragStartEventArguments(ChartEventArguments):
+    pass
+
+
+@dataclass(**KWONLY_SLOTS)
+class ChartPointDragEventArguments(ChartEventArguments):
+    series_index: int
+    point_index: int
+    point_x: float
+    point_y: float
+
+
+@dataclass(**KWONLY_SLOTS)
+class ChartPointDropEventArguments(ChartEventArguments):
+    series_index: int
+    point_index: int
+    point_x: float
+    point_y: float
 
 
 @dataclass(**KWONLY_SLOTS)

+ 24 - 23
website/more_documentation/chart_documentation.py

@@ -41,31 +41,32 @@ def more() -> None:
             ],
         }, extras=['solid-gauge']).classes('w-full h-64')
 
-    @text_demo('Chart with point dragging enabled', '''
-        This chart allows data manipulation by dragging the series points. Data can be read via the on_change callback.
+    @text_demo('Chart with draggable points', '''
+        This chart allows dragging the series points.
+        You can register callbacks for the following events:
+        
+        - `on_point_click`: called when a point is clicked
+        - `on_point_drag_start`: called when a point drag starts
+        - `on_point_drag`: called when a point is dragged
+        - `on_point_drop`: called when a point is dropped
     ''')
     def drag() -> None:
-        def handle_drag_drop(e):
-            if e.event_type == 'point_drag_start':
-                ui.notify('Point drag started.')
-            elif e.event_type == 'point_drag':
-                ui.notify(f"You are dragging point {e.point_index} in series {e.series_index}.")
-            elif e.event_type == 'point_drop':
-                ui.notify(f"You dropped point {e.point_index} in series {e.series_index} at [{e.point_x},{e.point_y}].")
-
-        ui.chart({
-            'title': False,
-            'plotOptions': {
-                'series': {
-                    'stickyTracking': False,
-                    'dragDrop': {'draggableX': True, 'draggableY': True, 'dragPrecisionX': 1, 'dragPrecisionY': 1},
+        ui.chart(
+            {
+                'title': False,
+                'plotOptions': {
+                    'series': {
+                        'stickyTracking': False,
+                        'dragDrop': {'draggableY': True, 'dragPrecisionY': 1},
+                    },
                 },
+                'series': [
+                    {'name': 'A', 'data': [[20, 10], [30, 20], [40, 30]]},
+                    {'name': 'B', 'data': [[50, 40], [60, 50], [70, 60]]},
+                ],
             },
-            'series': [
-                {'name': 'A', 'data': [[20, 10], [30, 20], [40, 30]]},
-                {'name': 'B', 'data': [[50, 40], [60, 50], [70, 60]]},
-            ],
-        },  extras=['draggable-points'],
-            on_event=lambda e: ui.notify(f"Event occured: {e}"),
-            on_drag_drop=lambda e: handle_drag_drop(e)
+            extras=['draggable-points'],
+            on_point_click=lambda e: ui.notify(f'Click: {e}'),
+            on_point_drag_start=lambda e: ui.notify(f'Drag start: {e}'),
+            on_point_drop=lambda e: ui.notify(f'Drop: {e}')
         ).classes('w-full h-64')