diff --git a/.ci/patch_pubspec_version.py b/.ci/patch_pubspec_version.py index a13a5c5..0dca20c 100644 --- a/.ci/patch_pubspec_version.py +++ b/.ci/patch_pubspec_version.py @@ -1,3 +1,7 @@ +# /// script +# dependencies = ["pyyaml"] +# /// + import os import pathlib import sys @@ -17,7 +21,7 @@ "flet", ] -with open(pubspec_path, "r") as f: +with open(pubspec_path) as f: data = yaml.safe_load(f) # patch version diff --git a/.ci/patch_toml_version.py b/.ci/patch_toml_version.py index 98d39e3..9ee9900 100644 --- a/.ci/patch_toml_version.py +++ b/.ci/patch_toml_version.py @@ -1,3 +1,7 @@ +# /// script +# dependencies = ["tomlkit"] +# /// + import os import pathlib import sys @@ -14,7 +18,7 @@ print(f"Patching TOML file {toml_path} to {ver}") # read -with open(toml_path, "r") as f: +with open(toml_path) as f: t = tomlkit.parse(f.read()) # patch version diff --git a/.docstr.yaml b/.docstr.yaml new file mode 100644 index 0000000..1693da9 --- /dev/null +++ b/.docstr.yaml @@ -0,0 +1,7 @@ +paths: src/flet_map +badge: ./docs/assets/badges/docs-coverage.svg +skip_file_doc: True +skip_private: False +ignore_patterns: # Dict with key/value pairs of file-pattern/node-pattern + .*: __post_init__ # Ignore __post_init__ in all files +fail_under: 50 diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 9c36a87..14f7cdc 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -4,7 +4,6 @@ on: push: branches: - main - - dev paths: - 'LICENSE' - 'CHANGELOG.md' diff --git a/README.md b/README.md index df22d86..54ef031 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,9 @@ This package supports the following platforms: | Android | ✅ | | Web | ✅ | -## Installation +## Usage + +### Installation To install the `flet-map` package and add it to your project dependencies: @@ -45,6 +47,6 @@ To install the `flet-map` package and add it to your project dependencies: poetry add flet-map ``` -## Examples +### Examples -For examples, see [this](./examples) +For examples, see [these](./examples). diff --git a/appveyor.yml b/appveyor.yml index 7a22ee8..4ffd0f7 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -3,23 +3,20 @@ image: ubuntu version: '0.1.{build}' environment: + PYTHON_VERSION: 3.12 UV_PUBLISH_TOKEN: secure: 174ncAbF5IjSIkmioPt62jeSnzmTlRNchUkE4QdjDWH8xK1olYtySXLJpo2q95HcP7lWJky1hv4APESiRRHnBWoY0XRFafzM/mbCDMzG1tZXiXZmpP1qzHAtRP2QSCIg18xh1TMktraUdTi7sbJnjjRhqzgbW1k0kLBxKw79MPFBhYQ/TiGcmaYWZbWVZNY3HCUCb6Dt7bG1OE2Ul9rD1gvs55xwO9Oq9FOVA1VnMYw= -stack: -- python 3.12 - install: - source .ci/update_build_version.sh -- python --version -- python -m ensurepip --upgrade -- pip3 install --upgrade tomlkit pyyaml - curl -LsSf https://astral.sh/uv/install.sh | sh -- export PATH=$HOME/.local/bin:$PATH +- export PATH="$HOME/.local/bin:$PATH" +- uv python install $PYTHON_VERSION +- uv python pin $PYTHON_VERSION build_script: -- python .ci/patch_toml_version.py pyproject.toml $PYPI_VER -- python .ci/patch_pubspec_version.py src/flutter/flet_*/pubspec.yaml $PKG_VER +- uv run .ci/patch_toml_version.py pyproject.toml $PYPI_VER +- uv run .ci/patch_pubspec_version.py src/flutter/flet_*/pubspec.yaml $PKG_VER - (cd src/flutter/* && dart pub get && dart analyze && cd -) - uv build @@ -32,4 +29,4 @@ deploy_script: artifacts: - path: dist/*.whl -test: off \ No newline at end of file +test: off diff --git a/docs/index.md b/docs/index.md index 6abb3c6..a09f1fd 100644 --- a/docs/index.md +++ b/docs/index.md @@ -44,9 +44,6 @@ To install the `flet-map` package and add it to your project dependencies: poetry add flet-map ``` +### Examples -## Example - -```python title="main.py" ---8<-- "examples/map_example/src/main.py" -``` +See [these](map.md#examples). diff --git a/docs/map.md b/docs/map.md index d1c14ce..39cde3e 100644 --- a/docs/map.md +++ b/docs/map.md @@ -1 +1,9 @@ -::: flet_map.map.Map \ No newline at end of file +## Examples + +### Example 1 + +```python title="example_1.py" +--8<-- "examples/map_example/src/example_1.py" +``` + +::: flet_map.map.Map diff --git a/examples/map_example/src/main.py b/examples/map_example/src/example_1.py similarity index 98% rename from examples/map_example/src/main.py rename to examples/map_example/src/example_1.py index 4b3ebc1..f151bbc 100644 --- a/examples/map_example/src/main.py +++ b/examples/map_example/src/example_1.py @@ -65,7 +65,7 @@ def handle_tap(e: ftm.MapTapEvent): ), ftm.SimpleAttribution( text="Flet", - alignment=ft.Alignment.top_right(), + alignment=ft.Alignment.TOP_RIGHT, on_click=lambda e: print("Clicked SimpleAttribution"), ), ftm.MarkerLayer( diff --git a/mkdocs.yml b/mkdocs.yml index a9f018f..421fcb8 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -149,6 +149,11 @@ plugins: alias_type: symlink - glightbox - section-index + - external-images: + mappings: + - source_dir: examples + target_url_path: examples + include_exts: [ ".png", ".gif", ".svg" ] - mkdocstrings: default_handler: python_xref handlers: @@ -174,7 +179,6 @@ plugins: preload_modules: [ flet ] filters: - "!^_" # Exclude private members starting with only one underscore - - "!get_event_field_type" extensions: - griffe_modernized_annotations - griffe_warnings_deprecated @@ -225,3 +229,4 @@ markdown_extensions: - pymdownx.tasklist: custom_checkbox: true - pymdownx.tilde + - pymdownx.blocks.caption diff --git a/pyproject.toml b/pyproject.toml index b89c27d..11d50d9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -52,6 +52,7 @@ docs = [ "markdown-exec[ansi] >=1.11.0", "pydocstyle >=6.3.0", "linkcheckmd >=1.4.0", + "mkdocs-external-images", { include-group = 'docs-coverage' }, ] all = [ @@ -59,6 +60,9 @@ all = [ { include-group = 'docs' }, ] +[tool.uv.sources] +mkdocs-external-images = { git = "https://github.com/flet-dev/mkdocs-external-images", tag = "v0.2.0" } + [build-system] requires = ["setuptools"] build-backend = "setuptools.build_meta" diff --git a/src/flet_map/__init__.py b/src/flet_map/__init__.py index 5bcbc45..9fcc52f 100644 --- a/src/flet_map/__init__.py +++ b/src/flet_map/__init__.py @@ -1,17 +1,17 @@ -from .circle_layer import CircleLayer, CircleMarker -from .map import Map -from .marker_layer import Marker, MarkerLayer -from .polygon_layer import PolygonLayer, PolygonMarker -from .polyline_layer import PolylineLayer, PolylineMarker -from .rich_attribution import RichAttribution -from .simple_attribution import SimpleAttribution -from .source_attribution import ( +from flet_map.circle_layer import CircleLayer, CircleMarker +from flet_map.map import Map +from flet_map.marker_layer import Marker, MarkerLayer +from flet_map.polygon_layer import PolygonLayer, PolygonMarker +from flet_map.polyline_layer import PolylineLayer, PolylineMarker +from flet_map.rich_attribution import RichAttribution +from flet_map.simple_attribution import SimpleAttribution +from flet_map.source_attribution import ( ImageSourceAttribution, SourceAttribution, TextSourceAttribution, ) -from .tile_layer import TileLayer -from .types import ( +from flet_map.tile_layer import TileLayer +from flet_map.types import ( AttributionAlignment, Camera, CameraFit, diff --git a/src/flet_map/circle_layer.py b/src/flet_map/circle_layer.py index eddec6f..59dc79b 100644 --- a/src/flet_map/circle_layer.py +++ b/src/flet_map/circle_layer.py @@ -2,8 +2,8 @@ import flet as ft -from .map_layer import MapLayer -from .types import MapLatitudeLongitude +from flet_map.map_layer import MapLayer +from flet_map.types import MapLatitudeLongitude __all__ = ["CircleLayer", "CircleMarker"] diff --git a/src/flet_map/map.py b/src/flet_map/map.py index ecc33e3..2737728 100644 --- a/src/flet_map/map.py +++ b/src/flet_map/map.py @@ -1,11 +1,10 @@ -import asyncio from dataclasses import field from typing import Optional import flet as ft -from .map_layer import MapLayer -from .types import ( +from flet_map.map_layer import MapLayer +from flet_map.types import ( CameraFit, InteractionConfiguration, MapEvent, @@ -155,7 +154,7 @@ class Map(ft.ConstrainedControl): Fires when a pointer up event occurs. """ - async def rotate_from_async( + async def rotate_from( self, degree: ft.Number, animation_curve: Optional[ft.AnimationCurve] = None, @@ -174,7 +173,7 @@ async def rotate_from_async( cancel_ongoing_animations: Whether to cancel/stop all ongoing map-animations before starting this new one. """ - await self._invoke_method_async( + await self._invoke_method( method_name="rotate_from", arguments={ "degree": degree, @@ -184,35 +183,7 @@ async def rotate_from_async( }, ) - def rotate_from( - self, - degree: ft.Number, - animation_curve: Optional[ft.AnimationCurve] = None, - animation_duration: Optional[ft.DurationValue] = None, - cancel_ongoing_animations: bool = False, - ) -> None: - """ - Applies a rotation of `degree` to the current rotation. - - Args: - degree: The number of degrees to increment to the current rotation. - animation_curve: The curve of the animation. If None (the default), - [`Map.animation_curve`][(p).] will be used. - animation_duration: The duration of the animation. - If None (the default), [`Map.animation_duration`][(p).] will be used. - cancel_ongoing_animations: Whether to cancel/stop all - ongoing map-animations before starting this new one. - """ - asyncio.create_task( - self.rotate_from_async( - degree=degree, - animation_curve=animation_curve, - animation_duration=animation_duration, - cancel_ongoing_animations=cancel_ongoing_animations, - ) - ) - - async def reset_rotation_async( + async def reset_rotation( self, animation_curve: Optional[ft.AnimationCurve] = None, animation_duration: Optional[ft.DurationValue] = None, @@ -229,7 +200,7 @@ async def reset_rotation_async( cancel_ongoing_animations: Whether to cancel/stop all ongoing map-animations before starting this new one. """ - await self._invoke_method_async( + await self._invoke_method( method_name="reset_rotation", arguments={ "curve": animation_curve or self.animation_curve, @@ -238,32 +209,7 @@ async def reset_rotation_async( }, ) - def reset_rotation( - self, - animation_curve: Optional[ft.AnimationCurve] = None, - animation_duration: ft.DurationValue = None, - cancel_ongoing_animations: bool = False, - ) -> None: - """ - Resets the map's rotation to 0 degrees. - - Args: - animation_curve: The curve of the animation. If None (the default), - [`Map.animation_curve`][(p).] will be used. - animation_duration: The duration of the animation. - If None (the default), [`Map.animation_duration`][(p).] will be used. - cancel_ongoing_animations: Whether to cancel/stop all - ongoing map-animations before starting this new one. - """ - asyncio.create_task( - self.reset_rotation_async( - animation_curve=animation_curve, - animation_duration=animation_duration, - cancel_ongoing_animations=cancel_ongoing_animations, - ) - ) - - async def zoom_in_async( + async def zoom_in( self, animation_curve: Optional[ft.AnimationCurve] = None, animation_duration: Optional[ft.DurationValue] = None, @@ -280,7 +226,7 @@ async def zoom_in_async( cancel_ongoing_animations: Whether to cancel/stop all ongoing map-animations before starting this new one. """ - await self._invoke_method_async( + await self._invoke_method( method_name="zoom_in", arguments={ "curve": animation_curve or self.animation_curve, @@ -289,32 +235,7 @@ async def zoom_in_async( }, ) - def zoom_in( - self, - animation_curve: Optional[ft.AnimationCurve] = None, - animation_duration: Optional[ft.DurationValue] = None, - cancel_ongoing_animations: bool = False, - ) -> None: - """ - Zooms in by one zoom-level from the current one. - - Args: - animation_curve: The curve of the animation. If None (the default), - [`Map.animation_curve`][(p).] will be used. - animation_duration: The duration of the animation. - If None (the default), [`Map.animation_duration`][(p).] will be used. - cancel_ongoing_animations: Whether to cancel/stop all - ongoing map-animations before starting this new one. - """ - asyncio.create_task( - self.zoom_in_async( - animation_curve=animation_curve, - animation_duration=animation_duration, - cancel_ongoing_animations=cancel_ongoing_animations, - ) - ) - - async def zoom_out_async( + async def zoom_out( self, animation_curve: Optional[ft.AnimationCurve] = None, animation_duration: Optional[ft.DurationValue] = None, @@ -331,7 +252,7 @@ async def zoom_out_async( cancel_ongoing_animations: Whether to cancel/stop all ongoing map-animations before starting this new one. """ - await self._invoke_method_async( + await self._invoke_method( method_name="zoom_out", arguments={ "curve": animation_curve or self.animation_curve, @@ -340,32 +261,7 @@ async def zoom_out_async( }, ) - def zoom_out( - self, - animation_curve: Optional[ft.AnimationCurve] = None, - animation_duration: Optional[ft.DurationValue] = None, - cancel_ongoing_animations: bool = False, - ) -> None: - """ - Zooms out by one zoom-level from the current one. - - Args: - animation_curve: The curve of the animation. If None (the default), - [`Map.animation_curve`][(p).] will be used. - animation_duration: The duration of the animation. - If None (the default), [`Map.animation_duration`][(p).] will be used. - cancel_ongoing_animations: Whether to cancel/stop all - ongoing map-animations before starting this new one. - """ - asyncio.create_task( - self.zoom_out_async( - animation_curve=animation_curve, - animation_duration=animation_duration, - cancel_ongoing_animations=cancel_ongoing_animations, - ) - ) - - async def zoom_to_async( + async def zoom_to( self, zoom: ft.Number, animation_curve: Optional[ft.AnimationCurve] = None, @@ -384,7 +280,7 @@ async def zoom_to_async( cancel_ongoing_animations: Whether to cancel/stop all ongoing map-animations before starting this new one. """ - await self._invoke_method_async( + await self._invoke_method( method_name="zoom_to", arguments={ "zoom": zoom, @@ -394,42 +290,14 @@ async def zoom_to_async( }, ) - def zoom_to( - self, - zoom: ft.Number, - animation_curve: Optional[ft.AnimationCurve] = None, - animation_duration: Optional[ft.DurationValue] = None, - cancel_ongoing_animations: bool = False, - ) -> None: - """ - Zoom the map to a specific zoom level. - - Args: - zoom: The zoom level to zoom to. - animation_curve: The curve of the animation. If None (the default), - [`Map.animation_curve`][(p).] will be used. - animation_duration: The duration of the animation. - If None (the default), [`Map.animation_duration`][(p).] will be used. - cancel_ongoing_animations: Whether to cancel/stop all - ongoing map-animations before starting this new one. - """ - asyncio.create_task( - self.zoom_to_async( - zoom=zoom, - animation_curve=animation_curve, - animation_duration=animation_duration, - cancel_ongoing_animations=cancel_ongoing_animations, - ) - ) - - async def move_to_async( + async def move_to( self, destination: Optional[MapLatitudeLongitude] = None, zoom: Optional[ft.Number] = None, rotation: Optional[ft.Number] = None, animation_curve: Optional[ft.AnimationCurve] = None, animation_duration: Optional[ft.DurationValue] = None, - offset: ft.OffsetValue = ft.Offset(0, 0), + offset: ft.OffsetValue = (0, 0), cancel_ongoing_animations: bool = False, ) -> None: """ @@ -454,7 +322,7 @@ async def move_to_async( assert zoom is None or zoom >= 0, ( f"zoom must be greater than or equal to zero, got {zoom}" ) - await self._invoke_method_async( + await self._invoke_method( method_name="move_to", arguments={ "destination": destination, @@ -467,48 +335,7 @@ async def move_to_async( }, ) - def move_to( - self, - destination: Optional[MapLatitudeLongitude] = None, - zoom: Optional[ft.Number] = None, - rotation: Optional[ft.Number] = None, - animation_curve: Optional[ft.AnimationCurve] = None, - animation_duration: Optional[ft.DurationValue] = None, - offset: ft.OffsetValue = ft.Offset(0, 0), - cancel_ongoing_animations: bool = False, - ) -> None: - """ - Moves to a specific location. - - Args: - destination: The destination point to move to. - zoom: The zoom level to be applied. If provided, - must be greater than or equal to `0.0`. - rotation: Rotation (in degrees) to be applied. - offset: The offset to be used. Only works when `rotation` is `None`. - animation_curve: The curve of the animation. If None (the default), - [`Map.animation_curve`][(p).] will be used. - animation_duration: The duration of the animation. - If None (the default), [`Map.animation_duration`][(p).] will be used. - cancel_ongoing_animations: Whether to cancel/stop all - ongoing map-animations before starting this new one. - - Raises: - AssertionError: If `zoom` is not `None` and is negative. - """ - asyncio.create_task( - self.move_to_async( - destination=destination, - zoom=zoom, - rotation=rotation, - animation_curve=animation_curve, - animation_duration=animation_duration, - offset=offset, - cancel_ongoing_animations=cancel_ongoing_animations, - ) - ) - - async def center_on_async( + async def center_on( self, point: MapLatitudeLongitude, zoom: Optional[ft.Number], @@ -522,14 +349,14 @@ async def center_on_async( Args: point: The point on which to center the map. zoom: The zoom level to be applied. - animation_curve: The curve of the animation. If None (the default), + animation_curve: The curve of the animation. If `None` (the default), [`Map.animation_curve`][(p).] will be used. animation_duration: The duration of the animation. If None (the default), [`Map.animation_duration`][(p).] will be used. cancel_ongoing_animations: Whether to cancel/stop all ongoing map-animations before starting this new one. """ - await self._invoke_method_async( + await self._invoke_method( method_name="center_on", arguments={ "point": point, @@ -539,34 +366,3 @@ async def center_on_async( "cancel_ongoing_animations": cancel_ongoing_animations, }, ) - - def center_on( - self, - point: Optional[MapLatitudeLongitude], - zoom: Optional[ft.Number], - animation_curve: Optional[ft.AnimationCurve] = None, - animation_duration: Optional[ft.DurationValue] = None, - cancel_ongoing_animations: bool = False, - ) -> None: - """ - Centers the map on the given point. - - Args: - point: The point on which to center the map. - zoom: The zoom level to be applied. - animation_curve: The curve of the animation. If None (the default), - [`Map.animation_curve`][(p).] will be used. - animation_duration: The duration of the animation. - If None (the default), [`Map.animation_duration`][(p).] will be used. - cancel_ongoing_animations: Whether to cancel/stop all - ongoing map-animations before starting this new one. - """ - asyncio.create_task( - self.center_on_async( - point=point, - zoom=zoom, - animation_curve=animation_curve, - animation_duration=animation_duration, - cancel_ongoing_animations=cancel_ongoing_animations, - ) - ) diff --git a/src/flet_map/marker_layer.py b/src/flet_map/marker_layer.py index 801ee19..a06b377 100644 --- a/src/flet_map/marker_layer.py +++ b/src/flet_map/marker_layer.py @@ -3,8 +3,8 @@ import flet as ft -from .map_layer import MapLayer -from .types import MapLatitudeLongitude +from flet_map.map_layer import MapLayer +from flet_map.types import MapLatitudeLongitude __all__ = ["Marker", "MarkerLayer"] diff --git a/src/flet_map/polygon_layer.py b/src/flet_map/polygon_layer.py index 0b78a9e..b58234d 100644 --- a/src/flet_map/polygon_layer.py +++ b/src/flet_map/polygon_layer.py @@ -2,8 +2,8 @@ import flet as ft -from .map_layer import MapLayer -from .types import MapLatitudeLongitude +from flet_map.map_layer import MapLayer +from flet_map.types import MapLatitudeLongitude __all__ = ["PolygonLayer", "PolygonMarker"] diff --git a/src/flet_map/polyline_layer.py b/src/flet_map/polyline_layer.py index dd21826..11b746a 100644 --- a/src/flet_map/polyline_layer.py +++ b/src/flet_map/polyline_layer.py @@ -3,8 +3,8 @@ import flet as ft -from .map_layer import MapLayer -from .types import MapLatitudeLongitude, SolidStrokePattern, StrokePattern +from flet_map.map_layer import MapLayer +from flet_map.types import MapLatitudeLongitude, SolidStrokePattern, StrokePattern __all__ = ["PolylineLayer", "PolylineMarker"] diff --git a/src/flet_map/rich_attribution.py b/src/flet_map/rich_attribution.py index d24ab87..66f97eb 100644 --- a/src/flet_map/rich_attribution.py +++ b/src/flet_map/rich_attribution.py @@ -3,9 +3,9 @@ import flet as ft -from .map_layer import MapLayer -from .source_attribution import SourceAttribution -from .types import AttributionAlignment +from flet_map.map_layer import MapLayer +from flet_map.source_attribution import SourceAttribution +from flet_map.types import AttributionAlignment __all__ = ["RichAttribution"] diff --git a/src/flet_map/simple_attribution.py b/src/flet_map/simple_attribution.py index b1cca4b..1f3213b 100644 --- a/src/flet_map/simple_attribution.py +++ b/src/flet_map/simple_attribution.py @@ -3,7 +3,7 @@ import flet as ft -from .map_layer import MapLayer +from flet_map.map_layer import MapLayer __all__ = ["SimpleAttribution"] diff --git a/src/flet_map/tile_layer.py b/src/flet_map/tile_layer.py index 84b54c1..18a9c7c 100644 --- a/src/flet_map/tile_layer.py +++ b/src/flet_map/tile_layer.py @@ -3,8 +3,8 @@ import flet as ft -from .map_layer import MapLayer -from .types import ( +from flet_map.map_layer import MapLayer +from flet_map.types import ( FadeInTileDisplay, MapLatitudeLongitudeBounds, TileDisplay, @@ -122,7 +122,8 @@ class TileLayer(MapLayer): keep_buffer: int = 2 """ - When panning the map, keep this many rows and columns of tiles before unloading them. + When panning the map, keep this many rows and columns of + tiles before unloading them. """ pan_buffer: int = 1 diff --git a/src/flet_map/types.py b/src/flet_map/types.py index 3bf91d7..6ff65c3 100644 --- a/src/flet_map/types.py +++ b/src/flet_map/types.py @@ -5,7 +5,7 @@ import flet as ft if TYPE_CHECKING: - from .map import Map # noqa + from flet_map.map import Map # noqa __all__ = [ "AttributionAlignment", diff --git a/src/flutter/flet_map/lib/src/utils/map.dart b/src/flutter/flet_map/lib/src/utils/map.dart index 740f80d..a5448ac 100644 --- a/src/flutter/flet_map/lib/src/utils/map.dart +++ b/src/flutter/flet_map/lib/src/utils/map.dart @@ -157,8 +157,10 @@ KeyboardOptions? parseKeyboardOptions(dynamic value, parseDouble(value["rotate_leap_velocity_multiplier"], 3)!, zoomLeapVelocityMultiplier: parseDouble(value["zoom_leap_velocity_multiplier"], 3)!, - performLeapTriggerDuration: parseDuration(value["perform_leap_trigger_duration"]), - animationCurveReverseDuration: parseDuration(value["animation_curve_reverse_duration"])); + performLeapTriggerDuration: + parseDuration(value["perform_leap_trigger_duration"]), + animationCurveReverseDuration: + parseDuration(value["animation_curve_reverse_duration"])); } CursorRotationBehaviour? parseCursorRotationBehaviour(String? value,