uplot-python 0.0.1__py3-none-any.whl → 1.1.0__py3-none-any.whl

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.
uplot/__init__.py CHANGED
@@ -2,13 +2,15 @@
2
2
  # -*- coding: utf-8 -*-
3
3
  #
4
4
  # SPDX-License-Identifier: Apache-2.0
5
- # Copyright 2022 Stéphane Caron
6
- # Copyright 2023-2024 Inria
7
5
 
8
- """Plot Python iterables with µPlot."""
6
+ """Python wrapper for μPlot time series."""
9
7
 
10
- __version__ = "0.0.1"
8
+ __version__ = "1.1.0"
11
9
 
12
10
  from .plot import plot
11
+ from .plot2 import plot2
13
12
 
14
- __all__ = ["plot"]
13
+ __all__ = [
14
+ "plot",
15
+ "plot2",
16
+ ]
uplot/color_picker.py CHANGED
@@ -2,8 +2,6 @@
2
2
  # -*- coding: utf-8 -*-
3
3
  #
4
4
  # SPDX-License-Identifier: Apache-2.0
5
- # Copyright 2022 Stéphane Caron
6
- # Copyright 2023-2024 Inria
7
5
 
8
6
  """Pick plot colors from a circular list."""
9
7
 
uplot/exceptions.py CHANGED
@@ -2,8 +2,6 @@
2
2
  # -*- coding: utf-8 -*-
3
3
  #
4
4
  # SPDX-License-Identifier: Apache-2.0
5
- # Copyright 2022 Stéphane Caron
6
- # Copyright 2024 Inria
7
5
 
8
6
  """Project exceptions."""
9
7
 
uplot/generate_html.py CHANGED
@@ -2,78 +2,54 @@
2
2
  # -*- coding: utf-8 -*-
3
3
  #
4
4
  # SPDX-License-Identifier: Apache-2.0
5
- # Copyright 2022 Stéphane Caron
6
- # Copyright 2023 Inria
7
5
 
8
6
  """Generate an HTML page containing the output plot."""
9
7
 
8
+ import json
9
+ from datetime import datetime
10
10
  from importlib import resources
11
- from math import isnan
12
- from typing import Dict, Iterable, List
11
+ from typing import List
13
12
 
14
13
  import numpy as np
15
- from numpy.typing import NDArray
16
14
 
17
- from .color_picker import ColorPicker
15
+ from .utils import array2string, js
18
16
 
19
17
 
20
- def __ensure_floats(series: Iterable) -> List[float]:
21
- return [float(x) for x in series]
22
-
23
-
24
- def __escape_null(series: Iterable) -> str:
25
- """Escape undefined values in a series.
18
+ def generate_html(opts: dict, data: List[np.ndarray], resize: bool) -> str:
19
+ """Generate plot in an HTML page.
26
20
 
27
21
  Args:
28
- series: Series to filter.
29
-
30
- Returns:
31
- String representation of the series.
32
- """
33
- return (
34
- "["
35
- + ", ".join(
36
- map(
37
- lambda x: (
38
- str(int(x))
39
- if isinstance(x, bool)
40
- else (
41
- str(x)
42
- if isinstance(x, (int, float)) and not isnan(x)
43
- else x if isinstance(x, str) else "null"
44
- )
45
- ),
46
- series,
47
- )
48
- )
49
- + "]"
50
- )
51
-
52
-
53
- def generate_html(
54
- opts: dict,
55
- data: List[Iterable[float, int]],
56
- ) -> str:
57
- """Generate plot in an HTML page.
22
+ opts: uPlot option dictionary.
23
+ data: List of NumPy arrays, one for each series in the plot.
24
+ resize: If set, add a resize event listener to fit the window.
58
25
 
59
26
  Returns:
60
27
  HTML contents of the page.
61
28
  """
62
- with resources.path("foxplot.uplot", "uPlot.min.css") as path:
29
+ with resources.path("uplot.static", "uPlot.min.css") as path:
63
30
  uplot_min_css = path
64
- with resources.path("foxplot.uplot", "uPlot.iife.js") as path:
31
+ with resources.path("uplot.static", "uPlot.iife.js") as path:
65
32
  uplot_iife_js = path
66
- with resources.path("foxplot.uplot", "uPlot.mousewheel.js") as path:
33
+ with resources.path("uplot.static", "uPlot.mousewheel.js") as path:
67
34
  uplot_mwheel_js = path
68
35
 
69
- color_picker = ColorPicker()
70
- right_axis_label = f" {right_axis_unit}" if right_axis_unit else ""
71
- left_labels = list(left_axis.keys())
72
- right_labels = list(right_axis.keys())
73
- labels = left_labels + [r for r in right_labels if r not in left_labels]
74
- series_from_label = {}
75
- series_from_label.update(left_axis)
76
- series_from_label.update(right_axis)
36
+ date = datetime.now().strftime("%Y%m%d-%H%M%S")
37
+ title = opts.get("title", f"Plot from {date}")
38
+
39
+ data_string = ""
40
+ for array in data:
41
+ data_string += f"""
42
+ {array2string(array)},"""
43
+
44
+ if "class" not in opts:
45
+ opts["class"] = "uplot-chart"
46
+ if resize:
47
+ opts["width"] = js("window.innerWidth - 20")
48
+ opts["height"] = js("window.innerHeight - 150")
49
+ opts_string = json.dumps(opts)
50
+ opts_string = opts_string.replace('"<script>', "")
51
+ opts_string = opts_string.replace('</script>"', "")
52
+
77
53
  html = f"""<!DOCTYPE html>
78
54
  <html lang="en">
79
55
  <head>
@@ -82,7 +58,7 @@ def generate_html(
82
58
  <meta name="viewport" content="width=device-width, initial-scale=1">
83
59
  <link rel="stylesheet" href="{uplot_min_css}">
84
60
  <style>
85
- div.my-chart {{
61
+ div.uplot-chart {{
86
62
  background-color: white;
87
63
  padding: 10px 0px; // appear in Right Click -> Take Screenshot
88
64
  }}
@@ -94,26 +70,22 @@ def generate_html(
94
70
  <script>
95
71
  const {{ linear, stepped, bars, spline, spline2 }} = uPlot.paths;
96
72
 
97
- let data = ["""
98
- for label in data.keys():
99
- html += f"""
100
- {__escape_null(data[label])},"""
101
- html += """
73
+ let data = [{data_string}
102
74
  ];
103
75
 
104
- const lineInterpolations = {
76
+ const lineInterpolations = {{
105
77
  linear: 0,
106
78
  stepAfter: 1,
107
79
  stepBefore: 2,
108
80
  spline: 3,
109
- };
81
+ }};
110
82
 
111
- const _stepBefore = stepped({align: -1});
112
- const _stepAfter = stepped({align: 1});
83
+ const _stepBefore = stepped({{align: -1}});
84
+ const _stepAfter = stepped({{align: 1}});
113
85
  const _linear = linear();
114
86
  const _spline = spline();
115
87
 
116
- function paths(u, seriesIdx, idx0, idx1, extendGap, buildClip) {
88
+ function paths(u, seriesIdx, idx0, idx1, extendGap, buildClip) {{
117
89
  let s = u.series[seriesIdx];
118
90
  let interp = s.lineInterpolation;
119
91
 
@@ -128,100 +100,20 @@ def generate_html(
128
100
  return renderer(
129
101
  u, seriesIdx, idx0, idx1, extendGap, buildClip
130
102
  );
131
- }
132
-
133
- let opts = {"""
134
- html += f"""
135
- title: "{title}","""
136
- html += """
137
- id: "chart1",
138
- class: "my-chart",
139
- width: window.innerWidth - 20,
140
- height: window.innerHeight - 150,
141
- cursor: {
142
- drag: {
143
- x: true,
144
- y: true,
145
- uni: 50,
146
- }
147
- },
148
- plugins: [
149
- wheelZoomPlugin({factor: 0.75})
150
- ],"""
151
- html += f"""
152
- scales: {{
153
- x: {{
154
- time: {"true" if timestamped else "false"},
155
- }},
156
- }},
157
- series: ["""
158
- html += f"""
159
- {{
160
- value: (self, rawValue) => Number.parseFloat(rawValue -
161
- {times[0]}).toPrecision(4),
162
- }},"""
163
- for label in labels:
164
- html += f"""
165
- {{
166
- // initial toggled state (optional)
167
- show: true,
168
- spanGaps: false,
169
-
170
- // in-legend display
171
- label: "{label}","""
172
- if label in right_labels:
173
- html += f"""
174
- value: (self, rawValue) =>
175
- Number.parseFloat(rawValue).toPrecision(2) +
176
- "{right_axis_label}",
177
- scale: "{right_axis_unit}","""
178
- else: # label in left_labels
179
- html += f"""
180
- value: (self, rawValue) =>
181
- Number.parseFloat(rawValue).toPrecision(2) +
182
- "{left_axis_label}","""
183
- html += f"""
184
- // series style
185
- stroke: "{color_picker.get_next_color()}",
186
- width: 2 / devicePixelRatio,
187
- lineInterpolation: lineInterpolations.stepAfter,
188
- paths,
189
- }},"""
190
- html += """
191
- ],
192
- axes: [
193
- {},
194
- {"""
195
- html += f"""
196
- size: {60 + 10 * len({left_axis_label})},
197
- values: (u, vals, space) => vals.map(
198
- v => v + "{left_axis_label}"
199
- ),"""
200
- html += """
201
- },
202
- {
203
- side: 1,"""
204
- html += f"""
205
- scale: "{right_axis_unit}",
206
- size: {60 + 10 * len({right_axis_label})},
207
- values: (u, vals, space) => vals.map(
208
- v => v + "{right_axis_label}"
209
- ),"""
210
- html += """
211
- grid: {show: false},
212
- },
213
- ],
214
- };
103
+ }}
215
104
 
216
- let uplot = new uPlot(opts, data, document.body);
105
+ let opts = {opts_string};
106
+ let uplot = new uPlot(opts, data, document.body);"""
107
+ if resize:
108
+ html += """
217
109
 
218
- // resize with window
219
110
  window.addEventListener("resize", e => {
220
111
  uplot.setSize({
221
112
  width: window.innerWidth - 20,
222
113
  height: window.innerHeight - 150,
223
114
  });
224
- });
115
+ });"""
116
+ html += """
225
117
  </script>
226
118
  </body>
227
119
  </html>"""
uplot/plot.py CHANGED
@@ -2,35 +2,25 @@
2
2
  # -*- coding: utf-8 -*-
3
3
  #
4
4
  # SPDX-License-Identifier: Apache-2.0
5
- # Copyright 2024 Inria
6
5
 
7
- """Main class to manipulate dictionary-series data."""
6
+ """Main plot function."""
8
7
 
9
- import logging
10
- import sys
11
8
  import webbrowser
12
- from typing import BinaryIO, Dict, List, Optional, TextIO, Union
9
+ from typing import List
13
10
 
14
11
  import numpy as np
15
- from numpy.typing import NDArray
16
12
 
17
13
  from .generate_html import generate_html
18
- from .write_tmpfile import write_tmpfile
14
+ from .write_html_tempfile import write_html_tempfile
19
15
 
20
16
 
21
- def plot(
22
- self,
23
- opts: dict,
24
- data: List[Iterable[float, int]],
25
- ) -> None:
26
- html = generate_html(
27
- times,
28
- left_series,
29
- right_series,
30
- title,
31
- left_axis_unit,
32
- right_axis_unit,
33
- timestamped=self.__time is not None,
34
- )
35
- filename = write_tmpfile(html)
17
+ def plot(opts: dict, data: List[np.ndarray]) -> None:
18
+ """Plot function with the same API as uPlot's `plot`.
19
+
20
+ Args:
21
+ opts: Options dictionary, as expected by uPlot.
22
+ data: List of series, as expected by uPlot.
23
+ """
24
+ html = generate_html(opts, data, resize=False)
25
+ filename = write_html_tempfile(html)
36
26
  webbrowser.open_new_tab(filename)
uplot/plot2.py ADDED
@@ -0,0 +1,166 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ #
4
+ # SPDX-License-Identifier: Apache-2.0
5
+
6
+ """Additional plot function."""
7
+
8
+ import webbrowser
9
+ from typing import List, Optional
10
+
11
+ import numpy as np
12
+
13
+ from .color_picker import ColorPicker
14
+ from .exceptions import UplotException
15
+ from .generate_html import generate_html
16
+ from .utils import js
17
+ from .write_html_tempfile import write_html_tempfile
18
+
19
+
20
+ def prepare_data(x, left, right):
21
+ """Combine x, left-axis, and right-axis arrays into a flat data list."""
22
+ if isinstance(left, np.ndarray) and left.ndim == 1:
23
+ left = [left]
24
+ data = [x, *left]
25
+ if right:
26
+ data.extend(right)
27
+ return data
28
+
29
+
30
+ def add_default_options(opts: dict) -> None:
31
+ """Populate cursor drag and wheel-zoom plugin defaults when absent."""
32
+ if "cursor" not in opts:
33
+ opts["cursor"] = {
34
+ "drag": {
35
+ "x": True,
36
+ "y": True,
37
+ "uni": 50,
38
+ }
39
+ }
40
+ if "plugins" not in opts:
41
+ opts["plugins"] = [
42
+ js("wheelZoomPlugin({factor: 0.75})"),
43
+ ]
44
+
45
+
46
+ def add_series(
47
+ opts: dict,
48
+ data: List[np.ndarray],
49
+ nb_left: int,
50
+ left_labels: Optional[List[str]],
51
+ right_labels: Optional[List[str]],
52
+ ) -> None:
53
+ """Build the uPlot series list and attach it to opts."""
54
+ if left_labels is not None and len(left_labels) < nb_left:
55
+ raise UplotException(
56
+ f"Not enough labels in left_labels ({len(left_labels)}) "
57
+ f"to label all {nb_left} left-axis series"
58
+ )
59
+
60
+ opts["series"] = [{}]
61
+ color_picker = ColorPicker()
62
+ for i, series in enumerate(data[1:]):
63
+ new_series = {
64
+ "show": True,
65
+ "spanGaps": False,
66
+ "stroke": color_picker.get_next_color(),
67
+ "width": js("2 / devicePixelRatio"),
68
+ }
69
+
70
+ def find_in_lists(
71
+ i: int,
72
+ left_list: Optional[List[str]],
73
+ right_list: Optional[List[str]],
74
+ ) -> Optional[str]:
75
+ return None
76
+
77
+ if left_labels is not None and i < nb_left:
78
+ new_series["label"] = left_labels[i]
79
+ if i >= nb_left:
80
+ if right_labels is not None:
81
+ new_series["label"] = right_labels[i - nb_left]
82
+ new_series["scale"] = "right_axis"
83
+
84
+ # scale = find_in_lists(i, left_scales, right_scales)
85
+ # if scale is not None:
86
+ # new_series["scale"] = scale
87
+
88
+ new_series["value"] = js(
89
+ "(self, rawValue) => Number.parseFloat(rawValue).toPrecision(2)"
90
+ )
91
+
92
+ # Last, we hack the wrapper to append `paths` after "lineInterpolation"
93
+ new_series["lineInterpolation"] = js(
94
+ "lineInterpolations.stepAfter, paths"
95
+ )
96
+ opts["series"].append(new_series)
97
+
98
+
99
+ def add_axes(opts: dict) -> None:
100
+ """Add default left and right axis definitions to opts."""
101
+ opts["axes"] = [
102
+ {},
103
+ {
104
+ "size": 60,
105
+ },
106
+ {
107
+ "side": 1,
108
+ "scale": "right_axis",
109
+ "size": 60,
110
+ "grid": {"show": False},
111
+ },
112
+ ]
113
+
114
+
115
+ def plot2(
116
+ x: np.ndarray,
117
+ left: List[np.ndarray],
118
+ right: Optional[List[np.ndarray]] = None,
119
+ resize: bool = True,
120
+ title: Optional[str] = None,
121
+ timestamped: bool = False,
122
+ width: Optional[int] = None,
123
+ height: Optional[int] = None,
124
+ left_labels: Optional[List[str]] = None,
125
+ right_labels: Optional[List[str]] = None,
126
+ **kwargs,
127
+ ) -> None:
128
+ """Plot function with additional defaults and parameters.
129
+
130
+ Args:
131
+ x: Values for the x-axis.
132
+ left: Values for the left y-axis.
133
+ right: Values for the (optional) right y-axis.
134
+ resize: If set (default), scale plot to page width and height.
135
+ title: Plot title.
136
+ timestamped: If set, x-axis values are treated as timestamps.
137
+ width: Plot width in pixels.
138
+ height: Plot height in pixels.
139
+ left_labels: Labels for the left-axis series, in order.
140
+ right_labels: Labels for the right-axis series, in order.
141
+ kwargs: Other keyword arguments are forward to uPlot as options.
142
+ """
143
+ data = prepare_data(x, left, right)
144
+
145
+ # Prepare options
146
+ opts = kwargs.copy()
147
+ add_default_options(opts)
148
+ if "id" not in opts:
149
+ opts["id"] = "chart1"
150
+ if title is not None:
151
+ opts["title"] = title
152
+ if timestamped is not None:
153
+ opts["scales"] = {"x": {"time": timestamped}}
154
+ if width is not None:
155
+ opts["width"] = width
156
+ if height is not None:
157
+ opts["height"] = height
158
+ if "series" not in opts:
159
+ add_series(opts, data, len(left), left_labels, right_labels)
160
+ if "axes" not in opts:
161
+ add_axes(opts)
162
+
163
+ # Generate and open plot
164
+ html = generate_html(opts, data, resize=resize)
165
+ filename = write_html_tempfile(html)
166
+ webbrowser.open_new_tab(filename)
uplot/utils.py ADDED
@@ -0,0 +1,38 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ #
4
+ # SPDX-License-Identifier: Apache-2.0
5
+
6
+ """Array and JavaScript code utilities."""
7
+
8
+ import json
9
+
10
+ import numpy as np
11
+
12
+
13
+ def array2string(array: np.ndarray) -> str:
14
+ """Get string representation of a NumPy array suitable for uPlot.
15
+
16
+ Args:
17
+ array: NumPy array to convert to JavaScript.
18
+
19
+ Returns:
20
+ String representation of the array.
21
+ """
22
+ if array.dtype.kind in "iu":
23
+ return json.dumps(array.tolist())
24
+ # tolist() unboxes to Python floats; ``v != v`` is true only for NaN.
25
+ values = array.tolist()
26
+ return json.dumps([None if v != v else v for v in values])
27
+
28
+
29
+ def js(code: str) -> str:
30
+ """Wrap a code string so that it is processed as output JavaScript.
31
+
32
+ Args:
33
+ code: Code string to wrap.
34
+
35
+ Returns:
36
+ Wrapped string.
37
+ """
38
+ return f"<script>{code}</script>"
@@ -2,8 +2,6 @@
2
2
  # -*- coding: utf-8 -*-
3
3
  #
4
4
  # SPDX-License-Identifier: Apache-2.0
5
- # Copyright 2022 Stéphane Caron
6
- # Copyright 2023-2024 Inria
7
5
 
8
6
  """Plot time series."""
9
7
 
@@ -11,14 +9,14 @@ import tempfile
11
9
  from datetime import datetime
12
10
 
13
11
 
14
- def write_tmpfile(html: str) -> str:
15
- """Write output page.
12
+ def write_html_tempfile(html: str) -> str:
13
+ """Write string to an HTML file.
16
14
 
17
15
  Args:
18
16
  html: HTML content.
19
17
 
20
18
  Returns:
21
- Name of the output file (a temporary file).
19
+ Name of the output temporary file.
22
20
  """
23
21
  filename: str = ""
24
22
  with tempfile.NamedTemporaryFile(
@@ -0,0 +1,83 @@
1
+ Metadata-Version: 2.4
2
+ Name: uplot-python
3
+ Version: 1.1.0
4
+ Summary: Python wrapper for μPlot time series.
5
+ Keywords: json,time,series,plot
6
+ Author-email: Stéphane Caron <stephane.caron@normalesup.org>
7
+ Maintainer-email: Stéphane Caron <stephane.caron@normalesup.org>
8
+ Requires-Python: >=3.10
9
+ Description-Content-Type: text/markdown
10
+ Classifier: Development Status :: 5 - Production/Stable
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: License :: OSI Approved :: GNU Lesser General Public License v3 (LGPLv3)
13
+ Classifier: Operating System :: OS Independent
14
+ Classifier: Programming Language :: Python :: 3.10
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Topic :: Scientific/Engineering :: Mathematics
18
+ License-File: LICENSE
19
+ Requires-Dist: ipython >=8.0.1
20
+ Requires-Dist: msgpack >=1.0.4
21
+ Requires-Dist: numpy >=1.15.4
22
+ Project-URL: Changelog, https://codeberg.org/stephane-caron/uplot-python/blob/master/CHANGELOG.md
23
+ Project-URL: Homepage, https://codeberg.org/stephane-caron/uplot-python
24
+ Project-URL: Source, https://codeberg.org/stephane-caron/uplot-python
25
+ Project-URL: Tracker, https://codeberg.org/stephane-caron/uplot-python/issues
26
+
27
+ # uplot-python
28
+
29
+ [![CI](https://ci.codeberg.org/api/badges/17009/status.svg)](https://ci.codeberg.org/repos/17009)
30
+ [![Conda version](https://img.shields.io/conda/vn/conda-forge/uplot-python.svg)](https://anaconda.org/conda-forge/uplot-python)
31
+ [![PyPI version](https://img.shields.io/pypi/v/uplot-python)](https://pypi.org/project/uplot-python/)
32
+
33
+ Python wrapper for [μPlot](https://github.com/leeoniya/uPlot) 📈
34
+
35
+ ## Installation
36
+
37
+ ### From PyPI
38
+
39
+ ```console
40
+ pip install uplot-python
41
+ ```
42
+
43
+ ## Usage
44
+
45
+ The `plot` function has the same API as µPlot's `uPlot.plot`:
46
+
47
+ ```py
48
+ import numpy as np
49
+ import uplot
50
+
51
+ t = np.linspace(0.0, 1.0, 10)
52
+ data = [t, np.exp(0.42 * t)]
53
+ opts = {
54
+ "width": 1920,
55
+ "height": 600,
56
+ "title": "Example with plot",
57
+ "series": [{}, { "stroke": "red", }, ],
58
+ }
59
+
60
+ uplot.plot(opts, data)
61
+ ```
62
+
63
+ For convenience, the library also provides a `plot2` function with additional defaults aimed at time series and line plots, for an experience closer to `matplotlib.pyplot.plot`:
64
+
65
+ ```py
66
+ import numpy as np
67
+ import uplot
68
+
69
+ t = np.linspace(0.0, 1.0, 10)
70
+ uplot.plot2(
71
+ t,
72
+ [np.exp(0.1 * t), np.exp(-10.0 * t), np.cos(t)],
73
+ title="Example with plot2",
74
+ left_labels=["exp(A t)", "exp(-B t)", "cos(t)"],
75
+ )
76
+ ```
77
+
78
+ ## See also
79
+
80
+ - [µPlot](https://github.com/leeoniya/uPlot): A small (~45 KB min), fast chart for time series, lines, areas, ohlc & bars.
81
+ - [Matplotlib](https://matplotlib.org/stable/): Comprehensive library for creating static, animated, and interactive visualizations.
82
+ - [matplotlive](https://codeberg.org/stephane-caron/matplotlive): Stream live plots to a Matplotlib figure.
83
+
@@ -0,0 +1,16 @@
1
+ uplot/__init__.py,sha256=FRPhRJ7xLuJLzle7fMRkAKoDbTzapOw5HxwJHkYBTMY,245
2
+ uplot/color_picker.py,sha256=FdA7Jd6mMungX9UuvJ9syYjvCcQJ9--LdxTBj-te0v8,901
3
+ uplot/exceptions.py,sha256=8_2xggNkMpMF-pRsfA8kLWT81l_t2cZ4cfOkwbPql_4,192
4
+ uplot/generate_html.py,sha256=NruX2zDzzTIOuJW4lQ6N60kg05MAH61eWEAijcFKhr0,3713
5
+ uplot/plot.py,sha256=MOGxCDlYdH-n6f380_3wmYCdfjwtU438AtcFYZ4gdiQ,643
6
+ uplot/plot2.py,sha256=P5tOyZxsm65mG1DN3TSK2q4qWBOk2GJPBcWx0xXz9Ks,4887
7
+ uplot/utils.py,sha256=Tl6ZDtMdtE27PuX3Vje_IQjhZZg40BcfmlXcU02o_Nc,880
8
+ uplot/write_html_tempfile.py,sha256=B68PrF3rQ3mC3i6ujKgHy8cf8V64h4D9DxIqkGdLRQI,661
9
+ uplot/static/__init__.py,sha256=1mykIjCDK2TC5EfbjCTNxfOtJx3P-hIFgbHQEtBfM_o,71
10
+ uplot/static/uPlot.iife.js,sha256=ZVvIqjN4Hochxa9rWfFpnAPJvalRqnSmq1GKwgzEMm0,120184
11
+ uplot/static/uPlot.min.css,sha256=PxIc1KmXtjTgeoh2WIUAGOR1wm8L3dh0EJqNOCwZ3oc,1839
12
+ uplot/static/uPlot.mousewheel.js,sha256=p2CcCwxxafvNqXsxw-zrqUUogxZ1URZhxWK59B0-tgw,4543
13
+ uplot_python-1.1.0.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
14
+ uplot_python-1.1.0.dist-info/WHEEL,sha256=G2gURzTEtmeR8nrdXUJfNiB3VYVxigPQ-bEQujpNiNs,82
15
+ uplot_python-1.1.0.dist-info/METADATA,sha256=IUFfatQ54RV4rz5-L8r3Y1jEZtMfxFP4G_M7oKdHBk0,2812
16
+ uplot_python-1.1.0.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: flit 3.9.0
2
+ Generator: flit 3.12.0
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
@@ -1,31 +0,0 @@
1
- Metadata-Version: 2.1
2
- Name: uplot-python
3
- Version: 0.0.1
4
- Summary: Plot Python iterables with µPlot.
5
- Keywords: json,time,series,plot
6
- Author-email: Stéphane Caron <stephane.caron@normalesup.org>
7
- Maintainer-email: Stéphane Caron <stephane.caron@normalesup.org>
8
- Requires-Python: >=3.8
9
- Description-Content-Type: text/markdown
10
- Classifier: Development Status :: 4 - Beta
11
- Classifier: Intended Audience :: Developers
12
- Classifier: License :: OSI Approved :: GNU Lesser General Public License v3 (LGPLv3)
13
- Classifier: Operating System :: OS Independent
14
- Classifier: Programming Language :: Python :: 3.8
15
- Classifier: Programming Language :: Python :: 3.9
16
- Classifier: Programming Language :: Python :: 3.10
17
- Classifier: Programming Language :: Python :: 3.11
18
- Classifier: Programming Language :: Python :: 3.12
19
- Classifier: Topic :: Scientific/Engineering :: Mathematics
20
- Requires-Dist: ipython >=8.0.1
21
- Requires-Dist: msgpack >=1.0.4
22
- Requires-Dist: numpy >=1.15.4
23
- Project-URL: Changelog, https://github.com/stephane-caron/uplot-python/blob/master/CHANGELOG.md
24
- Project-URL: Homepage, https://github.com/stephane-caron/uplot-python
25
- Project-URL: Source, https://github.com/stephane-caron/uplot-python
26
- Project-URL: Tracker, https://github.com/stephane-caron/uplot-python/issues
27
-
28
- # uplot-python
29
-
30
- Python wrapper for [μPlot](https://github.com/leeoniya/uPlot) 📈
31
-
@@ -1,14 +0,0 @@
1
- uplot/__init__.py,sha256=ltGGn3R24SRcO2DqcSLi8SQRCRDpO11k4h_FAzmeBWg,257
2
- uplot/color_picker.py,sha256=tBcc4t1gN2spSBqmy7gY827GvpzPE2u8N8-6xUMv-YY,962
3
- uplot/exceptions.py,sha256=iMQrMn8yzcU95-g0vL1Lg03M5FB8vo4ckJpZXEb0TyE,248
4
- uplot/generate_html.py,sha256=WJRV592DDT38VfghO03wLslptYko7brYm4_bZnoR7iM,7182
5
- uplot/plot.py,sha256=Fw2iIcH5QB_56WBJxUvef5cA4tI5MDTwir1lphlLrdw,771
6
- uplot/write_tmpfile.py,sha256=6M-GiNyHAV2vVD1H7SHjFITVFrRtwFgHGwcDRkxeeCA,714
7
- uplot/uplot/__init__.py,sha256=1mykIjCDK2TC5EfbjCTNxfOtJx3P-hIFgbHQEtBfM_o,71
8
- uplot/uplot/uPlot.iife.js,sha256=ZVvIqjN4Hochxa9rWfFpnAPJvalRqnSmq1GKwgzEMm0,120184
9
- uplot/uplot/uPlot.min.css,sha256=PxIc1KmXtjTgeoh2WIUAGOR1wm8L3dh0EJqNOCwZ3oc,1839
10
- uplot/uplot/uPlot.mousewheel.js,sha256=p2CcCwxxafvNqXsxw-zrqUUogxZ1URZhxWK59B0-tgw,4543
11
- uplot_python-0.0.1.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
12
- uplot_python-0.0.1.dist-info/WHEEL,sha256=EZbGkh7Ie4PoZfRQ8I0ZuP9VklN_TvcZ6DSE5Uar4z4,81
13
- uplot_python-0.0.1.dist-info/METADATA,sha256=haZ0NwZTVvTdz6_2rXO91WJhb3irB-iAfqy29MK_oBQ,1342
14
- uplot_python-0.0.1.dist-info/RECORD,,
File without changes
File without changes
File without changes
File without changes