From 75a4df4632f47397af4fbaea9b85e1d07f5a7f00 Mon Sep 17 00:00:00 2001 From: Draga Doncila Pop <17995243+DragaDoncila@users.noreply.github.com> Date: Thu, 19 May 2022 15:57:45 +1000 Subject: [PATCH] Add preference saving from dialog for folders with extensions (#4535) Co-authored-by: Juan Nunez-Iglesias --- docs/release/release_0_4_16.md | 33 ++++++++++++++++--- .../_qt/dialogs/_tests/test_reader_dialog.py | 10 ++++++ napari/_qt/dialogs/qt_reader_dialog.py | 24 +++++++++----- napari/_qt/qt_viewer.py | 4 ++- 4 files changed, 57 insertions(+), 14 deletions(-) diff --git a/docs/release/release_0_4_16.md b/docs/release/release_0_4_16.md index aefba87b6a6..f493f9eff6a 100644 --- a/docs/release/release_0_4_16.md +++ b/docs/release/release_0_4_16.md @@ -26,15 +26,38 @@ After discussion in [#4102](https://github.com/napari/napari/pull/4102) and [#41 - Calling `viewer.open` *without* passing a plugin will result in an error if you have not saved a reader preference for that file pattern *and* multiple plugins can claim the file - - You can address this error by associating a preference for the file pattern, or calling `viewer.open(file_path, plugin=...) -- A preferred reader failing to read your file will result in an error + - To save a preference for a file pattern in Python, use: + + ```python + from napari.settings import get_settings + get_settings().plugins.extension2reader['*.tif'] = 'napari_tifffile' + get_settings().plugins.extension2reader['*.zarr'] = 'napari-ome-zarr' + ``` + + - To specify a plugin in a Python script: + + ```python + import napari + + viewer = napari.Viewer() + viewer.open('my-path.tif') # this will throw MultipleReaderError if napari_tifffile is installed as both it and builtins could open the file + viewer.open('my-path.tif', plugin='napari_tifffile') # this won't + ``` + + - To specify a plugin at the command line, use: + + ```sh + napari my-path.tif --plugin napari_tifffile + ``` +- A preferred reader failing to read your file will result in an error e.g. if you saved `napari_tifffile` as a preference for TIFFs but then tried to open a broken file - When opening a file through a GUI pathway (drag & drop, File -> Open, Open Sample) you are provided with a dialog allowing you to choose among the various plugins that are compatible with your file - - This dialog also allows you to save a preference for files with extensions + - This dialog also allows you to save a preference for files and folders with extensions - This dialog also pops up if a preferred reader fails to open your file - Preference saving for file reading is now supported for filename patterns accepted by `npe2` readers, rather than strictly file extensions - Existing preferences for file extensions will be automatically updated e.g. `.tif` will become `*.tif` - Reader preferences for filename patterns can be saved in the GUI via the preference dialog - + - Reader preferences for folders are not yet supported in the GUI preference dialog - use the Python method above + - This will be addressed by the next release We have thought carefully about these choices, but there are still some open questions to address, and features to implement. Some of these are captured across the issues listed below, and we'd love to hear any feedback you have about the new behavior! @@ -150,6 +173,8 @@ We have thought carefully about these choices, but there are still some open que console, so this is strictly an improvement!) - Allow resizing left dock widgets (#4368) - Add filename pattern to reader associations to preference dialog (#4459) +- Add preference saving from dialog for folders with extensions #4535 + ## Deprecations diff --git a/napari/_qt/dialogs/_tests/test_reader_dialog.py b/napari/_qt/dialogs/_tests/test_reader_dialog.py index 5116e1eaa77..a7e04d275dc 100644 --- a/napari/_qt/dialogs/_tests/test_reader_dialog.py +++ b/napari/_qt/dialogs/_tests/test_reader_dialog.py @@ -52,6 +52,16 @@ def test_reader_dir(tmpdir, reader_dialog): assert not hasattr(widg, 'persist_checkbox') +def test_reader_dir_with_extension(tmpdir, reader_dialog): + dir = tmpdir.mkdir('my_dir.zarr') + widg = reader_dialog(pth=dir, readers={'p1': 'p1', 'p2': 'p2'}) + assert hasattr(widg, 'persist_checkbox') + assert ( + widg.persist_checkbox.text() + == "Remember this choice for files with a .zarr extension" + ) + + def test_get_plugin_choice(tmpdir, reader_dialog): file_pth = tmpdir.join('my_file.tif') widg = reader_dialog(pth=file_pth, readers={'p1': 'p1', 'p2': 'p2'}) diff --git a/napari/_qt/dialogs/qt_reader_dialog.py b/napari/_qt/dialogs/qt_reader_dialog.py index 594562541e3..4f71e12cfcf 100644 --- a/napari/_qt/dialogs/qt_reader_dialog.py +++ b/napari/_qt/dialogs/qt_reader_dialog.py @@ -26,7 +26,6 @@ def __init__( self, pth: str = '', parent: QWidget = None, - extension: str = '', readers: Dict[str, str] = {}, error_message: str = '', ): @@ -34,7 +33,11 @@ def __init__( self.setObjectName('Choose reader') self.setWindowTitle(trans._('Choose reader')) self._current_file = pth - self._extension = extension + + if os.path.isdir(pth) and str(pth).endswith('/'): + pth = os.path.dirname(pth) + self._extension = os.path.splitext(pth)[1] + self._reader_buttons = [] self.setup_ui(error_message, readers) @@ -62,11 +65,10 @@ def setup_ui(self, error_message, readers): self.btn_box.accepted.connect(self.accept) self.btn_box.rejected.connect(self.reject) - # checkbox to remember the choice (doesn't pop up for folders) - extension = os.path.splitext(self._current_file)[1] - if extension: + # checkbox to remember the choice (doesn't pop up for folders with no extension) + if self._extension: self.persist_checkbox = QCheckBox( - f'Remember this choice for files with a {extension} extension' + f'Remember this choice for files with a {self._extension} extension' ) self.persist_checkbox.toggle() layout.addWidget(self.persist_checkbox) @@ -142,18 +144,22 @@ def handle_gui_reading( readers = prepare_remaining_readers(paths, plugin_name, error) error_message = str(error) if error else '' - _, extension = os.path.splitext(_path) readerDialog = QtReaderDialog( parent=qt_viewer, pth=_path, - extension=extension, error_message=error_message, readers=readers, ) display_name, persist = readerDialog.get_user_choices() if display_name: open_with_dialog_choices( - display_name, persist, extension, readers, paths, stack, qt_viewer + display_name, + persist, + readerDialog._extension, + readers, + paths, + stack, + qt_viewer, ) diff --git a/napari/_qt/qt_viewer.py b/napari/_qt/qt_viewer.py index c83a6c5d824..ba9dd0c1279 100644 --- a/napari/_qt/qt_viewer.py +++ b/napari/_qt/qt_viewer.py @@ -3,6 +3,7 @@ import logging import traceback import warnings +from pathlib import Path from typing import TYPE_CHECKING, List, Optional, Sequence, Tuple from weakref import WeakSet @@ -1072,7 +1073,8 @@ def dropEvent(self, event): filenames = [] for url in event.mimeData().urls(): if url.isLocalFile(): - filenames.append(url.toLocalFile()) + # directories get a trailing "/", Path conversion removes it + filenames.append(str(Path(url.toLocalFile()))) else: filenames.append(url.toString())