1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141
|
import csv
import itertools
import numpy as np
from .. import ErrorBarItem, PlotItem
from ..parametertree import Parameter
from ..Qt import QtCore
from .Exporter import Exporter
translate = QtCore.QCoreApplication.translate
__all__ = ['CSVExporter']
class CSVExporter(Exporter):
Name = "CSV of original plot data"
windows = []
def __init__(self, item):
Exporter.__init__(self, item)
self.params = Parameter.create(name='params', type='group', children=[
{
'name': 'separator',
'title': translate("Exporter", 'separator'),
'type': 'list',
'value': 'comma',
'limits': ['comma', 'tab']
},
{
'name': 'precision',
'title': translate("Exporter", 'precision'),
'type': 'int',
'value': 10,
'limits': [0, None]
},
{
'name': 'columnMode',
'title': translate("Exporter", 'columnMode'),
'type': 'list',
'limits': ['(x,y) per plot', '(x,y,y,y) for all plots'],
'value': '(x,y) per plot',
}
])
self.index_counter = itertools.count(start=0)
self.header = []
self.data = []
def parameters(self):
return self.params
def _exportErrorBarItem(self, errorBarItem: ErrorBarItem) -> None:
error_data = []
index = next(self.index_counter)
# make sure the plot actually has data:
if errorBarItem.opts['x'] is None or errorBarItem.opts['y'] is None:
return None
header_naming_map = {
"left": "x_min_error",
"right": "x_max_error",
"bottom": "y_min_error",
"top": "y_max_error"
}
# grab the base-points
self.header.extend([f'x{index:04}_error', f'y{index:04}_error'])
error_data.extend([errorBarItem.opts['x'], errorBarItem.opts['y']])
# grab the error bars
for error_direction, header_label in header_naming_map.items():
if (error := errorBarItem.opts[error_direction]) is not None:
self.header.extend([f'{header_label}_{index:04}'])
error_data.append(error)
self.data.append(tuple(error_data))
return None
def _exportPlotDataItem(self, plotDataItem) -> None:
if hasattr(plotDataItem, 'getOriginalDataset'):
# try to access unmapped, unprocessed data
cd = plotDataItem.getOriginalDataset()
else:
# fall back to earlier access method
cd = plotDataItem.getData()
if cd[0] is None:
# no data found, break out...
return None
index = next(self.index_counter)
if plotDataItem.name() is not None:
name = plotDataItem.name().replace('"', '""') + '_'
xName = f"{name}x"
yName = f"{name}y"
else:
xName = f'x{index:04}'
yName = f'y{index:04}'
appendAllX = self.params['columnMode'] == '(x,y) per plot'
if appendAllX or index == 0:
self.header.extend([xName, yName])
self.data.append(cd)
else:
self.header.extend([yName])
self.data.append([cd[1]])
return None
def export(self, fileName=None):
if not isinstance(self.item, PlotItem):
raise TypeError("Must have a PlotItem selected for CSV export.")
if fileName is None:
self.fileSaveDialog(filter=["*.csv", "*.tsv"])
return
for item in self.item.items:
if isinstance(item, ErrorBarItem):
self._exportErrorBarItem(item)
elif hasattr(item, 'implements') and item.implements('plotData'):
self._exportPlotDataItem(item)
sep = "," if self.params['separator'] == 'comma' else "\t"
# we want to flatten the nested arrays of data into columns
columns = [column for dataset in self.data for column in dataset]
with open(fileName, 'w', newline='') as csvfile:
writer = csv.writer(csvfile, delimiter=sep, quoting=csv.QUOTE_MINIMAL)
writer.writerow(self.header)
for row in itertools.zip_longest(*columns, fillvalue=""):
row_to_write = [
item if isinstance(item, str)
else np.format_float_positional(
item, precision=self.params['precision']
)
for item in row
]
writer.writerow(row_to_write)
self.header.clear()
self.data.clear()
CSVExporter.register()
|