flet-charts 0.85.3.dev0__tar.gz → 0.86.0.dev0__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.3.dev0/src/flet_charts.egg-info → flet_charts-0.86.0.dev0}/PKG-INFO +2 -2
- {flet_charts-0.85.3.dev0 → flet_charts-0.86.0.dev0}/pyproject.toml +2 -2
- {flet_charts-0.85.3.dev0 → flet_charts-0.86.0.dev0}/src/flet_charts/matplotlib_chart.py +9 -4
- flet_charts-0.86.0.dev0/src/flet_charts/matplotlib_chart_canvas.py +166 -0
- {flet_charts-0.85.3.dev0 → flet_charts-0.86.0.dev0/src/flet_charts.egg-info}/PKG-INFO +2 -2
- flet_charts-0.86.0.dev0/src/flet_charts.egg-info/requires.txt +1 -0
- {flet_charts-0.85.3.dev0 → flet_charts-0.86.0.dev0}/src/flutter/flet_charts/lib/src/matplotlib_chart_canvas.dart +52 -30
- flet_charts-0.85.3.dev0/src/flet_charts/matplotlib_chart_canvas.py +0 -72
- flet_charts-0.85.3.dev0/src/flet_charts.egg-info/requires.txt +0 -1
- {flet_charts-0.85.3.dev0 → flet_charts-0.86.0.dev0}/LICENSE +0 -0
- {flet_charts-0.85.3.dev0 → flet_charts-0.86.0.dev0}/README.md +0 -0
- {flet_charts-0.85.3.dev0 → flet_charts-0.86.0.dev0}/setup.cfg +0 -0
- {flet_charts-0.85.3.dev0 → flet_charts-0.86.0.dev0}/src/flet_charts/__init__.py +0 -0
- {flet_charts-0.85.3.dev0 → flet_charts-0.86.0.dev0}/src/flet_charts/bar_chart.py +0 -0
- {flet_charts-0.85.3.dev0 → flet_charts-0.86.0.dev0}/src/flet_charts/bar_chart_group.py +0 -0
- {flet_charts-0.85.3.dev0 → flet_charts-0.86.0.dev0}/src/flet_charts/bar_chart_rod.py +0 -0
- {flet_charts-0.85.3.dev0 → flet_charts-0.86.0.dev0}/src/flet_charts/bar_chart_rod_stack_item.py +0 -0
- {flet_charts-0.85.3.dev0 → flet_charts-0.86.0.dev0}/src/flet_charts/candlestick_chart.py +0 -0
- {flet_charts-0.85.3.dev0 → flet_charts-0.86.0.dev0}/src/flet_charts/candlestick_chart_spot.py +0 -0
- {flet_charts-0.85.3.dev0 → flet_charts-0.86.0.dev0}/src/flet_charts/chart_axis.py +0 -0
- {flet_charts-0.85.3.dev0 → flet_charts-0.86.0.dev0}/src/flet_charts/line_chart.py +0 -0
- {flet_charts-0.85.3.dev0 → flet_charts-0.86.0.dev0}/src/flet_charts/line_chart_data.py +0 -0
- {flet_charts-0.85.3.dev0 → flet_charts-0.86.0.dev0}/src/flet_charts/line_chart_data_point.py +0 -0
- {flet_charts-0.85.3.dev0 → flet_charts-0.86.0.dev0}/src/flet_charts/matplotlib_backends/backend_flet_agg.py +0 -0
- {flet_charts-0.85.3.dev0 → flet_charts-0.86.0.dev0}/src/flet_charts/matplotlib_chart_with_toolbar.py +0 -0
- {flet_charts-0.85.3.dev0 → flet_charts-0.86.0.dev0}/src/flet_charts/pie_chart.py +0 -0
- {flet_charts-0.85.3.dev0 → flet_charts-0.86.0.dev0}/src/flet_charts/pie_chart_section.py +0 -0
- {flet_charts-0.85.3.dev0 → flet_charts-0.86.0.dev0}/src/flet_charts/plotly_chart.py +0 -0
- {flet_charts-0.85.3.dev0 → flet_charts-0.86.0.dev0}/src/flet_charts/radar_chart.py +0 -0
- {flet_charts-0.85.3.dev0 → flet_charts-0.86.0.dev0}/src/flet_charts/radar_data_set.py +0 -0
- {flet_charts-0.85.3.dev0 → flet_charts-0.86.0.dev0}/src/flet_charts/scatter_chart.py +0 -0
- {flet_charts-0.85.3.dev0 → flet_charts-0.86.0.dev0}/src/flet_charts/scatter_chart_spot.py +0 -0
- {flet_charts-0.85.3.dev0 → flet_charts-0.86.0.dev0}/src/flet_charts/types.py +0 -0
- {flet_charts-0.85.3.dev0 → flet_charts-0.86.0.dev0}/src/flet_charts.egg-info/SOURCES.txt +0 -0
- {flet_charts-0.85.3.dev0 → flet_charts-0.86.0.dev0}/src/flet_charts.egg-info/dependency_links.txt +0 -0
- {flet_charts-0.85.3.dev0 → flet_charts-0.86.0.dev0}/src/flet_charts.egg-info/top_level.txt +0 -0
- {flet_charts-0.85.3.dev0 → flet_charts-0.86.0.dev0}/src/flutter/flet_charts/lib/flet_charts.dart +0 -0
- {flet_charts-0.85.3.dev0 → flet_charts-0.86.0.dev0}/src/flutter/flet_charts/lib/src/bar_chart.dart +0 -0
- {flet_charts-0.85.3.dev0 → flet_charts-0.86.0.dev0}/src/flutter/flet_charts/lib/src/candlestick_chart.dart +0 -0
- {flet_charts-0.85.3.dev0 → flet_charts-0.86.0.dev0}/src/flutter/flet_charts/lib/src/extension.dart +0 -0
- {flet_charts-0.85.3.dev0 → flet_charts-0.86.0.dev0}/src/flutter/flet_charts/lib/src/line_chart.dart +0 -0
- {flet_charts-0.85.3.dev0 → flet_charts-0.86.0.dev0}/src/flutter/flet_charts/lib/src/pie_chart.dart +0 -0
- {flet_charts-0.85.3.dev0 → flet_charts-0.86.0.dev0}/src/flutter/flet_charts/lib/src/radar_chart.dart +0 -0
- {flet_charts-0.85.3.dev0 → flet_charts-0.86.0.dev0}/src/flutter/flet_charts/lib/src/scatter_chart.dart +0 -0
- {flet_charts-0.85.3.dev0 → flet_charts-0.86.0.dev0}/src/flutter/flet_charts/lib/src/utils/bar_chart.dart +0 -0
- {flet_charts-0.85.3.dev0 → flet_charts-0.86.0.dev0}/src/flutter/flet_charts/lib/src/utils/candlestick_chart.dart +0 -0
- {flet_charts-0.85.3.dev0 → flet_charts-0.86.0.dev0}/src/flutter/flet_charts/lib/src/utils/charts.dart +0 -0
- {flet_charts-0.85.3.dev0 → flet_charts-0.86.0.dev0}/src/flutter/flet_charts/lib/src/utils/line_chart.dart +0 -0
- {flet_charts-0.85.3.dev0 → flet_charts-0.86.0.dev0}/src/flutter/flet_charts/lib/src/utils/pie_chart.dart +0 -0
- {flet_charts-0.85.3.dev0 → flet_charts-0.86.0.dev0}/src/flutter/flet_charts/lib/src/utils/radar_chart.dart +0 -0
- {flet_charts-0.85.3.dev0 → flet_charts-0.86.0.dev0}/src/flutter/flet_charts/lib/src/utils/scatter_chart.dart +0 -0
- {flet_charts-0.85.3.dev0 → flet_charts-0.86.0.dev0}/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.
|
|
3
|
+
Version: 0.86.0.dev0
|
|
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.
|
|
14
|
+
Requires-Dist: flet==0.86.0.dev0
|
|
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.
|
|
3
|
+
version = "0.86.0.dev0"
|
|
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.
|
|
10
|
+
"flet==0.86.0.dev0",
|
|
11
11
|
]
|
|
12
12
|
|
|
13
13
|
[project.urls]
|
|
@@ -434,10 +434,15 @@ class MatplotlibChart(ft.GestureDetector):
|
|
|
434
434
|
if is_binary:
|
|
435
435
|
assert isinstance(content, (bytes, bytearray))
|
|
436
436
|
logger.debug(f"receive_binary({len(content)})")
|
|
437
|
-
# Hand the frame to the client widget — full PNG replaces
|
|
438
|
-
# backbuffer, diff PNG composites onto it.
|
|
439
|
-
#
|
|
440
|
-
#
|
|
437
|
+
# Hand the frame to the client widget — full PNG replaces
|
|
438
|
+
# the backbuffer, diff PNG composites onto it. `await`
|
|
439
|
+
# here serialises this receive loop on the Dart-side
|
|
440
|
+
# frame-applied ack: matplotlib "draw" notifications that
|
|
441
|
+
# arrive during the round-trip stay queued in
|
|
442
|
+
# `_receive_queue` and are processed after the ack returns,
|
|
443
|
+
# instead of being eagerly dropped against a stale
|
|
444
|
+
# `_waiting=True` gate. This is the same backpressure shape
|
|
445
|
+
# the 0.85 `_invoke_method` round-trip used to provide.
|
|
441
446
|
if self.__image_mode == "full":
|
|
442
447
|
await self.mpl_canvas.apply_full(bytes(content))
|
|
443
448
|
else:
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import contextlib
|
|
3
|
+
from collections import deque
|
|
4
|
+
from dataclasses import dataclass, field
|
|
5
|
+
from typing import Callable, Optional
|
|
6
|
+
|
|
7
|
+
import flet as ft
|
|
8
|
+
|
|
9
|
+
__all__ = ["MatplotlibChartCanvas", "MatplotlibChartCanvasResizeEvent"]
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@dataclass
|
|
13
|
+
class MatplotlibChartCanvasResizeEvent(ft.Event["MatplotlibChartCanvas"]):
|
|
14
|
+
"""
|
|
15
|
+
Event emitted when the canvas reports a new rendered size.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
width: float = field(metadata={"data_field": "w"})
|
|
19
|
+
"""New width of the canvas in logical pixels."""
|
|
20
|
+
|
|
21
|
+
height: float = field(metadata={"data_field": "h"})
|
|
22
|
+
"""New height of the canvas in logical pixels."""
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@ft.control("MatplotlibChartCanvas")
|
|
26
|
+
class MatplotlibChartCanvas(ft.LayoutControl):
|
|
27
|
+
"""
|
|
28
|
+
Display widget for matplotlib WebAgg-style image streams.
|
|
29
|
+
|
|
30
|
+
Receives full and incremental "diff" PNG frames and composites them in
|
|
31
|
+
CPU memory, holding at most one decoded image for display at a time.
|
|
32
|
+
Frames flow over a dedicated [ft.DataChannel] rather than the regular
|
|
33
|
+
Flet protocol, so the PNG bytes skip MsgPack encode/decode and travel
|
|
34
|
+
at memory-bandwidth-class speed in embedded native mode (per-channel
|
|
35
|
+
PythonBridge) and at near-bandwidth speed in web/dev modes (raw-byte
|
|
36
|
+
frames muxed over the protocol transport).
|
|
37
|
+
|
|
38
|
+
Wire format on the data channel (one byte of opcode followed by the
|
|
39
|
+
PNG payload):
|
|
40
|
+
|
|
41
|
+
| opcode | payload | meaning |
|
|
42
|
+
|--------|------------|--------------------------------------------|
|
|
43
|
+
| 0x01 | PNG bytes | apply_full — replace backdrop |
|
|
44
|
+
| 0x02 | PNG bytes | apply_diff — composite onto backbuffer |
|
|
45
|
+
| 0x03 | (empty) | clear — drop backdrop + backbuffer |
|
|
46
|
+
|
|
47
|
+
The reverse-direction `resize` event (Dart → Python, small JSON-shaped
|
|
48
|
+
payload) stays on the existing Flet protocol channel — no reason to
|
|
49
|
+
move tiny control events off it.
|
|
50
|
+
"""
|
|
51
|
+
|
|
52
|
+
resize_interval: ft.Number = 10
|
|
53
|
+
"""
|
|
54
|
+
Sampling interval in milliseconds for `on_resize` event.
|
|
55
|
+
"""
|
|
56
|
+
|
|
57
|
+
on_resize: Optional[ft.EventHandler[MatplotlibChartCanvasResizeEvent]] = None
|
|
58
|
+
"""
|
|
59
|
+
Called when the size of this canvas has changed.
|
|
60
|
+
"""
|
|
61
|
+
|
|
62
|
+
on_data_channel_open: Optional[ft.EventHandler[ft.DataChannelOpenEvent]] = None
|
|
63
|
+
"""
|
|
64
|
+
Framework hook — Dart fires this when it opens the data channel during
|
|
65
|
+
`initState`. The default handler captures the channel for use by
|
|
66
|
+
apply_full / apply_diff / clear. Override only if you need to do
|
|
67
|
+
something extra at attach-time.
|
|
68
|
+
"""
|
|
69
|
+
|
|
70
|
+
def init(self) -> None:
|
|
71
|
+
# `init` is the @ft.control post-construct lifecycle hook (runs
|
|
72
|
+
# before `did_mount`). Wire up the default channel-capture handler.
|
|
73
|
+
self._channel: Optional[ft.DataChannel] = None
|
|
74
|
+
# FIFO of per-frame ack futures. Each `apply_*` enqueues a future
|
|
75
|
+
# and `awaits` it; `_on_dart_message` pops the head and resolves
|
|
76
|
+
# it when Dart's `[0xFF]` ack arrives. The await is what makes
|
|
77
|
+
# producer-side callers (e.g. MatplotlibChart._receive_loop)
|
|
78
|
+
# block — events queued during the wait are processed *after*
|
|
79
|
+
# the ack instead of being dropped by stale gate checks.
|
|
80
|
+
self._pending_acks: deque[asyncio.Future] = deque()
|
|
81
|
+
# Optional plain callback for observers that want to be notified
|
|
82
|
+
# on every frame ack (e.g. perf instrumentation). Fires alongside
|
|
83
|
+
# the future resolution; not load-bearing for backpressure.
|
|
84
|
+
self._on_frame_applied: Optional[Callable[[], None]] = None
|
|
85
|
+
if self.on_data_channel_open is None:
|
|
86
|
+
self.on_data_channel_open = self._capture_channel
|
|
87
|
+
|
|
88
|
+
def _capture_channel(self, e: ft.DataChannelOpenEvent) -> None:
|
|
89
|
+
# Single-channel widget; no need to dispatch on e.channel_name.
|
|
90
|
+
self._channel = self.get_data_channel(e.channel_id)
|
|
91
|
+
self._channel.on_bytes(self._on_dart_message)
|
|
92
|
+
|
|
93
|
+
def _on_dart_message(self, payload: bytes) -> None:
|
|
94
|
+
# Wire format on the reverse direction (Dart → Python):
|
|
95
|
+
# [0xFF] — frame_applied ack. Sent by Dart after each apply_full /
|
|
96
|
+
# apply_diff / clear completes on its end. Restores the
|
|
97
|
+
# round-trip backpressure that `_invoke_method` used to
|
|
98
|
+
# provide implicitly in 0.85.
|
|
99
|
+
if not payload or payload[0] != 0xFF:
|
|
100
|
+
return
|
|
101
|
+
# Resolve the head future so the matching `apply_*` await returns.
|
|
102
|
+
if self._pending_acks:
|
|
103
|
+
fut = self._pending_acks.popleft()
|
|
104
|
+
if not fut.done():
|
|
105
|
+
fut.set_result(None)
|
|
106
|
+
# Then fire the observer callback (if any).
|
|
107
|
+
cb = self._on_frame_applied
|
|
108
|
+
if cb is not None:
|
|
109
|
+
with contextlib.suppress(Exception):
|
|
110
|
+
cb()
|
|
111
|
+
|
|
112
|
+
def set_on_frame_applied(self, cb: Optional[Callable[[], None]]) -> None:
|
|
113
|
+
"""Register a side-channel callback invoked on every frame ack.
|
|
114
|
+
|
|
115
|
+
Useful for instrumentation. Backpressure is handled by awaiting
|
|
116
|
+
the result of `apply_full` / `apply_diff` / `clear` directly —
|
|
117
|
+
this callback is a fire-and-forget observer, not part of the
|
|
118
|
+
gating path.
|
|
119
|
+
"""
|
|
120
|
+
self._on_frame_applied = cb
|
|
121
|
+
|
|
122
|
+
async def _send_and_wait(self, packet: bytes) -> None:
|
|
123
|
+
"""Send a channel packet and await Dart's ack.
|
|
124
|
+
|
|
125
|
+
Awaiting blocks the caller until `[0xFF]` arrives on the channel,
|
|
126
|
+
re-creating the 0.85 `_invoke_method` round-trip semantics:
|
|
127
|
+
events that arrive in the producer's queue during the wait stay
|
|
128
|
+
queued (instead of being processed eagerly against a stale
|
|
129
|
+
`_waiting` flag).
|
|
130
|
+
"""
|
|
131
|
+
if self._channel is None:
|
|
132
|
+
return
|
|
133
|
+
loop = asyncio.get_running_loop()
|
|
134
|
+
fut: asyncio.Future = loop.create_future()
|
|
135
|
+
self._pending_acks.append(fut)
|
|
136
|
+
self._channel.send(packet)
|
|
137
|
+
await fut
|
|
138
|
+
|
|
139
|
+
async def apply_full(self, image_bytes: bytes) -> None:
|
|
140
|
+
"""
|
|
141
|
+
Replace the current displayed image with a full PNG frame.
|
|
142
|
+
|
|
143
|
+
Args:
|
|
144
|
+
image_bytes: PNG bytes of the complete frame.
|
|
145
|
+
"""
|
|
146
|
+
await self._send_and_wait(b"\x01" + image_bytes)
|
|
147
|
+
|
|
148
|
+
async def apply_diff(self, image_bytes: bytes) -> None:
|
|
149
|
+
"""
|
|
150
|
+
Composite an incremental "diff" PNG frame onto the current image.
|
|
151
|
+
|
|
152
|
+
Pixels with non-zero alpha replace the corresponding pixels in the
|
|
153
|
+
existing backbuffer; transparent pixels leave the backbuffer
|
|
154
|
+
unchanged. If no backbuffer exists yet, the diff is treated as a
|
|
155
|
+
full frame.
|
|
156
|
+
|
|
157
|
+
Args:
|
|
158
|
+
image_bytes: PNG bytes of the diff frame.
|
|
159
|
+
"""
|
|
160
|
+
await self._send_and_wait(b"\x02" + image_bytes)
|
|
161
|
+
|
|
162
|
+
async def clear(self) -> None:
|
|
163
|
+
"""
|
|
164
|
+
Clear the displayed image and discard the backbuffer.
|
|
165
|
+
"""
|
|
166
|
+
await self._send_and_wait(b"\x03")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: flet-charts
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.86.0.dev0
|
|
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.
|
|
14
|
+
Requires-Dist: flet==0.86.0.dev0
|
|
15
15
|
Dynamic: license-file
|
|
16
16
|
|
|
17
17
|
# flet-charts
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
flet==0.86.0.dev0
|
|
@@ -35,22 +35,9 @@ class MatplotlibChartCanvasControl extends StatefulWidget {
|
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
// ---------------------------------------------------------------------------
|
|
38
|
-
// Shared
|
|
38
|
+
// Shared base
|
|
39
39
|
// ---------------------------------------------------------------------------
|
|
40
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
41
|
abstract class _MatplotlibChartCanvasStateBase
|
|
55
42
|
extends State<MatplotlibChartCanvasControl> {
|
|
56
43
|
// Serialize concurrent apply_full / apply_diff calls so backdrop mutations
|
|
@@ -60,15 +47,30 @@ abstract class _MatplotlibChartCanvasStateBase
|
|
|
60
47
|
Size _lastSize = Size.zero;
|
|
61
48
|
int _lastResize = DateTime.now().millisecondsSinceEpoch;
|
|
62
49
|
|
|
50
|
+
DataChannel? _channel;
|
|
51
|
+
StreamSubscription<Uint8List>? _channelSub;
|
|
52
|
+
|
|
63
53
|
@override
|
|
64
|
-
void
|
|
65
|
-
super.
|
|
66
|
-
|
|
54
|
+
void didChangeDependencies() {
|
|
55
|
+
super.didChangeDependencies();
|
|
56
|
+
// Open the data channel lazily on first dependency lookup — we need
|
|
57
|
+
// BuildContext to reach FletBackend, which isn't available in initState.
|
|
58
|
+
if (_channel != null) return;
|
|
59
|
+
_channel = FletBackend.of(context).openDataChannel();
|
|
60
|
+
_channelSub = _channel!.messages.listen(_onChannelFrame);
|
|
61
|
+
// Announce the channel to Python via the standard convention event.
|
|
62
|
+
widget.control.triggerEvent("data_channel_open", {
|
|
63
|
+
"channel_name": "frames",
|
|
64
|
+
"channel_id": _channel!.id,
|
|
65
|
+
});
|
|
67
66
|
}
|
|
68
67
|
|
|
69
68
|
@override
|
|
70
69
|
void dispose() {
|
|
71
|
-
|
|
70
|
+
_channelSub?.cancel();
|
|
71
|
+
_channelSub = null;
|
|
72
|
+
_channel?.close();
|
|
73
|
+
_channel = null;
|
|
72
74
|
disposeResources();
|
|
73
75
|
super.dispose();
|
|
74
76
|
}
|
|
@@ -81,22 +83,42 @@ abstract class _MatplotlibChartCanvasStateBase
|
|
|
81
83
|
Future<void> clearAll();
|
|
82
84
|
CustomPainter buildPainter();
|
|
83
85
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
86
|
+
// 1-byte ack sent back to Python after each apply completes. Restores
|
|
87
|
+
// round-trip backpressure: matplotlib's producer side keeps `_waiting`
|
|
88
|
+
// set until this ack arrives, so frames don't pile up in the Dart-side
|
|
89
|
+
// queue during interactive drags.
|
|
90
|
+
static final Uint8List _frameAppliedAck = Uint8List.fromList([0xFF]);
|
|
91
|
+
|
|
92
|
+
/// Inbound DataChannel frame. Wire format:
|
|
93
|
+
/// [0x01][PNG bytes] → apply_full
|
|
94
|
+
/// [0x02][PNG bytes] → apply_diff
|
|
95
|
+
/// [0x03] → clear
|
|
96
|
+
void _onChannelFrame(Uint8List bytes) {
|
|
97
|
+
if (bytes.isEmpty) return;
|
|
98
|
+
// Zero-copy slice of the same underlying buffer.
|
|
99
|
+
final payload = Uint8List.sublistView(bytes, 1);
|
|
100
|
+
switch (bytes[0]) {
|
|
101
|
+
case 0x01:
|
|
102
|
+
_enqueueAndAck(() => applyFull(payload));
|
|
103
|
+
break;
|
|
104
|
+
case 0x02:
|
|
105
|
+
_enqueueAndAck(() => applyDiff(payload));
|
|
106
|
+
break;
|
|
107
|
+
case 0x03:
|
|
108
|
+
_enqueueAndAck(clearAll);
|
|
109
|
+
break;
|
|
95
110
|
default:
|
|
96
|
-
|
|
111
|
+
debugPrint(
|
|
112
|
+
"MatplotlibChartCanvas: unknown data-channel opcode 0x${bytes[0].toRadixString(16)}");
|
|
97
113
|
}
|
|
98
114
|
}
|
|
99
115
|
|
|
116
|
+
void _enqueueAndAck(Future<void> Function() task) {
|
|
117
|
+
_enqueue(task).whenComplete(() {
|
|
118
|
+
_channel?.send(_frameAppliedAck);
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
|
|
100
122
|
Future<void> _enqueue(Future<void> Function() task) {
|
|
101
123
|
final prev = _applyChain ?? Future.value();
|
|
102
124
|
final next = prev.then((_) => task());
|
|
@@ -1,72 +0,0 @@
|
|
|
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 +0,0 @@
|
|
|
1
|
-
flet==0.85.3.dev0
|
|
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.3.dev0 → flet_charts-0.86.0.dev0}/src/flet_charts/bar_chart_rod_stack_item.py
RENAMED
|
File without changes
|
|
File without changes
|
{flet_charts-0.85.3.dev0 → flet_charts-0.86.0.dev0}/src/flet_charts/candlestick_chart_spot.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{flet_charts-0.85.3.dev0 → flet_charts-0.86.0.dev0}/src/flet_charts/line_chart_data_point.py
RENAMED
|
File without changes
|
|
File without changes
|
{flet_charts-0.85.3.dev0 → flet_charts-0.86.0.dev0}/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
|
|
File without changes
|
{flet_charts-0.85.3.dev0 → flet_charts-0.86.0.dev0}/src/flet_charts.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
{flet_charts-0.85.3.dev0 → flet_charts-0.86.0.dev0}/src/flutter/flet_charts/lib/flet_charts.dart
RENAMED
|
File without changes
|
{flet_charts-0.85.3.dev0 → flet_charts-0.86.0.dev0}/src/flutter/flet_charts/lib/src/bar_chart.dart
RENAMED
|
File without changes
|
|
File without changes
|
{flet_charts-0.85.3.dev0 → flet_charts-0.86.0.dev0}/src/flutter/flet_charts/lib/src/extension.dart
RENAMED
|
File without changes
|
{flet_charts-0.85.3.dev0 → flet_charts-0.86.0.dev0}/src/flutter/flet_charts/lib/src/line_chart.dart
RENAMED
|
File without changes
|
{flet_charts-0.85.3.dev0 → flet_charts-0.86.0.dev0}/src/flutter/flet_charts/lib/src/pie_chart.dart
RENAMED
|
File without changes
|
{flet_charts-0.85.3.dev0 → flet_charts-0.86.0.dev0}/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
|
|
File without changes
|