From 07a8e6938a9b5565668fc6f8371d030f2aaa4e53 Mon Sep 17 00:00:00 2001 From: Jitse Niesen Date: Sat, 26 Dec 2020 15:24:47 +0000 Subject: [PATCH] Plugin: Allow user to config notebook theme Add a Notebook config page, with a combo box to set the notebook theme. There are three possibilities: Dark, Light, Same as Spyder (default). Set the notebook theme according to the user's preference. Also add some tests. --- spyder_notebook/config.py | 38 ++++++++++++++++++++++++++++ spyder_notebook/notebookplugin.py | 26 +++++++++++++++---- spyder_notebook/tests/test_config.py | 32 +++++++++++++++++++++++ spyder_notebook/tests/test_plugin.py | 14 ++++++++++ 4 files changed, 105 insertions(+), 5 deletions(-) create mode 100644 spyder_notebook/config.py create mode 100644 spyder_notebook/tests/test_config.py diff --git a/spyder_notebook/config.py b/spyder_notebook/config.py new file mode 100644 index 00000000..40a2ca6f --- /dev/null +++ b/spyder_notebook/config.py @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- +# +# Copyright (c) Spyder Project Contributors +# Licensed under the terms of the MIT License + +"""Spyder configuration page for notebook plugin.""" + +# Qt imports +from qtpy.QtWidgets import QGridLayout, QGroupBox, QVBoxLayout + +# Spyder imports +from spyder.api.preferences import PluginConfigPage + +# Local imports +from spyder_notebook.utils.localization import _ + + +class NotebookConfigPage(PluginConfigPage): + """Widget with configuration options for notebook plugin.""" + + def setup_page(self): + """Fill configuration page with widgets.""" + themes = ['Same as Spyder', 'Light', 'Dark'] + theme_choices = list(zip(themes, + [theme.lower() for theme in themes])) + theme_combo = self.create_combobox( + _('Notebook theme'), theme_choices, 'theme', restart=True) + + interface_layout = QGridLayout() + interface_layout.addWidget(theme_combo.label, 0, 0) + interface_layout.addWidget(theme_combo.combobox, 0, 1) + interface_group = QGroupBox(_('Interface')) + interface_group.setLayout(interface_layout) + + vlayout = QVBoxLayout() + vlayout.addWidget(interface_group) + vlayout.addStretch(1) + self.setLayout(vlayout) diff --git a/spyder_notebook/notebookplugin.py b/spyder_notebook/notebookplugin.py index 46e58e40..063864e0 100644 --- a/spyder_notebook/notebookplugin.py +++ b/spyder_notebook/notebookplugin.py @@ -24,6 +24,7 @@ from spyder.utils.switcher import shorten_paths # Local imports +from spyder_notebook.config import NotebookConfigPage from spyder_notebook.utils.localization import _ from spyder_notebook.utils.servermanager import ServerManager from spyder_notebook.widgets.notebooktabwidget import NotebookTabWidget @@ -40,8 +41,10 @@ class NotebookPlugin(SpyderPluginWidget): CONF_SECTION = 'notebook' CONF_DEFAULTS = [(CONF_SECTION, { - 'recent_notebooks': [], # Items in "Open recent" menu - 'opened_notebooks': []})] # Notebooks to open at start + 'recent_notebooks': [], # Items in "Open recent" menu + 'opened_notebooks': [], # Notebooks to open at start + 'theme': 'same as spyder'})] # Notebook theme (light/dark) + CONFIGWIDGET_CLASS = NotebookConfigPage focus_changed = Signal() def __init__(self, parent, testing=False): @@ -72,18 +75,31 @@ def __init__(self, parent, testing=False): menu_btn.setPopupMode(menu_btn.InstantPopup) corner_widgets = {Qt.TopRightCorner: [new_notebook_btn, menu_btn]} - dark_theme = is_dark_interface() - self.server_manager = ServerManager(dark_theme) + self.server_manager = ServerManager(self.dark_theme) self.tabwidget = NotebookTabWidget( self, self.server_manager, menu=self._options_menu, actions=self.menu_actions, corner_widgets=corner_widgets, - dark_theme=dark_theme) + dark_theme=self.dark_theme) self.tabwidget.currentChanged.connect(self.refresh_plugin) layout.addWidget(self.tabwidget) self.setLayout(layout) + @property + def dark_theme(self): + """Whether to use the dark theme for notebooks (bool).""" + theme_config = self.get_option('theme', default='same as spyder') + if theme_config == 'same as spyder': + return is_dark_interface() + elif theme_config == 'dark': + return True + elif theme_config == 'light': + return False + else: + raise RuntimeError('theme config corrupted, value = {}' + .format(theme_config)) + # ------ SpyderPluginMixin API -------------------------------------------- def on_first_registration(self): """Action to be performed on first plugin registration.""" diff --git a/spyder_notebook/tests/test_config.py b/spyder_notebook/tests/test_config.py new file mode 100644 index 00000000..852d0231 --- /dev/null +++ b/spyder_notebook/tests/test_config.py @@ -0,0 +1,32 @@ +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# + +"""Tests for config.py""" + +# Third-party library imports +import pytest + +# Spyder imports +from spyder.preferences.configdialog import ConfigDialog + +# Local imports +from spyder_notebook.config import NotebookConfigPage +from spyder_notebook.tests.test_plugin import plugin_no_server + + +def test_config(mocker, plugin_no_server, qtbot): + """Test that config page can be created and shown.""" + dlg = ConfigDialog() + page = NotebookConfigPage(plugin_no_server, parent=plugin_no_server) + page.initialize() + dlg.add_page(page) + dlg.show() + qtbot.addWidget(dlg) + # no assert, just check that the config page can be created + + +if __name__ == "__main__": + pytest.main() diff --git a/spyder_notebook/tests/test_plugin.py b/spyder_notebook/tests/test_plugin.py index c6c54d42..8a4b5cab 100644 --- a/spyder_notebook/tests/test_plugin.py +++ b/spyder_notebook/tests/test_plugin.py @@ -366,5 +366,19 @@ def test_view_server_info(mocker, plugin_no_server): mock_ServerInfoDialog.return_value.show.assert_called_once() +@pytest.mark.parametrize('config_value', ['same as spyder', 'dark', 'light']) +@pytest.mark.parametrize('spyder_is_dark', [True, False]) +def test_dark_theme(mocker, plugin_no_server, config_value, spyder_is_dark): + plugin_no_server.set_option('theme', config_value) + mocker.patch('spyder_notebook.notebookplugin.is_dark_interface', + return_value=spyder_is_dark) + + value = plugin_no_server.dark_theme + + expected = (config_value == 'dark' or + (config_value == 'same as spyder' and spyder_is_dark)) + assert value == expected + + if __name__ == "__main__": pytest.main()