diff --git a/CHANGELOG.md b/CHANGELOG.md index d986821..541df40 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ This is an overview of major changes. Refer to the git repository for a full log Version 0.1.34 ------------- - #325 Command-line option to set window size +- #328 Menu commands to show cookie cutter and metadata template files Version 0.1.33 ------------- diff --git a/inselect/gui/cookie_cutter_choice.py b/inselect/gui/cookie_cutter_choice.py index 197e46d..29bf459 100644 --- a/inselect/gui/cookie_cutter_choice.py +++ b/inselect/gui/cookie_cutter_choice.py @@ -79,6 +79,14 @@ def create_and_use(self, boxes, path): cookie_cutter.save(path) self.load(path) + @property + def current_path(self): + """The path to the selected UserTemplate or None, if the default + template is selected + """ + current = QSettings().value(self.PATH_KEY) + return Path(current) if current else None + @property def current(self): "The selected CookieCutter" diff --git a/inselect/gui/cookie_cutter_widget.py b/inselect/gui/cookie_cutter_widget.py index 54b3c99..b82fae7 100644 --- a/inselect/gui/cookie_cutter_widget.py +++ b/inselect/gui/cookie_cutter_widget.py @@ -5,7 +5,7 @@ from inselect.lib.utils import debug_print from .cookie_cutter_choice import cookie_cutter_choice -from .utils import report_to_user, load_icon +from .utils import report_to_user, load_icon, reveal_path class CookieCutterWidget(QObject): @@ -32,6 +32,9 @@ def _create_actions(self): "Choose...", self, triggered=self.choose, icon=load_icon(':/icons/open.png') ) + self.reveal_action = QAction( + "Reveal cookie cutter", self, triggered=self.reveal + ) self.clear_action = QAction( "Do not use a cookie cutter", self, triggered=self.clear, icon=load_icon(':/icons/close.png') @@ -42,6 +45,7 @@ def inject_actions(self, menu): "Adds cookie cutter actions to menu" menu.addAction(self.choose_action) menu.addAction(self.apply_current_action) + menu.addAction(self.reveal_action) menu.addSeparator() menu.addAction(self.clear_action) menu.addSeparator() @@ -66,6 +70,10 @@ def choose(self): # Save the user's choice cookie_cutter_choice().load(path) + @report_to_user + def reveal(self): + reveal_path(cookie_cutter_choice().current_path) + def sync_ui(self, button, has_document, has_rows): "Sync state of actions" debug_print('CookieCutterWidget.sync_ui') @@ -82,4 +90,5 @@ def sync_ui(self, button, has_document, has_rows): self.save_to_new_action.setEnabled(has_rows) self.clear_action.setEnabled(has_current) + self.reveal_action.setEnabled(has_current) self.apply_current_action.setEnabled(has_document and has_current) diff --git a/inselect/gui/info_widget.py b/inselect/gui/info_widget.py index d433fa7..f4b4109 100644 --- a/inselect/gui/info_widget.py +++ b/inselect/gui/info_widget.py @@ -1,39 +1,15 @@ import humanize import locale -import platform -import subprocess -import sys - from PySide.QtCore import Qt from PySide.QtGui import QFormLayout, QLabel, QWidget from inselect.lib.utils import format_dt_display -from inselect.gui.utils import BoldLabel, HorizontalLine +from inselect.gui.utils import BoldLabel, HorizontalLine, reveal_path from .popup_panel import PopupPanel from .utils import report_to_user -def reveal_path(path): - """Shows path in Finder (on Mac) or in Explorer (on Windows) - """ - # http://stackoverflow.com/a/3546503 - path = path.resolve() - if sys.platform.startswith("win"): - res = subprocess.call(["explorer.exe", u"/select,{0}".format(path)]) - if 1 != res: - raise ValueError('Unexpected exit code [{0}]'.format(res)) - elif 'Darwin' == platform.system(): - reveal = u'tell application "Finder" to reveal POSIX file "{0}"' - activate = u'tell application "Finder" to activate "{0}"' - args = ['/usr/bin/osascript', '-e'] - subprocess.check_call(args + [reveal.format(path)]) - subprocess.check_call(args + [activate.format(path)]) - else: - # What to do on Linux? - pass - - class RevealPathLabel(QLabel): """A QLabel that, when clicked, reveals a path in Finder / Explorer """ diff --git a/inselect/gui/user_template_choice.py b/inselect/gui/user_template_choice.py index 26fa7e2..4d0c6cc 100644 --- a/inselect/gui/user_template_choice.py +++ b/inselect/gui/user_template_choice.py @@ -81,6 +81,14 @@ def refresh(self): # Using the default (DWC) template - no need to do anything pass + @property + def current_path(self): + """The path to the selected UserTemplate or None, if the default + template is selected + """ + current = QSettings().value(self.PATH_KEY) + return Path(current) if current else None + @property def current(self): "The selected UserTemplate" diff --git a/inselect/gui/user_template_popup_button.py b/inselect/gui/user_template_popup_button.py index 7c4ae1f..089daac 100644 --- a/inselect/gui/user_template_popup_button.py +++ b/inselect/gui/user_template_popup_button.py @@ -4,7 +4,7 @@ from inselect.lib.utils import debug_print from .user_template_choice import user_template_choice -from .utils import report_to_user, load_icon +from .utils import report_to_user, load_icon, reveal_path class UserTemplatePopupButton(QPushButton): @@ -42,15 +42,19 @@ def _create_actions(self): "Reload", self, triggered=self.refresh, icon=load_icon(':/icons/refresh.png') ) + self._reveal_template_action = QAction( + "Reveal template", self, triggered=self.reveal + ) self._default_action = QAction( u"Default ({0})".format(user_template_choice().DEFAULT.name), - self, triggered=self.default + self, triggered=self.default, icon=load_icon(':/icons/close.png') ) def inject_actions(self, menu): "Adds user template actions to menu" menu.addAction(self._choose_action) menu.addAction(self._refresh_action) + menu.addAction(self._reveal_template_action) menu.addSeparator() menu.addAction(self._default_action) @@ -78,6 +82,10 @@ def refresh(self): debug_print('UserTemplateWidget.refresh') user_template_choice().refresh() + @report_to_user + def reveal(self): + reveal_path(user_template_choice().current_path) + def changed(self): "Slot for UserTemplateChoice.template_changed" debug_print('UserTemplateWidget.changed') @@ -85,3 +93,4 @@ def changed(self): self.setText(choice.current.name) self._default_action.setEnabled(not choice.current_is_default) self._refresh_action.setEnabled(not choice.current_is_default) + self._reveal_template_action.setEnabled(not choice.current_is_default) diff --git a/inselect/gui/utils.py b/inselect/gui/utils.py index 9df3740..6e7c52a 100644 --- a/inselect/gui/utils.py +++ b/inselect/gui/utils.py @@ -1,3 +1,6 @@ +import platform +import subprocess +import sys import traceback from contextlib import contextmanager @@ -199,3 +202,23 @@ class BoldLabel(QLabel): """A label in a bold font """ pass + + +def reveal_path(path): + """Shows path in Finder (on Mac) or in Explorer (on Windows) + """ + # http://stackoverflow.com/a/3546503 + path = path.resolve() + if sys.platform.startswith("win"): + res = subprocess.call(["explorer.exe", u"/select,{0}".format(path)]) + if 1 != res: + raise ValueError('Unexpected exit code [{0}]'.format(res)) + elif 'Darwin' == platform.system(): + reveal = u'tell application "Finder" to reveal POSIX file "{0}"' + activate = u'tell application "Finder" to activate "{0}"' + args = ['/usr/bin/osascript', '-e'] + subprocess.check_call(args + [reveal.format(path)]) + subprocess.check_call(args + [activate.format(path)]) + else: + # What to do on Linux? + pass diff --git a/inselect/tests/gui/test_utils.py b/inselect/tests/gui/test_utils.py new file mode 100644 index 0000000..3002cf4 --- /dev/null +++ b/inselect/tests/gui/test_utils.py @@ -0,0 +1,44 @@ +import platform +import unittest +import subprocess +import sys + +from mock import patch + +from inselect.gui.utils import reveal_path +from inselect.tests.utils import temp_directory_with_files + + +class TestUtils(unittest.TestCase): + """Test GUI utils + """ + @unittest.skipUnless(sys.platform.startswith("win"), "requires Windows") + @patch.object(subprocess, 'call', return_value=1) + def test_reveal_path_windows(self, mock_subprocess): + with temp_directory_with_files() as tempdir: + path = tempdir / 'xyz' + path.open('w') + reveal_path(path) + expected = [ + "explorer.exe", + u"/select,{0}".format(str(path.resolve())) + ] + mock_subprocess.assert_called_once_with(expected) + + @unittest.skipUnless('Darwin' == platform.system(), "requires OS X") + @patch.object(subprocess, 'check_call') + def test_reveal_path_os_x(self, mock_subprocess): + with temp_directory_with_files() as tempdir: + path = tempdir / 'xyz' + path.open('w') + reveal_path(path) + expected = [ + '/usr/bin/osascript', + '-e', + u'tell application "Finder" to reveal POSIX file "{0}"'.format(str(path.resolve())) + ] + mock_subprocess.assert_any_call(expected) + + +if __name__ == '__main__': + unittest.main()