flet-charts 0.2.0.dev524__tar.gz → 0.2.0.dev534__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 (47) hide show
  1. {flet_charts-0.2.0.dev524/src/flet_charts.egg-info → flet_charts-0.2.0.dev534}/PKG-INFO +2 -2
  2. {flet_charts-0.2.0.dev524 → flet_charts-0.2.0.dev534}/pyproject.toml +2 -2
  3. {flet_charts-0.2.0.dev524 → flet_charts-0.2.0.dev534}/src/flet_charts/__init__.py +9 -1
  4. flet_charts-0.2.0.dev534/src/flet_charts/matplotlib_backends/backend_flet_agg.py +16 -0
  5. flet_charts-0.2.0.dev534/src/flet_charts/matplotlib_chart.py +382 -0
  6. flet_charts-0.2.0.dev534/src/flet_charts/matplotlib_chart_with_toolbar.py +110 -0
  7. {flet_charts-0.2.0.dev524 → flet_charts-0.2.0.dev534/src/flet_charts.egg-info}/PKG-INFO +2 -2
  8. {flet_charts-0.2.0.dev524 → flet_charts-0.2.0.dev534}/src/flet_charts.egg-info/SOURCES.txt +2 -0
  9. flet_charts-0.2.0.dev534/src/flet_charts.egg-info/requires.txt +1 -0
  10. {flet_charts-0.2.0.dev524 → flet_charts-0.2.0.dev534}/src/flutter/flet_charts/pubspec.lock +1 -1
  11. flet_charts-0.2.0.dev524/src/flet_charts/matplotlib_chart.py +0 -65
  12. flet_charts-0.2.0.dev524/src/flet_charts.egg-info/requires.txt +0 -1
  13. {flet_charts-0.2.0.dev524 → flet_charts-0.2.0.dev534}/LICENSE +0 -0
  14. {flet_charts-0.2.0.dev524 → flet_charts-0.2.0.dev534}/README.md +0 -0
  15. {flet_charts-0.2.0.dev524 → flet_charts-0.2.0.dev534}/setup.cfg +0 -0
  16. {flet_charts-0.2.0.dev524 → flet_charts-0.2.0.dev534}/src/flet_charts/bar_chart.py +0 -0
  17. {flet_charts-0.2.0.dev524 → flet_charts-0.2.0.dev534}/src/flet_charts/bar_chart_group.py +0 -0
  18. {flet_charts-0.2.0.dev524 → flet_charts-0.2.0.dev534}/src/flet_charts/bar_chart_rod.py +0 -0
  19. {flet_charts-0.2.0.dev524 → flet_charts-0.2.0.dev534}/src/flet_charts/bar_chart_rod_stack_item.py +0 -0
  20. {flet_charts-0.2.0.dev524 → flet_charts-0.2.0.dev534}/src/flet_charts/chart_axis.py +0 -0
  21. {flet_charts-0.2.0.dev524 → flet_charts-0.2.0.dev534}/src/flet_charts/line_chart.py +0 -0
  22. {flet_charts-0.2.0.dev524 → flet_charts-0.2.0.dev534}/src/flet_charts/line_chart_data.py +0 -0
  23. {flet_charts-0.2.0.dev524 → flet_charts-0.2.0.dev534}/src/flet_charts/line_chart_data_point.py +0 -0
  24. {flet_charts-0.2.0.dev524 → flet_charts-0.2.0.dev534}/src/flet_charts/pie_chart.py +0 -0
  25. {flet_charts-0.2.0.dev524 → flet_charts-0.2.0.dev534}/src/flet_charts/pie_chart_section.py +0 -0
  26. {flet_charts-0.2.0.dev524 → flet_charts-0.2.0.dev534}/src/flet_charts/plotly_chart.py +0 -0
  27. {flet_charts-0.2.0.dev524 → flet_charts-0.2.0.dev534}/src/flet_charts/scatter_chart.py +0 -0
  28. {flet_charts-0.2.0.dev524 → flet_charts-0.2.0.dev534}/src/flet_charts/scatter_chart_spot.py +0 -0
  29. {flet_charts-0.2.0.dev524 → flet_charts-0.2.0.dev534}/src/flet_charts/types.py +0 -0
  30. {flet_charts-0.2.0.dev524 → flet_charts-0.2.0.dev534}/src/flet_charts.egg-info/dependency_links.txt +0 -0
  31. {flet_charts-0.2.0.dev524 → flet_charts-0.2.0.dev534}/src/flet_charts.egg-info/top_level.txt +0 -0
  32. {flet_charts-0.2.0.dev524 → flet_charts-0.2.0.dev534}/src/flutter/flet_charts/CHANGELOG.md +0 -0
  33. {flet_charts-0.2.0.dev524 → flet_charts-0.2.0.dev534}/src/flutter/flet_charts/LICENSE +0 -0
  34. {flet_charts-0.2.0.dev524 → flet_charts-0.2.0.dev534}/src/flutter/flet_charts/README.md +0 -0
  35. {flet_charts-0.2.0.dev524 → flet_charts-0.2.0.dev534}/src/flutter/flet_charts/analysis_options.yaml +0 -0
  36. {flet_charts-0.2.0.dev524 → flet_charts-0.2.0.dev534}/src/flutter/flet_charts/lib/flet_charts.dart +0 -0
  37. {flet_charts-0.2.0.dev524 → flet_charts-0.2.0.dev534}/src/flutter/flet_charts/lib/src/bar_chart.dart +0 -0
  38. {flet_charts-0.2.0.dev524 → flet_charts-0.2.0.dev534}/src/flutter/flet_charts/lib/src/extension.dart +0 -0
  39. {flet_charts-0.2.0.dev524 → flet_charts-0.2.0.dev534}/src/flutter/flet_charts/lib/src/line_chart.dart +0 -0
  40. {flet_charts-0.2.0.dev524 → flet_charts-0.2.0.dev534}/src/flutter/flet_charts/lib/src/pie_chart.dart +0 -0
  41. {flet_charts-0.2.0.dev524 → flet_charts-0.2.0.dev534}/src/flutter/flet_charts/lib/src/scatter_chart.dart +0 -0
  42. {flet_charts-0.2.0.dev524 → flet_charts-0.2.0.dev534}/src/flutter/flet_charts/lib/src/utils/bar_chart.dart +0 -0
  43. {flet_charts-0.2.0.dev524 → flet_charts-0.2.0.dev534}/src/flutter/flet_charts/lib/src/utils/charts.dart +0 -0
  44. {flet_charts-0.2.0.dev524 → flet_charts-0.2.0.dev534}/src/flutter/flet_charts/lib/src/utils/line_chart.dart +0 -0
  45. {flet_charts-0.2.0.dev524 → flet_charts-0.2.0.dev534}/src/flutter/flet_charts/lib/src/utils/pie_chart.dart +0 -0
  46. {flet_charts-0.2.0.dev524 → flet_charts-0.2.0.dev534}/src/flutter/flet_charts/lib/src/utils/scatter_chart.dart +0 -0
  47. {flet_charts-0.2.0.dev524 → flet_charts-0.2.0.dev534}/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.2.0.dev524
3
+ Version: 0.2.0.dev534
4
4
  Summary: A Flet extension for creating interactive charts and graphs.
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-charts/issues
11
11
  Requires-Python: >=3.10
12
12
  Description-Content-Type: text/markdown
13
13
  License-File: LICENSE
14
- Requires-Dist: flet>=0.70.0.dev0
14
+ Requires-Dist: flet
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.2.0.dev524"
3
+ version = "0.2.0.dev534"
4
4
  description = "A Flet extension for creating interactive charts and graphs."
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.70.0.dev0",
10
+ "flet",
11
11
  ]
12
12
 
13
13
  [project.urls]
@@ -19,7 +19,12 @@ from flet_charts.line_chart_data_point import (
19
19
  LineChartDataPoint,
20
20
  LineChartDataPointTooltip,
21
21
  )
22
- from flet_charts.matplotlib_chart import MatplotlibChart
22
+ from flet_charts.matplotlib_chart import (
23
+ MatplotlibChart,
24
+ MatplotlibChartMessageEvent,
25
+ MatplotlibChartToolbarButtonsUpdateEvent,
26
+ )
27
+ from flet_charts.matplotlib_chart_with_toolbar import MatplotlibChartWithToolbar
23
28
  from flet_charts.pie_chart import PieChart, PieChartEvent
24
29
  from flet_charts.pie_chart_section import PieChartSection
25
30
  from flet_charts.plotly_chart import PlotlyChart
@@ -78,4 +83,7 @@ __all__ = [
78
83
  "ScatterChartSpot",
79
84
  "ScatterChartSpotTooltip",
80
85
  "ScatterChartTooltip",
86
+ "MatplotlibChartMessageEvent",
87
+ "MatplotlibChartToolbarButtonsUpdateEvent",
88
+ "MatplotlibChartWithToolbar",
81
89
  ]
@@ -0,0 +1,16 @@
1
+ from matplotlib import _api
2
+ from matplotlib.backends import backend_webagg_core
3
+
4
+
5
+ class FigureCanvasFletAgg(backend_webagg_core.FigureCanvasWebAggCore):
6
+ manager_class = _api.classproperty(lambda cls: FigureManagerFletAgg)
7
+ supports_blit = False
8
+
9
+
10
+ class FigureManagerFletAgg(backend_webagg_core.FigureManagerWebAgg):
11
+ _toolbar2_class = backend_webagg_core.NavigationToolbar2WebAgg
12
+
13
+
14
+ FigureCanvas = FigureCanvasFletAgg
15
+ FigureManager = FigureManagerFletAgg
16
+ interactive = True
@@ -0,0 +1,382 @@
1
+ import asyncio
2
+ import logging
3
+ from dataclasses import dataclass, field
4
+ from io import BytesIO
5
+ from typing import Optional
6
+
7
+ import flet as ft
8
+ import flet.canvas as fc
9
+
10
+ try:
11
+ import matplotlib
12
+ from matplotlib.figure import Figure
13
+ except ImportError as e:
14
+ raise Exception(
15
+ 'Install "matplotlib" Python package to use MatplotlibChart control.'
16
+ ) from e
17
+
18
+ __all__ = [
19
+ "MatplotlibChart",
20
+ "MatplotlibChartMessageEvent",
21
+ "MatplotlibChartToolbarButtonsUpdateEvent",
22
+ ]
23
+
24
+ logger = logging.getLogger("flet-charts.matplotlib")
25
+
26
+ matplotlib.use("module://flet_charts.matplotlib_backends.backend_flet_agg")
27
+
28
+ figure_cursors = {
29
+ "default": None,
30
+ "pointer": ft.MouseCursor.CLICK,
31
+ "crosshair": ft.MouseCursor.PRECISE,
32
+ "move": ft.MouseCursor.MOVE,
33
+ "wait": ft.MouseCursor.WAIT,
34
+ "ew-resize": ft.MouseCursor.RESIZE_LEFT_RIGHT,
35
+ "ns-resize": ft.MouseCursor.RESIZE_UP_DOWN,
36
+ }
37
+
38
+
39
+ @dataclass
40
+ class MatplotlibChartMessageEvent(ft.Event["MatplotlibChart"]):
41
+ message: str
42
+ """
43
+ Message text.
44
+ """
45
+
46
+
47
+ @dataclass
48
+ class MatplotlibChartToolbarButtonsUpdateEvent(ft.Event["MatplotlibChart"]):
49
+ back_enabled: bool
50
+ """
51
+ Whether Back button is enabled or not.
52
+ """
53
+ forward_enabled: bool
54
+ """
55
+ Whether Forward button is enabled or not.
56
+ """
57
+
58
+
59
+ @ft.control(kw_only=True, isolated=True)
60
+ class MatplotlibChart(ft.GestureDetector):
61
+ """
62
+ Displays a [Matplotlib](https://matplotlib.org/) chart.
63
+
64
+ Warning:
65
+ This control requires the [`matplotlib`](https://matplotlib.org/)
66
+ Python package to be installed.
67
+
68
+ See this [installation guide](index.md#installation) for more information.
69
+ """
70
+
71
+ figure: Figure = field(metadata={"skip": True})
72
+ """
73
+ Matplotlib figure to draw - an instance of
74
+ [`matplotlib.figure.Figure`](https://matplotlib.org/stable/api/_as_gen/matplotlib.figure.Figure.html#matplotlib.figure.Figure).
75
+ """
76
+
77
+ on_message: Optional[ft.EventHandler[MatplotlibChartMessageEvent]] = None
78
+ """
79
+ The event is triggered on figure message update.
80
+ """
81
+
82
+ on_toolbar_buttons_update: Optional[
83
+ ft.EventHandler[MatplotlibChartToolbarButtonsUpdateEvent]
84
+ ] = None
85
+ """
86
+ Triggers when toolbar buttons status is updated.
87
+ """
88
+
89
+ def build(self):
90
+ self.mouse_cursor = ft.MouseCursor.WAIT
91
+ self.__started = False
92
+ self.__dpr = self.page.media.device_pixel_ratio
93
+ logger.debug(f"DPR: {self.__dpr}")
94
+ self.__image_mode = "full"
95
+
96
+ self.canvas = fc.Canvas(
97
+ # resize_interval=10,
98
+ on_resize=self.on_canvas_resize,
99
+ expand=True,
100
+ )
101
+ self.keyboard_listener = ft.KeyboardListener(
102
+ self.canvas,
103
+ autofocus=True,
104
+ on_key_down=self._on_key_down,
105
+ on_key_up=self._on_key_up,
106
+ )
107
+ self.content = self.keyboard_listener
108
+ self.on_enter = self._on_enter
109
+ self.on_hover = self._on_hover
110
+ self.on_exit = self._on_exit
111
+ self.on_pan_start = self._pan_start
112
+ self.on_pan_update = self._pan_update
113
+ self.on_pan_end = self._pan_end
114
+ self.on_right_pan_start = self._right_pan_start
115
+ self.on_right_pan_update = self._right_pan_update
116
+ self.on_right_pan_end = self._right_pan_end
117
+ self.img_count = 1
118
+ self._receive_queue = asyncio.Queue()
119
+ self._main_loop = asyncio.get_event_loop()
120
+ self._width = 0
121
+ self._height = 0
122
+ self._waiting = False
123
+
124
+ def _on_key_down(self, e):
125
+ logger.debug(f"ON KEY DOWN: {e}")
126
+
127
+ def _on_key_up(self, e):
128
+ logger.debug(f"ON KEY UP: {e}")
129
+
130
+ def _on_enter(self, e: ft.HoverEvent):
131
+ logger.debug(f"_on_enter: {e.local_position.x}, {e.local_position.y}")
132
+ self.send_message(
133
+ {
134
+ "type": "figure_enter",
135
+ "x": e.local_position.x * self.__dpr,
136
+ "y": e.local_position.y * self.__dpr,
137
+ "button": 0,
138
+ "buttons": 0,
139
+ "modifiers": [],
140
+ }
141
+ )
142
+
143
+ def _on_hover(self, e: ft.HoverEvent):
144
+ logger.debug(f"_on_hover: {e.local_position.x}, {e.local_position.y}")
145
+ self.send_message(
146
+ {
147
+ "type": "motion_notify",
148
+ "x": e.local_position.x * self.__dpr,
149
+ "y": e.local_position.y * self.__dpr,
150
+ "button": 0,
151
+ "buttons": 0,
152
+ "modifiers": [],
153
+ }
154
+ )
155
+
156
+ def _on_exit(self, e: ft.HoverEvent):
157
+ logger.debug(f"_on_exit: {e.local_position.x}, {e.local_position.y}")
158
+ self.send_message(
159
+ {
160
+ "type": "figure_leave",
161
+ "x": e.local_position.x * self.__dpr,
162
+ "y": e.local_position.y * self.__dpr,
163
+ "button": 0,
164
+ "buttons": 0,
165
+ "modifiers": [],
166
+ }
167
+ )
168
+
169
+ def _pan_start(self, e: ft.DragStartEvent):
170
+ logger.debug(f"_pan_start: {e.local_position.x}, {e.local_position.y}")
171
+ asyncio.create_task(self.keyboard_listener.focus())
172
+ self.send_message(
173
+ {
174
+ "type": "button_press",
175
+ "x": e.local_position.x * self.__dpr,
176
+ "y": e.local_position.y * self.__dpr,
177
+ "button": 0,
178
+ "buttons": 1,
179
+ "modifiers": [],
180
+ }
181
+ )
182
+
183
+ def _pan_update(self, e: ft.DragUpdateEvent):
184
+ logger.debug(f"_pan_update: {e.local_position.x}, {e.local_position.y}")
185
+ self.send_message(
186
+ {
187
+ "type": "motion_notify",
188
+ "x": e.local_position.x * self.__dpr,
189
+ "y": e.local_position.y * self.__dpr,
190
+ "button": 0,
191
+ "buttons": 1,
192
+ "modifiers": [],
193
+ }
194
+ )
195
+
196
+ def _pan_end(self, e: ft.DragEndEvent):
197
+ logger.debug(f"_pan_end: {e.local_position.x}, {e.local_position.y}")
198
+ self.send_message(
199
+ {
200
+ "type": "button_release",
201
+ "x": e.local_position.x * self.__dpr,
202
+ "y": e.local_position.y * self.__dpr,
203
+ "button": 0,
204
+ "buttons": 0,
205
+ "modifiers": [],
206
+ }
207
+ )
208
+
209
+ def _right_pan_start(self, e: ft.PointerEvent):
210
+ logger.debug(f"_pan_start: {e.local_position.x}, {e.local_position.y}")
211
+ self.send_message(
212
+ {
213
+ "type": "button_press",
214
+ "x": e.local_position.x * self.__dpr,
215
+ "y": e.local_position.y * self.__dpr,
216
+ "button": 2,
217
+ "buttons": 2,
218
+ "modifiers": [],
219
+ }
220
+ )
221
+
222
+ def _right_pan_update(self, e: ft.PointerEvent):
223
+ logger.debug(f"_pan_update: {e.local_position.x}, {e.local_position.y}")
224
+ self.send_message(
225
+ {
226
+ "type": "motion_notify",
227
+ "x": e.local_position.x * self.__dpr,
228
+ "y": e.local_position.y * self.__dpr,
229
+ "button": 0,
230
+ "buttons": 2,
231
+ "modifiers": [],
232
+ }
233
+ )
234
+
235
+ def _right_pan_end(self, e: ft.PointerEvent):
236
+ logger.debug(f"_pan_end: {e.local_position.x}, {e.local_position.y}")
237
+ self.send_message(
238
+ {
239
+ "type": "button_release",
240
+ "x": e.local_position.x * self.__dpr,
241
+ "y": e.local_position.y * self.__dpr,
242
+ "button": 2,
243
+ "buttons": 0,
244
+ "modifiers": [],
245
+ }
246
+ )
247
+
248
+ def will_unmount(self):
249
+ self.figure.canvas.manager.remove_web_socket(self)
250
+
251
+ def home(self):
252
+ logger.debug("home)")
253
+ self.send_message({"type": "toolbar_button", "name": "home"})
254
+
255
+ def back(self):
256
+ logger.debug("back()")
257
+ self.send_message({"type": "toolbar_button", "name": "back"})
258
+
259
+ def forward(self):
260
+ logger.debug("forward)")
261
+ self.send_message({"type": "toolbar_button", "name": "forward"})
262
+
263
+ def pan(self):
264
+ logger.debug("pan()")
265
+ self.send_message({"type": "toolbar_button", "name": "pan"})
266
+
267
+ def zoom(self):
268
+ logger.debug("zoom()")
269
+ self.send_message({"type": "toolbar_button", "name": "zoom"})
270
+
271
+ def download(self, format):
272
+ logger.debug(f"Download in format: {format}")
273
+ buff = BytesIO()
274
+ self.figure.savefig(buff, format=format, dpi=self.figure.dpi * self.__dpr)
275
+ return buff.getvalue()
276
+
277
+ async def _receive_loop(self):
278
+ while True:
279
+ is_binary, content = await self._receive_queue.get()
280
+ if is_binary:
281
+ logger.debug(f"receive_binary({len(content)})")
282
+ if self.__image_mode == "full":
283
+ await self.canvas.clear_capture()
284
+
285
+ self.canvas.shapes = [
286
+ fc.Image(
287
+ src_bytes=content,
288
+ x=0,
289
+ y=0,
290
+ width=self.figure.bbox.size[0] / self.__dpr,
291
+ height=self.figure.bbox.size[1] / self.__dpr,
292
+ )
293
+ ]
294
+ ft.context.disable_auto_update()
295
+ self.canvas.update()
296
+ await self.canvas.capture()
297
+ self.img_count += 1
298
+ self._waiting = False
299
+ else:
300
+ logger.debug(f"receive_json({content})")
301
+ if content["type"] == "image_mode":
302
+ self.__image_mode = content["mode"]
303
+ elif content["type"] == "cursor":
304
+ self.mouse_cursor = figure_cursors[content["cursor"]]
305
+ self.update()
306
+ elif content["type"] == "draw" and not self._waiting:
307
+ self._waiting = True
308
+ self.send_message({"type": "draw"})
309
+ elif content["type"] == "rubberband":
310
+ if len(self.canvas.shapes) == 2:
311
+ self.canvas.shapes.pop()
312
+ if (
313
+ content["x0"] != -1
314
+ and content["y0"] != -1
315
+ and content["x1"] != -1
316
+ and content["y1"] != -1
317
+ ):
318
+ x0 = content["x0"] / self.__dpr
319
+ y0 = self._height - content["y0"] / self.__dpr
320
+ x1 = content["x1"] / self.__dpr
321
+ y1 = self._height - content["y1"] / self.__dpr
322
+ self.canvas.shapes.append(
323
+ fc.Rect(
324
+ x=x0,
325
+ y=y0,
326
+ width=x1 - x0,
327
+ height=y1 - y0,
328
+ paint=ft.Paint(
329
+ stroke_width=1, style=ft.PaintingStyle.STROKE
330
+ ),
331
+ )
332
+ )
333
+ self.canvas.update()
334
+ elif content["type"] == "resize":
335
+ self.send_message({"type": "refresh"})
336
+ elif content["type"] == "message":
337
+ await self._trigger_event(
338
+ "message", {"message": content["message"]}
339
+ )
340
+ elif content["type"] == "history_buttons":
341
+ await self._trigger_event(
342
+ "toolbar_buttons_update",
343
+ {
344
+ "back_enabled": content["Back"],
345
+ "forward_enabled": content["Forward"],
346
+ },
347
+ )
348
+
349
+ def send_message(self, message):
350
+ logger.debug(f"send_message({message})")
351
+ manager = self.figure.canvas.manager
352
+ if manager is not None:
353
+ manager.handle_json(message)
354
+
355
+ def send_json(self, content):
356
+ logger.debug(f"send_json: {content}")
357
+ self._main_loop.call_soon_threadsafe(
358
+ lambda: self._receive_queue.put_nowait((False, content))
359
+ )
360
+
361
+ def send_binary(self, blob):
362
+ self._main_loop.call_soon_threadsafe(
363
+ lambda: self._receive_queue.put_nowait((True, blob))
364
+ )
365
+
366
+ async def on_canvas_resize(self, e: fc.CanvasResizeEvent):
367
+ logger.debug(f"on_canvas_resize: {e.width}, {e.height}")
368
+
369
+ if not self.__started:
370
+ self.__started = True
371
+ asyncio.create_task(self._receive_loop())
372
+ self.figure.canvas.manager.add_web_socket(self)
373
+ self.send_message({"type": "send_image_mode"})
374
+ self.send_message(
375
+ {"type": "set_device_pixel_ratio", "device_pixel_ratio": self.__dpr}
376
+ )
377
+ self.send_message({"type": "refresh"})
378
+ self._width = e.width
379
+ self._height = e.height
380
+ self.send_message(
381
+ {"type": "resize", "width": self._width, "height": self._height}
382
+ )
@@ -0,0 +1,110 @@
1
+ from dataclasses import field
2
+
3
+ import flet as ft
4
+ from matplotlib.figure import Figure
5
+
6
+ import flet_charts
7
+
8
+ _download_formats = [
9
+ "eps",
10
+ "jpeg",
11
+ "pgf",
12
+ "pdf",
13
+ "png",
14
+ "ps",
15
+ "raw",
16
+ "svg",
17
+ "tif",
18
+ "webp",
19
+ ]
20
+
21
+
22
+ @ft.control(kw_only=True, isolated=True)
23
+ class MatplotlibChartWithToolbar(ft.Column):
24
+ figure: Figure = field(metadata={"skip": True})
25
+ """
26
+ Matplotlib figure to draw - an instance of
27
+ [`matplotlib.figure.Figure`](https://matplotlib.org/stable/api/_as_gen/matplotlib.figure.Figure.html#matplotlib.figure.Figure).
28
+ """
29
+
30
+ def build(self):
31
+ self.mpl = flet_charts.MatplotlibChart(
32
+ figure=self.figure,
33
+ expand=True,
34
+ on_message=self.on_message,
35
+ on_toolbar_buttons_update=self.on_toolbar_update,
36
+ )
37
+ self.home_btn = ft.IconButton(ft.Icons.HOME, on_click=lambda: self.mpl.home())
38
+ self.back_btn = ft.IconButton(
39
+ ft.Icons.ARROW_BACK_ROUNDED, on_click=lambda: self.mpl.back()
40
+ )
41
+ self.fwd_btn = ft.IconButton(
42
+ ft.Icons.ARROW_FORWARD_ROUNDED, on_click=lambda: self.mpl.forward()
43
+ )
44
+ self.pan_btn = ft.IconButton(
45
+ ft.Icons.OPEN_WITH,
46
+ selected_icon=ft.Icons.OPEN_WITH,
47
+ selected_icon_color=ft.Colors.AMBER_800,
48
+ on_click=self.pan_click,
49
+ )
50
+ self.zoom_btn = ft.IconButton(
51
+ ft.Icons.ZOOM_IN,
52
+ selected_icon=ft.Icons.ZOOM_IN,
53
+ selected_icon_color=ft.Colors.AMBER_800,
54
+ on_click=self.zoom_click,
55
+ )
56
+ self.download_btn = ft.IconButton(
57
+ ft.Icons.DOWNLOAD, on_click=self.download_click
58
+ )
59
+ self.download_fmt = ft.Dropdown(
60
+ value="png",
61
+ options=[ft.DropdownOption(fmt) for fmt in _download_formats],
62
+ )
63
+ self.msg = ft.Text()
64
+ self.controls = [
65
+ ft.Row(
66
+ [
67
+ self.home_btn,
68
+ self.back_btn,
69
+ self.fwd_btn,
70
+ self.pan_btn,
71
+ self.zoom_btn,
72
+ self.download_btn,
73
+ self.download_fmt,
74
+ self.msg,
75
+ ]
76
+ ),
77
+ self.mpl,
78
+ ]
79
+ if not self.expand:
80
+ if not self.height:
81
+ self.height = self.figure.bbox.height
82
+ if not self.width:
83
+ self.width = self.figure.bbox.width
84
+
85
+ def on_message(self, e: flet_charts.MatplotlibChartMessageEvent):
86
+ self.msg.value = e.message
87
+ self.msg.update()
88
+
89
+ def on_toolbar_update(
90
+ self, e: flet_charts.MatplotlibChartToolbarButtonsUpdateEvent
91
+ ):
92
+ self.back_btn.disabled = not e.back_enabled
93
+ self.fwd_btn.disabled = not e.forward_enabled
94
+ self.update()
95
+
96
+ def pan_click(self):
97
+ self.mpl.pan()
98
+ self.pan_btn.selected = not self.pan_btn.selected
99
+ self.zoom_btn.selected = False
100
+
101
+ def zoom_click(self):
102
+ self.mpl.zoom()
103
+ self.pan_btn.selected = False
104
+ self.zoom_btn.selected = not self.zoom_btn.selected
105
+
106
+ async def download_click(self):
107
+ fmt = self.download_fmt.value
108
+ buffer = self.mpl.download(fmt)
109
+ title = self.figure.canvas.manager.get_window_title()
110
+ await ft.FilePicker().save_file(file_name=f"{title}.{fmt}", src_bytes=buffer)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: flet-charts
3
- Version: 0.2.0.dev524
3
+ Version: 0.2.0.dev534
4
4
  Summary: A Flet extension for creating interactive charts and graphs.
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-charts/issues
11
11
  Requires-Python: >=3.10
12
12
  Description-Content-Type: text/markdown
13
13
  License-File: LICENSE
14
- Requires-Dist: flet>=0.70.0.dev0
14
+ Requires-Dist: flet
15
15
  Dynamic: license-file
16
16
 
17
17
  # flet-charts
@@ -11,6 +11,7 @@ src/flet_charts/line_chart.py
11
11
  src/flet_charts/line_chart_data.py
12
12
  src/flet_charts/line_chart_data_point.py
13
13
  src/flet_charts/matplotlib_chart.py
14
+ src/flet_charts/matplotlib_chart_with_toolbar.py
14
15
  src/flet_charts/pie_chart.py
15
16
  src/flet_charts/pie_chart_section.py
16
17
  src/flet_charts/plotly_chart.py
@@ -22,6 +23,7 @@ src/flet_charts.egg-info/SOURCES.txt
22
23
  src/flet_charts.egg-info/dependency_links.txt
23
24
  src/flet_charts.egg-info/requires.txt
24
25
  src/flet_charts.egg-info/top_level.txt
26
+ src/flet_charts/matplotlib_backends/backend_flet_agg.py
25
27
  src/flutter/flet_charts/CHANGELOG.md
26
28
  src/flutter/flet_charts/LICENSE
27
29
  src/flutter/flet_charts/README.md
@@ -142,7 +142,7 @@ packages:
142
142
  description:
143
143
  path: "packages/flet"
144
144
  ref: main
145
- resolved-ref: cbdae3a51670e2a124bbe5883280302efffcb3e0
145
+ resolved-ref: b51db65cd3ccdc47fe4915f9b6a9413c86a4f903
146
146
  url: "https://github.com/flet-dev/flet.git"
147
147
  source: git
148
148
  version: "0.70.0"
@@ -1,65 +0,0 @@
1
- import io
2
- import re
3
- import xml.etree.ElementTree as ET
4
- from dataclasses import field
5
-
6
- import flet as ft
7
-
8
- try:
9
- from matplotlib.figure import Figure
10
- except ImportError as e:
11
- raise Exception(
12
- 'Install "matplotlib" Python package to use MatplotlibChart control.'
13
- ) from e
14
-
15
- __all__ = ["MatplotlibChart"]
16
-
17
-
18
- @ft.control(kw_only=True)
19
- class MatplotlibChart(ft.Container):
20
- """
21
- Displays a [Matplotlib](https://matplotlib.org/) chart.
22
-
23
- Warning:
24
- This control requires the [`matplotlib`](https://matplotlib.org/)
25
- Python package to be installed.
26
-
27
- See this [installation guide](index.md#installation) for more information.
28
- """
29
-
30
- figure: Figure = field(metadata={"skip": True})
31
- """
32
- Matplotlib figure to draw - an instance of
33
- [`matplotlib.figure.Figure`](https://matplotlib.org/stable/api/_as_gen/matplotlib.figure.Figure.html#matplotlib.figure.Figure).
34
- """
35
-
36
- original_size: bool = False
37
- """
38
- Whether to display chart in original size.
39
-
40
- Set to `False` to display a chart that fits configured bounds.
41
- """
42
-
43
- transparent: bool = False
44
- """
45
- Whether to remove the background from the chart.
46
- """
47
-
48
- def init(self):
49
- self.alignment = ft.Alignment.CENTER
50
- self.__img = ft.Image(fit=ft.BoxFit.FILL)
51
- self.content = self.__img
52
-
53
- def before_update(self):
54
- super().before_update()
55
- if self.figure is not None:
56
- s = io.StringIO()
57
- self.figure.savefig(s, format="svg", transparent=self.transparent)
58
- svg = s.getvalue()
59
-
60
- if not self.original_size:
61
- root = ET.fromstring(svg)
62
- w = float(re.findall(r"\d+", root.attrib["width"])[0])
63
- h = float(re.findall(r"\d+", root.attrib["height"])[0])
64
- self.__img.aspect_ratio = w / h
65
- self.__img.src = svg
@@ -1 +0,0 @@
1
- flet>=0.70.0.dev0