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.
Files changed (52) hide show
  1. {flet_charts-0.85.3.dev0/src/flet_charts.egg-info → flet_charts-0.86.0.dev0}/PKG-INFO +2 -2
  2. {flet_charts-0.85.3.dev0 → flet_charts-0.86.0.dev0}/pyproject.toml +2 -2
  3. {flet_charts-0.85.3.dev0 → flet_charts-0.86.0.dev0}/src/flet_charts/matplotlib_chart.py +9 -4
  4. flet_charts-0.86.0.dev0/src/flet_charts/matplotlib_chart_canvas.py +166 -0
  5. {flet_charts-0.85.3.dev0 → flet_charts-0.86.0.dev0/src/flet_charts.egg-info}/PKG-INFO +2 -2
  6. flet_charts-0.86.0.dev0/src/flet_charts.egg-info/requires.txt +1 -0
  7. {flet_charts-0.85.3.dev0 → flet_charts-0.86.0.dev0}/src/flutter/flet_charts/lib/src/matplotlib_chart_canvas.dart +52 -30
  8. flet_charts-0.85.3.dev0/src/flet_charts/matplotlib_chart_canvas.py +0 -72
  9. flet_charts-0.85.3.dev0/src/flet_charts.egg-info/requires.txt +0 -1
  10. {flet_charts-0.85.3.dev0 → flet_charts-0.86.0.dev0}/LICENSE +0 -0
  11. {flet_charts-0.85.3.dev0 → flet_charts-0.86.0.dev0}/README.md +0 -0
  12. {flet_charts-0.85.3.dev0 → flet_charts-0.86.0.dev0}/setup.cfg +0 -0
  13. {flet_charts-0.85.3.dev0 → flet_charts-0.86.0.dev0}/src/flet_charts/__init__.py +0 -0
  14. {flet_charts-0.85.3.dev0 → flet_charts-0.86.0.dev0}/src/flet_charts/bar_chart.py +0 -0
  15. {flet_charts-0.85.3.dev0 → flet_charts-0.86.0.dev0}/src/flet_charts/bar_chart_group.py +0 -0
  16. {flet_charts-0.85.3.dev0 → flet_charts-0.86.0.dev0}/src/flet_charts/bar_chart_rod.py +0 -0
  17. {flet_charts-0.85.3.dev0 → flet_charts-0.86.0.dev0}/src/flet_charts/bar_chart_rod_stack_item.py +0 -0
  18. {flet_charts-0.85.3.dev0 → flet_charts-0.86.0.dev0}/src/flet_charts/candlestick_chart.py +0 -0
  19. {flet_charts-0.85.3.dev0 → flet_charts-0.86.0.dev0}/src/flet_charts/candlestick_chart_spot.py +0 -0
  20. {flet_charts-0.85.3.dev0 → flet_charts-0.86.0.dev0}/src/flet_charts/chart_axis.py +0 -0
  21. {flet_charts-0.85.3.dev0 → flet_charts-0.86.0.dev0}/src/flet_charts/line_chart.py +0 -0
  22. {flet_charts-0.85.3.dev0 → flet_charts-0.86.0.dev0}/src/flet_charts/line_chart_data.py +0 -0
  23. {flet_charts-0.85.3.dev0 → flet_charts-0.86.0.dev0}/src/flet_charts/line_chart_data_point.py +0 -0
  24. {flet_charts-0.85.3.dev0 → flet_charts-0.86.0.dev0}/src/flet_charts/matplotlib_backends/backend_flet_agg.py +0 -0
  25. {flet_charts-0.85.3.dev0 → flet_charts-0.86.0.dev0}/src/flet_charts/matplotlib_chart_with_toolbar.py +0 -0
  26. {flet_charts-0.85.3.dev0 → flet_charts-0.86.0.dev0}/src/flet_charts/pie_chart.py +0 -0
  27. {flet_charts-0.85.3.dev0 → flet_charts-0.86.0.dev0}/src/flet_charts/pie_chart_section.py +0 -0
  28. {flet_charts-0.85.3.dev0 → flet_charts-0.86.0.dev0}/src/flet_charts/plotly_chart.py +0 -0
  29. {flet_charts-0.85.3.dev0 → flet_charts-0.86.0.dev0}/src/flet_charts/radar_chart.py +0 -0
  30. {flet_charts-0.85.3.dev0 → flet_charts-0.86.0.dev0}/src/flet_charts/radar_data_set.py +0 -0
  31. {flet_charts-0.85.3.dev0 → flet_charts-0.86.0.dev0}/src/flet_charts/scatter_chart.py +0 -0
  32. {flet_charts-0.85.3.dev0 → flet_charts-0.86.0.dev0}/src/flet_charts/scatter_chart_spot.py +0 -0
  33. {flet_charts-0.85.3.dev0 → flet_charts-0.86.0.dev0}/src/flet_charts/types.py +0 -0
  34. {flet_charts-0.85.3.dev0 → flet_charts-0.86.0.dev0}/src/flet_charts.egg-info/SOURCES.txt +0 -0
  35. {flet_charts-0.85.3.dev0 → flet_charts-0.86.0.dev0}/src/flet_charts.egg-info/dependency_links.txt +0 -0
  36. {flet_charts-0.85.3.dev0 → flet_charts-0.86.0.dev0}/src/flet_charts.egg-info/top_level.txt +0 -0
  37. {flet_charts-0.85.3.dev0 → flet_charts-0.86.0.dev0}/src/flutter/flet_charts/lib/flet_charts.dart +0 -0
  38. {flet_charts-0.85.3.dev0 → flet_charts-0.86.0.dev0}/src/flutter/flet_charts/lib/src/bar_chart.dart +0 -0
  39. {flet_charts-0.85.3.dev0 → flet_charts-0.86.0.dev0}/src/flutter/flet_charts/lib/src/candlestick_chart.dart +0 -0
  40. {flet_charts-0.85.3.dev0 → flet_charts-0.86.0.dev0}/src/flutter/flet_charts/lib/src/extension.dart +0 -0
  41. {flet_charts-0.85.3.dev0 → flet_charts-0.86.0.dev0}/src/flutter/flet_charts/lib/src/line_chart.dart +0 -0
  42. {flet_charts-0.85.3.dev0 → flet_charts-0.86.0.dev0}/src/flutter/flet_charts/lib/src/pie_chart.dart +0 -0
  43. {flet_charts-0.85.3.dev0 → flet_charts-0.86.0.dev0}/src/flutter/flet_charts/lib/src/radar_chart.dart +0 -0
  44. {flet_charts-0.85.3.dev0 → flet_charts-0.86.0.dev0}/src/flutter/flet_charts/lib/src/scatter_chart.dart +0 -0
  45. {flet_charts-0.85.3.dev0 → flet_charts-0.86.0.dev0}/src/flutter/flet_charts/lib/src/utils/bar_chart.dart +0 -0
  46. {flet_charts-0.85.3.dev0 → flet_charts-0.86.0.dev0}/src/flutter/flet_charts/lib/src/utils/candlestick_chart.dart +0 -0
  47. {flet_charts-0.85.3.dev0 → flet_charts-0.86.0.dev0}/src/flutter/flet_charts/lib/src/utils/charts.dart +0 -0
  48. {flet_charts-0.85.3.dev0 → flet_charts-0.86.0.dev0}/src/flutter/flet_charts/lib/src/utils/line_chart.dart +0 -0
  49. {flet_charts-0.85.3.dev0 → flet_charts-0.86.0.dev0}/src/flutter/flet_charts/lib/src/utils/pie_chart.dart +0 -0
  50. {flet_charts-0.85.3.dev0 → flet_charts-0.86.0.dev0}/src/flutter/flet_charts/lib/src/utils/radar_chart.dart +0 -0
  51. {flet_charts-0.85.3.dev0 → flet_charts-0.86.0.dev0}/src/flutter/flet_charts/lib/src/utils/scatter_chart.dart +0 -0
  52. {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.85.3.dev0
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.85.3.dev0
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.85.3.dev0"
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.85.3.dev0",
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 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.
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.85.3.dev0
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.85.3.dev0
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 helpers
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 initState() {
65
- super.initState();
66
- widget.control.addInvokeMethodListener(_invokeMethod);
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
- widget.control.removeInvokeMethodListener(_invokeMethod);
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
- 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;
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
- throw Exception("Unknown MatplotlibChartCanvas method: $name");
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