From 37136c94ce97fc2743fa5ec9d4a36079c2628230 Mon Sep 17 00:00:00 2001 From: "Philipp S. Sommer" Date: Tue, 10 Nov 2020 13:23:05 +0100 Subject: [PATCH 1/2] add reload functionality --- psy_view/ds_widget.py | 59 +++++++++++++++++++++++++++++++++++++++++ tests/conftest.py | 7 +++-- tests/test_ds_widget.py | 25 +++++++++++++++++ 3 files changed, 89 insertions(+), 2 deletions(-) diff --git a/psy_view/ds_widget.py b/psy_view/ds_widget.py index 87e2043..0e785c9 100644 --- a/psy_view/ds_widget.py +++ b/psy_view/ds_widget.py @@ -179,6 +179,12 @@ def __init__(self, ds: Optional[Dataset] = None, *args, **kwargs) -> None: # third row, navigation self.navigation_box = QtWidgets.QHBoxLayout() + self.btn_reload = utils.add_pushbutton( + get_psy_icon("refresh.png"), self.reload, + "Close all open datasets and recreate the plots", + self.navigation_box, icon=True + ) + # -- animate backwards button self.btn_animate_backward = utils.add_pushbutton( "◀◀", lambda: self.animate_backward(), @@ -311,6 +317,26 @@ def __init__(self, ds: Optional[Dataset] = None, *args, **kwargs) -> None: self.cids: Dict[str, int] = {} + def reload(self) -> None: + """Close the plot and recreate it.""" + import psyplot.project as psy + + sp = self._sp + fname = sp.dsnames_map[self.ds.psy.num] # type: ignore + project = sp.save_project() + sp.close(True, True, True) + self.ds_tree.clear() + self._ds_nums.clear() + self.refresh() + self._sp = sp = psy.Project.load_project(project) + self._ds_nums = sp.datasets + num = next(num for num, f in sp.dsnames_map.items() if f == fname) + self.ds = self.open_datasets[num] + for ds in self._ds_nums.values(): + self._add_ds_item(ds) + sp.show() + self.refresh() + def setup_ds_tree(self) -> None: """Setup the number of columns and the header of the dataset tree.""" self.ds_tree = tree = QtWidgets.QTreeWidget() @@ -443,6 +469,9 @@ def set_dataset(self, ds: Optional[Dataset] = None) -> None: def add_ds_item(self) -> None: """Add a new :class:`DatasetTreeItem` for the current :attr:`ds`.""" ds: Dataset = self.ds # type: ignore + self._add_ds_item(ds) + + def _add_ds_item(self, ds: Dataset) -> None: tree = self.ds_tree ds_item = DatasetTreeItem(ds, self.ds_attr_columns, 0) fname = psyd.get_filename_ds(ds, False)[0] @@ -463,6 +492,7 @@ def add_ds_item(self) -> None: # make sure we do not loose track of open datasets self._ds_nums[ds.psy.num] = ds + @property def open_datasets(self) -> Dict[int, Dataset]: """Get a mapping from path to dataset number of the open datasets.""" @@ -1640,6 +1670,33 @@ def close_sp(self) -> None: if ds.psy.num not in self._sp.datasets: self.set_dataset(ds) + def reload(self) -> None: + """Close the plot and recreate it.""" + import psyplot.project as psy + + if not all(self._sp.dsnames_map.values()): + # we have datasets that only exist in memory, so better ask + answer = QtWidgets.QMessageBox.question( + self, "Shall I close this?", + "Reloading the data closes all open plots. Any data in the memory " + "is lost and open files are reloaded from disk! " + "Shall I really continue?") + if answer != QtWidgets.QMessageBox.Yes: + return + + sp = self._sp + fname = sp.dsnames_map[self.ds.psy.num] # type: ignore + project = sp.save_project() + sp.close(True, True, True) + self.ds_tree.clear() + self._ds_nums.clear() + self.refresh() + self._sp = sp = psy.Project.load_project(project) + num = next(num for num, f in sp.dsnames_map.items() if f == fname) + self.ds = self.open_datasets[num] + sp.show() + self.refresh() + def oncpchange(self, sp: Optional[Project]) -> None: """Update this widget from the current psyplot main (or sub) project.""" self.reset_combo_array() @@ -1651,6 +1708,8 @@ def oncpchange(self, sp: Optional[Project]) -> None: self.btn_del.setEnabled(False) elif self.ds is None and self._sp: self.set_dataset(next(iter(self._sp.datasets.values()))) + elif self.ds is not None and self._sp: + self.enable_navigation() def show_fig(self, sp: Optional[Project]) -> None: """Show the figure of the the current subproject.""" diff --git a/tests/conftest.py b/tests/conftest.py index 1f1a068..f19b6fb 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -3,14 +3,17 @@ import psyplot_gui.compat.qtcompat -test_dir = osp.dirname(__file__) +_test_dir = osp.dirname(__file__) +@pytest.fixture +def test_dir() -> str: + return _test_dir @pytest.fixture(params=["regular-test.nc", "regional-icon-test.nc", "rotated-pole-test.nc", "icon-test.nc"]) -def test_file(request): +def test_file(test_dir, request): return osp.join(test_dir, request.param) diff --git a/tests/test_ds_widget.py b/tests/test_ds_widget.py index db5e824..a1ba3fe 100644 --- a/tests/test_ds_widget.py +++ b/tests/test_ds_widget.py @@ -1,5 +1,6 @@ """Test the main functionality of the psy-view package, namely the widget""" import os.path as osp +import shutil from PyQt5.QtCore import Qt from PyQt5 import QtWidgets import pytest @@ -429,3 +430,27 @@ def test_export_animation(qtbot, ds_widget, plotmethod, tmpdir, monkeypatch): assert not ds_widget._animating assert osp.exists(osp.join(tmpdir, "test.gif")) + + +def test_reload(qtbot, test_dir, tmp_path) -> None: + """Test the reload button.""" + import psyplot.project as psy + from psy_view.ds_widget import DatasetWidget + + f1, f2 = "regular-test.nc", "regional-icon-test.nc" + shutil.copy(osp.join(test_dir, f1), str(tmp_path / f1)) + + ds_widget = DatasetWidget(psy.open_dataset(str(tmp_path / f1))) + qtbot.addWidget(ds_widget) + qtbot.mouseClick(ds_widget.variable_buttons['t2m'], Qt.LeftButton) + + assert ds_widget.ds_tree.topLevelItemCount() == 1 + assert ds_widget.ds["t2m"].ndim == 4 + + # now copy the icon file to the same destination and reload everything + shutil.copy(osp.join(test_dir, f2), str(tmp_path / f1)) + ds_widget.reload() + + assert ds_widget.ds_tree.topLevelItemCount() == 1 + assert ds_widget.ds["t2m"].ndim == 3 + assert len(psy.gcp(True)) == 1 From de88110ba14e5ae4af063713ae6c88280556fa67 Mon Sep 17 00:00:00 2001 From: "Philipp S. Sommer" Date: Tue, 10 Nov 2020 13:24:41 +0100 Subject: [PATCH 2/2] add reload button the changelog --- CHANGELOG.rst | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 326cd4e..f3d2802 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -12,4 +12,7 @@ Changed Added ----- - A widget to control the plot type for mapplot and plot2d (see - `#46 `__) \ No newline at end of file + `#46 `__) +- A button to reload all plots. This is useful, for instance, if the data on + your disk changed and you just want to update the plot + `#48 `__) \ No newline at end of file