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
|
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(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']
}
])
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
self.data.append(cd)
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])
else:
self.header.extend([yName])
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()
|