diff --git a/src/yt_napari/_widget_reader.py b/src/yt_napari/_widget_reader.py index 5df980d..9964e21 100644 --- a/src/yt_napari/_widget_reader.py +++ b/src/yt_napari/_widget_reader.py @@ -5,11 +5,14 @@ from magicgui import widgets from napari.qt.threading import thread_worker from qtpy import QtCore -from qtpy.QtWidgets import QComboBox, QHBoxLayout, QPushButton, QVBoxLayout, QWidget +from qtpy.QtWidgets import QComboBox, QHBoxLayout, QPushButton, QVBoxLayout, QWidget,QFileDialog,QApplication from yt_napari import _data_model, _gui_utilities, _model_ingestor from yt_napari._ds_cache import dataset_cache from yt_napari.viewer import _check_for_reference_layer +import sys +import json +from pydantic.error_wrappers import ValidationError class YTReader(QWidget): @@ -94,6 +97,9 @@ def remove_selection(self): class ReaderWidget(YTReader): _pydantic_model = _data_model.DataContainer + app = QApplication(sys.argv) + + def add_load_group_widgets(self): load_group = QHBoxLayout() @@ -106,6 +112,30 @@ def add_load_group_widgets(self): load_group.addWidget(cc.native) self.layout().addLayout(load_group) + ss = widgets.PushButton(text="Save Selection") + ss.clicked.connect(self.save_selection) + load_group.addWidget(ss.native) + + def save_selection(self): + py_kwargs = {} + py_kwargs = self._validate_data_model() + #model = _data_model.InputModel.parse_obj(py_kwargs) + + file_dialog = QFileDialog() + file_dialog.setFileMode(QFileDialog.AnyFile) + file_dialog.setAcceptMode(QFileDialog.AcceptSave) + file_dialog.setNameFilter("JSON Files (*.json);;All Files (*)") + + if file_dialog.exec_(): + file_path = file_dialog.selectedFiles()[0] + if file_path: + # Save the JSON data to the selected file + with open(file_path, 'w') as json_file: + json.dump(py_kwargs, json_file, indent=4) + app.exec_() + + + def clear_cache(self): dataset_cache.rm_all() @@ -114,7 +144,31 @@ def load_data(self): # instantiate pydantic objects, which are then handed off to the # same data ingestion function as the json loader. - # first, get the pydantic args for each selection type, embed in lists + + py_kwargs = {} + py_kwargs = self._validate_data_model() + model = _data_model.InputModel.parse_obj(py_kwargs) + + + # process each layer + layer_list, _ = _model_ingestor._process_validated_model(model) + + # align all layers after checking for or setting the reference layer + ref_layer = _check_for_reference_layer(self.viewer.layers) + if ref_layer is None: + ref_layer = _model_ingestor._choose_ref_layer(layer_list) + layer_list = ref_layer.align_sanitize_layers(layer_list) + + for new_layer in layer_list: + im_arr, im_kwargs, _ = new_layer + if self._post_load_function is not None: + im_arr = self._post_load_function(im_arr) + + # add the new layer + self.viewer.add_image(im_arr, **im_kwargs) + + def _validate_data_model(self): + #this function save json data selections_by_type = defaultdict(list) for selection in self.active_selections.values(): py_kwargs = selection.get_current_pydantic_kwargs() @@ -129,34 +183,17 @@ def load_data(self): py_kwargs, ignore_attrs="selections", ) - # add selections in py_kwargs["selections"] = selections_by_type # now ready to instantiate the base model py_kwargs = { + "$schema": "https://yt-napari.readthedocs.io/en/latest/_static/yt-napari_latest.json", "datasets": [ py_kwargs, ] } - model = _data_model.InputModel.parse_obj(py_kwargs) - - # process each layer - layer_list, _ = _model_ingestor._process_validated_model(model) - - # align all layers after checking for or setting the reference layer - ref_layer = _check_for_reference_layer(self.viewer.layers) - if ref_layer is None: - ref_layer = _model_ingestor._choose_ref_layer(layer_list) - layer_list = ref_layer.align_sanitize_layers(layer_list) - - for new_layer in layer_list: - im_arr, im_kwargs, _ = new_layer - if self._post_load_function is not None: - im_arr = self._post_load_function(im_arr) - - # add the new layer - self.viewer.add_image(im_arr, **im_kwargs) + return py_kwargs class SelectionEntry(QWidget): @@ -213,6 +250,7 @@ def get_current_pydantic_kwargs(self) -> dict: class TimeSeriesReader(YTReader): _pydantic_model = _data_model.Timeseries + app = QApplication(sys.argv) def add_load_group_widgets(self): # the load and clear buttons @@ -223,7 +261,53 @@ def add_load_group_widgets(self): load_group.addWidget(pb.native) self.layout().addLayout(load_group) + ss = widgets.PushButton(text="Save Selection") + ss.clicked.connect(self.save_selection) + load_group.addWidget(ss.native) + + def save_selection(self): + py_kwargs = {} + py_kwargs = self._validate_data_model() + #model = _data_model.InputModel.parse_obj(py_kwargs) + + + file_dialog = QFileDialog() + file_dialog.setFileMode(QFileDialog.AnyFile) + file_dialog.setAcceptMode(QFileDialog.AcceptSave) + file_dialog.setNameFilter("JSON Files (*.json);;All Files (*)") + + if file_dialog.exec_(): + file_path = file_dialog.selectedFiles()[0] + if file_path: + # Save the JSON data to the selected file + with open(file_path, 'w') as json_file: + json.dump(py_kwargs, json_file, indent=4) + app.exec_() + + def load_data(self): + py_kwargs = {} + py_kwargs = self._validate_data_model() + model = _data_model.InputModel.parse_obj(py_kwargs) + + if _use_threading: + worker = time_series_load(model) + worker.returned.connect(self.process_timeseries_layers) + worker.start() + else: + _, layer_list = _model_ingestor._process_validated_model(model) + self.process_timeseries_layers(layer_list) + + def process_timeseries_layers(self, layer_list): + for new_layer in layer_list: + im_arr, im_kwargs, _ = new_layer + # probably can remove since the _special_loaders can be used + # if self._post_load_function is not None: + # im_arr = self._post_load_function(im_arr) + # add the new layer + self.viewer.add_image(im_arr, **im_kwargs) + + def _validate_data_model(self): # first, get the pydantic args for each selection type, embed in lists selections_by_type = defaultdict(list) for selection in self.active_selections.values(): @@ -254,30 +338,12 @@ def load_data(self): # now ready to instantiate the base model py_kwargs = { + "$schema": "https://yt-napari.readthedocs.io/en/latest/_static/yt-napari_latest.json", "timeseries": [ py_kwargs, ] } - - model = _data_model.InputModel.parse_obj(py_kwargs) - - if _use_threading: - worker = time_series_load(model) - worker.returned.connect(self.process_timeseries_layers) - worker.start() - else: - _, layer_list = _model_ingestor._process_validated_model(model) - self.process_timeseries_layers(layer_list) - - def process_timeseries_layers(self, layer_list): - for new_layer in layer_list: - im_arr, im_kwargs, _ = new_layer - # probably can remove since the _special_loaders can be used - # if self._post_load_function is not None: - # im_arr = self._post_load_function(im_arr) - # add the new layer - self.viewer.add_image(im_arr, **im_kwargs) - + return py_kwargs @thread_worker(progress=True) def time_series_load(model):