flet-charts 0.85.0.dev2__tar.gz → 0.85.0.dev4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {flet_charts-0.85.0.dev2/src/flet_charts.egg-info → flet_charts-0.85.0.dev4}/PKG-INFO +2 -2
- {flet_charts-0.85.0.dev2 → flet_charts-0.85.0.dev4}/pyproject.toml +2 -2
- {flet_charts-0.85.0.dev2 → flet_charts-0.85.0.dev4}/src/flet_charts/__init__.py +6 -0
- {flet_charts-0.85.0.dev2 → flet_charts-0.85.0.dev4}/src/flet_charts/line_chart.py +4 -0
- {flet_charts-0.85.0.dev2 → flet_charts-0.85.0.dev4}/src/flet_charts/matplotlib_chart.py +33 -33
- flet_charts-0.85.0.dev4/src/flet_charts/matplotlib_chart_canvas.py +72 -0
- {flet_charts-0.85.0.dev2 → flet_charts-0.85.0.dev4/src/flet_charts.egg-info}/PKG-INFO +2 -2
- {flet_charts-0.85.0.dev2 → flet_charts-0.85.0.dev4}/src/flet_charts.egg-info/SOURCES.txt +2 -0
- flet_charts-0.85.0.dev4/src/flet_charts.egg-info/requires.txt +1 -0
- {flet_charts-0.85.0.dev2 → flet_charts-0.85.0.dev4}/src/flutter/flet_charts/lib/src/extension.dart +3 -0
- flet_charts-0.85.0.dev4/src/flutter/flet_charts/lib/src/matplotlib_chart_canvas.dart +457 -0
- {flet_charts-0.85.0.dev2 → flet_charts-0.85.0.dev4}/src/flutter/flet_charts/lib/src/utils/line_chart.dart +1 -1
- flet_charts-0.85.0.dev2/src/flet_charts.egg-info/requires.txt +0 -1
- {flet_charts-0.85.0.dev2 → flet_charts-0.85.0.dev4}/LICENSE +0 -0
- {flet_charts-0.85.0.dev2 → flet_charts-0.85.0.dev4}/README.md +0 -0
- {flet_charts-0.85.0.dev2 → flet_charts-0.85.0.dev4}/setup.cfg +0 -0
- {flet_charts-0.85.0.dev2 → flet_charts-0.85.0.dev4}/src/flet_charts/bar_chart.py +0 -0
- {flet_charts-0.85.0.dev2 → flet_charts-0.85.0.dev4}/src/flet_charts/bar_chart_group.py +0 -0
- {flet_charts-0.85.0.dev2 → flet_charts-0.85.0.dev4}/src/flet_charts/bar_chart_rod.py +0 -0
- {flet_charts-0.85.0.dev2 → flet_charts-0.85.0.dev4}/src/flet_charts/bar_chart_rod_stack_item.py +0 -0
- {flet_charts-0.85.0.dev2 → flet_charts-0.85.0.dev4}/src/flet_charts/candlestick_chart.py +0 -0
- {flet_charts-0.85.0.dev2 → flet_charts-0.85.0.dev4}/src/flet_charts/candlestick_chart_spot.py +0 -0
- {flet_charts-0.85.0.dev2 → flet_charts-0.85.0.dev4}/src/flet_charts/chart_axis.py +0 -0
- {flet_charts-0.85.0.dev2 → flet_charts-0.85.0.dev4}/src/flet_charts/line_chart_data.py +0 -0
- {flet_charts-0.85.0.dev2 → flet_charts-0.85.0.dev4}/src/flet_charts/line_chart_data_point.py +0 -0
- {flet_charts-0.85.0.dev2 → flet_charts-0.85.0.dev4}/src/flet_charts/matplotlib_backends/backend_flet_agg.py +0 -0
- {flet_charts-0.85.0.dev2 → flet_charts-0.85.0.dev4}/src/flet_charts/matplotlib_chart_with_toolbar.py +0 -0
- {flet_charts-0.85.0.dev2 → flet_charts-0.85.0.dev4}/src/flet_charts/pie_chart.py +0 -0
- {flet_charts-0.85.0.dev2 → flet_charts-0.85.0.dev4}/src/flet_charts/pie_chart_section.py +0 -0
- {flet_charts-0.85.0.dev2 → flet_charts-0.85.0.dev4}/src/flet_charts/plotly_chart.py +0 -0
- {flet_charts-0.85.0.dev2 → flet_charts-0.85.0.dev4}/src/flet_charts/radar_chart.py +0 -0
- {flet_charts-0.85.0.dev2 → flet_charts-0.85.0.dev4}/src/flet_charts/radar_data_set.py +0 -0
- {flet_charts-0.85.0.dev2 → flet_charts-0.85.0.dev4}/src/flet_charts/scatter_chart.py +0 -0
- {flet_charts-0.85.0.dev2 → flet_charts-0.85.0.dev4}/src/flet_charts/scatter_chart_spot.py +0 -0
- {flet_charts-0.85.0.dev2 → flet_charts-0.85.0.dev4}/src/flet_charts/types.py +0 -0
- {flet_charts-0.85.0.dev2 → flet_charts-0.85.0.dev4}/src/flet_charts.egg-info/dependency_links.txt +0 -0
- {flet_charts-0.85.0.dev2 → flet_charts-0.85.0.dev4}/src/flet_charts.egg-info/top_level.txt +0 -0
- {flet_charts-0.85.0.dev2 → flet_charts-0.85.0.dev4}/src/flutter/flet_charts/lib/flet_charts.dart +0 -0
- {flet_charts-0.85.0.dev2 → flet_charts-0.85.0.dev4}/src/flutter/flet_charts/lib/src/bar_chart.dart +0 -0
- {flet_charts-0.85.0.dev2 → flet_charts-0.85.0.dev4}/src/flutter/flet_charts/lib/src/candlestick_chart.dart +0 -0
- {flet_charts-0.85.0.dev2 → flet_charts-0.85.0.dev4}/src/flutter/flet_charts/lib/src/line_chart.dart +0 -0
- {flet_charts-0.85.0.dev2 → flet_charts-0.85.0.dev4}/src/flutter/flet_charts/lib/src/pie_chart.dart +0 -0
- {flet_charts-0.85.0.dev2 → flet_charts-0.85.0.dev4}/src/flutter/flet_charts/lib/src/radar_chart.dart +0 -0
- {flet_charts-0.85.0.dev2 → flet_charts-0.85.0.dev4}/src/flutter/flet_charts/lib/src/scatter_chart.dart +0 -0
- {flet_charts-0.85.0.dev2 → flet_charts-0.85.0.dev4}/src/flutter/flet_charts/lib/src/utils/bar_chart.dart +0 -0
- {flet_charts-0.85.0.dev2 → flet_charts-0.85.0.dev4}/src/flutter/flet_charts/lib/src/utils/candlestick_chart.dart +0 -0
- {flet_charts-0.85.0.dev2 → flet_charts-0.85.0.dev4}/src/flutter/flet_charts/lib/src/utils/charts.dart +0 -0
- {flet_charts-0.85.0.dev2 → flet_charts-0.85.0.dev4}/src/flutter/flet_charts/lib/src/utils/pie_chart.dart +0 -0
- {flet_charts-0.85.0.dev2 → flet_charts-0.85.0.dev4}/src/flutter/flet_charts/lib/src/utils/radar_chart.dart +0 -0
- {flet_charts-0.85.0.dev2 → flet_charts-0.85.0.dev4}/src/flutter/flet_charts/lib/src/utils/scatter_chart.dart +0 -0
- {flet_charts-0.85.0.dev2 → flet_charts-0.85.0.dev4}/src/flutter/flet_charts/pubspec.yaml +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: flet-charts
|
|
3
|
-
Version: 0.85.0.
|
|
3
|
+
Version: 0.85.0.dev4
|
|
4
4
|
Summary: Interactive chart controls for Flet apps.
|
|
5
5
|
Author-email: Flet contributors <hello@flet.dev>
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -11,7 +11,7 @@ Project-URL: Issues, https://github.com/flet-dev/flet/issues
|
|
|
11
11
|
Requires-Python: >=3.10
|
|
12
12
|
Description-Content-Type: text/markdown
|
|
13
13
|
License-File: LICENSE
|
|
14
|
-
Requires-Dist: flet==0.85.0.
|
|
14
|
+
Requires-Dist: flet==0.85.0.dev4
|
|
15
15
|
Dynamic: license-file
|
|
16
16
|
|
|
17
17
|
# flet-charts
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "flet-charts"
|
|
3
|
-
version = "0.85.0.
|
|
3
|
+
version = "0.85.0.dev4"
|
|
4
4
|
description = "Interactive chart controls for Flet apps."
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
authors = [{ name = "Flet contributors", email = "hello@flet.dev" }]
|
|
7
7
|
license = "Apache-2.0"
|
|
8
8
|
requires-python = ">=3.10"
|
|
9
9
|
dependencies = [
|
|
10
|
-
"flet==0.85.0.
|
|
10
|
+
"flet==0.85.0.dev4",
|
|
11
11
|
]
|
|
12
12
|
|
|
13
13
|
[project.urls]
|
|
@@ -33,6 +33,10 @@ from flet_charts.matplotlib_chart import (
|
|
|
33
33
|
MatplotlibChartMessageEvent,
|
|
34
34
|
MatplotlibChartToolbarButtonsUpdateEvent,
|
|
35
35
|
)
|
|
36
|
+
from flet_charts.matplotlib_chart_canvas import (
|
|
37
|
+
MatplotlibChartCanvas,
|
|
38
|
+
MatplotlibChartCanvasResizeEvent,
|
|
39
|
+
)
|
|
36
40
|
from flet_charts.matplotlib_chart_with_toolbar import MatplotlibChartWithToolbar
|
|
37
41
|
from flet_charts.pie_chart import PieChart, PieChartEvent
|
|
38
42
|
from flet_charts.pie_chart_section import PieChartSection
|
|
@@ -95,6 +99,8 @@ __all__ = [
|
|
|
95
99
|
"LineChartEventSpot",
|
|
96
100
|
"LineChartTooltip",
|
|
97
101
|
"MatplotlibChart",
|
|
102
|
+
"MatplotlibChartCanvas",
|
|
103
|
+
"MatplotlibChartCanvasResizeEvent",
|
|
98
104
|
"MatplotlibChartMessageEvent",
|
|
99
105
|
"MatplotlibChartToolbarButtonsUpdateEvent",
|
|
100
106
|
"MatplotlibChartWithToolbar",
|
|
@@ -59,6 +59,10 @@ class LineChartEvent(ft.Event["LineChart"]):
|
|
|
59
59
|
spots: list[LineChartEventSpot]
|
|
60
60
|
"""
|
|
61
61
|
Spots on which the event occurred.
|
|
62
|
+
|
|
63
|
+
Note:
|
|
64
|
+
This list is empty when the event does not target a concrete point, for
|
|
65
|
+
example when the pointer hovers over or taps empty chart space.
|
|
62
66
|
"""
|
|
63
67
|
|
|
64
68
|
|
|
@@ -5,7 +5,10 @@ from io import BytesIO
|
|
|
5
5
|
from typing import Any, Optional
|
|
6
6
|
|
|
7
7
|
import flet as ft
|
|
8
|
-
|
|
8
|
+
from flet_charts.matplotlib_chart_canvas import (
|
|
9
|
+
MatplotlibChartCanvas,
|
|
10
|
+
MatplotlibChartCanvasResizeEvent,
|
|
11
|
+
)
|
|
9
12
|
|
|
10
13
|
_MATPLOTLIB_IMPORT_ERROR: Optional[ImportError] = None
|
|
11
14
|
|
|
@@ -124,13 +127,21 @@ class MatplotlibChart(ft.GestureDetector):
|
|
|
124
127
|
logger.debug(f"DPR: {self.__dpr}")
|
|
125
128
|
self.__image_mode = "full"
|
|
126
129
|
|
|
127
|
-
self.
|
|
128
|
-
# resize_interval=10,
|
|
130
|
+
self.mpl_canvas = MatplotlibChartCanvas(
|
|
129
131
|
on_resize=self._on_canvas_resize,
|
|
130
132
|
expand=True,
|
|
131
133
|
)
|
|
134
|
+
# Rubberband (zoom selection) overlay drawn on top of the chart image.
|
|
135
|
+
self._rubberband = ft.Container(
|
|
136
|
+
visible=False,
|
|
137
|
+
border=ft.Border.all(1, ft.Colors.with_opacity(0.6, ft.Colors.GREY)),
|
|
138
|
+
)
|
|
139
|
+
self._stack = ft.Stack(
|
|
140
|
+
controls=[self.mpl_canvas, self._rubberband],
|
|
141
|
+
expand=True,
|
|
142
|
+
)
|
|
132
143
|
self.keyboard_listener = ft.KeyboardListener(
|
|
133
|
-
self.
|
|
144
|
+
self._stack,
|
|
134
145
|
autofocus=True,
|
|
135
146
|
on_key_down=self._on_key_down,
|
|
136
147
|
on_key_up=self._on_key_up,
|
|
@@ -419,23 +430,18 @@ class MatplotlibChart(ft.GestureDetector):
|
|
|
419
430
|
|
|
420
431
|
while True:
|
|
421
432
|
is_binary, content = await self._receive_queue.get()
|
|
433
|
+
|
|
422
434
|
if is_binary:
|
|
435
|
+
assert isinstance(content, (bytes, bytearray))
|
|
423
436
|
logger.debug(f"receive_binary({len(content)})")
|
|
437
|
+
# Hand the frame to the client widget — full PNG replaces the
|
|
438
|
+
# backbuffer, diff PNG composites onto it. Awaiting naturally
|
|
439
|
+
# rate-limits this loop to the client's processing speed and
|
|
440
|
+
# yields the asyncio loop for incoming events.
|
|
424
441
|
if self.__image_mode == "full":
|
|
425
|
-
await self.
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
fc.Image(
|
|
429
|
-
src=content,
|
|
430
|
-
x=0,
|
|
431
|
-
y=0,
|
|
432
|
-
width=self.figure.bbox.size[0] / self.__dpr,
|
|
433
|
-
height=self.figure.bbox.size[1] / self.__dpr,
|
|
434
|
-
)
|
|
435
|
-
]
|
|
436
|
-
ft.context.disable_auto_update()
|
|
437
|
-
self.canvas.update()
|
|
438
|
-
await self.canvas.capture()
|
|
442
|
+
await self.mpl_canvas.apply_full(bytes(content))
|
|
443
|
+
else:
|
|
444
|
+
await self.mpl_canvas.apply_diff(bytes(content))
|
|
439
445
|
self.img_count += 1
|
|
440
446
|
self._waiting = False
|
|
441
447
|
else:
|
|
@@ -449,8 +455,6 @@ class MatplotlibChart(ft.GestureDetector):
|
|
|
449
455
|
self._waiting = True
|
|
450
456
|
self.send_message({"type": "draw"})
|
|
451
457
|
elif content["type"] == "rubberband":
|
|
452
|
-
if len(self.canvas.shapes) == 2:
|
|
453
|
-
self.canvas.shapes.pop()
|
|
454
458
|
if (
|
|
455
459
|
content["x0"] != -1
|
|
456
460
|
and content["y0"] != -1
|
|
@@ -461,18 +465,14 @@ class MatplotlibChart(ft.GestureDetector):
|
|
|
461
465
|
y0 = self._height - content["y0"] / self.__dpr
|
|
462
466
|
x1 = content["x1"] / self.__dpr
|
|
463
467
|
y1 = self._height - content["y1"] / self.__dpr
|
|
464
|
-
self.
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
),
|
|
473
|
-
)
|
|
474
|
-
)
|
|
475
|
-
self.canvas.update()
|
|
468
|
+
self._rubberband.left = min(x0, x1)
|
|
469
|
+
self._rubberband.top = min(y0, y1)
|
|
470
|
+
self._rubberband.width = abs(x1 - x0)
|
|
471
|
+
self._rubberband.height = abs(y1 - y0)
|
|
472
|
+
self._rubberband.visible = True
|
|
473
|
+
else:
|
|
474
|
+
self._rubberband.visible = False
|
|
475
|
+
self._rubberband.update()
|
|
476
476
|
elif content["type"] == "resize":
|
|
477
477
|
self.send_message({"type": "refresh"})
|
|
478
478
|
elif content["type"] == "message":
|
|
@@ -508,7 +508,7 @@ class MatplotlibChart(ft.GestureDetector):
|
|
|
508
508
|
lambda: self._receive_queue.put_nowait((True, blob))
|
|
509
509
|
)
|
|
510
510
|
|
|
511
|
-
async def _on_canvas_resize(self, e:
|
|
511
|
+
async def _on_canvas_resize(self, e: MatplotlibChartCanvasResizeEvent):
|
|
512
512
|
"""
|
|
513
513
|
Handle canvas resize and initialize backend session on first resize.
|
|
514
514
|
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
from dataclasses import dataclass, field
|
|
2
|
+
from typing import Optional
|
|
3
|
+
|
|
4
|
+
import flet as ft
|
|
5
|
+
|
|
6
|
+
__all__ = ["MatplotlibChartCanvas", "MatplotlibChartCanvasResizeEvent"]
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@dataclass
|
|
10
|
+
class MatplotlibChartCanvasResizeEvent(ft.Event["MatplotlibChartCanvas"]):
|
|
11
|
+
"""
|
|
12
|
+
Event emitted when the canvas reports a new rendered size.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
width: float = field(metadata={"data_field": "w"})
|
|
16
|
+
"""New width of the canvas in logical pixels."""
|
|
17
|
+
|
|
18
|
+
height: float = field(metadata={"data_field": "h"})
|
|
19
|
+
"""New height of the canvas in logical pixels."""
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@ft.control("MatplotlibChartCanvas")
|
|
23
|
+
class MatplotlibChartCanvas(ft.LayoutControl):
|
|
24
|
+
"""
|
|
25
|
+
Display widget for matplotlib WebAgg-style image streams.
|
|
26
|
+
|
|
27
|
+
Receives full and incremental "diff" PNG frames and composites them in
|
|
28
|
+
CPU memory, holding at most one decoded image for display at a time.
|
|
29
|
+
Avoids the per-frame `Picture.toImage` allocations that the generic
|
|
30
|
+
`flet.canvas.Canvas` capture path uses, which on Flutter web
|
|
31
|
+
(CanvasKit/WASM) accumulate and aren't promptly reclaimed by the JS GC
|
|
32
|
+
during animation, causing browser memory growth.
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
resize_interval: ft.Number = 10
|
|
36
|
+
"""
|
|
37
|
+
Sampling interval in milliseconds for `on_resize` event.
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
on_resize: Optional[ft.EventHandler[MatplotlibChartCanvasResizeEvent]] = None
|
|
41
|
+
"""
|
|
42
|
+
Called when the size of this canvas has changed.
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
async def apply_full(self, image_bytes: bytes) -> None:
|
|
46
|
+
"""
|
|
47
|
+
Replace the current displayed image with a full PNG frame.
|
|
48
|
+
|
|
49
|
+
Args:
|
|
50
|
+
image_bytes: PNG bytes of the complete frame.
|
|
51
|
+
"""
|
|
52
|
+
await self._invoke_method("apply_full", arguments={"bytes": image_bytes})
|
|
53
|
+
|
|
54
|
+
async def apply_diff(self, image_bytes: bytes) -> None:
|
|
55
|
+
"""
|
|
56
|
+
Composite an incremental "diff" PNG frame onto the current image.
|
|
57
|
+
|
|
58
|
+
Pixels with non-zero alpha replace the corresponding pixels in the
|
|
59
|
+
existing backbuffer; transparent pixels leave the backbuffer
|
|
60
|
+
unchanged. If no backbuffer exists yet, the diff is treated as a
|
|
61
|
+
full frame.
|
|
62
|
+
|
|
63
|
+
Args:
|
|
64
|
+
image_bytes: PNG bytes of the diff frame.
|
|
65
|
+
"""
|
|
66
|
+
await self._invoke_method("apply_diff", arguments={"bytes": image_bytes})
|
|
67
|
+
|
|
68
|
+
async def clear(self) -> None:
|
|
69
|
+
"""
|
|
70
|
+
Clear the displayed image and discard the backbuffer.
|
|
71
|
+
"""
|
|
72
|
+
await self._invoke_method("clear")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: flet-charts
|
|
3
|
-
Version: 0.85.0.
|
|
3
|
+
Version: 0.85.0.dev4
|
|
4
4
|
Summary: Interactive chart controls for Flet apps.
|
|
5
5
|
Author-email: Flet contributors <hello@flet.dev>
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -11,7 +11,7 @@ Project-URL: Issues, https://github.com/flet-dev/flet/issues
|
|
|
11
11
|
Requires-Python: >=3.10
|
|
12
12
|
Description-Content-Type: text/markdown
|
|
13
13
|
License-File: LICENSE
|
|
14
|
-
Requires-Dist: flet==0.85.0.
|
|
14
|
+
Requires-Dist: flet==0.85.0.dev4
|
|
15
15
|
Dynamic: license-file
|
|
16
16
|
|
|
17
17
|
# flet-charts
|
|
@@ -13,6 +13,7 @@ src/flet_charts/line_chart.py
|
|
|
13
13
|
src/flet_charts/line_chart_data.py
|
|
14
14
|
src/flet_charts/line_chart_data_point.py
|
|
15
15
|
src/flet_charts/matplotlib_chart.py
|
|
16
|
+
src/flet_charts/matplotlib_chart_canvas.py
|
|
16
17
|
src/flet_charts/matplotlib_chart_with_toolbar.py
|
|
17
18
|
src/flet_charts/pie_chart.py
|
|
18
19
|
src/flet_charts/pie_chart_section.py
|
|
@@ -34,6 +35,7 @@ src/flutter/flet_charts/lib/src/bar_chart.dart
|
|
|
34
35
|
src/flutter/flet_charts/lib/src/candlestick_chart.dart
|
|
35
36
|
src/flutter/flet_charts/lib/src/extension.dart
|
|
36
37
|
src/flutter/flet_charts/lib/src/line_chart.dart
|
|
38
|
+
src/flutter/flet_charts/lib/src/matplotlib_chart_canvas.dart
|
|
37
39
|
src/flutter/flet_charts/lib/src/pie_chart.dart
|
|
38
40
|
src/flutter/flet_charts/lib/src/radar_chart.dart
|
|
39
41
|
src/flutter/flet_charts/lib/src/scatter_chart.dart
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
flet==0.85.0.dev4
|
{flet_charts-0.85.0.dev2 → flet_charts-0.85.0.dev4}/src/flutter/flet_charts/lib/src/extension.dart
RENAMED
|
@@ -4,6 +4,7 @@ import 'package:flutter/material.dart';
|
|
|
4
4
|
import 'bar_chart.dart';
|
|
5
5
|
import 'candlestick_chart.dart';
|
|
6
6
|
import 'line_chart.dart';
|
|
7
|
+
import 'matplotlib_chart_canvas.dart';
|
|
7
8
|
import 'radar_chart.dart';
|
|
8
9
|
import 'pie_chart.dart';
|
|
9
10
|
import 'scatter_chart.dart';
|
|
@@ -18,6 +19,8 @@ class Extension extends FletExtension {
|
|
|
18
19
|
return CandlestickChartControl(key: key, control: control);
|
|
19
20
|
case "LineChart":
|
|
20
21
|
return LineChartControl(key: key, control: control);
|
|
22
|
+
case "MatplotlibChartCanvas":
|
|
23
|
+
return MatplotlibChartCanvasControl(key: key, control: control);
|
|
21
24
|
case "RadarChart":
|
|
22
25
|
return RadarChartControl(key: key, control: control);
|
|
23
26
|
case "PieChart":
|
|
@@ -0,0 +1,457 @@
|
|
|
1
|
+
import 'dart:async';
|
|
2
|
+
import 'dart:typed_data';
|
|
3
|
+
import 'dart:ui' as ui;
|
|
4
|
+
|
|
5
|
+
import 'package:flet/flet.dart';
|
|
6
|
+
import 'package:flutter/foundation.dart' show kIsWeb;
|
|
7
|
+
import 'package:flutter/material.dart';
|
|
8
|
+
|
|
9
|
+
/// Display widget for matplotlib WebAgg-style image streams.
|
|
10
|
+
///
|
|
11
|
+
/// Two rendering strategies, picked at runtime by platform:
|
|
12
|
+
///
|
|
13
|
+
/// - **GPU + flatten** (native): keeps an `_backdrop` plus a list of pending
|
|
14
|
+
/// diff `ui.Image`s, paints all of them per frame, and bakes them into a
|
|
15
|
+
/// fresh backdrop via `Picture.toImage` every [_GpuMatplotlibChartCanvasState._flattenInterval]
|
|
16
|
+
/// diffs. Fast (no GPU↔CPU readback) and memory-stable on native runtimes
|
|
17
|
+
/// where Dart GC is aggressive enough to reclaim layer-held SkImage refs.
|
|
18
|
+
///
|
|
19
|
+
/// - **CPU composite** (web): decodes each PNG to RGBA bytes, composites
|
|
20
|
+
/// onto a single backbuffer in Dart, and uploads ONE fresh `ui.Image` per
|
|
21
|
+
/// frame. Slower per frame, but holds at most one `ui.Image` at a time so
|
|
22
|
+
/// layer-ref accumulation stays bounded under Flutter web (CanvasKit/WASM)
|
|
23
|
+
/// where Dart GC doesn't promptly reclaim native SkImage refs.
|
|
24
|
+
class MatplotlibChartCanvasControl extends StatefulWidget {
|
|
25
|
+
final Control control;
|
|
26
|
+
|
|
27
|
+
MatplotlibChartCanvasControl({Key? key, required this.control})
|
|
28
|
+
: super(key: key ?? ValueKey("control_${control.id}"));
|
|
29
|
+
|
|
30
|
+
@override
|
|
31
|
+
// ignore: no_logic_in_create_state
|
|
32
|
+
State<MatplotlibChartCanvasControl> createState() => kIsWeb
|
|
33
|
+
? _CpuMatplotlibChartCanvasState()
|
|
34
|
+
: _GpuMatplotlibChartCanvasState();
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// ---------------------------------------------------------------------------
|
|
38
|
+
// Shared helpers
|
|
39
|
+
// ---------------------------------------------------------------------------
|
|
40
|
+
|
|
41
|
+
Uint8List _extractBytes(dynamic args) {
|
|
42
|
+
final v = args is Map ? args["bytes"] : args;
|
|
43
|
+
if (v is Uint8List) return v;
|
|
44
|
+
if (v is ByteData) {
|
|
45
|
+
return v.buffer.asUint8List(v.offsetInBytes, v.lengthInBytes);
|
|
46
|
+
}
|
|
47
|
+
if (v is List<int>) return Uint8List.fromList(v);
|
|
48
|
+
if (v is List && v.every((e) => e is int)) {
|
|
49
|
+
return Uint8List.fromList(v.cast<int>());
|
|
50
|
+
}
|
|
51
|
+
throw ArgumentError("Expected bytes for image data, got ${v.runtimeType}");
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
abstract class _MatplotlibChartCanvasStateBase
|
|
55
|
+
extends State<MatplotlibChartCanvasControl> {
|
|
56
|
+
// Serialize concurrent apply_full / apply_diff calls so backdrop mutations
|
|
57
|
+
// happen in arrival order.
|
|
58
|
+
Future<void>? _applyChain;
|
|
59
|
+
|
|
60
|
+
Size _lastSize = Size.zero;
|
|
61
|
+
int _lastResize = DateTime.now().millisecondsSinceEpoch;
|
|
62
|
+
|
|
63
|
+
@override
|
|
64
|
+
void initState() {
|
|
65
|
+
super.initState();
|
|
66
|
+
widget.control.addInvokeMethodListener(_invokeMethod);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
@override
|
|
70
|
+
void dispose() {
|
|
71
|
+
widget.control.removeInvokeMethodListener(_invokeMethod);
|
|
72
|
+
disposeResources();
|
|
73
|
+
super.dispose();
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/// Subclass hook — release any held `ui.Image`s / backbuffers.
|
|
77
|
+
void disposeResources();
|
|
78
|
+
|
|
79
|
+
Future<void> applyFull(Uint8List bytes);
|
|
80
|
+
Future<void> applyDiff(Uint8List bytes);
|
|
81
|
+
Future<void> clearAll();
|
|
82
|
+
CustomPainter buildPainter();
|
|
83
|
+
|
|
84
|
+
Future<dynamic> _invokeMethod(String name, dynamic args) async {
|
|
85
|
+
switch (name) {
|
|
86
|
+
case "apply_full":
|
|
87
|
+
await _enqueue(() => applyFull(_extractBytes(args)));
|
|
88
|
+
return;
|
|
89
|
+
case "apply_diff":
|
|
90
|
+
await _enqueue(() => applyDiff(_extractBytes(args)));
|
|
91
|
+
return;
|
|
92
|
+
case "clear":
|
|
93
|
+
await _enqueue(clearAll);
|
|
94
|
+
return;
|
|
95
|
+
default:
|
|
96
|
+
throw Exception("Unknown MatplotlibChartCanvas method: $name");
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
Future<void> _enqueue(Future<void> Function() task) {
|
|
101
|
+
final prev = _applyChain ?? Future.value();
|
|
102
|
+
final next = prev.then((_) => task());
|
|
103
|
+
_applyChain = next.catchError((_) {});
|
|
104
|
+
return next;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
void _maybeReportResize(Size size) {
|
|
108
|
+
final resizeInterval = widget.control.getInt("resize_interval", 10)!;
|
|
109
|
+
final now = DateTime.now().millisecondsSinceEpoch;
|
|
110
|
+
if ((now - _lastResize > resizeInterval && _lastSize != size) ||
|
|
111
|
+
_lastSize.isEmpty) {
|
|
112
|
+
_lastSize = size;
|
|
113
|
+
_lastResize = now;
|
|
114
|
+
widget.control
|
|
115
|
+
.triggerEvent("resize", {"w": size.width, "h": size.height});
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
@override
|
|
120
|
+
Widget build(BuildContext context) {
|
|
121
|
+
return LayoutBuilder(
|
|
122
|
+
builder: (context, constraints) {
|
|
123
|
+
WidgetsBinding.instance.addPostFrameCallback((_) {
|
|
124
|
+
if (!mounted) return;
|
|
125
|
+
_maybeReportResize(constraints.biggest);
|
|
126
|
+
});
|
|
127
|
+
return CustomPaint(
|
|
128
|
+
size: constraints.biggest,
|
|
129
|
+
painter: buildPainter(),
|
|
130
|
+
);
|
|
131
|
+
},
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/// Decodes PNG bytes to a [ui.Image], staying GPU-resident.
|
|
137
|
+
Future<ui.Image?> _decodeImage(Uint8List bytes) async {
|
|
138
|
+
if (bytes.isEmpty) {
|
|
139
|
+
debugPrint("MatplotlibChartCanvas: skipping empty image bytes");
|
|
140
|
+
return null;
|
|
141
|
+
}
|
|
142
|
+
// Defensive copy; Safari's WASM runtime can free underlying buffers across
|
|
143
|
+
// async boundaries and trigger "EncodingError: Loading error.".
|
|
144
|
+
final owned = Uint8List.fromList(bytes);
|
|
145
|
+
ui.Codec? codec;
|
|
146
|
+
try {
|
|
147
|
+
codec = await ui.instantiateImageCodec(owned, allowUpscaling: false);
|
|
148
|
+
final frame = await codec.getNextFrame();
|
|
149
|
+
return frame.image;
|
|
150
|
+
} catch (e) {
|
|
151
|
+
debugPrint(
|
|
152
|
+
"MatplotlibChartCanvas: decode failed (${owned.length} bytes): $e");
|
|
153
|
+
rethrow;
|
|
154
|
+
} finally {
|
|
155
|
+
codec?.dispose();
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// ---------------------------------------------------------------------------
|
|
160
|
+
// GPU + flatten strategy (native runtimes)
|
|
161
|
+
// ---------------------------------------------------------------------------
|
|
162
|
+
|
|
163
|
+
class _GpuMatplotlibChartCanvasState
|
|
164
|
+
extends _MatplotlibChartCanvasStateBase {
|
|
165
|
+
// Number of diffs to accumulate before flattening into a fresh backdrop.
|
|
166
|
+
// Larger = fewer Picture.toImage calls; smaller = lower transient memory.
|
|
167
|
+
static const int _flattenInterval = 10;
|
|
168
|
+
|
|
169
|
+
ui.Image? _backdrop;
|
|
170
|
+
final List<ui.Image> _diffs = [];
|
|
171
|
+
|
|
172
|
+
@override
|
|
173
|
+
void disposeResources() {
|
|
174
|
+
_backdrop?.dispose();
|
|
175
|
+
_backdrop = null;
|
|
176
|
+
for (final img in _diffs) {
|
|
177
|
+
img.dispose();
|
|
178
|
+
}
|
|
179
|
+
_diffs.clear();
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
@override
|
|
183
|
+
Future<void> applyFull(Uint8List bytes) async {
|
|
184
|
+
final image = await _decodeImage(bytes);
|
|
185
|
+
if (image == null) return;
|
|
186
|
+
_replaceBackdrop(image);
|
|
187
|
+
_disposeDiffs();
|
|
188
|
+
if (mounted) setState(() {});
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
@override
|
|
192
|
+
Future<void> applyDiff(Uint8List bytes) async {
|
|
193
|
+
if (_backdrop == null) {
|
|
194
|
+
// No baseline yet — first frame must be a full.
|
|
195
|
+
await applyFull(bytes);
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
final image = await _decodeImage(bytes);
|
|
199
|
+
if (image == null) return;
|
|
200
|
+
_diffs.add(image);
|
|
201
|
+
if (_diffs.length >= _flattenInterval) {
|
|
202
|
+
await _flatten();
|
|
203
|
+
}
|
|
204
|
+
if (mounted) setState(() {});
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
@override
|
|
208
|
+
Future<void> clearAll() async {
|
|
209
|
+
_replaceBackdrop(null);
|
|
210
|
+
_disposeDiffs();
|
|
211
|
+
if (mounted) setState(() {});
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/// Bakes [_backdrop] + pending [_diffs] into a single new backdrop via
|
|
215
|
+
/// `Picture.toImage`, replaces [_backdrop], and drops the diffs.
|
|
216
|
+
Future<void> _flatten() async {
|
|
217
|
+
final backdrop = _backdrop;
|
|
218
|
+
if (backdrop == null || _diffs.isEmpty) return;
|
|
219
|
+
|
|
220
|
+
final w = backdrop.width;
|
|
221
|
+
final h = backdrop.height;
|
|
222
|
+
|
|
223
|
+
final recorder = ui.PictureRecorder();
|
|
224
|
+
final canvas = Canvas(recorder);
|
|
225
|
+
final paint = Paint();
|
|
226
|
+
canvas.drawImage(backdrop, Offset.zero, paint);
|
|
227
|
+
for (final diff in _diffs) {
|
|
228
|
+
canvas.drawImage(diff, Offset.zero, paint);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
final picture = recorder.endRecording();
|
|
232
|
+
final ui.Image newBackdrop;
|
|
233
|
+
try {
|
|
234
|
+
newBackdrop = await picture.toImage(w, h);
|
|
235
|
+
} finally {
|
|
236
|
+
picture.dispose();
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
_replaceBackdrop(newBackdrop);
|
|
240
|
+
_disposeDiffs();
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
void _replaceBackdrop(ui.Image? image) {
|
|
244
|
+
final old = _backdrop;
|
|
245
|
+
_backdrop = image;
|
|
246
|
+
if (old != null) {
|
|
247
|
+
WidgetsBinding.instance.addPostFrameCallback((_) {
|
|
248
|
+
old.dispose();
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
void _disposeDiffs() {
|
|
254
|
+
if (_diffs.isEmpty) return;
|
|
255
|
+
final old = List<ui.Image>.of(_diffs);
|
|
256
|
+
_diffs.clear();
|
|
257
|
+
WidgetsBinding.instance.addPostFrameCallback((_) {
|
|
258
|
+
for (final img in old) {
|
|
259
|
+
img.dispose();
|
|
260
|
+
}
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
@override
|
|
265
|
+
CustomPainter buildPainter() => _GpuMatplotlibImagePainter(
|
|
266
|
+
backdrop: _backdrop,
|
|
267
|
+
diffs: List<ui.Image>.unmodifiable(_diffs),
|
|
268
|
+
);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
class _GpuMatplotlibImagePainter extends CustomPainter {
|
|
272
|
+
final ui.Image? backdrop;
|
|
273
|
+
final List<ui.Image> diffs;
|
|
274
|
+
|
|
275
|
+
const _GpuMatplotlibImagePainter({
|
|
276
|
+
required this.backdrop,
|
|
277
|
+
required this.diffs,
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
@override
|
|
281
|
+
void paint(Canvas canvas, Size size) {
|
|
282
|
+
final bg = backdrop;
|
|
283
|
+
if (bg == null) return;
|
|
284
|
+
final dst = Rect.fromLTWH(0, 0, size.width, size.height);
|
|
285
|
+
final paint = Paint();
|
|
286
|
+
final bgSrc =
|
|
287
|
+
Rect.fromLTWH(0, 0, bg.width.toDouble(), bg.height.toDouble());
|
|
288
|
+
canvas.drawImageRect(bg, bgSrc, dst, paint);
|
|
289
|
+
for (final diff in diffs) {
|
|
290
|
+
final src =
|
|
291
|
+
Rect.fromLTWH(0, 0, diff.width.toDouble(), diff.height.toDouble());
|
|
292
|
+
canvas.drawImageRect(diff, src, dst, paint);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
@override
|
|
297
|
+
bool shouldRepaint(_GpuMatplotlibImagePainter old) {
|
|
298
|
+
return backdrop != old.backdrop || diffs.length != old.diffs.length;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// ---------------------------------------------------------------------------
|
|
303
|
+
// CPU composite strategy (web — CanvasKit/WASM)
|
|
304
|
+
// ---------------------------------------------------------------------------
|
|
305
|
+
|
|
306
|
+
class _CpuMatplotlibChartCanvasState
|
|
307
|
+
extends _MatplotlibChartCanvasStateBase {
|
|
308
|
+
ui.Image? _displayImage;
|
|
309
|
+
Uint8List? _backbuffer;
|
|
310
|
+
int _bbWidth = 0;
|
|
311
|
+
int _bbHeight = 0;
|
|
312
|
+
|
|
313
|
+
@override
|
|
314
|
+
void disposeResources() {
|
|
315
|
+
_displayImage?.dispose();
|
|
316
|
+
_displayImage = null;
|
|
317
|
+
_backbuffer = null;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
@override
|
|
321
|
+
Future<void> applyFull(Uint8List bytes) async {
|
|
322
|
+
final decoded = await _decodeRgba(bytes);
|
|
323
|
+
if (decoded == null) return;
|
|
324
|
+
|
|
325
|
+
_backbuffer = decoded.bytes;
|
|
326
|
+
_bbWidth = decoded.width;
|
|
327
|
+
_bbHeight = decoded.height;
|
|
328
|
+
|
|
329
|
+
final image = await _makeImage(decoded.bytes, decoded.width, decoded.height);
|
|
330
|
+
_swapDisplay(image);
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
@override
|
|
334
|
+
Future<void> applyDiff(Uint8List bytes) async {
|
|
335
|
+
if (_backbuffer == null) {
|
|
336
|
+
// No baseline yet — treat as full.
|
|
337
|
+
await applyFull(bytes);
|
|
338
|
+
return;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
final decoded = await _decodeRgba(bytes);
|
|
342
|
+
if (decoded == null) return;
|
|
343
|
+
|
|
344
|
+
// Promote to a full replace if the frame size changed.
|
|
345
|
+
if (decoded.width != _bbWidth ||
|
|
346
|
+
decoded.height != _bbHeight ||
|
|
347
|
+
decoded.bytes.length != _backbuffer!.length) {
|
|
348
|
+
_backbuffer = decoded.bytes;
|
|
349
|
+
_bbWidth = decoded.width;
|
|
350
|
+
_bbHeight = decoded.height;
|
|
351
|
+
final image =
|
|
352
|
+
await _makeImage(decoded.bytes, decoded.width, decoded.height);
|
|
353
|
+
_swapDisplay(image);
|
|
354
|
+
return;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
// Composite: matplotlib's diff PNG has alpha=0 for unchanged pixels.
|
|
358
|
+
// Where alpha != 0, copy the new pixel into the backbuffer.
|
|
359
|
+
final bb = _backbuffer!.buffer.asUint32List();
|
|
360
|
+
final df = decoded.bytes.buffer.asUint32List();
|
|
361
|
+
for (int i = 0; i < df.length; i++) {
|
|
362
|
+
// RGBA8888 on little-endian: alpha is the highest byte (0xFF000000).
|
|
363
|
+
if ((df[i] & 0xFF000000) != 0) {
|
|
364
|
+
bb[i] = df[i];
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
final image = await _makeImage(_backbuffer!, _bbWidth, _bbHeight);
|
|
369
|
+
_swapDisplay(image);
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
@override
|
|
373
|
+
Future<void> clearAll() async {
|
|
374
|
+
final old = _displayImage;
|
|
375
|
+
_displayImage = null;
|
|
376
|
+
_backbuffer = null;
|
|
377
|
+
_bbWidth = 0;
|
|
378
|
+
_bbHeight = 0;
|
|
379
|
+
if (old != null) {
|
|
380
|
+
WidgetsBinding.instance.addPostFrameCallback((_) {
|
|
381
|
+
old.dispose();
|
|
382
|
+
});
|
|
383
|
+
}
|
|
384
|
+
if (mounted) setState(() {});
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
Future<_DecodedRgba?> _decodeRgba(Uint8List bytes) async {
|
|
388
|
+
final img = await _decodeImage(bytes);
|
|
389
|
+
if (img == null) return null;
|
|
390
|
+
try {
|
|
391
|
+
final byteData =
|
|
392
|
+
await img.toByteData(format: ui.ImageByteFormat.rawRgba);
|
|
393
|
+
if (byteData == null) return null;
|
|
394
|
+
return _DecodedRgba(
|
|
395
|
+
bytes: byteData.buffer.asUint8List(),
|
|
396
|
+
width: img.width,
|
|
397
|
+
height: img.height,
|
|
398
|
+
);
|
|
399
|
+
} finally {
|
|
400
|
+
img.dispose();
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
Future<ui.Image> _makeImage(Uint8List rgba, int width, int height) {
|
|
405
|
+
final completer = Completer<ui.Image>();
|
|
406
|
+
ui.decodeImageFromPixels(
|
|
407
|
+
rgba,
|
|
408
|
+
width,
|
|
409
|
+
height,
|
|
410
|
+
ui.PixelFormat.rgba8888,
|
|
411
|
+
completer.complete,
|
|
412
|
+
);
|
|
413
|
+
return completer.future;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
void _swapDisplay(ui.Image newImage) {
|
|
417
|
+
final old = _displayImage;
|
|
418
|
+
_displayImage = newImage;
|
|
419
|
+
if (mounted) setState(() {});
|
|
420
|
+
if (old != null) {
|
|
421
|
+
WidgetsBinding.instance.addPostFrameCallback((_) {
|
|
422
|
+
old.dispose();
|
|
423
|
+
});
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
@override
|
|
428
|
+
CustomPainter buildPainter() =>
|
|
429
|
+
_CpuMatplotlibImagePainter(image: _displayImage);
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
class _DecodedRgba {
|
|
433
|
+
final Uint8List bytes;
|
|
434
|
+
final int width;
|
|
435
|
+
final int height;
|
|
436
|
+
_DecodedRgba(
|
|
437
|
+
{required this.bytes, required this.width, required this.height});
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
class _CpuMatplotlibImagePainter extends CustomPainter {
|
|
441
|
+
final ui.Image? image;
|
|
442
|
+
|
|
443
|
+
const _CpuMatplotlibImagePainter({required this.image});
|
|
444
|
+
|
|
445
|
+
@override
|
|
446
|
+
void paint(Canvas canvas, Size size) {
|
|
447
|
+
final img = image;
|
|
448
|
+
if (img == null) return;
|
|
449
|
+
final src =
|
|
450
|
+
Rect.fromLTWH(0, 0, img.width.toDouble(), img.height.toDouble());
|
|
451
|
+
final dst = Rect.fromLTWH(0, 0, size.width, size.height);
|
|
452
|
+
canvas.drawImageRect(img, src, dst, Paint());
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
@override
|
|
456
|
+
bool shouldRepaint(_CpuMatplotlibImagePainter old) => old.image != image;
|
|
457
|
+
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
flet==0.85.0.dev2
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{flet_charts-0.85.0.dev2 → flet_charts-0.85.0.dev4}/src/flet_charts/bar_chart_rod_stack_item.py
RENAMED
|
File without changes
|
|
File without changes
|
{flet_charts-0.85.0.dev2 → flet_charts-0.85.0.dev4}/src/flet_charts/candlestick_chart_spot.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{flet_charts-0.85.0.dev2 → flet_charts-0.85.0.dev4}/src/flet_charts/line_chart_data_point.py
RENAMED
|
File without changes
|
|
File without changes
|
{flet_charts-0.85.0.dev2 → flet_charts-0.85.0.dev4}/src/flet_charts/matplotlib_chart_with_toolbar.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{flet_charts-0.85.0.dev2 → flet_charts-0.85.0.dev4}/src/flet_charts.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
{flet_charts-0.85.0.dev2 → flet_charts-0.85.0.dev4}/src/flutter/flet_charts/lib/flet_charts.dart
RENAMED
|
File without changes
|
{flet_charts-0.85.0.dev2 → flet_charts-0.85.0.dev4}/src/flutter/flet_charts/lib/src/bar_chart.dart
RENAMED
|
File without changes
|
|
File without changes
|
{flet_charts-0.85.0.dev2 → flet_charts-0.85.0.dev4}/src/flutter/flet_charts/lib/src/line_chart.dart
RENAMED
|
File without changes
|
{flet_charts-0.85.0.dev2 → flet_charts-0.85.0.dev4}/src/flutter/flet_charts/lib/src/pie_chart.dart
RENAMED
|
File without changes
|
{flet_charts-0.85.0.dev2 → flet_charts-0.85.0.dev4}/src/flutter/flet_charts/lib/src/radar_chart.dart
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|