Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,7 @@ Changed
Added
-----
- A widget to control the plot type for mapplot and plot2d (see
`#46 <https://github.com/psyplot/psy-view/pull/46>`__)
`#46 <https://github.com/psyplot/psy-view/pull/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 <https://github.com/psyplot/psy-view/pull/48>`__)
59 changes: 59 additions & 0 deletions psy_view/ds_widget.py
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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]
Expand All @@ -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."""
Expand Down Expand Up @@ -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()
Expand All @@ -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."""
Expand Down
7 changes: 5 additions & 2 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)


Expand Down
25 changes: 25 additions & 0 deletions tests/test_ds_widget.py
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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