← Back to team overview

openlp-core team mailing list archive

[Merge] lp:~raoul-snyman/openlp/dark-style into lp:openlp

 

Raoul Snyman has proposed merging lp:~raoul-snyman/openlp/dark-style into lp:openlp.

Requested reviews:
  OpenLP Core (openlp-core)

For more details, see:
https://code.launchpad.net/~raoul-snyman/openlp/dark-style/+merge/331539

Enable the use of QDarkStyle, if it is installed. This is mainly for those users on Windows and macOS who want a dark theme.

QDarkStyle is only pip-installable at present, but we can include it in the PyInstaller builds.
-- 
Your team OpenLP Core is requested to review the proposed merge of lp:~raoul-snyman/openlp/dark-style into lp:openlp.
=== modified file 'openlp/core/__init__.py'
--- openlp/core/__init__.py	2017-09-23 04:04:41 +0000
+++ openlp/core/__init__.py	2017-09-29 00:06:52 +0000
@@ -46,34 +46,14 @@
 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']
 
 
 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):
     """
@@ -118,14 +98,7 @@
                 QtCore.QCoreApplication.exit()
                 sys.exit()
         # Correct stylesheet bugs
-        application_stylesheet = ''
-        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')

=== modified file 'openlp/core/common/settings.py'
--- openlp/core/common/settings.py	2017-09-09 20:00:48 +0000
+++ openlp/core/common/settings.py	2017-09-29 00:06:52 +0000
@@ -136,6 +136,7 @@
         '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,

=== modified file 'openlp/core/ui/advancedtab.py'
--- openlp/core/ui/advancedtab.py	2017-09-24 20:26:39 +0000
+++ openlp/core/ui/advancedtab.py	2017-09-29 00:06:52 +0000
@@ -32,6 +32,7 @@
 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,45 +110,10 @@
         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)
-        # Default service name
-        self.service_name_group_box = QtWidgets.QGroupBox(self.left_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)
-        self.service_name_check_box.setObjectName('service_name_check_box')
-        self.service_name_layout.setObjectName('service_name_layout')
-        self.service_name_layout.addRow(self.service_name_check_box)
-        self.service_name_time_label = QtWidgets.QLabel(self.service_name_group_box)
-        self.service_name_time_label.setObjectName('service_name_time_label')
-        self.service_name_day = QtWidgets.QComboBox(self.service_name_group_box)
-        self.service_name_day.addItems(['', '', '', '', '', '', '', ''])
-        self.service_name_day.setObjectName('service_name_day')
-        self.service_name_time = QtWidgets.QTimeEdit(self.service_name_group_box)
-        self.service_name_time.setObjectName('service_name_time')
-        self.service_name_time_layout = QtWidgets.QHBoxLayout()
-        self.service_name_time_layout.setObjectName('service_name_time_layout')
-        self.service_name_time_layout.addWidget(self.service_name_day)
-        self.service_name_time_layout.addWidget(self.service_name_time)
-        self.service_name_layout.addRow(self.service_name_time_label, self.service_name_time_layout)
-        self.service_name_label = QtWidgets.QLabel(self.service_name_group_box)
-        self.service_name_label.setObjectName('service_name_label')
-        self.service_name_edit = QtWidgets.QLineEdit(self.service_name_group_box)
-        self.service_name_edit.setObjectName('service_name_edit')
-        self.service_name_edit.setValidator(QtGui.QRegExpValidator(QtCore.QRegExp(r'[^/\\?*|<>\[\]":+]+'), self))
-        self.service_name_revert_button = QtWidgets.QToolButton(self.service_name_group_box)
-        self.service_name_revert_button.setObjectName('service_name_revert_button')
-        self.service_name_revert_button.setIcon(build_icon(':/general/general_revert.png'))
-        self.service_name_button_layout = QtWidgets.QHBoxLayout()
-        self.service_name_button_layout.setObjectName('service_name_button_layout')
-        self.service_name_button_layout.addWidget(self.service_name_edit)
-        self.service_name_button_layout.addWidget(self.service_name_revert_button)
-        self.service_name_layout.addRow(self.service_name_label, self.service_name_button_layout)
-        self.service_name_example_label = QtWidgets.QLabel(self.service_name_group_box)
-        self.service_name_example_label.setObjectName('service_name_example_label')
-        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)
+        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')
@@ -174,7 +140,6 @@
         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.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')
@@ -203,7 +168,7 @@
         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 = 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')
@@ -217,7 +182,49 @@
         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.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)
+        self.service_name_check_box.setObjectName('service_name_check_box')
+        self.service_name_layout.setObjectName('service_name_layout')
+        self.service_name_layout.addRow(self.service_name_check_box)
+        self.service_name_time_label = QtWidgets.QLabel(self.service_name_group_box)
+        self.service_name_time_label.setObjectName('service_name_time_label')
+        self.service_name_day = QtWidgets.QComboBox(self.service_name_group_box)
+        self.service_name_day.addItems(['', '', '', '', '', '', '', ''])
+        self.service_name_day.setObjectName('service_name_day')
+        self.service_name_time = QtWidgets.QTimeEdit(self.service_name_group_box)
+        self.service_name_time.setObjectName('service_name_time')
+        self.service_name_time_layout = QtWidgets.QHBoxLayout()
+        self.service_name_time_layout.setObjectName('service_name_time_layout')
+        self.service_name_time_layout.addWidget(self.service_name_day)
+        self.service_name_time_layout.addWidget(self.service_name_time)
+        self.service_name_layout.addRow(self.service_name_time_label, self.service_name_time_layout)
+        self.service_name_label = QtWidgets.QLabel(self.service_name_group_box)
+        self.service_name_label.setObjectName('service_name_label')
+        self.service_name_edit = QtWidgets.QLineEdit(self.service_name_group_box)
+        self.service_name_edit.setObjectName('service_name_edit')
+        self.service_name_edit.setValidator(QtGui.QRegExpValidator(QtCore.QRegExp(r'[^/\\?*|<>\[\]":+]+'), self))
+        self.service_name_revert_button = QtWidgets.QToolButton(self.service_name_group_box)
+        self.service_name_revert_button.setObjectName('service_name_revert_button')
+        self.service_name_revert_button.setIcon(build_icon(':/general/general_revert.png'))
+        self.service_name_button_layout = QtWidgets.QHBoxLayout()
+        self.service_name_button_layout.setObjectName('service_name_button_layout')
+        self.service_name_button_layout.addWidget(self.service_name_edit)
+        self.service_name_button_layout.addWidget(self.service_name_revert_button)
+        self.service_name_layout.addRow(self.service_name_label, self.service_name_button_layout)
+        self.service_name_example_label = QtWidgets.QLabel(self.service_name_group_box)
+        self.service_name_example_label.setObjectName('service_name_example_label')
+        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.right_layout.addWidget(self.service_name_group_box)
+        # After the last item on each side, add some spacing
+        self.left_layout.addStretch()
         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,8 @@
                                                             'Auto-scroll the next slide to bottom'))
         self.enable_auto_close_check_box.setText(translate('OpenLP.AdvancedTab',
                                                            'Enable application exit confirmation'))
+        if HAS_DARK_STYLE:
+            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 +358,8 @@
             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 +431,8 @@
             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):

=== modified file 'openlp/core/ui/mainwindow.py'
--- openlp/core/ui/mainwindow.py	2017-09-25 19:55:33 +0000
+++ openlp/core/ui/mainwindow.py	2017-09-29 00:06:52 +0000
@@ -51,31 +51,12 @@
 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):
     """
@@ -155,7 +136,7 @@
         # Create the MediaManager
         self.media_manager_dock = OpenLPDockWidget(main_window, 'media_manager_dock',
                                                    ':/system/system_mediamanager.png')
-        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')

=== added file 'openlp/core/ui/style.py'
--- openlp/core/ui/style.py	1970-01-01 00:00:00 +0000
+++ openlp/core/ui/style.py	2017-09-29 00:06:52 +0000
@@ -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 ''


References