flet-charts 0.2.0.dev506__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.dev506/src/flet_charts.egg-info → flet_charts-0.2.0.dev534}/PKG-INFO +6 -16
  2. {flet_charts-0.2.0.dev506 → flet_charts-0.2.0.dev534}/README.md +4 -14
  3. {flet_charts-0.2.0.dev506 → flet_charts-0.2.0.dev534}/pyproject.toml +3 -2
  4. {flet_charts-0.2.0.dev506 → flet_charts-0.2.0.dev534}/src/flet_charts/__init__.py +9 -1
  5. {flet_charts-0.2.0.dev506 → flet_charts-0.2.0.dev534}/src/flet_charts/bar_chart.py +1 -3
  6. {flet_charts-0.2.0.dev506 → flet_charts-0.2.0.dev534}/src/flet_charts/line_chart.py +1 -3
  7. flet_charts-0.2.0.dev534/src/flet_charts/matplotlib_backends/backend_flet_agg.py +16 -0
  8. flet_charts-0.2.0.dev534/src/flet_charts/matplotlib_chart.py +382 -0
  9. flet_charts-0.2.0.dev534/src/flet_charts/matplotlib_chart_with_toolbar.py +110 -0
  10. {flet_charts-0.2.0.dev506 → flet_charts-0.2.0.dev534}/src/flet_charts/pie_chart.py +1 -3
  11. {flet_charts-0.2.0.dev506 → flet_charts-0.2.0.dev534}/src/flet_charts/plotly_chart.py +3 -2
  12. {flet_charts-0.2.0.dev506 → flet_charts-0.2.0.dev534}/src/flet_charts/scatter_chart.py +1 -1
  13. {flet_charts-0.2.0.dev506 → flet_charts-0.2.0.dev534/src/flet_charts.egg-info}/PKG-INFO +6 -16
  14. {flet_charts-0.2.0.dev506 → flet_charts-0.2.0.dev534}/src/flet_charts.egg-info/SOURCES.txt +2 -0
  15. flet_charts-0.2.0.dev534/src/flet_charts.egg-info/requires.txt +1 -0
  16. {flet_charts-0.2.0.dev506 → flet_charts-0.2.0.dev534}/src/flutter/flet_charts/pubspec.lock +3 -3
  17. flet_charts-0.2.0.dev506/src/flet_charts/matplotlib_chart.py +0 -65
  18. flet_charts-0.2.0.dev506/src/flet_charts.egg-info/requires.txt +0 -1
  19. {flet_charts-0.2.0.dev506 → flet_charts-0.2.0.dev534}/LICENSE +0 -0
  20. {flet_charts-0.2.0.dev506 → flet_charts-0.2.0.dev534}/setup.cfg +0 -0
  21. {flet_charts-0.2.0.dev506 → flet_charts-0.2.0.dev534}/src/flet_charts/bar_chart_group.py +0 -0
  22. {flet_charts-0.2.0.dev506 → flet_charts-0.2.0.dev534}/src/flet_charts/bar_chart_rod.py +0 -0
  23. {flet_charts-0.2.0.dev506 → flet_charts-0.2.0.dev534}/src/flet_charts/bar_chart_rod_stack_item.py +0 -0
  24. {flet_charts-0.2.0.dev506 → flet_charts-0.2.0.dev534}/src/flet_charts/chart_axis.py +0 -0
  25. {flet_charts-0.2.0.dev506 → flet_charts-0.2.0.dev534}/src/flet_charts/line_chart_data.py +0 -0
  26. {flet_charts-0.2.0.dev506 → flet_charts-0.2.0.dev534}/src/flet_charts/line_chart_data_point.py +0 -0
  27. {flet_charts-0.2.0.dev506 → flet_charts-0.2.0.dev534}/src/flet_charts/pie_chart_section.py +0 -0
  28. {flet_charts-0.2.0.dev506 → flet_charts-0.2.0.dev534}/src/flet_charts/scatter_chart_spot.py +0 -0
  29. {flet_charts-0.2.0.dev506 → flet_charts-0.2.0.dev534}/src/flet_charts/types.py +0 -0
  30. {flet_charts-0.2.0.dev506 → flet_charts-0.2.0.dev534}/src/flet_charts.egg-info/dependency_links.txt +0 -0
  31. {flet_charts-0.2.0.dev506 → flet_charts-0.2.0.dev534}/src/flet_charts.egg-info/top_level.txt +0 -0
  32. {flet_charts-0.2.0.dev506 → flet_charts-0.2.0.dev534}/src/flutter/flet_charts/CHANGELOG.md +0 -0
  33. {flet_charts-0.2.0.dev506 → flet_charts-0.2.0.dev534}/src/flutter/flet_charts/LICENSE +0 -0
  34. {flet_charts-0.2.0.dev506 → flet_charts-0.2.0.dev534}/src/flutter/flet_charts/README.md +0 -0
  35. {flet_charts-0.2.0.dev506 → flet_charts-0.2.0.dev534}/src/flutter/flet_charts/analysis_options.yaml +0 -0
  36. {flet_charts-0.2.0.dev506 → flet_charts-0.2.0.dev534}/src/flutter/flet_charts/lib/flet_charts.dart +0 -0
  37. {flet_charts-0.2.0.dev506 → flet_charts-0.2.0.dev534}/src/flutter/flet_charts/lib/src/bar_chart.dart +0 -0
  38. {flet_charts-0.2.0.dev506 → flet_charts-0.2.0.dev534}/src/flutter/flet_charts/lib/src/extension.dart +0 -0
  39. {flet_charts-0.2.0.dev506 → flet_charts-0.2.0.dev534}/src/flutter/flet_charts/lib/src/line_chart.dart +0 -0
  40. {flet_charts-0.2.0.dev506 → flet_charts-0.2.0.dev534}/src/flutter/flet_charts/lib/src/pie_chart.dart +0 -0
  41. {flet_charts-0.2.0.dev506 → flet_charts-0.2.0.dev534}/src/flutter/flet_charts/lib/src/scatter_chart.dart +0 -0
  42. {flet_charts-0.2.0.dev506 → flet_charts-0.2.0.dev534}/src/flutter/flet_charts/lib/src/utils/bar_chart.dart +0 -0
  43. {flet_charts-0.2.0.dev506 → flet_charts-0.2.0.dev534}/src/flutter/flet_charts/lib/src/utils/charts.dart +0 -0
  44. {flet_charts-0.2.0.dev506 → flet_charts-0.2.0.dev534}/src/flutter/flet_charts/lib/src/utils/line_chart.dart +0 -0
  45. {flet_charts-0.2.0.dev506 → flet_charts-0.2.0.dev534}/src/flutter/flet_charts/lib/src/utils/pie_chart.dart +0 -0
  46. {flet_charts-0.2.0.dev506 → flet_charts-0.2.0.dev534}/src/flutter/flet_charts/lib/src/utils/scatter_chart.dart +0 -0
  47. {flet_charts-0.2.0.dev506 → 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.dev506
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
@@ -26,20 +26,15 @@ It is based on the [fl_chart](https://pub.dev/packages/fl_chart) Flutter package
26
26
 
27
27
  ## Documentation
28
28
 
29
- Detailed documentation to this package can be found [here](https://flet-dev.github.io/flet-charts/).
29
+ Detailed documentation to this package can be found [here](https://flet-charts.docs.flet.dev/).
30
30
 
31
31
  ## Platform Support
32
32
 
33
33
  This package supports the following platforms:
34
34
 
35
- | Platform | Supported |
36
- |----------|:---------:|
37
- | Windows | |
38
- | macOS | ✅ |
39
- | Linux | ✅ |
40
- | iOS | ✅ |
41
- | Android | ✅ |
42
- | Web | ✅ |
35
+ | Platform | Windows | macOS | Linux | iOS | Android | Web |
36
+ |----------|---------|-------|-------|-----|---------|-----|
37
+ | Supported| ✅ | ✅ | ✅ | | | ✅ |
43
38
 
44
39
  ## Usage
45
40
 
@@ -58,11 +53,6 @@ To install the `flet-charts` package and add it to your project dependencies:
58
53
  ```
59
54
  After this, you will have to manually add this package to your `requirements.txt` or `pyproject.toml`.
60
55
 
61
- - Using `poetry`:
62
- ```bash
63
- poetry add flet-charts
64
- ```
65
-
66
56
  ### Examples
67
57
 
68
58
  For examples, see [these](./examples).
@@ -10,20 +10,15 @@ It is based on the [fl_chart](https://pub.dev/packages/fl_chart) Flutter package
10
10
 
11
11
  ## Documentation
12
12
 
13
- Detailed documentation to this package can be found [here](https://flet-dev.github.io/flet-charts/).
13
+ Detailed documentation to this package can be found [here](https://flet-charts.docs.flet.dev/).
14
14
 
15
15
  ## Platform Support
16
16
 
17
17
  This package supports the following platforms:
18
18
 
19
- | Platform | Supported |
20
- |----------|:---------:|
21
- | Windows | |
22
- | macOS | ✅ |
23
- | Linux | ✅ |
24
- | iOS | ✅ |
25
- | Android | ✅ |
26
- | Web | ✅ |
19
+ | Platform | Windows | macOS | Linux | iOS | Android | Web |
20
+ |----------|---------|-------|-------|-----|---------|-----|
21
+ | Supported| ✅ | ✅ | ✅ | | | ✅ |
27
22
 
28
23
  ## Usage
29
24
 
@@ -42,11 +37,6 @@ To install the `flet-charts` package and add it to your project dependencies:
42
37
  ```
43
38
  After this, you will have to manually add this package to your `requirements.txt` or `pyproject.toml`.
44
39
 
45
- - Using `poetry`:
46
- ```bash
47
- poetry add flet-charts
48
- ```
49
-
50
40
  ### Examples
51
41
 
52
42
  For examples, see [these](./examples).
@@ -1,13 +1,13 @@
1
1
  [project]
2
2
  name = "flet-charts"
3
- version = "0.2.0.dev506"
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]
@@ -53,6 +53,7 @@ docs = [
53
53
  "pydocstyle >=6.3.0",
54
54
  "linkcheckmd >=1.4.0",
55
55
  "mkdocs-external-images",
56
+ "mkdocs-macros-plugin>=1.4.0",
56
57
  { include-group = 'docs-coverage' },
57
58
  ]
58
59
  all = [
@@ -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
  ]
@@ -164,11 +164,9 @@ class BarChartEvent(ft.Event["BarChart"]):
164
164
 
165
165
 
166
166
  @ft.control("BarChart")
167
- class BarChart(ft.ConstrainedControl):
167
+ class BarChart(ft.LayoutControl):
168
168
  """
169
169
  Draws a bar chart.
170
-
171
- ![Overview](assets/bar-chart-diagram.svg)
172
170
  """
173
171
 
174
172
  groups: list[BarChartGroup] = field(default_factory=list)
@@ -165,11 +165,9 @@ class LineChartTooltip:
165
165
 
166
166
 
167
167
  @ft.control("LineChart")
168
- class LineChart(ft.ConstrainedControl):
168
+ class LineChart(ft.LayoutControl):
169
169
  """
170
170
  Draws a line chart.
171
-
172
- ![Overview](assets/line-chart-diagram.svg)
173
171
  """
174
172
 
175
173
  data_series: list[LineChartData] = field(default_factory=list)
@@ -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)
@@ -33,11 +33,9 @@ class PieChartEvent(ft.Event["PieChart"]):
33
33
 
34
34
 
35
35
  @ft.control("PieChart")
36
- class PieChart(ft.ConstrainedControl):
36
+ class PieChart(ft.LayoutControl):
37
37
  """
38
38
  A pie chart control displaying multiple sections as slices of a circle.
39
-
40
- ![Overview](assets/pie-chart-diagram.svg)
41
39
  """
42
40
 
43
41
  sections: list[PieChartSection] = field(default_factory=list)
@@ -28,8 +28,9 @@ class PlotlyChart(ft.Container):
28
28
 
29
29
  figure: Figure = field(metadata={"skip": True})
30
30
  """
31
- Plotly figure to draw -
32
- an instance of [`plotly.graph_objects.Figure`](https://plotly.com/python-api-reference/generated/plotly.graph_objects.Figure.html).
31
+ Plotly figure to draw.
32
+
33
+ The value is an instance of [`plotly.graph_objects.Figure`](https://plotly.com/python-api-reference/generated/plotly.graph_objects.Figure.html).
33
34
  """
34
35
 
35
36
  original_size: bool = False
@@ -121,7 +121,7 @@ class ScatterChartEvent(ft.Event["ScatterChart"]):
121
121
 
122
122
 
123
123
  @ft.control("ScatterChart")
124
- class ScatterChart(ft.ConstrainedControl):
124
+ class ScatterChart(ft.LayoutControl):
125
125
  """
126
126
  A scatter chart control.
127
127
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: flet-charts
3
- Version: 0.2.0.dev506
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
@@ -26,20 +26,15 @@ It is based on the [fl_chart](https://pub.dev/packages/fl_chart) Flutter package
26
26
 
27
27
  ## Documentation
28
28
 
29
- Detailed documentation to this package can be found [here](https://flet-dev.github.io/flet-charts/).
29
+ Detailed documentation to this package can be found [here](https://flet-charts.docs.flet.dev/).
30
30
 
31
31
  ## Platform Support
32
32
 
33
33
  This package supports the following platforms:
34
34
 
35
- | Platform | Supported |
36
- |----------|:---------:|
37
- | Windows | |
38
- | macOS | ✅ |
39
- | Linux | ✅ |
40
- | iOS | ✅ |
41
- | Android | ✅ |
42
- | Web | ✅ |
35
+ | Platform | Windows | macOS | Linux | iOS | Android | Web |
36
+ |----------|---------|-------|-------|-----|---------|-----|
37
+ | Supported| ✅ | ✅ | ✅ | | | ✅ |
43
38
 
44
39
  ## Usage
45
40
 
@@ -58,11 +53,6 @@ To install the `flet-charts` package and add it to your project dependencies:
58
53
  ```
59
54
  After this, you will have to manually add this package to your `requirements.txt` or `pyproject.toml`.
60
55
 
61
- - Using `poetry`:
62
- ```bash
63
- poetry add flet-charts
64
- ```
65
-
66
56
  ### Examples
67
57
 
68
58
  For examples, see [these](./examples).
@@ -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"
@@ -506,10 +506,10 @@ packages:
506
506
  dependency: transitive
507
507
  description:
508
508
  name: shared_preferences_android
509
- sha256: a2608114b1ffdcbc9c120eb71a0e207c71da56202852d4aab8a5e30a82269e74
509
+ sha256: bd14436108211b0d4ee5038689a56d4ae3620fd72fd6036e113bf1345bc74d9e
510
510
  url: "https://pub.dev"
511
511
  source: hosted
512
- version: "2.4.12"
512
+ version: "2.4.13"
513
513
  shared_preferences_foundation:
514
514
  dependency: transitive
515
515
  description:
@@ -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