toucan-plot 0.2.7__tar.gz → 0.3.0__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.
- {toucan_plot-0.2.7/src/toucan_plot.egg-info → toucan_plot-0.3.0}/PKG-INFO +2 -1
- {toucan_plot-0.2.7 → toucan_plot-0.3.0}/pyproject.toml +2 -1
- {toucan_plot-0.2.7 → toucan_plot-0.3.0}/src/toucan_plot/main.py +164 -3
- {toucan_plot-0.2.7 → toucan_plot-0.3.0}/src/toucan_plot/utils/__init__.py +2 -0
- {toucan_plot-0.2.7 → toucan_plot-0.3.0}/src/toucan_plot/utils/loaders.py +38 -0
- {toucan_plot-0.2.7 → toucan_plot-0.3.0/src/toucan_plot.egg-info}/PKG-INFO +2 -1
- {toucan_plot-0.2.7 → toucan_plot-0.3.0}/src/toucan_plot.egg-info/requires.txt +1 -0
- {toucan_plot-0.2.7 → toucan_plot-0.3.0}/LICENSE +0 -0
- {toucan_plot-0.2.7 → toucan_plot-0.3.0}/README.md +0 -0
- {toucan_plot-0.2.7 → toucan_plot-0.3.0}/setup.cfg +0 -0
- {toucan_plot-0.2.7 → toucan_plot-0.3.0}/src/toucan_plot/__main__.py +0 -0
- {toucan_plot-0.2.7 → toucan_plot-0.3.0}/src/toucan_plot/assets/adjustments-dark.svg +0 -0
- {toucan_plot-0.2.7 → toucan_plot-0.3.0}/src/toucan_plot/assets/adjustments-light.svg +0 -0
- {toucan_plot-0.2.7 → toucan_plot-0.3.0}/src/toucan_plot/assets/arrow-autofit-height-dark.svg +0 -0
- {toucan_plot-0.2.7 → toucan_plot-0.3.0}/src/toucan_plot/assets/arrow-autofit-height-light.svg +0 -0
- {toucan_plot-0.2.7 → toucan_plot-0.3.0}/src/toucan_plot/assets/arrow-narrow-left-dark.svg +0 -0
- {toucan_plot-0.2.7 → toucan_plot-0.3.0}/src/toucan_plot/assets/arrow-narrow-left-light.svg +0 -0
- {toucan_plot-0.2.7 → toucan_plot-0.3.0}/src/toucan_plot/assets/arrow-narrow-right-dark.svg +0 -0
- {toucan_plot-0.2.7 → toucan_plot-0.3.0}/src/toucan_plot/assets/arrow-narrow-right-light.svg +0 -0
- {toucan_plot-0.2.7 → toucan_plot-0.3.0}/src/toucan_plot/assets/device-floppy-dark.svg +0 -0
- {toucan_plot-0.2.7 → toucan_plot-0.3.0}/src/toucan_plot/assets/device-floppy-light.svg +0 -0
- {toucan_plot-0.2.7 → toucan_plot-0.3.0}/src/toucan_plot/assets/hand-stop-dark.svg +0 -0
- {toucan_plot-0.2.7 → toucan_plot-0.3.0}/src/toucan_plot/assets/hand-stop-light.svg +0 -0
- {toucan_plot-0.2.7 → toucan_plot-0.3.0}/src/toucan_plot/assets/home-dark.svg +0 -0
- {toucan_plot-0.2.7 → toucan_plot-0.3.0}/src/toucan_plot/assets/home-light.svg +0 -0
- {toucan_plot-0.2.7 → toucan_plot-0.3.0}/src/toucan_plot/assets/ico/toucan-plot.ico +0 -0
- {toucan_plot-0.2.7 → toucan_plot-0.3.0}/src/toucan_plot/assets/layout-grid-add-dark.svg +0 -0
- {toucan_plot-0.2.7 → toucan_plot-0.3.0}/src/toucan_plot/assets/layout-grid-add-light.svg +0 -0
- {toucan_plot-0.2.7 → toucan_plot-0.3.0}/src/toucan_plot/assets/math-function-dark.svg +0 -0
- {toucan_plot-0.2.7 → toucan_plot-0.3.0}/src/toucan_plot/assets/math-function-light.svg +0 -0
- {toucan_plot-0.2.7 → toucan_plot-0.3.0}/src/toucan_plot/assets/ruler-measure-dark.svg +0 -0
- {toucan_plot-0.2.7 → toucan_plot-0.3.0}/src/toucan_plot/assets/ruler-measure-light.svg +0 -0
- {toucan_plot-0.2.7 → toucan_plot-0.3.0}/src/toucan_plot/assets/zoom-dark.svg +0 -0
- {toucan_plot-0.2.7 → toucan_plot-0.3.0}/src/toucan_plot/assets/zoom-light.svg +0 -0
- {toucan_plot-0.2.7 → toucan_plot-0.3.0}/src/toucan_plot/utils/styles.py +0 -0
- {toucan_plot-0.2.7 → toucan_plot-0.3.0}/src/toucan_plot.egg-info/SOURCES.txt +0 -0
- {toucan_plot-0.2.7 → toucan_plot-0.3.0}/src/toucan_plot.egg-info/dependency_links.txt +0 -0
- {toucan_plot-0.2.7 → toucan_plot-0.3.0}/src/toucan_plot.egg-info/entry_points.txt +0 -0
- {toucan_plot-0.2.7 → toucan_plot-0.3.0}/src/toucan_plot.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: toucan-plot
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: A versatile PyQt6 and Matplotlib-based plotting tool for CSV and SMV data files.
|
|
5
5
|
License-Expression: MIT
|
|
6
6
|
Requires-Python: >=3.11
|
|
@@ -14,6 +14,7 @@ Requires-Dist: scienceplots>=2.2.1
|
|
|
14
14
|
Requires-Dist: python-can>=4.0
|
|
15
15
|
Requires-Dist: cantools>=39.0
|
|
16
16
|
Requires-Dist: asammdf>=7.0
|
|
17
|
+
Requires-Dist: pyarrow>=23.0.1
|
|
17
18
|
Dynamic: license-file
|
|
18
19
|
|
|
19
20
|
# Toucan-Plot
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "toucan-plot"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.3.0"
|
|
4
4
|
description = "A versatile PyQt6 and Matplotlib-based plotting tool for CSV and SMV data files."
|
|
5
5
|
license = "MIT"
|
|
6
6
|
readme = { file = "README.md", content-type = "text/markdown" }
|
|
@@ -14,6 +14,7 @@ dependencies = [
|
|
|
14
14
|
"python-can>=4.0",
|
|
15
15
|
"cantools>=39.0",
|
|
16
16
|
"asammdf>=7.0",
|
|
17
|
+
"pyarrow>=23.0.1",
|
|
17
18
|
]
|
|
18
19
|
|
|
19
20
|
[project.scripts]
|
|
@@ -9,7 +9,7 @@ from matplotlib.backends.backend_qtagg import FigureCanvasQTAgg as FigureCanvas
|
|
|
9
9
|
from matplotlib.backends.backend_qt import NavigationToolbar2QT as NavigationToolbar
|
|
10
10
|
import qdarktheme
|
|
11
11
|
|
|
12
|
-
from toucan_plot.utils import styles, csv_load_worker, can_log_load_worker, mf4_load_worker
|
|
12
|
+
from toucan_plot.utils import styles, csv_load_worker, can_log_load_worker, mf4_load_worker, feather_load_worker
|
|
13
13
|
|
|
14
14
|
LEGEND_POSITIONS = [
|
|
15
15
|
'best', 'upper right', 'upper left', 'lower left', 'lower right',
|
|
@@ -687,6 +687,7 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
|
687
687
|
dbc_files = [p for p in paths if os.path.splitext(p)[1].lower() == '.dbc']
|
|
688
688
|
csv_files = [p for p in paths if os.path.splitext(p)[1].lower() in ('.csv', '.smv')]
|
|
689
689
|
mf4_files = [p for p in paths if os.path.splitext(p)[1].lower() in ('.mf4', '.mf4z')]
|
|
690
|
+
feather_files = [p for p in paths if os.path.splitext(p)[1].lower() == '.feather']
|
|
690
691
|
|
|
691
692
|
first_loaded = False
|
|
692
693
|
|
|
@@ -715,19 +716,27 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
|
715
716
|
self._merged_mode = True
|
|
716
717
|
self._merge_load_mf4(path, os.path.basename(path))
|
|
717
718
|
|
|
719
|
+
for path in feather_files:
|
|
720
|
+
if not first_loaded:
|
|
721
|
+
self._load_feather(path)
|
|
722
|
+
first_loaded = True
|
|
723
|
+
else:
|
|
724
|
+
self._merged_mode = True
|
|
725
|
+
self._merge_load_feather(path, os.path.basename(path))
|
|
726
|
+
|
|
718
727
|
if self._merged_mode and self.series_list:
|
|
719
728
|
file_names = [f['name'] for f in self._loaded_files]
|
|
720
729
|
self.update_plot()
|
|
721
730
|
self.setWindowTitle(f'Toucan-Plot — Merged: {", ".join(file_names)}')
|
|
722
731
|
|
|
723
732
|
def open_file(self):
|
|
724
|
-
file_filter = "All supported files (*.csv *.smv *.blf *.trc *.asc *.dbc *.mf4 *.mf4z);;CSV files (*.csv
|
|
733
|
+
file_filter = "All supported files (*.csv *.smv *.blf *.trc *.asc *.dbc *.mf4 *.mf4z *.feather);;CSV files (*.csv *.smv);;MF4 files (*.mf4 *.mf4z);;Feather files (*.feather);;CAN files (*.blf *.trc *.asc *.dbc);;All files (*)"
|
|
725
734
|
paths, _ = QtWidgets.QFileDialog.getOpenFileNames(self, 'Open File', '', file_filter)
|
|
726
735
|
self.open_files(paths)
|
|
727
736
|
|
|
728
737
|
def merge_file(self):
|
|
729
738
|
"""Open additional files and append their series to the existing plot session."""
|
|
730
|
-
file_filter = "All supported files (*.csv *.smv *.blf *.trc *.asc *.dbc *.mf4 *.mf4z);;CSV files (*.csv
|
|
739
|
+
file_filter = "All supported files (*.csv *.smv *.blf *.trc *.asc *.dbc *.mf4 *.mf4z *.feather);;CSV files (*.csv *.smv);;MF4 files (*.mf4 *.mf4z);;Feather files (*.feather);;CAN files (*.blf *.trc *.asc *.dbc);;All files (*)"
|
|
731
740
|
paths, _ = QtWidgets.QFileDialog.getOpenFileNames(self, 'Merge Files', '', file_filter)
|
|
732
741
|
if not paths:
|
|
733
742
|
return
|
|
@@ -738,6 +747,7 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
|
738
747
|
dbc_files = [p for p in paths if os.path.splitext(p)[1].lower() == '.dbc']
|
|
739
748
|
csv_files = [p for p in paths if os.path.splitext(p)[1].lower() in ('.csv', '.smv')]
|
|
740
749
|
mf4_files = [p for p in paths if os.path.splitext(p)[1].lower() in ('.mf4', '.mf4z')]
|
|
750
|
+
feather_files = [p for p in paths if os.path.splitext(p)[1].lower() == '.feather']
|
|
741
751
|
|
|
742
752
|
for path in csv_files:
|
|
743
753
|
basename = os.path.basename(path)
|
|
@@ -752,6 +762,10 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
|
752
762
|
basename = os.path.basename(path)
|
|
753
763
|
self._merge_load_mf4(path, basename)
|
|
754
764
|
|
|
765
|
+
for path in feather_files:
|
|
766
|
+
basename = os.path.basename(path)
|
|
767
|
+
self._merge_load_feather(path, basename)
|
|
768
|
+
|
|
755
769
|
if self.series_list:
|
|
756
770
|
file_names = [f['name'] for f in self._loaded_files]
|
|
757
771
|
self.update_plot()
|
|
@@ -1203,6 +1217,153 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
|
1203
1217
|
file_entry['series_indices'].append(idx)
|
|
1204
1218
|
self._loaded_files.append(file_entry)
|
|
1205
1219
|
|
|
1220
|
+
def _load_feather(self, path):
|
|
1221
|
+
"""Load a Feather file using a worker process with live progress."""
|
|
1222
|
+
queue = multiprocessing.Queue()
|
|
1223
|
+
proc = multiprocessing.Process(target=feather_load_worker, args=(path, queue))
|
|
1224
|
+
|
|
1225
|
+
progress = QtWidgets.QProgressDialog(f'Loading {os.path.basename(path)}...', None, 0, 100, self)
|
|
1226
|
+
progress.setWindowTitle('Loading')
|
|
1227
|
+
progress.setWindowModality(QtCore.Qt.WindowModality.WindowModal)
|
|
1228
|
+
progress.setMinimumDuration(0)
|
|
1229
|
+
progress.setValue(0)
|
|
1230
|
+
|
|
1231
|
+
result_data = {}
|
|
1232
|
+
|
|
1233
|
+
def poll():
|
|
1234
|
+
try:
|
|
1235
|
+
while not queue.empty():
|
|
1236
|
+
msg = queue.get_nowait()
|
|
1237
|
+
if msg[0] == 'progress':
|
|
1238
|
+
pct = msg[1]
|
|
1239
|
+
label = msg[2] if len(msg) > 2 else ''
|
|
1240
|
+
if pct >= 0:
|
|
1241
|
+
progress.setValue(pct)
|
|
1242
|
+
if label:
|
|
1243
|
+
progress.setLabelText(label)
|
|
1244
|
+
elif msg[0] == 'result':
|
|
1245
|
+
result_data['data'] = msg[1]
|
|
1246
|
+
progress.setValue(100)
|
|
1247
|
+
progress.close()
|
|
1248
|
+
elif msg[0] == 'error':
|
|
1249
|
+
result_data['error'] = msg[1]
|
|
1250
|
+
progress.close()
|
|
1251
|
+
except Exception:
|
|
1252
|
+
pass
|
|
1253
|
+
|
|
1254
|
+
timer = QtCore.QTimer(progress)
|
|
1255
|
+
timer.timeout.connect(poll)
|
|
1256
|
+
timer.start(50)
|
|
1257
|
+
proc.start()
|
|
1258
|
+
progress.exec()
|
|
1259
|
+
timer.stop()
|
|
1260
|
+
proc.join(timeout=10)
|
|
1261
|
+
|
|
1262
|
+
if 'error' in result_data:
|
|
1263
|
+
QtWidgets.QMessageBox.warning(self, 'Error', result_data['error'])
|
|
1264
|
+
return
|
|
1265
|
+
if 'data' not in result_data:
|
|
1266
|
+
return
|
|
1267
|
+
|
|
1268
|
+
x_col, all_columns = result_data['data']
|
|
1269
|
+
x_array = all_columns[x_col]
|
|
1270
|
+
|
|
1271
|
+
self._x = x_array
|
|
1272
|
+
self._all_columns = all_columns
|
|
1273
|
+
self._x_col_name = x_col
|
|
1274
|
+
self._merged_mode = False
|
|
1275
|
+
|
|
1276
|
+
# Clear existing series and subplots
|
|
1277
|
+
self.series_list.clear()
|
|
1278
|
+
self.subplot_series.clear()
|
|
1279
|
+
self.subplot_series_props.clear()
|
|
1280
|
+
self.subplot_axes_labels.clear()
|
|
1281
|
+
self.subplot_config.clear()
|
|
1282
|
+
self._loaded_files.clear()
|
|
1283
|
+
|
|
1284
|
+
# Build series_list from all_columns
|
|
1285
|
+
basename = os.path.basename(path)
|
|
1286
|
+
file_entry = {'name': basename, 'series_indices': []}
|
|
1287
|
+
for name in all_columns:
|
|
1288
|
+
if name == x_col:
|
|
1289
|
+
continue
|
|
1290
|
+
data = all_columns[name]
|
|
1291
|
+
idx = len(self.series_list)
|
|
1292
|
+
self.series_list.append((name, lambda x, f, _d=data: _d, x_array))
|
|
1293
|
+
file_entry['series_indices'].append(idx)
|
|
1294
|
+
self._loaded_files.append(file_entry)
|
|
1295
|
+
self._merge_action.setEnabled(True)
|
|
1296
|
+
|
|
1297
|
+
self.update_plot()
|
|
1298
|
+
self.setWindowTitle(f'Toucan-Plot — {basename}')
|
|
1299
|
+
self.show_series_selector()
|
|
1300
|
+
|
|
1301
|
+
def _merge_load_feather(self, path, prefix):
|
|
1302
|
+
"""Load a Feather file and append its series (prefixed) to the existing series_list."""
|
|
1303
|
+
queue = multiprocessing.Queue()
|
|
1304
|
+
proc = multiprocessing.Process(target=feather_load_worker, args=(path, queue))
|
|
1305
|
+
|
|
1306
|
+
progress = QtWidgets.QProgressDialog(f'Loading {os.path.basename(path)}...', None, 0, 100, self)
|
|
1307
|
+
progress.setWindowTitle('Loading')
|
|
1308
|
+
progress.setWindowModality(QtCore.Qt.WindowModality.WindowModal)
|
|
1309
|
+
progress.setMinimumDuration(0)
|
|
1310
|
+
progress.setValue(0)
|
|
1311
|
+
|
|
1312
|
+
result_data = {}
|
|
1313
|
+
|
|
1314
|
+
def poll():
|
|
1315
|
+
try:
|
|
1316
|
+
while not queue.empty():
|
|
1317
|
+
msg = queue.get_nowait()
|
|
1318
|
+
if msg[0] == 'progress':
|
|
1319
|
+
pct = msg[1]
|
|
1320
|
+
label = msg[2] if len(msg) > 2 else ''
|
|
1321
|
+
if pct >= 0:
|
|
1322
|
+
progress.setValue(pct)
|
|
1323
|
+
if label:
|
|
1324
|
+
progress.setLabelText(label)
|
|
1325
|
+
elif msg[0] == 'result':
|
|
1326
|
+
result_data['data'] = msg[1]
|
|
1327
|
+
progress.setValue(100)
|
|
1328
|
+
progress.close()
|
|
1329
|
+
elif msg[0] == 'error':
|
|
1330
|
+
result_data['error'] = msg[1]
|
|
1331
|
+
progress.close()
|
|
1332
|
+
except Exception:
|
|
1333
|
+
pass
|
|
1334
|
+
|
|
1335
|
+
timer = QtCore.QTimer(progress)
|
|
1336
|
+
timer.timeout.connect(poll)
|
|
1337
|
+
timer.start(50)
|
|
1338
|
+
proc.start()
|
|
1339
|
+
progress.exec()
|
|
1340
|
+
timer.stop()
|
|
1341
|
+
proc.join(timeout=10)
|
|
1342
|
+
|
|
1343
|
+
if 'error' in result_data:
|
|
1344
|
+
QtWidgets.QMessageBox.warning(self, 'Error', result_data['error'])
|
|
1345
|
+
return
|
|
1346
|
+
if 'data' not in result_data:
|
|
1347
|
+
return
|
|
1348
|
+
|
|
1349
|
+
x_col, all_columns = result_data['data']
|
|
1350
|
+
x_array = all_columns[x_col]
|
|
1351
|
+
|
|
1352
|
+
if len(self.series_list) == 0:
|
|
1353
|
+
self._x = x_array
|
|
1354
|
+
self._x_col_name = x_col
|
|
1355
|
+
|
|
1356
|
+
file_entry = {'name': prefix, 'series_indices': []}
|
|
1357
|
+
for name in all_columns:
|
|
1358
|
+
if name == x_col:
|
|
1359
|
+
continue
|
|
1360
|
+
data = all_columns[name]
|
|
1361
|
+
prefixed_name = f'{prefix}: {name}'
|
|
1362
|
+
idx = len(self.series_list)
|
|
1363
|
+
self.series_list.append((prefixed_name, lambda x, f, _d=data: _d, x_array))
|
|
1364
|
+
file_entry['series_indices'].append(idx)
|
|
1365
|
+
self._loaded_files.append(file_entry)
|
|
1366
|
+
|
|
1206
1367
|
# ---- Measure cursors ----
|
|
1207
1368
|
|
|
1208
1369
|
def _toggle_measure(self, checked: bool):
|
|
@@ -4,6 +4,7 @@ from .loaders import (
|
|
|
4
4
|
csv_load_worker,
|
|
5
5
|
can_log_load_worker,
|
|
6
6
|
mf4_load_worker,
|
|
7
|
+
feather_load_worker,
|
|
7
8
|
)
|
|
8
9
|
|
|
9
10
|
__all__ = [
|
|
@@ -11,4 +12,5 @@ __all__ = [
|
|
|
11
12
|
"csv_load_worker",
|
|
12
13
|
"can_log_load_worker",
|
|
13
14
|
"mf4_load_worker",
|
|
15
|
+
"feather_load_worker",
|
|
14
16
|
]
|
|
@@ -217,3 +217,41 @@ def mf4_load_worker(path, queue):
|
|
|
217
217
|
queue.put(('result', (x_col, all_columns)))
|
|
218
218
|
except Exception as e:
|
|
219
219
|
queue.put(('error', str(e)))
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
def feather_load_worker(path, queue):
|
|
223
|
+
"""Multiprocessing worker for Feather loading via pandas. Sends progress via queue."""
|
|
224
|
+
try:
|
|
225
|
+
import pandas as pd
|
|
226
|
+
|
|
227
|
+
queue.put(('progress', 5, 'Reading Feather file...'))
|
|
228
|
+
df = pd.read_feather(path)
|
|
229
|
+
|
|
230
|
+
if df.empty:
|
|
231
|
+
queue.put(('error', 'Feather file is empty or has no data rows.'))
|
|
232
|
+
return
|
|
233
|
+
|
|
234
|
+
queue.put(('progress', 30, 'Building columns...'))
|
|
235
|
+
all_columns = {}
|
|
236
|
+
num_cols = len(df.columns)
|
|
237
|
+
|
|
238
|
+
x_col = None
|
|
239
|
+
for candidate in ('Time', 'timestamp', 'time'):
|
|
240
|
+
if candidate in df.columns:
|
|
241
|
+
x_col = candidate
|
|
242
|
+
break
|
|
243
|
+
if x_col is None:
|
|
244
|
+
x_col = df.columns[0]
|
|
245
|
+
|
|
246
|
+
for col_idx, col_name in enumerate(df.columns):
|
|
247
|
+
try:
|
|
248
|
+
all_columns[col_name] = df[col_name].to_numpy(dtype=float, na_value=np.nan)
|
|
249
|
+
except (ValueError, TypeError):
|
|
250
|
+
continue
|
|
251
|
+
pct = 30 + int(65 * (col_idx + 1) / num_cols)
|
|
252
|
+
queue.put(('progress', pct, f'Processing column {col_idx + 1}/{num_cols}...'))
|
|
253
|
+
|
|
254
|
+
queue.put(('progress', 98, 'Finalizing...'))
|
|
255
|
+
queue.put(('result', (x_col, all_columns)))
|
|
256
|
+
except Exception as e:
|
|
257
|
+
queue.put(('error', str(e)))
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: toucan-plot
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: A versatile PyQt6 and Matplotlib-based plotting tool for CSV and SMV data files.
|
|
5
5
|
License-Expression: MIT
|
|
6
6
|
Requires-Python: >=3.11
|
|
@@ -14,6 +14,7 @@ Requires-Dist: scienceplots>=2.2.1
|
|
|
14
14
|
Requires-Dist: python-can>=4.0
|
|
15
15
|
Requires-Dist: cantools>=39.0
|
|
16
16
|
Requires-Dist: asammdf>=7.0
|
|
17
|
+
Requires-Dist: pyarrow>=23.0.1
|
|
17
18
|
Dynamic: license-file
|
|
18
19
|
|
|
19
20
|
# Toucan-Plot
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{toucan_plot-0.2.7 → toucan_plot-0.3.0}/src/toucan_plot/assets/arrow-autofit-height-dark.svg
RENAMED
|
File without changes
|
{toucan_plot-0.2.7 → toucan_plot-0.3.0}/src/toucan_plot/assets/arrow-autofit-height-light.svg
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|