ソースを参照

Merge pull request #2418 from zauberzeug/layer-methods

Introduce layer methods for Leaflet elements
Rodja Trappe 1 年間 前
コミット
8fb7a78181

+ 14 - 0
nicegui/elements/leaflet.js

@@ -120,7 +120,21 @@ export default {
       this.map.eachLayer((layer) => this.map.removeLayer(layer));
     },
     run_map_method(name, ...args) {
+      if (name.startsWith(":")) {
+        name = name.slice(1);
+        args = args.map((arg) => new Function("return " + arg)());
+      }
       return this.map[name](...args);
     },
+    run_layer_method(id, name, ...args) {
+      this.map.eachLayer((layer) => {
+        if (layer.id !== id) return;
+        if (name.startsWith(":")) {
+          name = name.slice(1);
+          args = args.map((arg) => new Function("return " + arg)());
+        }
+        return layer[name](...args);
+      });
+    },
   },
 };

+ 17 - 1
nicegui/elements/leaflet.py

@@ -117,7 +117,7 @@ class Leaflet(Element, component='leaflet.js'):
         If the function is awaited, the result of the method call is returned.
         Otherwise, the method is executed without waiting for a response.
 
-        :param name: name of the method
+        :param name: name of the method (a prefix ":" indicates that the arguments are JavaScript expressions)
         :param args: arguments to pass to the method
         :param timeout: timeout in seconds (default: 1 second)
         :param check_interval: interval in seconds to check for a response (default: 0.01 seconds)
@@ -126,6 +126,22 @@ class Leaflet(Element, component='leaflet.js'):
         """
         return self.run_method('run_map_method', name, *args, timeout=timeout, check_interval=check_interval)
 
+    def run_layer_method(self, layer_id: str, name: str, *args, timeout: float = 1, check_interval: float = 0.01) -> AwaitableResponse:
+        """Run a method of a Leaflet layer.
+
+        If the function is awaited, the result of the method call is returned.
+        Otherwise, the method is executed without waiting for a response.
+
+        :param layer_id: ID of the layer
+        :param name: name of the method (a prefix ":" indicates that the arguments are JavaScript expressions)
+        :param args: arguments to pass to the method
+        :param timeout: timeout in seconds (default: 1 second)
+        :param check_interval: interval in seconds to check for a response (default: 0.01 seconds)
+
+        :return: AwaitableResponse that can be awaited to get the result of the method call
+        """
+        return self.run_method('run_layer_method', layer_id, name, *args, timeout=timeout, check_interval=check_interval)
+
     def _handle_delete(self) -> None:
         binding.remove(self.layers, Layer)
         super().delete()

+ 17 - 1
nicegui/elements/leaflet_layer.py

@@ -3,8 +3,9 @@ from __future__ import annotations
 import uuid
 from abc import abstractmethod
 from dataclasses import dataclass, field
-from typing import TYPE_CHECKING, ClassVar, Optional
+from typing import TYPE_CHECKING, Any, ClassVar, Optional
 
+from ..awaitable_response import AwaitableResponse
 from ..dataclasses import KWONLY_SLOTS
 
 if TYPE_CHECKING:
@@ -27,3 +28,18 @@ class Layer:
     @abstractmethod
     def to_dict(self) -> dict:
         """Return a dictionary representation of the layer."""
+
+    def run_method(self, name: str, *args: Any, timeout: float = 1, check_interval: float = 0.01) -> AwaitableResponse:
+        """Run a method of the Leaflet layer.
+
+        If the function is awaited, the result of the method call is returned.
+        Otherwise, the method is executed without waiting for a response.
+
+        :param name: name of the method (a prefix ":" indicates that the arguments are JavaScript expressions)
+        :param args: arguments to pass to the method
+        :param timeout: timeout in seconds (default: 1 second)
+        :param check_interval: interval in seconds to check for a response (default: 0.01 seconds)
+
+        :return: AwaitableResponse that can be awaited to get the result of the method call
+        """
+        return self.leaflet.run_method('run_layer_method', self.id, name, *args, timeout=timeout, check_interval=check_interval)

+ 9 - 0
nicegui/elements/leaflet_layers.py

@@ -46,3 +46,12 @@ class Marker(Layer):
         """Make the marker draggable."""
         self.options['draggable'] = value
         return self
+
+    def move(self, lat: float, lng: float) -> None:
+        """Move the marker to a new position.
+
+        :param lat: latitude
+        :param lng: longitude
+        """
+        self.latlng = (lat, lng)
+        self.run_method('setLatLng', (lat, lng))

+ 24 - 2
website/documentation/content/leaflet_documentation.py

@@ -55,6 +55,15 @@ def markers() -> None:
     m.on('map-click', handle_click)
 
 
+@doc.demo('Move Markers', '''
+    You can move markers with the `move` method.
+''')
+def move_markers() -> None:
+    m = ui.leaflet(center=(51.505, -0.09))
+    marker = m.marker(latlng=m.center)
+    ui.button('Move marker', on_click=lambda: marker.move(51.51, -0.09))
+
+
 @doc.demo('Vector Layers', '''
     Leaflet supports a set of [vector layers](https://leafletjs.com/reference.html#:~:text=VideoOverlay-,Vector%20Layers,-Path) like circle, polygon etc.
     These can be added with the `generic_layer` method.
@@ -62,8 +71,7 @@ def markers() -> None:
 ''')
 def vector_layers() -> None:
     m = ui.leaflet(center=(51.505, -0.09)).classes('h-32')
-    m.generic_layer(name='circle',
-                    args=[[51.505, -0.09], {'color': 'red', 'radius': 300}])
+    m.generic_layer(name='circle', args=[m.center, {'color': 'red', 'radius': 300}])
 
 
 @doc.demo('Disable Pan and Zoom', '''
@@ -121,4 +129,18 @@ def run_map_methods() -> None:
     ui.button('Fit world', on_click=lambda: m.run_map_method('fitWorld'))
 
 
+@doc.demo('Run Layer Methods', '''
+    You can run methods of the Leaflet layer objects with `run_layer_method`.
+    This demo shows how to change the opacity of a marker or change its icon.
+''')
+def run_layer_methods() -> None:
+    m = ui.leaflet(center=(51.505, -0.09)).classes('h-32')
+    marker = m.marker(latlng=m.center)
+    ui.button('Hide', on_click=lambda: marker.run_method('setOpacity', 0.3))
+    ui.button('Show', on_click=lambda: marker.run_method('setOpacity', 1.0))
+
+    icon = 'L.icon({iconUrl: "http://leafletjs.com/examples/custom-icons/leaf-green.png"})'
+    ui.button('Change icon', on_click=lambda: marker.run_method(':setIcon', icon))
+
+
 doc.reference(ui.leaflet)