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.
- {flet_charts-0.2.0.dev524/src/flet_charts.egg-info → flet_charts-0.2.0.dev534}/PKG-INFO +2 -2
- {flet_charts-0.2.0.dev524 → flet_charts-0.2.0.dev534}/pyproject.toml +2 -2
- {flet_charts-0.2.0.dev524 → flet_charts-0.2.0.dev534}/src/flet_charts/__init__.py +9 -1
- flet_charts-0.2.0.dev534/src/flet_charts/matplotlib_backends/backend_flet_agg.py +16 -0
- flet_charts-0.2.0.dev534/src/flet_charts/matplotlib_chart.py +382 -0
- flet_charts-0.2.0.dev534/src/flet_charts/matplotlib_chart_with_toolbar.py +110 -0
- {flet_charts-0.2.0.dev524 → flet_charts-0.2.0.dev534/src/flet_charts.egg-info}/PKG-INFO +2 -2
- {flet_charts-0.2.0.dev524 → flet_charts-0.2.0.dev534}/src/flet_charts.egg-info/SOURCES.txt +2 -0
- flet_charts-0.2.0.dev534/src/flet_charts.egg-info/requires.txt +1 -0
- {flet_charts-0.2.0.dev524 → flet_charts-0.2.0.dev534}/src/flutter/flet_charts/pubspec.lock +1 -1
- flet_charts-0.2.0.dev524/src/flet_charts/matplotlib_chart.py +0 -65
- flet_charts-0.2.0.dev524/src/flet_charts.egg-info/requires.txt +0 -1
- {flet_charts-0.2.0.dev524 → flet_charts-0.2.0.dev534}/LICENSE +0 -0
- {flet_charts-0.2.0.dev524 → flet_charts-0.2.0.dev534}/README.md +0 -0
- {flet_charts-0.2.0.dev524 → flet_charts-0.2.0.dev534}/setup.cfg +0 -0
- {flet_charts-0.2.0.dev524 → flet_charts-0.2.0.dev534}/src/flet_charts/bar_chart.py +0 -0
- {flet_charts-0.2.0.dev524 → flet_charts-0.2.0.dev534}/src/flet_charts/bar_chart_group.py +0 -0
- {flet_charts-0.2.0.dev524 → flet_charts-0.2.0.dev534}/src/flet_charts/bar_chart_rod.py +0 -0
- {flet_charts-0.2.0.dev524 → flet_charts-0.2.0.dev534}/src/flet_charts/bar_chart_rod_stack_item.py +0 -0
- {flet_charts-0.2.0.dev524 → flet_charts-0.2.0.dev534}/src/flet_charts/chart_axis.py +0 -0
- {flet_charts-0.2.0.dev524 → flet_charts-0.2.0.dev534}/src/flet_charts/line_chart.py +0 -0
- {flet_charts-0.2.0.dev524 → flet_charts-0.2.0.dev534}/src/flet_charts/line_chart_data.py +0 -0
- {flet_charts-0.2.0.dev524 → flet_charts-0.2.0.dev534}/src/flet_charts/line_chart_data_point.py +0 -0
- {flet_charts-0.2.0.dev524 → flet_charts-0.2.0.dev534}/src/flet_charts/pie_chart.py +0 -0
- {flet_charts-0.2.0.dev524 → flet_charts-0.2.0.dev534}/src/flet_charts/pie_chart_section.py +0 -0
- {flet_charts-0.2.0.dev524 → flet_charts-0.2.0.dev534}/src/flet_charts/plotly_chart.py +0 -0
- {flet_charts-0.2.0.dev524 → flet_charts-0.2.0.dev534}/src/flet_charts/scatter_chart.py +0 -0
- {flet_charts-0.2.0.dev524 → flet_charts-0.2.0.dev534}/src/flet_charts/scatter_chart_spot.py +0 -0
- {flet_charts-0.2.0.dev524 → flet_charts-0.2.0.dev534}/src/flet_charts/types.py +0 -0
- {flet_charts-0.2.0.dev524 → flet_charts-0.2.0.dev534}/src/flet_charts.egg-info/dependency_links.txt +0 -0
- {flet_charts-0.2.0.dev524 → flet_charts-0.2.0.dev534}/src/flet_charts.egg-info/top_level.txt +0 -0
- {flet_charts-0.2.0.dev524 → flet_charts-0.2.0.dev534}/src/flutter/flet_charts/CHANGELOG.md +0 -0
- {flet_charts-0.2.0.dev524 → flet_charts-0.2.0.dev534}/src/flutter/flet_charts/LICENSE +0 -0
- {flet_charts-0.2.0.dev524 → flet_charts-0.2.0.dev534}/src/flutter/flet_charts/README.md +0 -0
- {flet_charts-0.2.0.dev524 → flet_charts-0.2.0.dev534}/src/flutter/flet_charts/analysis_options.yaml +0 -0
- {flet_charts-0.2.0.dev524 → flet_charts-0.2.0.dev534}/src/flutter/flet_charts/lib/flet_charts.dart +0 -0
- {flet_charts-0.2.0.dev524 → flet_charts-0.2.0.dev534}/src/flutter/flet_charts/lib/src/bar_chart.dart +0 -0
- {flet_charts-0.2.0.dev524 → flet_charts-0.2.0.dev534}/src/flutter/flet_charts/lib/src/extension.dart +0 -0
- {flet_charts-0.2.0.dev524 → flet_charts-0.2.0.dev534}/src/flutter/flet_charts/lib/src/line_chart.dart +0 -0
- {flet_charts-0.2.0.dev524 → flet_charts-0.2.0.dev534}/src/flutter/flet_charts/lib/src/pie_chart.dart +0 -0
- {flet_charts-0.2.0.dev524 → flet_charts-0.2.0.dev534}/src/flutter/flet_charts/lib/src/scatter_chart.dart +0 -0
- {flet_charts-0.2.0.dev524 → flet_charts-0.2.0.dev534}/src/flutter/flet_charts/lib/src/utils/bar_chart.dart +0 -0
- {flet_charts-0.2.0.dev524 → flet_charts-0.2.0.dev534}/src/flutter/flet_charts/lib/src/utils/charts.dart +0 -0
- {flet_charts-0.2.0.dev524 → flet_charts-0.2.0.dev534}/src/flutter/flet_charts/lib/src/utils/line_chart.dart +0 -0
- {flet_charts-0.2.0.dev524 → flet_charts-0.2.0.dev534}/src/flutter/flet_charts/lib/src/utils/pie_chart.dart +0 -0
- {flet_charts-0.2.0.dev524 → flet_charts-0.2.0.dev534}/src/flutter/flet_charts/lib/src/utils/scatter_chart.dart +0 -0
- {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.
|
|
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
|
|
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.
|
|
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
|
|
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
|
|
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.
|
|
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
|
|
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
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
flet
|
|
@@ -142,7 +142,7 @@ packages:
|
|
|
142
142
|
description:
|
|
143
143
|
path: "packages/flet"
|
|
144
144
|
ref: main
|
|
145
|
-
resolved-ref:
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{flet_charts-0.2.0.dev524 → flet_charts-0.2.0.dev534}/src/flet_charts/bar_chart_rod_stack_item.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{flet_charts-0.2.0.dev524 → flet_charts-0.2.0.dev534}/src/flet_charts/line_chart_data_point.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{flet_charts-0.2.0.dev524 → flet_charts-0.2.0.dev534}/src/flet_charts.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
{flet_charts-0.2.0.dev524 → flet_charts-0.2.0.dev534}/src/flet_charts.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{flet_charts-0.2.0.dev524 → flet_charts-0.2.0.dev534}/src/flutter/flet_charts/analysis_options.yaml
RENAMED
|
File without changes
|
{flet_charts-0.2.0.dev524 → flet_charts-0.2.0.dev534}/src/flutter/flet_charts/lib/flet_charts.dart
RENAMED
|
File without changes
|
{flet_charts-0.2.0.dev524 → flet_charts-0.2.0.dev534}/src/flutter/flet_charts/lib/src/bar_chart.dart
RENAMED
|
File without changes
|
{flet_charts-0.2.0.dev524 → flet_charts-0.2.0.dev534}/src/flutter/flet_charts/lib/src/extension.dart
RENAMED
|
File without changes
|
|
File without changes
|
{flet_charts-0.2.0.dev524 → flet_charts-0.2.0.dev534}/src/flutter/flet_charts/lib/src/pie_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
|