diff --git a/openlp/core/__init__.py b/openlp/core/__init__.py index ef67ec745..c9e446143 100644 --- a/openlp/core/__init__.py +++ b/openlp/core/__init__.py @@ -42,11 +42,11 @@ from openlp.core.version import check_for_update, get_version from openlp.core.lib import ScreenList from openlp.core.resources import qInitResources from openlp.core.ui import SplashScreen -from openlp.core.ui.dark import HAS_DARK_STYLE, DARK_STYLESHEET from openlp.core.ui.exceptionform import ExceptionForm from openlp.core.ui.firsttimeform import FirstTimeForm from openlp.core.ui.firsttimelanguageform import FirstTimeLanguageForm from openlp.core.ui.mainwindow import MainWindow +from openlp.core.ui.style import get_application_stylesheet __all__ = ['OpenLP', 'main'] @@ -54,28 +54,6 @@ __all__ = ['OpenLP', 'main'] log = logging.getLogger() -WIN_REPAIR_STYLESHEET = """ -QMainWindow::separator -{ - border: none; -} - -QDockWidget::title -{ - border: 1px solid palette(dark); - padding-left: 5px; - padding-top: 2px; - margin: 1px 0; -} - -QToolBar -{ - border: none; - margin: 0; - padding: 0; -} -""" - class OpenLP(OpenLPMixin, QtWidgets.QApplication): """ @@ -120,17 +98,7 @@ class OpenLP(OpenLPMixin, QtWidgets.QApplication): QtCore.QCoreApplication.exit() sys.exit() # Correct stylesheet bugs - application_stylesheet = '' - if HAS_DARK_STYLE: - application_stylesheet = DARK_STYLESHEET - else: - if not Settings().value('advanced/alternate rows'): - base_color = self.palette().color(QtGui.QPalette.Active, QtGui.QPalette.Base) - alternate_rows_repair_stylesheet = \ - 'QTableWidget, QListWidget, QTreeWidget {alternate-background-color: ' + base_color.name() + ';}\n' - application_stylesheet += alternate_rows_repair_stylesheet - if is_win(): - application_stylesheet += WIN_REPAIR_STYLESHEET + application_stylesheet = get_application_stylesheet() if application_stylesheet: self.setStyleSheet(application_stylesheet) can_show_splash = Settings().value('core/show splash') diff --git a/openlp/core/common/settings.py b/openlp/core/common/settings.py index be1b29c75..0540ff81e 100644 --- a/openlp/core/common/settings.py +++ b/openlp/core/common/settings.py @@ -136,6 +136,7 @@ class Settings(QtCore.QSettings): 'advanced/single click service preview': False, 'advanced/x11 bypass wm': X11_BYPASS_DEFAULT, 'advanced/search as type': True, + 'advanced/use_dark_style': False, 'api/twelve hour': True, 'api/port': 4316, 'api/websocket port': 4317, diff --git a/openlp/core/ui/advancedtab.py b/openlp/core/ui/advancedtab.py index 8ec55999b..b655b3899 100644 --- a/openlp/core/ui/advancedtab.py +++ b/openlp/core/ui/advancedtab.py @@ -32,6 +32,7 @@ from openlp.core.common.languagemanager import format_time from openlp.core.common.path import path_to_str from openlp.core.lib import SettingsTab, build_icon from openlp.core.ui.lib import PathEdit, PathType +from openlp.core.ui.style import HAS_DARK_STYLE log = logging.getLogger(__name__) @@ -109,8 +110,80 @@ class AdvancedTab(SettingsTab): self.enable_auto_close_check_box.setObjectName('enable_auto_close_check_box') self.ui_layout.addRow(self.enable_auto_close_check_box) self.left_layout.addWidget(self.ui_group_box) + if HAS_DARK_STYLE: + self.use_dark_style_checkbox = QtWidgets.QCheckBox(self.ui_group_box) + self.use_dark_style_checkbox.setObjectName('use_dark_style_checkbox') + self.ui_layout.addRow(self.use_dark_style_checkbox) + # Data Directory + self.data_directory_group_box = QtWidgets.QGroupBox(self.left_column) + self.data_directory_group_box.setObjectName('data_directory_group_box') + self.data_directory_layout = QtWidgets.QFormLayout(self.data_directory_group_box) + self.data_directory_layout.setObjectName('data_directory_layout') + self.data_directory_new_label = QtWidgets.QLabel(self.data_directory_group_box) + self.data_directory_new_label.setObjectName('data_directory_current_label') + self.data_directory_path_edit = PathEdit(self.data_directory_group_box, path_type=PathType.Directories, + default_path=AppLocation.get_directory(AppLocation.DataDir)) + self.data_directory_layout.addRow(self.data_directory_new_label, self.data_directory_path_edit) + self.new_data_directory_has_files_label = QtWidgets.QLabel(self.data_directory_group_box) + self.new_data_directory_has_files_label.setObjectName('new_data_directory_has_files_label') + self.new_data_directory_has_files_label.setWordWrap(True) + self.data_directory_cancel_button = QtWidgets.QToolButton(self.data_directory_group_box) + self.data_directory_cancel_button.setObjectName('data_directory_cancel_button') + self.data_directory_cancel_button.setIcon(build_icon(':/general/general_delete.png')) + self.data_directory_copy_check_layout = QtWidgets.QHBoxLayout() + self.data_directory_copy_check_layout.setObjectName('data_directory_copy_check_layout') + self.data_directory_copy_check_box = QtWidgets.QCheckBox(self.data_directory_group_box) + self.data_directory_copy_check_box.setObjectName('data_directory_copy_check_box') + self.data_directory_copy_check_layout.addWidget(self.data_directory_copy_check_box) + self.data_directory_copy_check_layout.addStretch() + self.data_directory_copy_check_layout.addWidget(self.data_directory_cancel_button) + self.data_directory_layout.addRow(self.data_directory_copy_check_layout) + self.data_directory_layout.addRow(self.new_data_directory_has_files_label) + self.left_layout.addWidget(self.data_directory_group_box) + # Hide mouse + self.hide_mouse_group_box = QtWidgets.QGroupBox(self.right_column) + self.hide_mouse_group_box.setObjectName('hide_mouse_group_box') + self.hide_mouse_layout = QtWidgets.QVBoxLayout(self.hide_mouse_group_box) + self.hide_mouse_layout.setObjectName('hide_mouse_layout') + self.hide_mouse_check_box = QtWidgets.QCheckBox(self.hide_mouse_group_box) + self.hide_mouse_check_box.setObjectName('hide_mouse_check_box') + self.hide_mouse_layout.addWidget(self.hide_mouse_check_box) + self.right_layout.addWidget(self.hide_mouse_group_box) + # Service Item Slide Limits + self.slide_group_box = QtWidgets.QGroupBox(self.right_column) + self.slide_group_box.setObjectName('slide_group_box') + self.slide_layout = QtWidgets.QVBoxLayout(self.slide_group_box) + self.slide_layout.setObjectName('slide_layout') + self.slide_label = QtWidgets.QLabel(self.slide_group_box) + self.slide_label.setWordWrap(True) + self.slide_layout.addWidget(self.slide_label) + self.end_slide_radio_button = QtWidgets.QRadioButton(self.slide_group_box) + self.end_slide_radio_button.setObjectName('end_slide_radio_button') + self.slide_layout.addWidget(self.end_slide_radio_button) + self.wrap_slide_radio_button = QtWidgets.QRadioButton(self.slide_group_box) + self.wrap_slide_radio_button.setObjectName('wrap_slide_radio_button') + self.slide_layout.addWidget(self.wrap_slide_radio_button) + self.next_item_radio_button = QtWidgets.QRadioButton(self.slide_group_box) + self.next_item_radio_button.setObjectName('next_item_radio_button') + self.slide_layout.addWidget(self.next_item_radio_button) + self.right_layout.addWidget(self.slide_group_box) + # Display Workarounds + self.display_workaround_group_box = QtWidgets.QGroupBox(self.right_column) + self.display_workaround_group_box.setObjectName('display_workaround_group_box') + self.display_workaround_layout = QtWidgets.QVBoxLayout(self.display_workaround_group_box) + self.display_workaround_layout.setObjectName('display_workaround_layout') + self.ignore_aspect_ratio_check_box = QtWidgets.QCheckBox(self.display_workaround_group_box) + self.ignore_aspect_ratio_check_box.setObjectName('ignore_aspect_ratio_check_box') + self.display_workaround_layout.addWidget(self.ignore_aspect_ratio_check_box) + self.x11_bypass_check_box = QtWidgets.QCheckBox(self.display_workaround_group_box) + self.x11_bypass_check_box.setObjectName('x11_bypass_check_box') + self.display_workaround_layout.addWidget(self.x11_bypass_check_box) + self.alternate_rows_check_box = QtWidgets.QCheckBox(self.display_workaround_group_box) + self.alternate_rows_check_box.setObjectName('alternate_rows_check_box') + self.display_workaround_layout.addWidget(self.alternate_rows_check_box) + self.right_layout.addWidget(self.display_workaround_group_box) # Default service name - self.service_name_group_box = QtWidgets.QGroupBox(self.left_column) + self.service_name_group_box = QtWidgets.QGroupBox(self.right_column) self.service_name_group_box.setObjectName('service_name_group_box') self.service_name_layout = QtWidgets.QFormLayout(self.service_name_group_box) self.service_name_check_box = QtWidgets.QCheckBox(self.service_name_group_box) @@ -147,77 +220,11 @@ class AdvancedTab(SettingsTab): self.service_name_example = QtWidgets.QLabel(self.service_name_group_box) self.service_name_example.setObjectName('service_name_example') self.service_name_layout.addRow(self.service_name_example_label, self.service_name_example) - self.left_layout.addWidget(self.service_name_group_box) - # Data Directory - self.data_directory_group_box = QtWidgets.QGroupBox(self.left_column) - self.data_directory_group_box.setObjectName('data_directory_group_box') - self.data_directory_layout = QtWidgets.QFormLayout(self.data_directory_group_box) - self.data_directory_layout.setObjectName('data_directory_layout') - self.data_directory_new_label = QtWidgets.QLabel(self.data_directory_group_box) - self.data_directory_new_label.setObjectName('data_directory_current_label') - self.data_directory_path_edit = PathEdit(self.data_directory_group_box, path_type=PathType.Directories, - default_path=AppLocation.get_directory(AppLocation.DataDir)) - self.data_directory_layout.addRow(self.data_directory_new_label, self.data_directory_path_edit) - self.new_data_directory_has_files_label = QtWidgets.QLabel(self.data_directory_group_box) - self.new_data_directory_has_files_label.setObjectName('new_data_directory_has_files_label') - self.new_data_directory_has_files_label.setWordWrap(True) - self.data_directory_cancel_button = QtWidgets.QToolButton(self.data_directory_group_box) - self.data_directory_cancel_button.setObjectName('data_directory_cancel_button') - self.data_directory_cancel_button.setIcon(build_icon(':/general/general_delete.png')) - self.data_directory_copy_check_layout = QtWidgets.QHBoxLayout() - self.data_directory_copy_check_layout.setObjectName('data_directory_copy_check_layout') - self.data_directory_copy_check_box = QtWidgets.QCheckBox(self.data_directory_group_box) - self.data_directory_copy_check_box.setObjectName('data_directory_copy_check_box') - self.data_directory_copy_check_layout.addWidget(self.data_directory_copy_check_box) - self.data_directory_copy_check_layout.addStretch() - self.data_directory_copy_check_layout.addWidget(self.data_directory_cancel_button) - self.data_directory_layout.addRow(self.data_directory_copy_check_layout) - self.data_directory_layout.addRow(self.new_data_directory_has_files_label) - self.left_layout.addWidget(self.data_directory_group_box) + self.right_layout.addWidget(self.service_name_group_box) + # After the last item on each side, add some spacing self.left_layout.addStretch() - # Hide mouse - self.hide_mouse_group_box = QtWidgets.QGroupBox(self.right_column) - self.hide_mouse_group_box.setObjectName('hide_mouse_group_box') - self.hide_mouse_layout = QtWidgets.QVBoxLayout(self.hide_mouse_group_box) - self.hide_mouse_layout.setObjectName('hide_mouse_layout') - self.hide_mouse_check_box = QtWidgets.QCheckBox(self.hide_mouse_group_box) - self.hide_mouse_check_box.setObjectName('hide_mouse_check_box') - self.hide_mouse_layout.addWidget(self.hide_mouse_check_box) - self.right_layout.addWidget(self.hide_mouse_group_box) - # Service Item Slide Limits - self.slide_group_box = QtWidgets.QGroupBox(self.right_column) - self.slide_group_box.setObjectName('slide_group_box') - self.slide_layout = QtWidgets.QVBoxLayout(self.slide_group_box) - self.slide_layout.setObjectName('slide_layout') - self.slide_label = QtWidgets.QLabel(self.slide_group_box) - self.slide_label.setWordWrap(True) - self.slide_layout.addWidget(self.slide_label) - self.end_slide_radio_button = QtWidgets.QRadioButton(self.slide_group_box) - self.end_slide_radio_button.setObjectName('end_slide_radio_button') - self.slide_layout.addWidget(self.end_slide_radio_button) - self.wrap_slide_radio_button = QtWidgets.QRadioButton(self.slide_group_box) - self.wrap_slide_radio_button.setObjectName('wrap_slide_radio_button') - self.slide_layout.addWidget(self.wrap_slide_radio_button) - self.next_item_radio_button = QtWidgets.QRadioButton(self.slide_group_box) - self.next_item_radio_button.setObjectName('next_item_radio_button') - self.slide_layout.addWidget(self.next_item_radio_button) - self.right_layout.addWidget(self.slide_group_box) - # Display Workarounds - self.display_workaround_group_box = QtWidgets.QGroupBox(self.left_column) - self.display_workaround_group_box.setObjectName('display_workaround_group_box') - self.display_workaround_layout = QtWidgets.QVBoxLayout(self.display_workaround_group_box) - self.display_workaround_layout.setObjectName('display_workaround_layout') - self.ignore_aspect_ratio_check_box = QtWidgets.QCheckBox(self.display_workaround_group_box) - self.ignore_aspect_ratio_check_box.setObjectName('ignore_aspect_ratio_check_box') - self.display_workaround_layout.addWidget(self.ignore_aspect_ratio_check_box) - self.x11_bypass_check_box = QtWidgets.QCheckBox(self.display_workaround_group_box) - self.x11_bypass_check_box.setObjectName('x11_bypass_check_box') - self.display_workaround_layout.addWidget(self.x11_bypass_check_box) - self.alternate_rows_check_box = QtWidgets.QCheckBox(self.display_workaround_group_box) - self.alternate_rows_check_box.setObjectName('alternate_rows_check_box') - self.display_workaround_layout.addWidget(self.alternate_rows_check_box) - self.right_layout.addWidget(self.display_workaround_group_box) self.right_layout.addStretch() + # Set up all the connections and things self.should_update_service_name_example = False self.service_name_check_box.toggled.connect(self.service_name_check_box_toggled) self.service_name_day.currentIndexChanged.connect(self.on_service_name_day_changed) @@ -282,6 +289,7 @@ class AdvancedTab(SettingsTab): 'Auto-scroll the next slide to bottom')) self.enable_auto_close_check_box.setText(translate('OpenLP.AdvancedTab', 'Enable application exit confirmation')) + self.use_dark_style_checkbox.setText(translate('OpenLP.AdvancedTab', 'Use dark style (needs restart)')) self.service_name_group_box.setTitle(translate('OpenLP.AdvancedTab', 'Default Service Name')) self.service_name_check_box.setText(translate('OpenLP.AdvancedTab', 'Enable default service name')) self.service_name_time_label.setText(translate('OpenLP.AdvancedTab', 'Date and Time:')) @@ -349,6 +357,8 @@ class AdvancedTab(SettingsTab): if self.autoscroll_map[i] == autoscroll_value and i < self.autoscroll_combo_box.count(): self.autoscroll_combo_box.setCurrentIndex(i) self.enable_auto_close_check_box.setChecked(settings.value('enable exit confirmation')) + if HAS_DARK_STYLE: + self.use_dark_style_checkbox.setChecked(settings.value('use_dark_style')) self.hide_mouse_check_box.setChecked(settings.value('hide mouse')) self.service_name_day.setCurrentIndex(settings.value('default service day')) self.service_name_time.setTime(QtCore.QTime(settings.value('default service hour'), @@ -420,6 +430,8 @@ class AdvancedTab(SettingsTab): self.settings_form.register_post_process('config_screen_changed') self.settings_form.register_post_process('slidecontroller_update_slide_limits') settings.setValue('search as type', self.is_search_as_you_type_enabled) + if HAS_DARK_STYLE: + settings.setValue('use_dark_style', self.use_dark_style_checkbox.isChecked()) settings.endGroup() def on_search_as_type_check_box_changed(self, check_state): diff --git a/openlp/core/ui/dark.py b/openlp/core/ui/dark.py deleted file mode 100644 index 762c1ee4a..000000000 --- a/openlp/core/ui/dark.py +++ /dev/null @@ -1,32 +0,0 @@ -# -*- coding: utf-8 -*- -# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 - -############################################################################### -# OpenLP - Open Source Lyrics Projection # -# --------------------------------------------------------------------------- # -# Copyright (c) 2008-2017 OpenLP Developers # -# --------------------------------------------------------------------------- # -# This program is free software; you can redistribute it and/or modify it # -# under the terms of the GNU General Public License as published by the Free # -# Software Foundation; version 2 of the License. # -# # -# This program is distributed in the hope that it will be useful, but WITHOUT # -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or # -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for # -# more details. # -# # -# You should have received a copy of the GNU General Public License along # -# with this program; if not, write to the Free Software Foundation, Inc., 59 # -# Temple Place, Suite 330, Boston, MA 02111-1307 USA # -############################################################################### -""" -The :mod:`~openlp.core.ui.dark` module looks for and loads a dark theme -""" - -try: - import qdarkstyle - HAS_DARK_STYLE = True - DARK_STYLESHEET = qdarkstyle.load_stylesheet_pyqt5() -except: - HAS_DARK_STYLE = False - DARK_STYLESHEET = '' diff --git a/openlp/core/ui/mainwindow.py b/openlp/core/ui/mainwindow.py index 077883513..25fe818ee 100644 --- a/openlp/core/ui/mainwindow.py +++ b/openlp/core/ui/mainwindow.py @@ -44,7 +44,6 @@ from openlp.core.lib import Renderer, PluginManager, ImageManager, PluginStatus, from openlp.core.lib.ui import create_action from openlp.core.ui import AboutForm, SettingsForm, ServiceManager, ThemeManager, LiveController, PluginForm, \ ShortcutListForm, FormattingTagForm, PreviewController -from openlp.core.ui.dark import HAS_DARK_STYLE from openlp.core.ui.firsttimeform import FirstTimeForm from openlp.core.ui.media import MediaController from openlp.core.ui.printserviceform import PrintServiceForm @@ -52,31 +51,12 @@ from openlp.core.ui.projector.manager import ProjectorManager from openlp.core.ui.lib.dockwidget import OpenLPDockWidget from openlp.core.ui.lib.filedialog import FileDialog from openlp.core.ui.lib.mediadockmanager import MediaDockManager +from openlp.core.ui.style import PROGRESSBAR_STYLE, get_library_stylesheet from openlp.core.version import get_version log = logging.getLogger(__name__) -MEDIA_MANAGER_STYLE = """ -::tab#media_tool_box { - background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, - stop: 0 palette(button), stop: 1.0 palette(mid)); - border: 0; - border-radius: 2px; - margin-top: 0; - margin-bottom: 0; - text-align: left; -} -/* This is here to make the tabs on KDE with the Breeze theme work */ -::tab:selected {} -""" - -PROGRESSBAR_STYLE = """ -QProgressBar{ - height: 10px; -} -""" - class Ui_MainWindow(object): """ @@ -156,8 +136,7 @@ class Ui_MainWindow(object): # Create the MediaManager self.media_manager_dock = OpenLPDockWidget(main_window, 'media_manager_dock', ':/system/system_mediamanager.png') - if not HAS_DARK_STYLE: - self.media_manager_dock.setStyleSheet(MEDIA_MANAGER_STYLE) + self.media_manager_dock.setStyleSheet(get_library_stylesheet()) # Create the media toolbox self.media_tool_box = QtWidgets.QToolBox(self.media_manager_dock) self.media_tool_box.setObjectName('media_tool_box') diff --git a/openlp/core/ui/style.py b/openlp/core/ui/style.py new file mode 100644 index 000000000..7ffeb9533 --- /dev/null +++ b/openlp/core/ui/style.py @@ -0,0 +1,119 @@ +# -*- coding: utf-8 -*- +# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 + +############################################################################### +# OpenLP - Open Source Lyrics Projection # +# --------------------------------------------------------------------------- # +# Copyright (c) 2008-2017 OpenLP Developers # +# --------------------------------------------------------------------------- # +# This program is free software; you can redistribute it and/or modify it # +# under the terms of the GNU General Public License as published by the Free # +# Software Foundation; version 2 of the License. # +# # +# This program is distributed in the hope that it will be useful, but WITHOUT # +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or # +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for # +# more details. # +# # +# You should have received a copy of the GNU General Public License along # +# with this program; if not, write to the Free Software Foundation, Inc., 59 # +# Temple Place, Suite 330, Boston, MA 02111-1307 USA # +############################################################################### +""" +The :mod:`~openlp.core.ui.dark` module looks for and loads a dark theme +""" +from PyQt5 import QtGui + +from openlp.core.common import is_macosx, is_win +from openlp.core.common.registry import Registry +from openlp.core.common.settings import Settings + +try: + import qdarkstyle + HAS_DARK_STYLE = True +except ImportError: + HAS_DARK_STYLE = False + +WIN_REPAIR_STYLESHEET = """ +QMainWindow::separator +{ + border: none; +} + +QDockWidget::title +{ + border: 1px solid palette(dark); + padding-left: 5px; + padding-top: 2px; + margin: 1px 0; +} + +QToolBar +{ + border: none; + margin: 0; + padding: 0; +} +""" + +MEDIA_MANAGER_STYLE = """ +::tab#media_tool_box { + background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, + stop: 0 palette(button), stop: 1.0 palette(mid)); + border: 0; + border-radius: 2px; + margin-top: 0; + margin-bottom: 0; + text-align: left; +} +/* This is here to make the tabs on KDE with the Breeze theme work */ +::tab:selected {} +""" + +PROGRESSBAR_STYLE = """ +QProgressBar{ + height: 10px; +} +""" + + +def can_show_icon(): + """ + A global method to determine if an icon can be shown on a widget + + .. note:: + This method uses internal imports to prevent circular imports. + """ + return not is_macosx() or Settings.value('advanced/use_dark_style') + + +def get_application_stylesheet(): + """ + Return the correct application stylesheet based on the current style and operating system + + :return str: The correct stylesheet as a string + """ + stylesheet = '' + if HAS_DARK_STYLE and Settings().value('advanced/use_dark_style'): + stylesheet = qdarkstyle.load_stylesheet_pyqt5() + else: + if not Settings().value('advanced/alternate rows'): + base_color = Registry().get('application').palette().color(QtGui.QPalette.Active, QtGui.QPalette.Base) + alternate_rows_repair_stylesheet = \ + 'QTableWidget, QListWidget, QTreeWidget {alternate-background-color: ' + base_color.name() + ';}\n' + stylesheet += alternate_rows_repair_stylesheet + if is_win(): + stylesheet += WIN_REPAIR_STYLESHEET + return stylesheet + + +def get_library_stylesheet(): + """ + Return the correct stylesheet for the main window + + :return str: The correct stylesheet as a string + """ + if not HAS_DARK_STYLE or not Settings().value('advanced/use_dark_style'): + return MEDIA_MANAGER_STYLE + else: + return ''