diff --git a/dosview/__init__.py b/dosview/__init__.py index 2390360..c85c1dd 100644 --- a/dosview/__init__.py +++ b/dosview/__init__.py @@ -804,6 +804,11 @@ def initUI(self): self.upload_file_button.setMaximumHeight(20) self.upload_file_button.clicked.connect(lambda: UploadFileDialog().exec_()) + self.export_csv_button = QPushButton("Export spectrum") + self.export_csv_button.setMaximumHeight(20) + self.export_csv_button.clicked.connect(self.export_spectrum_csv) + self.export_csv_button.setEnabled(False) + log_view_widget = QWidget() self.left_panel = QSplitter(Qt.Vertical) @@ -814,6 +819,7 @@ def initUI(self): vb = QHBoxLayout() vb.addWidget(self.open_img_view_button) vb.addWidget(self.upload_file_button) + vb.addWidget(self.export_csv_button) self.left_panel.setLayout(vb) self.logView_splitter = QSplitter(Qt.Horizontal) @@ -843,8 +849,9 @@ def start_data_loading(self): self.load_data_thread.start() def on_data_loaded(self, data): - self.data = data # TODO>.. tohle do budoucna zrusit a nahradit tridou parseru.. + self.data = data # TODO>.. tohle do budoucna zrusit a nahradit tridou parseru.. print("Data are fully loaded...") + self.export_csv_button.setEnabled(True) self.plot_canvas.plot(data) print("After plot data canvas") @@ -888,12 +895,28 @@ def add_properties_to_tree(item, properties): def open_spectrogram_view(self): - matrix = self.data[-1] #TODO .. tohle predelat na nejakou tridu pro parserovani + matrix = self.data[-1] #TODO .. tohle predelat na nejakou tridu pro parserovani w = DataSpectrumView(self) w.show() w.plot_data(matrix) + def export_spectrum_csv(self): + path, _ = QFileDialog.getSaveFileName( + self, "Export spectrum", "", "CSV files (*.csv)" + ) + if not path: + return + if not path.endswith(".csv"): + path += ".csv" + import csv + hist = self.data[2] + with open(path, "w", newline="") as f: + writer = csv.writer(f) + writer.writerow(["channel", "counts"]) + for ch, cnt in enumerate(hist): + writer.writerow([ch, int(cnt)]) + class UploadFileDialog(QDialog): def __init__(self, parent=None): diff --git a/dosview/parsers.py b/dosview/parsers.py index a050d08..de45bbd 100644 --- a/dosview/parsers.py +++ b/dosview/parsers.py @@ -29,38 +29,43 @@ def parse(self): # pragma: no cover - concrete classes implement raise NotImplementedError -class Airdos04CLogParser(BaseLogParser): - """Parser for AIRDOS04C log files.""" +class AirdosV2LogParser(BaseLogParser): + """Parser for AIRDOS log files using data format version 2.x.""" @staticmethod def detect(file_path: str | Path) -> bool: with open(file_path, "r") as f: for line in f: - if line.startswith("$DOS") and "AIRDOS04C" in line: - return True + if line.startswith("$DOS"): + parts = line.strip().split(",") + # parts[2] = fw-version; MAJOR.MINOR encodes the data format version + if len(parts) > 2 and parts[2].startswith("2."): + return True return False def parse(self): start_time = time.time() - print("AIRDOS04C parser start") + print("AIRDOS v2 parser start") metadata = { "log_runs_count": 0, "log_device_info": {}, "log_info": {}, } - hist = np.zeros(1024, dtype=int) + hist = np.zeros(65536, dtype=int) total_counts = 0 sums: List[int] = [] time_axis: List[float] = [] inside_run = False current_hist = None current_counts = 0 + device_type = "unknown" with open(self.file_path, "r") as file: for line in file: parts = line.strip().split(",") match parts[0]: case "$DOS": + device_type = parts[1] if len(parts) > 1 else "unknown" metadata["log_device_info"]["DOS"] = { "type": parts[0], "hw-model": parts[1], @@ -102,12 +107,16 @@ def parse(self): metadata["log_info"]["events_total"] = int(total_counts) metadata["log_info"]["log_type_version"] = "2.0" metadata["log_info"]["log_type"] = "xDOS_SPECTRAL" - metadata["log_info"]["detector_type"] = "AIRDOS04C" - print("Parsed AIRDOS04C format in", time.time() - start_time, "s") + metadata["log_info"]["detector_type"] = device_type + print("Parsed AIRDOS v2 format in", time.time() - start_time, "s") return [np.array(time_axis), np.array(sums), hist, metadata] +# Backwards-compatible alias +Airdos04CLogParser = AirdosV2LogParser + + class OldLogParser(BaseLogParser): """Parser for legacy (pre-AIRDOS04C) log files.""" @@ -203,7 +212,7 @@ def parse(self): return [time_column, sums, hist, metadata] -LOG_PARSERS: Sequence[type[BaseLogParser]] = [Airdos04CLogParser, OldLogParser] +LOG_PARSERS: Sequence[type[BaseLogParser]] = [AirdosV2LogParser, OldLogParser] def get_parser_for_file(file_path: str | Path) -> BaseLogParser: @@ -220,7 +229,8 @@ def parse_file(file_path: str | Path): __all__ = [ "BaseLogParser", - "Airdos04CLogParser", + "AirdosV2LogParser", + "Airdos04CLogParser", # backwards-compatible alias "OldLogParser", "get_parser_for_file", "parse_file",