diff --git a/openlp/core/lib/theme.py b/openlp/core/lib/theme.py
index 3c3ec1e24..a840a4a3c 100644
--- a/openlp/core/lib/theme.py
+++ b/openlp/core/lib/theme.py
@@ -214,6 +214,20 @@ class HorizontalType(object):
Names = ['left', 'right', 'center', 'justify']
+ @staticmethod
+ def to_string(align):
+ """
+ Return a string representation of the alignment
+ """
+ return HorizontalType.Names[align]
+
+ @staticmethod
+ def from_string(align):
+ """
+ Return an alignment for a given string
+ """
+ return HorizontalType.Names.index(align)
+
class VerticalType(object):
"""
@@ -225,6 +239,20 @@ class VerticalType(object):
Names = ['top', 'middle', 'bottom']
+ @staticmethod
+ def to_string(align):
+ """
+ Return a string representation of the alignment
+ """
+ return VerticalType.Names[align]
+
+ @staticmethod
+ def from_string(align):
+ """
+ Return an alignment for a given string
+ """
+ return VerticalType.Names.index(align)
+
BOOLEAN_LIST = ['bold', 'italics', 'override', 'outline', 'shadow', 'slide_transition']
diff --git a/openlp/core/lib/ui.py b/openlp/core/lib/ui.py
index bcf3300ac..3190fe76e 100644
--- a/openlp/core/lib/ui.py
+++ b/openlp/core/lib/ui.py
@@ -31,6 +31,7 @@ from openlp.core.common.i18n import UiStrings, translate
from openlp.core.common.registry import Registry
from openlp.core.lib import build_icon
from openlp.core.ui.icons import UiIcons
+from openlp.core.widgets.labels import FormLabel
log = logging.getLogger(__name__)
@@ -306,7 +307,7 @@ def create_valign_selection_widgets(parent):
:param parent: The parent object. This should be a ``QWidget`` descendant.
"""
- label = QtWidgets.QLabel(parent)
+ label = FormLabel(parent)
label.setText(translate('OpenLP.Ui', '&Vertical Align:'))
combo_box = QtWidgets.QComboBox(parent)
combo_box.addItems([UiStrings().Top, UiStrings().Middle, UiStrings().Bottom])
diff --git a/openlp/core/pages/__init__.py b/openlp/core/pages/__init__.py
new file mode 100644
index 000000000..f282672cc
--- /dev/null
+++ b/openlp/core/pages/__init__.py
@@ -0,0 +1,61 @@
+# -*- coding: utf-8 -*-
+
+##########################################################################
+# OpenLP - Open Source Lyrics Projection #
+# ---------------------------------------------------------------------- #
+# Copyright (c) 2008-2019 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, either version 3 of the License, or #
+# (at your option) any later version. #
+# #
+# 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, see . #
+##########################################################################
+"""
+The :mod:`~openlp.core.pages` module contains wizard pages used in OpenLP
+"""
+from PyQt5 import QtWidgets
+
+
+class GridLayoutPage(QtWidgets.QWizardPage):
+ """
+ A class that has a QGridLayout for its layout which automatically ensure all columns are equal width
+ """
+ def __init__(self, parent=None):
+ super().__init__(parent)
+ self._column_width = 0
+ self.layout = QtWidgets.QGridLayout(self)
+ self.setup_ui()
+ self.retranslate_ui()
+ self.resize_columns()
+
+ def resizeEvent(self, event):
+ """
+ Override inherited resize method
+ """
+ super().resizeEvent(event)
+ self.resize_columns()
+
+ def resize_columns(self):
+ """
+ Resize all the column widths
+ """
+ width = self.layout.contentsRect().width()
+ spacing = self.layout.horizontalSpacing()
+ column_count = self.layout.columnCount()
+ self._column_width = (width - (spacing * (column_count - 1))) // column_count
+ for column_number in range(column_count):
+ self.layout.setColumnMinimumWidth(column_number, self._column_width)
+
+ def setup_ui(self):
+ raise NotImplementedError('Descendant pages need to implement setup_ui')
+
+ def retranslate_ui(self):
+ raise NotImplementedError('Descendant pages need to implement retranslate_ui')
diff --git a/openlp/core/pages/alignment.py b/openlp/core/pages/alignment.py
new file mode 100644
index 000000000..b25a4e305
--- /dev/null
+++ b/openlp/core/pages/alignment.py
@@ -0,0 +1,169 @@
+# -*- coding: utf-8 -*-
+
+##########################################################################
+# OpenLP - Open Source Lyrics Projection #
+# ---------------------------------------------------------------------- #
+# Copyright (c) 2008-2019 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, either version 3 of the License, or #
+# (at your option) any later version. #
+# #
+# 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, see . #
+##########################################################################
+"""
+The :mod:`~openlp.core.pages.alignment` module contains the alignment page used in the theme wizard
+"""
+from PyQt5 import QtWidgets
+
+from openlp.core.common.i18n import translate
+from openlp.core.lib.theme import HorizontalType, VerticalType, TransitionType, TransitionSpeed
+from openlp.core.lib.ui import create_valign_selection_widgets
+from openlp.core.pages import GridLayoutPage
+from openlp.core.widgets.labels import FormLabel
+
+
+class AlignmentTransitionsPage(GridLayoutPage):
+ """
+ A widget containing the alignment and transitions options
+ """
+ def setup_ui(self):
+ """
+ Set up the UI
+ """
+ # Alignment
+ self.horizontal_label = FormLabel(self)
+ self.horizontal_label.setObjectName('horizontal_label')
+ self.layout.addWidget(self.horizontal_label, 0, 0)
+ self.horizontal_combo_box = QtWidgets.QComboBox(self)
+ self.horizontal_combo_box.addItems(['', '', '', ''])
+ self.horizontal_combo_box.setObjectName('horizontal_combo_box')
+ self.layout.addWidget(self.horizontal_combo_box, 0, 1, 1, 3)
+ self.vertical_label, self.vertical_combo_box = create_valign_selection_widgets(self)
+ self.vertical_label.setObjectName('vertical_label')
+ self.layout.addWidget(self.vertical_label, 1, 0)
+ self.vertical_combo_box.setObjectName('vertical_combo_box')
+ self.layout.addWidget(self.vertical_combo_box, 1, 1, 1, 3)
+ # Line
+ self.line = QtWidgets.QFrame(self)
+ self.line.setFrameShape(QtWidgets.QFrame.HLine)
+ self.line.setObjectName('line')
+ self.layout.addWidget(self.line, 2, 0, 1, 4)
+ # Transitions
+ self.transitions_enabled_check_box = QtWidgets.QCheckBox(self)
+ self.transitions_enabled_check_box.setObjectName('transitions_enabled_check_box')
+ self.layout.addWidget(self.transitions_enabled_check_box, 3, 1)
+ self.transition_effect_label = FormLabel(self)
+ self.transition_effect_label.setObjectName('transition_effect_label')
+ self.layout.addWidget(self.transition_effect_label, 4, 0)
+ self.transition_effect_combo_box = QtWidgets.QComboBox(self)
+ self.transition_effect_combo_box.setObjectName('transition_effect_combo_box')
+ self.transition_effect_combo_box.addItems(['', '', '', '', ''])
+ self.layout.addWidget(self.transition_effect_combo_box, 4, 1)
+ self.transition_speed_label = FormLabel(self)
+ self.transition_speed_label.setObjectName('transition_speed_label')
+ self.layout.addWidget(self.transition_speed_label, 4, 2)
+ self.transition_speed_combo_box = QtWidgets.QComboBox(self)
+ self.transition_speed_combo_box.setObjectName('transition_speed_combo_box')
+ self.transition_speed_combo_box.addItems(['', '', ''])
+ self.layout.addWidget(self.transition_speed_combo_box, 4, 3)
+ # Connect slots
+ self.transitions_enabled_check_box.stateChanged.connect(self._on_transition_enabled_changed)
+
+ def retranslate_ui(self):
+ """
+ Translate the widgets
+ """
+ self.horizontal_label.setText(translate('OpenLP.ThemeWizard', 'Horizontal Align:'))
+ self.horizontal_combo_box.setItemText(HorizontalType.Left, translate('OpenLP.ThemeWizard', 'Left'))
+ self.horizontal_combo_box.setItemText(HorizontalType.Right, translate('OpenLP.ThemeWizard', 'Right'))
+ self.horizontal_combo_box.setItemText(HorizontalType.Center, translate('OpenLP.ThemeWizard', 'Center'))
+ self.horizontal_combo_box.setItemText(HorizontalType.Justify, translate('OpenLP.ThemeWizard', 'Justify'))
+ self.transitions_enabled_check_box.setText(translate('OpenLP.ThemeWizard', 'Enable transitions'))
+ self.transition_effect_label.setText(translate('OpenLP.ThemeWizard', 'Effect:'))
+ self.transition_effect_combo_box.setItemText(TransitionType.Fade, translate('OpenLP.ThemeWizard', 'Fade'))
+ self.transition_effect_combo_box.setItemText(TransitionType.Slide, translate('OpenLP.ThemeWizard', 'Slide'))
+ self.transition_effect_combo_box.setItemText(TransitionType.Concave, translate('OpenLP.ThemeWizard', 'Concave'))
+ self.transition_effect_combo_box.setItemText(TransitionType.Convex, translate('OpenLP.ThemeWizard', 'Convex'))
+ self.transition_effect_combo_box.setItemText(TransitionType.Zoom, translate('OpenLP.ThemeWizard', 'Zoom'))
+ self.transition_speed_label.setText(translate('OpenLP.ThemeWizard', 'Speed:'))
+ self.transition_speed_combo_box.setItemText(TransitionSpeed.Normal, translate('OpenLP.ThemeWizard', 'Normal'))
+ self.transition_speed_combo_box.setItemText(TransitionSpeed.Fast, translate('OpenLP.ThemeWizard', 'Fast'))
+ self.transition_speed_combo_box.setItemText(TransitionSpeed.Slow, translate('OpenLP.ThemeWizard', 'Slow'))
+
+ def _on_transition_enabled_changed(self, value):
+ """
+ Update the UI widgets when the transition is enabled or disabled
+ """
+ self.transition_effect_label.setEnabled(value)
+ self.transition_effect_combo_box.setEnabled(value)
+ self.transition_speed_label.setEnabled(value)
+ self.transition_speed_combo_box.setEnabled(value)
+
+ @property
+ def horizontal_align(self):
+ return self.horizontal_combo_box.currentIndex()
+
+ @horizontal_align.setter
+ def horizontal_align(self, value):
+ if isinstance(value, str):
+ self.horizontal_combo_box.setCurrentIndex(HorizontalType.from_string(value))
+ elif isinstance(value, int):
+ self.horizontal_combo_box.setCurrentIndex(value)
+ else:
+ raise TypeError('horizontal_align must either be a string or an int')
+
+ @property
+ def vertical_align(self):
+ return self.vertical_combo_box.currentIndex()
+
+ @vertical_align.setter
+ def vertical_align(self, value):
+ if isinstance(value, str):
+ self.vertical_combo_box.setCurrentIndex(VerticalType.from_string(value))
+ elif isinstance(value, int):
+ self.vertical_combo_box.setCurrentIndex(value)
+ else:
+ raise TypeError('vertical_align must either be a string or an int')
+
+ @property
+ def is_transition_enabled(self):
+ return self.transitions_enabled_check_box.isChecked()
+
+ @is_transition_enabled.setter
+ def is_transition_enabled(self, value):
+ self.transitions_enabled_check_box.setChecked(value)
+ self._on_transition_enabled_changed(value)
+
+ @property
+ def transition_type(self):
+ return self.transition_effect_combo_box.currentIndex()
+
+ @transition_type.setter
+ def transition_type(self, value):
+ if isinstance(value, str):
+ self.transition_effect_combo_box.setCurrentIndex(TransitionType.from_string(value))
+ elif isinstance(value, int):
+ self.transition_effect_combo_box.setCurrentIndex(value)
+ else:
+ raise TypeError('transition_type must either be a string or an int')
+
+ @property
+ def transition_speed(self):
+ return self.transition_speed_combo_box.currentIndex()
+
+ @transition_speed.setter
+ def transition_speed(self, value):
+ if isinstance(value, str):
+ self.transition_speed_combo_box.setCurrentIndex(TransitionSpeed.from_string(value))
+ elif isinstance(value, int):
+ self.transition_speed_combo_box.setCurrentIndex(value)
+ else:
+ raise TypeError('transition_speed must either be a string or an int')
diff --git a/openlp/core/pages/areaposition.py b/openlp/core/pages/areaposition.py
new file mode 100644
index 000000000..37715e6fc
--- /dev/null
+++ b/openlp/core/pages/areaposition.py
@@ -0,0 +1,226 @@
+# -*- coding: utf-8 -*-
+
+##########################################################################
+# OpenLP - Open Source Lyrics Projection #
+# ---------------------------------------------------------------------- #
+# Copyright (c) 2008-2019 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, either version 3 of the License, or #
+# (at your option) any later version. #
+# #
+# 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, see . #
+##########################################################################
+"""
+The :mod:`~openlp.core.pages.areaposition` module contains the area position page used in the theme wizard
+"""
+from PyQt5 import QtWidgets
+
+from openlp.core.common.i18n import translate
+from openlp.core.pages import GridLayoutPage
+from openlp.core.widgets.labels import FormLabel
+
+
+class AreaPositionPage(GridLayoutPage):
+ """
+ A wizard page for the area positioning widgets in the theme wizard
+ """
+ def setup_ui(self):
+ """
+ Set up the UI
+ """
+ # Main area position
+ self.main_position_group_box = QtWidgets.QGroupBox(self)
+ self.main_position_group_box.setObjectName('main_position_group_box')
+ self.main_position_layout = QtWidgets.QGridLayout(self.main_position_group_box)
+ self.main_position_layout.setObjectName('main_position_layout')
+ self.main_position_check_box = QtWidgets.QCheckBox(self.main_position_group_box)
+ self.main_position_check_box.setObjectName('main_position_check_box')
+ self.main_position_layout.addWidget(self.main_position_check_box, 0, 0, 1, 2)
+ self.main_x_label = FormLabel(self.main_position_group_box)
+ self.main_x_label.setObjectName('main_x_label')
+ self.main_position_layout.addWidget(self.main_x_label, 1, 0)
+ self.main_x_spin_box = QtWidgets.QSpinBox(self.main_position_group_box)
+ self.main_x_spin_box.setMaximum(9999)
+ self.main_x_spin_box.setObjectName('main_x_spin_box')
+ self.main_position_layout.addWidget(self.main_x_spin_box, 1, 1)
+ self.main_y_label = FormLabel(self.main_position_group_box)
+ self.main_y_label.setObjectName('main_y_label')
+ self.main_position_layout.addWidget(self.main_y_label, 2, 0)
+ self.main_y_spin_box = QtWidgets.QSpinBox(self.main_position_group_box)
+ self.main_y_spin_box.setMaximum(9999)
+ self.main_y_spin_box.setObjectName('main_y_spin_box')
+ self.main_position_layout.addWidget(self.main_y_spin_box, 2, 1)
+ self.main_width_label = FormLabel(self.main_position_group_box)
+ self.main_width_label.setObjectName('main_width_label')
+ self.main_width_spin_box = QtWidgets.QSpinBox(self.main_position_group_box)
+ self.main_width_spin_box.setMaximum(9999)
+ self.main_width_spin_box.setObjectName('main_width_spin_box')
+ self.main_position_layout.addWidget(self.main_width_label, 3, 0)
+ self.main_position_layout.addWidget(self.main_width_spin_box, 3, 1)
+ self.main_height_label = FormLabel(self.main_position_group_box)
+ self.main_height_label.setObjectName('main_height_label')
+ self.main_height_spin_box = QtWidgets.QSpinBox(self.main_position_group_box)
+ self.main_height_spin_box.setMaximum(9999)
+ self.main_height_spin_box.setObjectName('main_height_spin_box')
+ self.main_position_layout.addWidget(self.main_height_label, 4, 0)
+ self.main_position_layout.addWidget(self.main_height_spin_box, 4, 1)
+ self.layout.addWidget(self.main_position_group_box, 0, 0)
+ # Footer area position
+ self.footer_position_group_box = QtWidgets.QGroupBox(self)
+ self.footer_position_group_box.setObjectName('footer_position_group_box')
+ self.footer_position_layout = QtWidgets.QGridLayout(self.footer_position_group_box)
+ self.footer_position_layout.setObjectName('footer_position_layout')
+ self.footer_position_check_box = QtWidgets.QCheckBox(self.footer_position_group_box)
+ self.footer_position_check_box.setObjectName('footer_position_check_box')
+ self.footer_position_layout.addWidget(self.footer_position_check_box, 0, 0, 1, 2)
+ self.footer_x_label = FormLabel(self.footer_position_group_box)
+ self.footer_x_label.setObjectName('footer_x_label')
+ self.footer_x_spin_box = QtWidgets.QSpinBox(self.footer_position_group_box)
+ self.footer_x_spin_box.setMaximum(9999)
+ self.footer_x_spin_box.setObjectName('footer_x_spin_box')
+ self.footer_position_layout.addWidget(self.footer_x_label, 1, 0)
+ self.footer_position_layout.addWidget(self.footer_x_spin_box, 1, 1)
+ self.footer_y_label = FormLabel(self.footer_position_group_box)
+ self.footer_y_label.setObjectName('footer_y_label')
+ self.footer_y_spin_box = QtWidgets.QSpinBox(self.footer_position_group_box)
+ self.footer_y_spin_box.setMaximum(9999)
+ self.footer_y_spin_box.setObjectName('footer_y_spin_box')
+ self.footer_position_layout.addWidget(self.footer_y_label, 2, 0)
+ self.footer_position_layout.addWidget(self.footer_y_spin_box, 2, 1)
+ self.footer_width_label = FormLabel(self.footer_position_group_box)
+ self.footer_width_label.setObjectName('footer_width_label')
+ self.footer_width_spin_box = QtWidgets.QSpinBox(self.footer_position_group_box)
+ self.footer_width_spin_box.setMaximum(9999)
+ self.footer_width_spin_box.setObjectName('footer_width_spin_box')
+ self.footer_position_layout.addWidget(self.footer_width_label, 3, 0)
+ self.footer_position_layout.addWidget(self.footer_width_spin_box, 3, 1)
+ self.footer_height_label = FormLabel(self.footer_position_group_box)
+ self.footer_height_label.setObjectName('footer_height_label')
+ self.footer_height_spin_box = QtWidgets.QSpinBox(self.footer_position_group_box)
+ self.footer_height_spin_box.setMaximum(9999)
+ self.footer_height_spin_box.setObjectName('footer_height_spin_box')
+ self.footer_position_layout.addWidget(self.footer_height_label, 4, 0)
+ self.footer_position_layout.addWidget(self.footer_height_spin_box, 4, 1)
+ self.layout.addWidget(self.footer_position_group_box, 0, 1)
+ # Connect signals to slots
+ self.main_position_check_box.toggled.connect(self.main_x_spin_box.setDisabled)
+ self.main_position_check_box.toggled.connect(self.main_y_spin_box.setDisabled)
+ self.main_position_check_box.toggled.connect(self.main_width_spin_box.setDisabled)
+ self.main_position_check_box.toggled.connect(self.main_height_spin_box.setDisabled)
+ self.footer_position_check_box.toggled.connect(self.footer_x_spin_box.setDisabled)
+ self.footer_position_check_box.toggled.connect(self.footer_y_spin_box.setDisabled)
+ self.footer_position_check_box.toggled.connect(self.footer_width_spin_box.setDisabled)
+ self.footer_position_check_box.toggled.connect(self.footer_height_spin_box.setDisabled)
+
+ def retranslate_ui(self):
+ """
+ Translate the UI
+ """
+ self.main_position_group_box.setTitle(translate('OpenLP.ThemeWizard', '&Main Area'))
+ self.main_position_check_box.setText(translate('OpenLP.ThemeWizard', '&Use default location'))
+ self.main_x_label.setText(translate('OpenLP.ThemeWizard', 'X position:'))
+ self.main_x_spin_box.setSuffix(translate('OpenLP.ThemeWizard', 'px'))
+ self.main_y_spin_box.setSuffix(translate('OpenLP.ThemeWizard', 'px'))
+ self.main_y_label.setText(translate('OpenLP.ThemeWizard', 'Y position:'))
+ self.main_width_spin_box.setSuffix(translate('OpenLP.ThemeWizard', 'px'))
+ self.main_width_label.setText(translate('OpenLP.ThemeWizard', 'Width:'))
+ self.main_height_spin_box.setSuffix(translate('OpenLP.ThemeWizard', 'px'))
+ self.main_height_label.setText(translate('OpenLP.ThemeWizard', 'Height:'))
+ self.footer_position_group_box.setTitle(translate('OpenLP.ThemeWizard', '&Footer Area'))
+ self.footer_x_label.setText(translate('OpenLP.ThemeWizard', 'X position:'))
+ self.footer_x_spin_box.setSuffix(translate('OpenLP.ThemeWizard', 'px'))
+ self.footer_y_label.setText(translate('OpenLP.ThemeWizard', 'Y position:'))
+ self.footer_y_spin_box.setSuffix(translate('OpenLP.ThemeWizard', 'px'))
+ self.footer_width_label.setText(translate('OpenLP.ThemeWizard', 'Width:'))
+ self.footer_width_spin_box.setSuffix(translate('OpenLP.ThemeWizard', 'px'))
+ self.footer_height_label.setText(translate('OpenLP.ThemeWizard', 'Height:'))
+ self.footer_height_spin_box.setSuffix(translate('OpenLP.ThemeWizard', 'px'))
+ self.footer_position_check_box.setText(translate('OpenLP.ThemeWizard', 'Use default location'))
+
+ @property
+ def use_main_default_location(self):
+ return self.main_position_check_box.isChecked()
+
+ @use_main_default_location.setter
+ def use_main_default_location(self, value):
+ self.main_position_check_box.setChecked(value)
+
+ @property
+ def main_x(self):
+ return self.main_x_spin_box.value()
+
+ @main_x.setter
+ def main_x(self, value):
+ self.main_x_spin_box.setValue(value)
+
+ @property
+ def main_y(self):
+ return self.main_y_spin_box.value()
+
+ @main_y.setter
+ def main_y(self, value):
+ self.main_y_spin_box.setValue(value)
+
+ @property
+ def main_width(self):
+ return self.main_width_spin_box.value()
+
+ @main_width.setter
+ def main_width(self, value):
+ self.main_width_spin_box.setValue(value)
+
+ @property
+ def main_height(self):
+ return self.main_height_spin_box.value()
+
+ @main_height.setter
+ def main_height(self, value):
+ self.main_height_spin_box.setValue(value)
+
+ @property
+ def use_footer_default_location(self):
+ return self.footer_position_check_box.isChecked()
+
+ @use_footer_default_location.setter
+ def use_footer_default_location(self, value):
+ self.footer_position_check_box.setChecked(value)
+
+ @property
+ def footer_x(self):
+ return self.footer_x_spin_box.value()
+
+ @footer_x.setter
+ def footer_x(self, value):
+ self.footer_x_spin_box.setValue(value)
+
+ @property
+ def footer_y(self):
+ return self.footer_y_spin_box.value()
+
+ @footer_y.setter
+ def footer_y(self, value):
+ self.footer_y_spin_box.setValue(value)
+
+ @property
+ def footer_width(self):
+ return self.footer_width_spin_box.value()
+
+ @footer_width.setter
+ def footer_width(self, value):
+ self.footer_width_spin_box.setValue(value)
+
+ @property
+ def footer_height(self):
+ return self.footer_height_spin_box.value()
+
+ @footer_height.setter
+ def footer_height(self, value):
+ self.footer_height_spin_box.setValue(value)
diff --git a/openlp/core/pages/background.py b/openlp/core/pages/background.py
new file mode 100644
index 000000000..1814630af
--- /dev/null
+++ b/openlp/core/pages/background.py
@@ -0,0 +1,253 @@
+# -*- coding: utf-8 -*-
+
+##########################################################################
+# OpenLP - Open Source Lyrics Projection #
+# ---------------------------------------------------------------------- #
+# Copyright (c) 2008-2019 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, either version 3 of the License, or #
+# (at your option) any later version. #
+# #
+# 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, see . #
+##########################################################################
+"""
+The :mod:`~openlp.core.pages.background` module contains the background page used in the theme wizard
+"""
+from PyQt5 import QtWidgets
+
+from openlp.core.common import get_images_filter
+from openlp.core.common.i18n import UiStrings, translate
+from openlp.core.lib.theme import BackgroundGradientType, BackgroundType
+from openlp.core.pages import GridLayoutPage
+from openlp.core.ui.media import VIDEO_EXT
+from openlp.core.widgets.buttons import ColorButton
+from openlp.core.widgets.edits import PathEdit
+from openlp.core.widgets.labels import FormLabel
+
+
+class BackgroundPage(GridLayoutPage):
+ """
+ A background selection widget
+ """
+ Color = 'color'
+ Gradient = 'gradient'
+ Image = 'image'
+ Video = 'video'
+
+ def setup_ui(self):
+ """
+ Set up the ui
+ """
+ # background type
+ self.background_label = FormLabel(self)
+ self.background_label.setObjectName('background_label')
+ self.layout.addWidget(self.background_label, 0, 0)
+ self.background_combo_box = QtWidgets.QComboBox(self)
+ self.background_combo_box.addItems(['', '', '', ''])
+ self.background_combo_box.setObjectName('background_combo_box')
+ self.layout.addWidget(self.background_combo_box, 0, 1, 1, 3)
+ # color
+ self.color_label = FormLabel(self)
+ self.color_label.setObjectName('color_label')
+ self.layout.addWidget(self.color_label, 1, 0)
+ self.color_button = ColorButton(self)
+ self.color_button.setObjectName('color_button')
+ self.layout.addWidget(self.color_button, 1, 1)
+ self.color_widgets = [self.color_label, self.color_button]
+ # gradient
+ self.gradient_type_label = FormLabel(self)
+ self.gradient_type_label.setObjectName('gradient_type_label')
+ self.layout.addWidget(self.gradient_type_label, 2, 0)
+ self.gradient_combo_box = QtWidgets.QComboBox(self)
+ self.gradient_combo_box.setObjectName('gradient_combo_box')
+ self.gradient_combo_box.addItems(['', '', '', '', ''])
+ self.layout.addWidget(self.gradient_combo_box, 2, 1, 1, 3)
+ self.gradient_start_label = FormLabel(self)
+ self.gradient_start_label.setObjectName('gradient_start_label')
+ self.layout.addWidget(self.gradient_start_label, 3, 0)
+ self.gradient_start_button = ColorButton(self)
+ self.gradient_start_button.setObjectName('gradient_start_button')
+ self.layout.addWidget(self.gradient_start_button, 3, 1)
+ self.gradient_end_label = FormLabel(self)
+ self.gradient_end_label.setObjectName('gradient_end_label')
+ self.layout.addWidget(self.gradient_end_label, 3, 2)
+ self.gradient_end_button = ColorButton(self)
+ self.gradient_end_button.setObjectName('gradient_end_button')
+ self.layout.addWidget(self.gradient_end_button, 3, 3)
+ self.gradient_widgets = [self.gradient_type_label, self.gradient_combo_box, self.gradient_start_label,
+ self.gradient_start_button, self.gradient_end_label, self.gradient_end_button]
+ # image
+ self.image_label = FormLabel(self)
+ self.image_label.setObjectName('image_label')
+ self.layout.addWidget(self.image_label, 4, 0)
+ self.image_path_edit = PathEdit(self, dialog_caption=translate('OpenLP.ThemeWizard', 'Select Image'),
+ show_revert=False)
+ self.layout.addWidget(self.image_path_edit, 4, 1, 1, 3)
+ self.image_color_label = FormLabel(self)
+ self.image_color_label.setObjectName('image_color_label')
+ self.layout.addWidget(self.image_color_label, 5, 0)
+ self.image_color_button = ColorButton(self)
+ self.image_color_button.color = '#000000'
+ self.image_color_button.setObjectName('image_color_button')
+ self.layout.addWidget(self.image_color_button, 5, 1)
+ self.image_widgets = [self.image_label, self.image_path_edit, self.image_color_label, self.image_color_button]
+ # video
+ self.video_label = FormLabel(self)
+ self.video_label.setObjectName('video_label')
+ self.layout.addWidget(self.video_label, 6, 0)
+ self.video_path_edit = PathEdit(self, dialog_caption=translate('OpenLP.ThemeWizard', 'Select Video'),
+ show_revert=False)
+ self.layout.addWidget(self.video_path_edit, 6, 1, 1, 3)
+ self.video_color_label = FormLabel(self)
+ self.video_color_label.setObjectName('video_color_label')
+ self.layout.addWidget(self.video_color_label, 7, 0)
+ self.video_color_button = ColorButton(self)
+ self.video_color_button.color = '#000000'
+ self.video_color_button.setObjectName('video_color_button')
+ self.layout.addWidget(self.video_color_button, 7, 1)
+ self.video_widgets = [self.video_label, self.video_path_edit, self.video_color_label, self.video_color_button]
+ # Force everything up
+ self.layout_spacer = QtWidgets.QSpacerItem(1, 1)
+ self.layout.addItem(self.layout_spacer, 8, 0, 1, 4)
+ # Connect slots
+ self.background_combo_box.currentIndexChanged.connect(self._on_background_type_index_changed)
+ # Force the first set of widgets to show
+ self._on_background_type_index_changed(0)
+
+ def retranslate_ui(self):
+ """
+ Translate the text elements of the widget
+ """
+ self.background_label.setText(translate('OpenLP.ThemeWizard', 'Background type:'))
+ self.background_combo_box.setItemText(BackgroundType.Solid, translate('OpenLP.ThemeWizard', 'Solid color'))
+ self.background_combo_box.setItemText(BackgroundType.Gradient, translate('OpenLP.ThemeWizard', 'Gradient'))
+ self.background_combo_box.setItemText(BackgroundType.Image, UiStrings().Image)
+ self.background_combo_box.setItemText(BackgroundType.Transparent,
+ translate('OpenLP.ThemeWizard', 'Transparent'))
+ self.color_label.setText(translate('OpenLP.ThemeWizard', 'Color:'))
+ self.gradient_start_label.setText(translate('OpenLP.ThemeWizard', 'Starting color:'))
+ self.gradient_end_label.setText(translate('OpenLP.ThemeWizard', 'Ending color:'))
+ self.gradient_type_label.setText(translate('OpenLP.ThemeWizard', 'Gradient:'))
+ self.gradient_combo_box.setItemText(BackgroundGradientType.Horizontal,
+ translate('OpenLP.ThemeWizard', 'Horizontal'))
+ self.gradient_combo_box.setItemText(BackgroundGradientType.Vertical,
+ translate('OpenLP.ThemeWizard', 'Vertical'))
+ self.gradient_combo_box.setItemText(BackgroundGradientType.Circular,
+ translate('OpenLP.ThemeWizard', 'Circular'))
+ self.gradient_combo_box.setItemText(BackgroundGradientType.LeftTop,
+ translate('OpenLP.ThemeWizard', 'Top Left - Bottom Right'))
+ self.gradient_combo_box.setItemText(BackgroundGradientType.LeftBottom,
+ translate('OpenLP.ThemeWizard', 'Bottom Left - Top Right'))
+ self.image_color_label.setText(translate('OpenLP.ThemeWizard', 'Background color:'))
+ self.image_label.setText('{text}:'.format(text=UiStrings().Image))
+ self.video_color_label.setText(translate('OpenLP.ThemeWizard', 'Background color:'))
+ self.video_label.setText('{text}:'.format(text=UiStrings().Video))
+ self.image_path_edit.filters = \
+ '{name};;{text} (*)'.format(name=get_images_filter(), text=UiStrings().AllFiles)
+ visible_formats = '(*.{name})'.format(name='; *.'.join(VIDEO_EXT))
+ actual_formats = '(*.{name})'.format(name=' *.'.join(VIDEO_EXT))
+ video_filter = '{trans} {visible} {actual}'.format(trans=translate('OpenLP', 'Video Files'),
+ visible=visible_formats, actual=actual_formats)
+ self.video_path_edit.filters = '{video};;{ui} (*)'.format(video=video_filter, ui=UiStrings().AllFiles)
+
+ def _on_background_type_index_changed(self, index):
+ """
+ Hide and show widgets based on index
+ """
+ widget_sets = [self.color_widgets, self.gradient_widgets, self.image_widgets, [], self.video_widgets]
+ for widgets in widget_sets:
+ for widget in widgets:
+ widget.hide()
+ if index < len(widget_sets):
+ for widget in widget_sets[index]:
+ widget.show()
+
+ @property
+ def background_type(self):
+ return BackgroundType.to_string(self.background_combo_box.currentIndex())
+
+ @background_type.setter
+ def background_type(self, value):
+ if isinstance(value, str):
+ self.background_combo_box.setCurrentIndex(BackgroundType.from_string(value))
+ elif isinstance(value, int):
+ self.background_combo_box.setCurrentIndex(value)
+ else:
+ raise TypeError('background_type must either be a string or an int')
+
+ @property
+ def color(self):
+ return self.color_button.color
+
+ @color.setter
+ def color(self, value):
+ self.color_button.color = value
+
+ @property
+ def gradient_type(self):
+ return BackgroundGradientType.to_string(self.gradient_combo_box.currentIndex())
+
+ @gradient_type.setter
+ def gradient_type(self, value):
+ if isinstance(value, str):
+ self.gradient_combo_box.setCurrentIndex(BackgroundGradientType.from_string(value))
+ elif isinstance(value, int):
+ self.gradient_combo_box.setCurrentIndex(value)
+ else:
+ raise TypeError('gradient_type must either be a string or an int')
+
+ @property
+ def gradient_start(self):
+ return self.gradient_start_button.color
+
+ @gradient_start.setter
+ def gradient_start(self, value):
+ self.gradient_start_button.color = value
+
+ @property
+ def gradient_end(self):
+ return self.gradient_end_button.color
+
+ @gradient_end.setter
+ def gradient_end(self, value):
+ self.gradient_end_button.color = value
+
+ @property
+ def image_color(self):
+ return self.image_color_button.color
+
+ @image_color.setter
+ def image_color(self, value):
+ self.image_color_button.color = value
+
+ @property
+ def image_path(self):
+ return self.image_path_edit.path
+
+ @image_path.setter
+ def image_path(self, value):
+ self.image_path_edit.path = value
+
+ @property
+ def video_color(self):
+ return self.video_color_button.color
+
+ @video_color.setter
+ def video_color(self, value):
+ self.video_color_button.color = value
+
+ @property
+ def video_path(self):
+ return self.video_path_edit.path
+
+ @video_path.setter
+ def video_path(self, value):
+ self.video_path_edit.path = value
diff --git a/openlp/core/pages/fontselect.py b/openlp/core/pages/fontselect.py
new file mode 100644
index 000000000..8242acb89
--- /dev/null
+++ b/openlp/core/pages/fontselect.py
@@ -0,0 +1,345 @@
+# -*- coding: utf-8 -*-
+
+##########################################################################
+# OpenLP - Open Source Lyrics Projection #
+# ---------------------------------------------------------------------- #
+# Copyright (c) 2008-2019 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, either version 3 of the License, or #
+# (at your option) any later version. #
+# #
+# 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, see . #
+##########################################################################
+"""
+The :mod:`~openlp.core.pages.fontselect` module contains the font selection page used in the theme wizard
+"""
+from PyQt5 import QtCore, QtGui, QtWidgets
+
+from openlp.core.common.i18n import UiStrings, translate
+from openlp.core.pages import GridLayoutPage
+from openlp.core.ui.icons import UiIcons
+from openlp.core.widgets.buttons import ColorButton
+from openlp.core.widgets.labels import FormLabel
+
+
+class FontSelectPage(GridLayoutPage):
+ """
+ A font selection widget
+ """
+ Outline = 'outline'
+ Shadow = 'shadow'
+ LineSpacing = 'line_spacing'
+
+ font_name_changed = QtCore.pyqtSignal(str)
+ font_size_changed = QtCore.pyqtSignal(int)
+ font_color_changed = QtCore.pyqtSignal(str)
+ is_bold_changed = QtCore.pyqtSignal(bool)
+ is_italic_changed = QtCore.pyqtSignal(bool)
+ line_spacing_changed = QtCore.pyqtSignal(int)
+ is_outline_enabled_changed = QtCore.pyqtSignal(bool)
+ outline_color_changed = QtCore.pyqtSignal(str)
+ outline_size_changed = QtCore.pyqtSignal(int)
+ is_shadow_enabled_changed = QtCore.pyqtSignal(bool)
+ shadow_color_changed = QtCore.pyqtSignal(str)
+ shadow_size_changed = QtCore.pyqtSignal(int)
+
+ def __init__(self, parent=None):
+ super().__init__(parent)
+ self.feature_widgets = {
+ FontSelectPage.Outline: [self.outline_groupbox],
+ FontSelectPage.Shadow: [self.shadow_groupbox],
+ FontSelectPage.LineSpacing: [self.line_spacing_label, self.line_spacing_spinbox]
+ }
+
+ def setup_ui(self):
+ # Font name
+ self.font_name_label = FormLabel(self)
+ self.font_name_label.setObjectName('font_name_label')
+ self.layout.addWidget(self.font_name_label, 0, 0)
+ self.font_name_combobox = QtWidgets.QFontComboBox(self)
+ self.font_name_combobox.setObjectName('font_name_combobox')
+ self.layout.addWidget(self.font_name_combobox, 0, 1, 1, 3)
+ # Font color
+ self.font_color_label = FormLabel(self)
+ self.font_color_label.setObjectName('font_color_label')
+ self.layout.addWidget(self.font_color_label, 1, 0)
+ self.font_color_button = ColorButton(self)
+ self.font_color_button.setObjectName('font_color_button')
+ self.layout.addWidget(self.font_color_button, 1, 1)
+ # Font style
+ self.font_style_label = FormLabel(self)
+ self.font_style_label.setObjectName('font_style_label')
+ self.layout.addWidget(self.font_style_label, 1, 2)
+ self.style_layout = QtWidgets.QHBoxLayout()
+ self.style_bold_button = QtWidgets.QToolButton(self)
+ self.style_bold_button.setCheckable(True)
+ self.style_bold_button.setIcon(UiIcons().bold)
+ self.style_bold_button.setShortcut(QtGui.QKeySequence(QtGui.QKeySequence.Bold))
+ self.style_bold_button.setObjectName('style_bold_button')
+ self.style_layout.addWidget(self.style_bold_button)
+ self.style_italic_button = QtWidgets.QToolButton(self)
+ self.style_italic_button.setCheckable(True)
+ self.style_italic_button.setIcon(UiIcons().italic)
+ self.style_italic_button.setShortcut(QtGui.QKeySequence(QtGui.QKeySequence.Italic))
+ self.style_italic_button.setObjectName('style_italic_button')
+ self.style_layout.addWidget(self.style_italic_button)
+ self.style_layout.addStretch(1)
+ self.layout.addLayout(self.style_layout, 1, 3)
+ # Font size
+ self.font_size_label = FormLabel(self)
+ self.font_size_label.setObjectName('font_size_label')
+ self.layout.addWidget(self.font_size_label, 2, 0)
+ self.font_size_spinbox = QtWidgets.QSpinBox(self)
+ self.font_size_spinbox.setMaximum(999)
+ self.font_size_spinbox.setValue(16)
+ self.font_size_spinbox.setObjectName('font_size_spinbox')
+ self.layout.addWidget(self.font_size_spinbox, 2, 1)
+ # Line spacing
+ self.line_spacing_label = FormLabel(self)
+ self.line_spacing_label.setObjectName('line_spacing_label')
+ self.layout.addWidget(self.line_spacing_label, 2, 2)
+ self.line_spacing_spinbox = QtWidgets.QSpinBox(self)
+ self.line_spacing_spinbox.setMinimum(-250)
+ self.line_spacing_spinbox.setMaximum(250)
+ self.line_spacing_spinbox.setObjectName('line_spacing_spinbox')
+ self.layout.addWidget(self.line_spacing_spinbox, 2, 3)
+ # Outline
+ self.outline_groupbox = QtWidgets.QGroupBox(self)
+ self.outline_groupbox.setCheckable(True)
+ self.outline_groupbox.setChecked(False)
+ self.outline_groupbox.setObjectName('outline_groupbox')
+ self.outline_layout = QtWidgets.QGridLayout(self.outline_groupbox)
+ self.layout.addWidget(self.outline_groupbox, 3, 0, 1, 2)
+ # Outline colour
+ self.outline_color_label = FormLabel(self.outline_groupbox)
+ self.outline_color_label.setObjectName('outline_color_label')
+ self.outline_layout.addWidget(self.outline_color_label, 0, 0)
+ self.outline_color_button = ColorButton(self.outline_groupbox)
+ self.outline_color_button.setObjectName('outline_color_button')
+ self.outline_layout.addWidget(self.outline_color_button, 0, 1)
+ # Outline size
+ self.outline_size_label = FormLabel(self.outline_groupbox)
+ self.outline_size_label.setObjectName('outline_size_label')
+ self.outline_layout.addWidget(self.outline_size_label, 1, 0)
+ self.outline_size_spinbox = QtWidgets.QSpinBox(self.outline_groupbox)
+ self.outline_size_spinbox.setMaximum(9999)
+ self.outline_size_spinbox.setObjectName('outline_size_spinbox')
+ self.outline_layout.addWidget(self.outline_size_spinbox, 1, 1)
+ # Shadow
+ self.shadow_groupbox = QtWidgets.QGroupBox(self)
+ self.shadow_groupbox.setCheckable(True)
+ self.shadow_groupbox.setChecked(False)
+ self.shadow_groupbox.setObjectName('shadow_groupbox')
+ self.shadow_layout = QtWidgets.QGridLayout(self.shadow_groupbox)
+ self.layout.addWidget(self.shadow_groupbox, 3, 2, 1, 2)
+ # Shadow color
+ self.shadow_color_label = FormLabel(self.shadow_groupbox)
+ self.shadow_color_label.setObjectName('shadow_color_label')
+ self.shadow_layout.addWidget(self.shadow_color_label, 0, 0)
+ self.shadow_color_button = ColorButton(self.shadow_groupbox)
+ self.shadow_color_button.setObjectName('shadow_color_button')
+ self.shadow_layout.addWidget(self.shadow_color_button, 0, 1)
+ # Shadow size
+ self.shadow_size_label = FormLabel(self.shadow_groupbox)
+ self.shadow_size_label.setObjectName('shadow_size_label')
+ self.shadow_layout.addWidget(self.shadow_size_label, 1, 0)
+ self.shadow_size_spinbox = QtWidgets.QSpinBox(self.shadow_groupbox)
+ self.shadow_size_spinbox.setMaximum(9999)
+ self.shadow_size_spinbox.setObjectName('shadow_size_spinbox')
+ self.shadow_layout.addWidget(self.shadow_size_spinbox, 1, 1)
+ # Connect all the signals
+ self.font_name_combobox.activated.connect(self._on_font_name_changed)
+ self.font_color_button.colorChanged.connect(self._on_font_color_changed)
+ self.style_bold_button.toggled.connect(self._on_style_bold_toggled)
+ self.style_italic_button.toggled.connect(self._on_style_italic_toggled)
+ self.font_size_spinbox.valueChanged.connect(self._on_font_size_changed)
+ self.line_spacing_spinbox.valueChanged.connect(self._on_line_spacing_changed)
+ self.outline_groupbox.toggled.connect(self._on_outline_toggled)
+ self.outline_color_button.colorChanged.connect(self._on_outline_color_changed)
+ self.outline_size_spinbox.valueChanged.connect(self._on_outline_size_changed)
+ self.shadow_groupbox.toggled.connect(self._on_shadow_toggled)
+ self.shadow_color_button.colorChanged.connect(self._on_shadow_color_changed)
+ self.shadow_size_spinbox.valueChanged.connect(self._on_shadow_size_changed)
+
+ def retranslate_ui(self):
+ self.font_name_label.setText(translate('OpenLP.FontSelectWidget', 'Font:'))
+ self.font_color_label.setText(translate('OpenLP.FontSelectWidget', 'Color:'))
+ self.font_style_label.setText(translate('OpenLP.FontSelectWidget', 'Style:'))
+ self.style_bold_button.setToolTip('{name} ({shortcut})'.format(
+ name=translate('OpenLP.FontSelectWidget', 'Bold'),
+ shortcut=QtGui.QKeySequence(QtGui.QKeySequence.Bold).toString()
+ ))
+ self.style_italic_button.setToolTip('{name} ({shortcut})'.format(
+ name=translate('OpenLP.FontSelectWidget', 'Italic'),
+ shortcut=QtGui.QKeySequence(QtGui.QKeySequence.Italic).toString()
+ ))
+ self.font_size_label.setText(translate('OpenLP.FontSelectWidget', 'Size:'))
+ self.font_size_spinbox.setSuffix(' {unit}'.format(unit=UiStrings().FontSizePtUnit))
+ self.line_spacing_label.setText(translate('OpenLP.FontSelectWidget', 'Line Spacing:'))
+ self.outline_groupbox.setTitle(translate('OpenLP.FontSelectWidget', 'Outline'))
+ self.outline_color_label.setText(translate('OpenLP.FontSelectWidget', 'Color:'))
+ self.outline_size_label.setText(translate('OpenLP.FontSelectWidget', 'Size:'))
+ self.shadow_groupbox.setTitle(translate('OpenLP.FontSelectWidget', 'Shadow'))
+ self.shadow_color_label.setText(translate('OpenLP.FontSelectWidget', 'Color:'))
+ self.shadow_size_label.setText(translate('OpenLP.FontSelectWidget', 'Size:'))
+
+ def _on_font_name_changed(self, name):
+ if isinstance(name, str):
+ self.font_name_changed.emit(name)
+
+ def _on_font_color_changed(self, color):
+ self.font_color_changed.emit(color)
+
+ def _on_style_bold_toggled(self, is_bold):
+ self.is_bold_changed.emit(is_bold)
+
+ def _on_style_italic_toggled(self, is_italic):
+ self.is_italic_changed.emit(is_italic)
+
+ def _on_font_size_changed(self, size):
+ self.font_size_changed.emit(size)
+
+ def _on_line_spacing_changed(self, spacing):
+ self.line_spacing_changed.emit(spacing)
+
+ def _on_outline_toggled(self, is_enabled):
+ self.is_outline_enabled_changed.emit(is_enabled)
+
+ def _on_outline_color_changed(self, color):
+ self.outline_color_changed.emit(color)
+
+ def _on_outline_size_changed(self, size):
+ self.outline_size_changed.emit(size)
+
+ def _on_shadow_toggled(self, is_enabled):
+ self.is_shadow_enabled_changed.emit(is_enabled)
+
+ def _on_shadow_color_changed(self, color):
+ self.shadow_color_changed.emit(color)
+
+ def _on_shadow_size_changed(self, size):
+ self.shadow_size_changed.emit(size)
+
+ def enable_features(self, *features):
+ """
+ Enable a feature
+ """
+ for feature_name in features:
+ if feature_name not in self.feature_widgets.keys():
+ raise KeyError('No such feature: {feature_name}'.format(feature_name=feature_name))
+ for widget in self.feature_widgets[feature_name]:
+ widget.show()
+
+ def disable_features(self, *features):
+ """
+ Disable a feature
+ """
+ for feature_name in features:
+ if feature_name not in self.feature_widgets.keys():
+ raise KeyError('No such feature: {feature_name}'.format(feature_name=feature_name))
+ for widget in self.feature_widgets[feature_name]:
+ widget.hide()
+
+ @property
+ def font_name(self):
+ return self.font_name_combobox.currentFont().family()
+
+ @font_name.setter
+ def font_name(self, font):
+ self.font_name_combobox.setCurrentFont(QtGui.QFont(font))
+
+ @property
+ def font_color(self):
+ return self.font_color_button.color
+
+ @font_color.setter
+ def font_color(self, color):
+ self.font_color_button.color = color
+
+ @property
+ def is_bold(self):
+ return self.style_bold_button.isChecked()
+
+ @is_bold.setter
+ def is_bold(self, is_bold):
+ self.style_bold_button.setChecked(is_bold)
+
+ @property
+ def is_italic(self):
+ return self.style_italic_button.isChecked()
+
+ @is_italic.setter
+ def is_italic(self, is_italic):
+ self.style_italic_button.setChecked(is_italic)
+
+ @property
+ def font_size(self):
+ return self.font_size_spinbox.value()
+
+ @font_size.setter
+ def font_size(self, size):
+ self.font_size_spinbox.setValue(size)
+
+ @property
+ def line_spacing(self):
+ return self.line_spacing_spinbox.value()
+
+ @line_spacing.setter
+ def line_spacing(self, line_spacing):
+ self.line_spacing_spinbox.setValue(line_spacing)
+
+ @property
+ def is_outline_enabled(self):
+ return self.outline_groupbox.isChecked()
+
+ @is_outline_enabled.setter
+ def is_outline_enabled(self, is_enabled):
+ self.outline_groupbox.setChecked(is_enabled)
+
+ @property
+ def outline_color(self):
+ return self.outline_color_button.color
+
+ @outline_color.setter
+ def outline_color(self, color):
+ self.outline_color_button.color = color
+
+ @property
+ def outline_size(self):
+ return self.outline_size_spinbox.value()
+
+ @outline_size.setter
+ def outline_size(self, size):
+ self.outline_size_spinbox.setValue(size)
+
+ @property
+ def is_shadow_enabled(self):
+ return self.shadow_groupbox.isChecked()
+
+ @is_shadow_enabled.setter
+ def is_shadow_enabled(self, is_enabled):
+ self.shadow_groupbox.setChecked(is_enabled)
+
+ @property
+ def shadow_color(self):
+ return self.shadow_color_button.color
+
+ @shadow_color.setter
+ def shadow_color(self, color):
+ self.shadow_color_button.color = color
+
+ @property
+ def shadow_size(self):
+ return self.shadow_size_spinbox.value()
+
+ @shadow_size.setter
+ def shadow_size(self, size):
+ self.shadow_size_spinbox.setValue(size)
diff --git a/openlp/core/ui/themeform.py b/openlp/core/ui/themeform.py
index edb7e5359..5f253851f 100644
--- a/openlp/core/ui/themeform.py
+++ b/openlp/core/ui/themeform.py
@@ -25,13 +25,12 @@ import logging
from PyQt5 import QtCore, QtGui, QtWidgets
-from openlp.core.common import get_images_filter, is_not_image_file
+from openlp.core.common import is_not_image_file
from openlp.core.common.i18n import UiStrings, translate
from openlp.core.common.mixins import RegistryProperties
from openlp.core.common.registry import Registry
-from openlp.core.lib.theme import BackgroundGradientType, BackgroundType
+from openlp.core.lib.theme import BackgroundType
from openlp.core.lib.ui import critical_error_message_box
-from openlp.core.ui.media import VIDEO_EXT
from openlp.core.ui.themelayoutform import ThemeLayoutForm
from openlp.core.ui.themewizard import Ui_ThemeWizard
@@ -60,42 +59,21 @@ class ThemeForm(QtWidgets.QWizard, Ui_ThemeWizard, RegistryProperties):
Set up the class. This method is mocked out by the tests.
"""
self.setup_ui(self)
- self.registerFields()
- self.update_theme_allowed = True
+ self.can_update_theme = True
self.temp_background_filename = None
self.theme_layout_form = ThemeLayoutForm(self)
- self.background_combo_box.currentIndexChanged.connect(self.on_background_combo_box_current_index_changed)
- self.gradient_combo_box.currentIndexChanged.connect(self.on_gradient_combo_box_current_index_changed)
- self.color_button.colorChanged.connect(self.on_color_changed)
- self.image_color_button.colorChanged.connect(self.on_image_color_changed)
- self.video_color_button.colorChanged.connect(self.on_video_color_changed)
- self.gradient_start_button.colorChanged.connect(self.on_gradient_start_color_changed)
- self.gradient_end_button.colorChanged.connect(self.on_gradient_end_color_changed)
- self.image_path_edit.filters = \
- '{name};;{text} (*)'.format(name=get_images_filter(), text=UiStrings().AllFiles)
- self.image_path_edit.pathChanged.connect(self.on_image_path_edit_path_changed)
- visible_formats = '(*.{name})'.format(name='; *.'.join(VIDEO_EXT))
- actual_formats = '(*.{name})'.format(name=' *.'.join(VIDEO_EXT))
- video_filter = '{trans} {visible} {actual}'.format(trans=translate('OpenLP', 'Video Files'),
- visible=visible_formats, actual=actual_formats)
- self.video_path_edit.filters = '{video};;{ui} (*)'.format(video=video_filter, ui=UiStrings().AllFiles)
- self.video_path_edit.pathChanged.connect(self.on_video_path_edit_path_changed)
- self.footer_color_button.colorChanged.connect(self.on_footer_color_changed)
self.customButtonClicked.connect(self.on_custom_1_button_clicked)
- self.main_position_check_box.stateChanged.connect(self.on_main_position_check_box_state_changed)
- self.footer_position_check_box.stateChanged.connect(self.on_footer_position_check_box_state_changed)
self.currentIdChanged.connect(self.on_current_id_changed)
Registry().register_function('theme_line_count', self.update_lines_text)
- self.main_font.font_name_changed.connect(self.calculate_lines)
- self.main_font.font_size_changed.connect(self.calculate_lines)
- self.main_font.line_spacing_changed.connect(self.calculate_lines)
- self.main_font.is_outline_enabled_changed.connect(self.on_outline_toggled)
- self.main_font.outline_size_changed.connect(self.calculate_lines)
- self.main_font.is_shadow_enabled_changed.connect(self.on_shadow_toggled)
- self.main_font.shadow_size_changed.connect(self.calculate_lines)
- self.footer_font_combo_box.activated.connect(self.update_theme)
- self.footer_size_spin_box.valueChanged.connect(self.update_theme)
- self.transitions_check_box.stateChanged.connect(self.on_transitions_check_box_state_changed)
+ self.main_area_page.font_name_changed.connect(self.calculate_lines)
+ self.main_area_page.font_size_changed.connect(self.calculate_lines)
+ self.main_area_page.line_spacing_changed.connect(self.calculate_lines)
+ self.main_area_page.is_outline_enabled_changed.connect(self.on_outline_toggled)
+ self.main_area_page.outline_size_changed.connect(self.calculate_lines)
+ self.main_area_page.is_shadow_enabled_changed.connect(self.on_shadow_toggled)
+ self.main_area_page.shadow_size_changed.connect(self.calculate_lines)
+ self.footer_area_page.font_name_changed.connect(self.update_theme)
+ self.footer_area_page.font_size_changed.connect(self.update_theme)
def set_defaults(self):
"""
@@ -109,33 +87,6 @@ class ThemeForm(QtWidgets.QWizard, Ui_ThemeWizard, RegistryProperties):
self.set_position_page_values()
self.set_preview_page_values()
- def registerFields(self):
- """
- Map field names to screen names,
- """
- self.background_page.registerField('background_type', self.background_combo_box)
- self.background_page.registerField('color', self.color_button)
- self.background_page.registerField('gradient_start', self.gradient_start_button)
- self.background_page.registerField('gradient_end', self.gradient_end_button)
- self.background_page.registerField('background_image', self.image_path_edit,
- 'path', self.image_path_edit.pathChanged)
- self.background_page.registerField('gradient', self.gradient_combo_box)
- self.main_area_page.registerField('footer_size_spin_box', self.footer_size_spin_box)
- self.area_position_page.registerField('main_position_x', self.main_x_spin_box)
- self.area_position_page.registerField('main_position_y', self.main_y_spin_box)
- self.area_position_page.registerField('main_position_width', self.main_width_spin_box)
- self.area_position_page.registerField('main_position_height', self.main_height_spin_box)
- self.area_position_page.registerField('footer_position_x', self.footer_x_spin_box)
- self.area_position_page.registerField('footer_position_y', self.footer_y_spin_box)
- self.area_position_page.registerField('footer_position_width', self.footer_width_spin_box)
- self.area_position_page.registerField('footer_position_height', self.footer_height_spin_box)
- self.background_page.registerField('horizontal', self.horizontal_combo_box)
- self.background_page.registerField('vertical', self.vertical_combo_box)
- self.background_page.registerField('slide_transition', self.transitions_check_box)
- self.background_page.registerField('slide_transition_type', self.transition_combo_box)
- self.background_page.registerField('slide_transition_speed', self.transition_speed_combo_box)
- self.background_page.registerField('name', self.theme_name_edit)
-
def calculate_lines(self, *args):
"""
Calculate the number of lines on a page by rendering text
@@ -175,7 +126,8 @@ class ThemeForm(QtWidgets.QWizard, Ui_ThemeWizard, RegistryProperties):
"""
background_image = BackgroundType.to_string(BackgroundType.Image)
if self.page(self.currentId()) == self.background_page and \
- self.theme.background_type == background_image and is_not_image_file(self.theme.background_filename):
+ self.background_page.background_type == background_image and \
+ is_not_image_file(self.background_page.image_path):
QtWidgets.QMessageBox.critical(self, translate('OpenLP.ThemeWizard', 'Background Image Empty'),
translate('OpenLP.ThemeWizard', 'You have not selected a '
'background image. Please select one before continuing.'))
@@ -229,7 +181,7 @@ class ThemeForm(QtWidgets.QWizard, Ui_ThemeWizard, RegistryProperties):
"""
Change state as Outline check box changed
"""
- if self.update_theme_allowed:
+ if self.can_update_theme:
self.theme.font_main_outline = is_enabled
self.calculate_lines()
@@ -237,45 +189,19 @@ class ThemeForm(QtWidgets.QWizard, Ui_ThemeWizard, RegistryProperties):
"""
Change state as Shadow check box changed
"""
- if self.update_theme_allowed:
+ if self.can_update_theme:
self.theme.font_main_shadow = is_enabled
self.calculate_lines()
- def on_main_position_check_box_state_changed(self, value):
- """
- Change state as Main Area _position check box changed
- NOTE the font_main_override is the inverse of the check box value
- """
- if self.update_theme_allowed:
- self.theme.font_main_override = (value != QtCore.Qt.Checked)
-
- def on_footer_position_check_box_state_changed(self, value):
- """
- Change state as Footer Area _position check box changed
- NOTE the font_footer_override is the inverse of the check box value
- """
- if self.update_theme_allowed:
- self.theme.font_footer_override = (value != QtCore.Qt.Checked)
-
- def on_transitions_check_box_state_changed(self, state):
- """
- Change state as Transitions check box is changed
- """
- if self.update_theme_allowed:
- self.theme.display_slide_transition = state == QtCore.Qt.Checked
- self.transition_combo_box.setEnabled(self.theme.display_slide_transition)
- self.transition_speed_combo_box.setEnabled(self.theme.display_slide_transition)
- self.calculate_lines()
-
def exec(self, edit=False):
"""
Run the wizard.
"""
log.debug('Editing theme {name}'.format(name=self.theme.theme_name))
self.temp_background_filename = self.theme.background_source
- self.update_theme_allowed = False
+ self.can_update_theme = False
self.set_defaults()
- self.update_theme_allowed = True
+ self.can_update_theme = True
self.theme_name_label.setVisible(not edit)
self.theme_name_edit.setVisible(not edit)
self.edit_mode = edit
@@ -308,228 +234,150 @@ class ThemeForm(QtWidgets.QWizard, Ui_ThemeWizard, RegistryProperties):
"""
Handle the display and state of the Background page.
"""
+ self.background_page.background_type = self.theme.background_type
if self.theme.background_type == BackgroundType.to_string(BackgroundType.Solid):
- self.color_button.color = self.theme.background_color
- self.setField('background_type', 0)
+ self.background_page.color = self.theme.background_color
elif self.theme.background_type == BackgroundType.to_string(BackgroundType.Gradient):
- self.gradient_start_button.color = self.theme.background_start_color
- self.gradient_end_button.color = self.theme.background_end_color
- self.setField('background_type', 1)
+ self.background_page.gradient_start = self.theme.background_start_color
+ self.background_page.gradient_end = self.theme.background_end_color
+ self.background_page.gradient_type = self.theme.background_direction
elif self.theme.background_type == BackgroundType.to_string(BackgroundType.Image):
- self.image_color_button.color = self.theme.background_border_color
- self.image_path_edit.path = self.theme.background_source
- self.setField('background_type', 2)
+ self.background_page.image_color = self.theme.background_border_color
+ if self.theme.background_source and self.theme.background_source.exists():
+ self.background_page.image_path = self.theme.background_source
+ else:
+ self.background_page.image_path = self.theme.background_filename
elif self.theme.background_type == BackgroundType.to_string(BackgroundType.Video):
- self.video_color_button.color = self.theme.background_border_color
- self.video_path_edit.path = self.theme.background_source
- self.setField('background_type', 4)
- elif self.theme.background_type == BackgroundType.to_string(BackgroundType.Stream):
- self.setField('background_type', 5)
- elif self.theme.background_type == BackgroundType.to_string(BackgroundType.Transparent):
- self.setField('background_type', 3)
- if self.theme.background_direction == BackgroundGradientType.to_string(BackgroundGradientType.Horizontal):
- self.setField('gradient', 0)
- elif self.theme.background_direction == BackgroundGradientType.to_string(BackgroundGradientType.Vertical):
- self.setField('gradient', 1)
- elif self.theme.background_direction == BackgroundGradientType.to_string(BackgroundGradientType.Circular):
- self.setField('gradient', 2)
- elif self.theme.background_direction == BackgroundGradientType.to_string(BackgroundGradientType.LeftTop):
- self.setField('gradient', 3)
- else:
- self.setField('gradient', 4)
+ self.background_page.video_color = self.theme.background_border_color
+ if self.theme.background_source and self.theme.background_source.exists():
+ self.background_page.video_path = self.theme.background_source
+ else:
+ self.background_page.video_path = self.theme.background_filename
def set_main_area_page_values(self):
"""
Handle the display and state of the Main Area page.
"""
- self.main_font.font_name = self.theme.font_main_name
- self.main_font.font_color = self.theme.font_main_color
- self.main_font.font_size = self.theme.font_main_size
- self.main_font.line_spacing = self.theme.font_main_line_adjustment
- self.main_font.is_outline_enabled = self.theme.font_main_outline
- self.main_font.outline_color = self.theme.font_main_outline_color
- self.main_font.outline_size = self.theme.font_main_outline_size
- self.main_font.is_shadow_enabled = self.theme.font_main_shadow
- self.main_font.shadow_color = self.theme.font_main_shadow_color
- self.main_font.shadow_size = self.theme.font_main_shadow_size
- self.main_font.is_bold = self.theme.font_main_bold
- self.main_font.is_italic = self.theme.font_main_italics
+ self.main_area_page.font_name = self.theme.font_main_name
+ self.main_area_page.font_color = self.theme.font_main_color
+ self.main_area_page.font_size = self.theme.font_main_size
+ self.main_area_page.line_spacing = self.theme.font_main_line_adjustment
+ self.main_area_page.is_outline_enabled = self.theme.font_main_outline
+ self.main_area_page.outline_color = self.theme.font_main_outline_color
+ self.main_area_page.outline_size = self.theme.font_main_outline_size
+ self.main_area_page.is_shadow_enabled = self.theme.font_main_shadow
+ self.main_area_page.shadow_color = self.theme.font_main_shadow_color
+ self.main_area_page.shadow_size = self.theme.font_main_shadow_size
+ self.main_area_page.is_bold = self.theme.font_main_bold
+ self.main_area_page.is_italic = self.theme.font_main_italics
def set_footer_area_page_values(self):
"""
Handle the display and state of the Footer Area page.
"""
- self.footer_font_combo_box.setCurrentFont(QtGui.QFont(self.theme.font_footer_name))
- self.footer_color_button.color = self.theme.font_footer_color
- self.setField('footer_size_spin_box', self.theme.font_footer_size)
+ self.footer_area_page.font_name = self.theme.font_footer_name
+ self.footer_area_page.font_color = self.theme.font_footer_color
def set_position_page_values(self):
"""
Handle the display and state of the _position page.
"""
# Main Area
- self.main_position_check_box.setChecked(not self.theme.font_main_override)
- self.setField('main_position_x', self.theme.font_main_x)
- self.setField('main_position_y', self.theme.font_main_y)
- self.setField('main_position_height', self.theme.font_main_height)
- self.setField('main_position_width', self.theme.font_main_width)
+ self.area_position_page.use_main_default_location = not self.theme.font_main_override
+ self.area_position_page.main_x = self.theme.font_main_x
+ self.area_position_page.main_y = self.theme.font_main_y
+ self.area_position_page.main_height = self.theme.font_main_height
+ self.area_position_page.main_width = self.theme.font_main_width
# Footer
- self.footer_position_check_box.setChecked(not self.theme.font_footer_override)
- self.setField('footer_position_x', self.theme.font_footer_x)
- self.setField('footer_position_y', self.theme.font_footer_y)
- self.setField('footer_position_height', self.theme.font_footer_height)
- self.setField('footer_position_width', self.theme.font_footer_width)
+ self.area_position_page.use_footer_default_location = not self.theme.font_footer_override
+ self.area_position_page.footer_x = self.theme.font_footer_x
+ self.area_position_page.footer_y = self.theme.font_footer_y
+ self.area_position_page.footer_height = self.theme.font_footer_height
+ self.area_position_page.footer_width = self.theme.font_footer_width
def set_alignment_page_values(self):
"""
Handle the display and state of the Alignments page.
"""
- self.setField('horizontal', self.theme.display_horizontal_align)
- self.setField('vertical', self.theme.display_vertical_align)
- self.setField('slide_transition', self.theme.display_slide_transition)
- self.setField('slide_transition_type', self.theme.display_slide_transition_type)
- self.setField('slide_transition_speed', self.theme.display_slide_transition_speed)
+ self.alignment_page.horizontal_align = self.theme.display_horizontal_align
+ self.alignment_page.vertical_align = self.theme.display_vertical_align
+ self.alignment_page.is_transition_enabled = self.theme.display_slide_transition
+ self.alignment_page.transition_type = self.theme.display_slide_transition_type
+ self.alignment_page.transition_speed = self.theme.display_slide_transition_speed
def set_preview_page_values(self):
"""
Handle the display and state of the Preview page.
"""
- self.setField('name', self.theme.theme_name)
+ self.theme_name_edit.setText(self.theme.theme_name)
self.preview_box.set_theme(self.theme)
- def on_background_combo_box_current_index_changed(self, index):
- """
- Background style Combo box has changed.
- """
- # do not allow updates when screen is building for the first time.
- if self.update_theme_allowed:
- self.theme.background_type = BackgroundType.to_string(index)
- if self.theme.background_type != BackgroundType.to_string(BackgroundType.Image) and \
- self.theme.background_type != BackgroundType.to_string(BackgroundType.Video) and \
- self.temp_background_filename is None:
- self.temp_background_filename = self.theme.background_filename
- self.theme.background_filename = None
- if (self.theme.background_type == BackgroundType.to_string(BackgroundType.Image) or
- self.theme.background_type != BackgroundType.to_string(BackgroundType.Video)) and \
- self.temp_background_filename is not None:
- self.theme.background_filename = self.temp_background_filename
- self.temp_background_filename = None
- self.set_background_page_values()
-
- def on_gradient_combo_box_current_index_changed(self, index):
- """
- Background gradient Combo box has changed.
- """
- if self.update_theme_allowed:
- self.theme.background_direction = BackgroundGradientType.to_string(index)
- self.set_background_page_values()
-
- def on_color_changed(self, color):
- """
- Background / Gradient 1 _color button pushed.
- """
- self.theme.background_color = color
-
- def on_image_color_changed(self, color):
- """
- Background / Gradient 1 _color button pushed.
- """
- self.theme.background_border_color = color
-
- def on_video_color_changed(self, color):
- """
- Background / Gradient 1 _color button pushed.
- """
- self.theme.background_border_color = color
-
- def on_gradient_start_color_changed(self, color):
- """
- Gradient 2 _color button pushed.
- """
- self.theme.background_start_color = color
-
- def on_gradient_end_color_changed(self, color):
- """
- Gradient 2 _color button pushed.
- """
- self.theme.background_end_color = color
-
- def on_image_path_edit_path_changed(self, new_path):
- """
- Handle the `pathEditChanged` signal from image_path_edit
-
- :param pathlib.Path new_path: Path to the new image
- :rtype: None
- """
- self.theme.background_source = new_path
- self.theme.background_filename = new_path
- self.set_background_page_values()
-
- def on_video_path_edit_path_changed(self, new_path):
- """
- Handle the `pathEditChanged` signal from video_path_edit
-
- :param pathlib.Path new_path: Path to the new video
- :rtype: None
- """
- self.theme.background_source = new_path
- self.theme.background_filename = new_path
- self.set_background_page_values()
-
- def on_footer_color_changed(self, color):
- """
- Set the footer colour value
- """
- self.theme.font_footer_color = color
-
def update_theme(self):
"""
Update the theme object from the UI for fields not already updated
when the are changed.
"""
- if not self.update_theme_allowed:
+ if not self.can_update_theme:
return
log.debug('update_theme')
+ # background page
+ self.theme.background_type = self.background_page.background_type
+ if self.theme.background_type == BackgroundType.to_string(BackgroundType.Solid):
+ self.theme.background_color = self.background_page.color
+ elif self.theme.background_type == BackgroundType.to_string(BackgroundType.Gradient):
+ self.theme.background_direction = self.background_page.gradient_type
+ self.theme.background_start_color = self.background_page.gradient_start
+ self.theme.background_end_color = self.background_page.gradient_end
+ elif self.theme.background_type == BackgroundType.to_string(BackgroundType.Image):
+ self.theme.background_border_color = self.background_page.image_color
+ self.theme.background_source = self.background_page.image_path
+ self.theme.background_filename = self.background_page.image_path
+ elif self.theme.background_type == BackgroundType.to_string(BackgroundType.Video):
+ self.theme.background_border_color = self.background_page.video_color
+ self.theme.background_source = self.background_page.video_path
+ self.theme.background_filename = self.background_page.video_path
# main page
- self.theme.font_main_name = self.main_font.font_name
- self.theme.font_main_size = self.main_font.font_size
- self.theme.font_main_line_adjustment = self.main_font.line_spacing
- self.theme.font_main_outline_size = self.main_font.outline_size
- self.theme.font_main_shadow_size = self.main_font.shadow_size
- self.theme.font_main_bold = self.main_font.is_bold
- self.theme.font_main_italics = self.main_font.is_italic
+ self.theme.font_main_name = self.main_area_page.font_name
+ self.theme.font_main_size = self.main_area_page.font_size
+ self.theme.font_main_line_adjustment = self.main_area_page.line_spacing
+ self.theme.font_main_outline_size = self.main_area_page.outline_size
+ self.theme.font_main_shadow_size = self.main_area_page.shadow_size
+ self.theme.font_main_bold = self.main_area_page.is_bold
+ self.theme.font_main_italics = self.main_area_page.is_italic
# footer page
- self.theme.font_footer_name = self.footer_font_combo_box.currentFont().family()
- self.theme.font_footer_size = self.field('footer_size_spin_box')
+ self.theme.font_footer_name = self.footer_area_page.font_name
+ self.theme.font_footer_size = self.footer_area_page.font_size
# position page (main)
+ self.theme.font_main_override = not self.area_position_page.use_main_default_location
if self.theme.font_main_override:
- self.theme.font_main_x = self.field('main_position_x')
- self.theme.font_main_y = self.field('main_position_y')
- self.theme.font_main_height = self.field('main_position_height')
- self.theme.font_main_width = self.field('main_position_width')
+ self.theme.font_main_x = self.area_position_page.main_x
+ self.theme.font_main_y = self.area_position_page.main_y
+ self.theme.font_main_height = self.area_position_page.main_height
+ self.theme.font_main_width = self.area_position_page.main_width
else:
self.theme.set_default_header()
# position page (footer)
+ self.theme.font_footer_override = not self.area_position_page.use_footer_default_location
if self.theme.font_footer_override:
- self.theme.font_footer_x = self.field('footer_position_x')
- self.theme.font_footer_y = self.field('footer_position_y')
- self.theme.font_footer_height = self.field('footer_position_height')
- self.theme.font_footer_width = self.field('footer_position_width')
+ self.theme.font_footer_x = self.area_position_page.footer_x
+ self.theme.font_footer_y = self.area_position_page.footer_y
+ self.theme.font_footer_height = self.area_position_page.footer_height
+ self.theme.font_footer_width = self.area_position_page.footer_width
else:
self.theme.set_default_footer()
- # position page
- self.theme.display_horizontal_align = self.horizontal_combo_box.currentIndex()
- self.theme.display_vertical_align = self.vertical_combo_box.currentIndex()
- self.theme.display_slide_transition = self.field('slide_transition')
- self.theme.display_slide_transition_type = self.field('slide_transition_type')
- self.theme.display_slide_transition_speed = self.field('slide_transition_speed')
+ # alignment page
+ self.theme.display_horizontal_align = self.alignment_page.horizontal_align
+ self.theme.display_vertical_align = self.alignment_page.vertical_align
+ self.theme.display_slide_transition = self.alignment_page.is_transition_enabled
+ self.theme.display_slide_transition_type = self.alignment_page.transition_type
+ self.theme.display_slide_transition_speed = self.alignment_page.transition_speed
def accept(self):
"""
Lets save the theme as Finish has been triggered
"""
# Save the theme name
- self.theme.theme_name = self.field('name')
+ self.theme.theme_name = self.theme_name_edit.text()
if not self.theme.theme_name:
critical_error_message_box(
translate('OpenLP.ThemeWizard', 'Theme Name Missing'),
@@ -542,7 +390,7 @@ class ThemeForm(QtWidgets.QWizard, Ui_ThemeWizard, RegistryProperties):
return
destination_path = None
if self.theme.background_type == BackgroundType.to_string(BackgroundType.Image) or \
- self.theme.background_type == BackgroundType.to_string(BackgroundType.Video):
+ self.theme.background_type == BackgroundType.to_string(BackgroundType.Video):
file_name = self.theme.background_filename.name
destination_path = self.path / self.theme.theme_name / file_name
if not self.edit_mode and not self.theme_manager.check_if_theme_exists(self.theme.theme_name):
diff --git a/openlp/core/ui/themewizard.py b/openlp/core/ui/themewizard.py
index 9b902b7fc..c47084fcf 100644
--- a/openlp/core/ui/themewizard.py
+++ b/openlp/core/ui/themewizard.py
@@ -24,16 +24,15 @@ The Create/Edit theme wizard
from PyQt5 import QtCore, QtGui, QtWidgets
from openlp.core.common import is_macosx
-from openlp.core.common.i18n import UiStrings, translate
+from openlp.core.common.i18n import translate
from openlp.core.display.render import ThemePreviewRenderer
-from openlp.core.lib.theme import BackgroundGradientType, BackgroundType, HorizontalType, TransitionType, \
- TransitionSpeed
-from openlp.core.lib.ui import add_welcome_page, create_valign_selection_widgets
+from openlp.core.lib.ui import add_welcome_page
+from openlp.core.pages.alignment import AlignmentTransitionsPage
+from openlp.core.pages.areaposition import AreaPositionPage
+from openlp.core.pages.background import BackgroundPage
+from openlp.core.pages.fontselect import FontSelectPage
from openlp.core.ui.icons import UiIcons
-from openlp.core.widgets.buttons import ColorButton
-from openlp.core.widgets.edits import PathEdit
from openlp.core.widgets.layouts import AspectRatioLayout
-from openlp.core.widgets.widgets import FontSelectWidget
class Ui_ThemeWizard(object):
@@ -58,234 +57,26 @@ class Ui_ThemeWizard(object):
# Welcome Page
add_welcome_page(theme_wizard, ':/wizards/wizard_createtheme.bmp')
# Background Page
- self.background_page = QtWidgets.QWizardPage()
+ self.background_page = BackgroundPage()
self.background_page.setObjectName('background_page')
- self.background_layout = QtWidgets.QVBoxLayout(self.background_page)
- self.background_layout.setObjectName('background_layout')
- self.background_type_layout = QtWidgets.QFormLayout()
- self.background_type_layout.setObjectName('background_type_layout')
- self.background_label = QtWidgets.QLabel(self.background_page)
- self.background_label.setObjectName('background_label')
- self.background_combo_box = QtWidgets.QComboBox(self.background_page)
- self.background_combo_box.addItems(['', '', '', ''])
- self.background_combo_box.setObjectName('background_combo_box')
- self.background_type_layout.addRow(self.background_label, self.background_combo_box)
- self.background_type_layout.setItem(1, QtWidgets.QFormLayout.LabelRole, self.spacer)
- self.background_layout.addLayout(self.background_type_layout)
- self.background_stack = QtWidgets.QStackedLayout()
- self.background_stack.setObjectName('background_stack')
- self.color_widget = QtWidgets.QWidget(self.background_page)
- self.color_widget.setObjectName('color_widget')
- self.color_layout = QtWidgets.QFormLayout(self.color_widget)
- self.color_layout.setContentsMargins(0, 0, 0, 0)
- self.color_layout.setObjectName('color_layout')
- self.color_label = QtWidgets.QLabel(self.color_widget)
- self.color_label.setObjectName('color_label')
- self.color_button = ColorButton(self.color_widget)
- self.color_button.setObjectName('color_button')
- self.color_layout.addRow(self.color_label, self.color_button)
- self.color_layout.setItem(1, QtWidgets.QFormLayout.LabelRole, self.spacer)
- self.background_stack.addWidget(self.color_widget)
- self.gradient_widget = QtWidgets.QWidget(self.background_page)
- self.gradient_widget.setObjectName('Gradient_widget')
- self.gradient_layout = QtWidgets.QFormLayout(self.gradient_widget)
- self.gradient_layout.setContentsMargins(0, 0, 0, 0)
- self.gradient_layout.setObjectName('gradient_layout')
- self.gradient_start_label = QtWidgets.QLabel(self.gradient_widget)
- self.gradient_start_label.setObjectName('gradient_start_label')
- self.gradient_start_button = ColorButton(self.gradient_widget)
- self.gradient_start_button.setObjectName('gradient_start_button')
- self.gradient_layout.addRow(self.gradient_start_label, self.gradient_start_button)
- self.gradient_end_label = QtWidgets.QLabel(self.gradient_widget)
- self.gradient_end_label.setObjectName('gradient_end_label')
- self.gradient_end_button = ColorButton(self.gradient_widget)
- self.gradient_end_button.setObjectName('gradient_end_button')
- self.gradient_layout.addRow(self.gradient_end_label, self.gradient_end_button)
- self.gradient_type_label = QtWidgets.QLabel(self.gradient_widget)
- self.gradient_type_label.setObjectName('Gradient_type_label')
- self.gradient_combo_box = QtWidgets.QComboBox(self.gradient_widget)
- self.gradient_combo_box.setObjectName('gradient_combo_box')
- self.gradient_combo_box.addItems(['', '', '', '', ''])
- self.gradient_layout.addRow(self.gradient_type_label, self.gradient_combo_box)
- self.gradient_layout.setItem(3, QtWidgets.QFormLayout.LabelRole, self.spacer)
- self.background_stack.addWidget(self.gradient_widget)
- self.image_widget = QtWidgets.QWidget(self.background_page)
- self.image_widget.setObjectName('image_widget')
- self.image_layout = QtWidgets.QFormLayout(self.image_widget)
- self.image_layout.setContentsMargins(0, 0, 0, 0)
- self.image_layout.setObjectName('image_layout')
- self.image_color_label = QtWidgets.QLabel(self.color_widget)
- self.image_color_label.setObjectName('image_color_label')
- self.image_color_button = ColorButton(self.color_widget)
- self.image_color_button.setObjectName('image_color_button')
- self.image_layout.addRow(self.image_color_label, self.image_color_button)
- self.image_label = QtWidgets.QLabel(self.image_widget)
- self.image_label.setObjectName('image_label')
- self.image_path_edit = PathEdit(self.image_widget,
- dialog_caption=translate('OpenLP.ThemeWizard', 'Select Image'),
- show_revert=False)
- self.image_layout.addRow(self.image_label, self.image_path_edit)
- self.image_layout.setItem(2, QtWidgets.QFormLayout.LabelRole, self.spacer)
- self.background_stack.addWidget(self.image_widget)
- self.transparent_widget = QtWidgets.QWidget(self.background_page)
- self.transparent_widget.setObjectName('TransparentWidget')
- self.transparent_layout = QtWidgets.QFormLayout(self.transparent_widget)
- self.transparent_layout.setContentsMargins(0, 0, 0, 0)
- self.transparent_layout.setObjectName('Transparent_layout')
- self.background_stack.addWidget(self.transparent_widget)
- self.background_layout.addLayout(self.background_stack)
- self.video_widget = QtWidgets.QWidget(self.background_page)
- self.video_widget.setObjectName('video_widget')
- self.video_layout = QtWidgets.QFormLayout(self.video_widget)
- self.video_layout.setContentsMargins(0, 0, 0, 0)
- self.video_layout.setObjectName('video_layout')
- self.video_color_label = QtWidgets.QLabel(self.color_widget)
- self.video_color_label.setObjectName('video_color_label')
- self.video_color_button = ColorButton(self.color_widget)
- self.video_color_button.setObjectName('video_color_button')
- self.video_layout.addRow(self.video_color_label, self.video_color_button)
- self.video_label = QtWidgets.QLabel(self.video_widget)
- self.video_label.setObjectName('video_label')
- self.video_path_edit = PathEdit(self.video_widget,
- dialog_caption=translate('OpenLP.ThemeWizard', 'Select Video'),
- show_revert=False)
- self.video_layout.addRow(self.video_label, self.video_path_edit)
- self.video_layout.setItem(2, QtWidgets.QFormLayout.LabelRole, self.spacer)
- self.background_stack.addWidget(self.video_widget)
theme_wizard.addPage(self.background_page)
# Main Area Page
- self.main_area_page = QtWidgets.QWizardPage()
+ self.main_area_page = FontSelectPage()
self.main_area_page.setObjectName('main_area_page')
- self.main_area_layout = QtWidgets.QVBoxLayout(self.main_area_page)
- self.main_area_layout.setObjectName('main_area_layout')
- self.main_font = FontSelectWidget(self.main_area_page)
- self.main_area_layout.addWidget(self.main_font)
theme_wizard.addPage(self.main_area_page)
# Footer Area Page
- self.footer_area_page = QtWidgets.QWizardPage()
+ self.footer_area_page = FontSelectPage()
self.footer_area_page.setObjectName('footer_area_page')
- self.footer_area_layout = QtWidgets.QFormLayout(self.footer_area_page)
- self.footer_area_layout.setObjectName('footer_area_layout')
- self.footer_font_label = QtWidgets.QLabel(self.footer_area_page)
- self.footer_font_label.setObjectName('FooterFontLabel')
- self.footer_font_combo_box = QtWidgets.QFontComboBox(self.footer_area_page)
- self.footer_font_combo_box.setObjectName('footer_font_combo_box')
- self.footer_area_layout.addRow(self.footer_font_label, self.footer_font_combo_box)
- self.footer_color_label = QtWidgets.QLabel(self.footer_area_page)
- self.footer_color_label.setObjectName('footer_color_label')
- self.footer_color_button = ColorButton(self.footer_area_page)
- self.footer_color_button.setObjectName('footer_color_button')
- self.footer_area_layout.addRow(self.footer_color_label, self.footer_color_button)
- self.footer_size_label = QtWidgets.QLabel(self.footer_area_page)
- self.footer_size_label.setObjectName('footer_size_label')
- self.footer_size_spin_box = QtWidgets.QSpinBox(self.footer_area_page)
- self.footer_size_spin_box.setMaximum(999)
- self.footer_size_spin_box.setValue(10)
- self.footer_size_spin_box.setObjectName('FooterSizeSpinBox')
- self.footer_area_layout.addRow(self.footer_size_label, self.footer_size_spin_box)
- self.footer_area_layout.setItem(3, QtWidgets.QFormLayout.LabelRole, self.spacer)
+ self.footer_area_page.disable_features(FontSelectPage.Outline, FontSelectPage.Shadow,
+ FontSelectPage.LineSpacing)
theme_wizard.addPage(self.footer_area_page)
# Alignment Page
- self.alignment_page = QtWidgets.QWizardPage()
+ self.alignment_page = AlignmentTransitionsPage()
self.alignment_page.setObjectName('alignment_page')
- self.alignment_layout = QtWidgets.QFormLayout(self.alignment_page)
- self.alignment_layout.setObjectName('alignment_layout')
- self.horizontal_label = QtWidgets.QLabel(self.alignment_page)
- self.horizontal_label.setObjectName('horizontal_label')
- self.horizontal_combo_box = QtWidgets.QComboBox(self.alignment_page)
- self.horizontal_combo_box.addItems(['', '', '', ''])
- self.horizontal_combo_box.setObjectName('horizontal_combo_box')
- self.alignment_layout.addRow(self.horizontal_label, self.horizontal_combo_box)
- self.vertical_label, self.vertical_combo_box = create_valign_selection_widgets(self.alignment_page)
- self.vertical_label.setObjectName('vertical_label')
- self.vertical_combo_box.setObjectName('vertical_combo_box')
- self.alignment_layout.addRow(self.vertical_label, self.vertical_combo_box)
- self.transitions_check_box = QtWidgets.QCheckBox(self.alignment_page)
- self.transitions_check_box.setObjectName('transitions_check_box')
- self.transition_layout = QtWidgets.QHBoxLayout()
- self.transition_layout.setObjectName("transition_layout")
- self.transition_combo_box = QtWidgets.QComboBox(self.alignment_page)
- self.transition_combo_box.setObjectName("transition_combo_box")
- self.transition_combo_box.addItems(['', '', '', '', ''])
- self.transition_layout.addWidget(self.transition_combo_box)
- self.transition_speed_label = QtWidgets.QLabel(self.alignment_page)
- self.transition_speed_label.setObjectName("transition_speed_label")
- self.transition_layout.addWidget(self.transition_speed_label)
- self.transition_speed_combo_box = QtWidgets.QComboBox(self.alignment_page)
- self.transition_speed_combo_box.setObjectName("transition_speed_combo_box")
- self.transition_speed_combo_box.addItems(['', '', ''])
- self.transition_layout.addWidget(self.transition_speed_combo_box)
- self.alignment_layout.addRow(self.transitions_check_box, self.transition_layout)
theme_wizard.addPage(self.alignment_page)
# Area Position Page
- self.area_position_page = QtWidgets.QWizardPage()
+ self.area_position_page = AreaPositionPage()
self.area_position_page.setObjectName('area_position_page')
- self.area_position_layout = QtWidgets.QHBoxLayout(self.area_position_page)
- self.area_position_layout.setObjectName('area_position_layout')
- self.main_position_group_box = QtWidgets.QGroupBox(self.area_position_page)
- self.main_position_group_box.setObjectName('main_position_group_box')
- self.main_position_layout = QtWidgets.QFormLayout(self.main_position_group_box)
- self.main_position_layout.setObjectName('main_position_layout')
- self.main_position_check_box = QtWidgets.QCheckBox(self.main_position_group_box)
- self.main_position_check_box.setObjectName('main_position_check_box')
- self.main_position_layout.addRow(self.main_position_check_box)
- self.main_x_label = QtWidgets.QLabel(self.main_position_group_box)
- self.main_x_label.setObjectName('main_x_label')
- self.main_x_spin_box = QtWidgets.QSpinBox(self.main_position_group_box)
- self.main_x_spin_box.setMaximum(9999)
- self.main_x_spin_box.setObjectName('main_x_spin_box')
- self.main_position_layout.addRow(self.main_x_label, self.main_x_spin_box)
- self.main_y_label = QtWidgets.QLabel(self.main_position_group_box)
- self.main_y_label.setObjectName('main_y_label')
- self.main_y_spin_box = QtWidgets.QSpinBox(self.main_position_group_box)
- self.main_y_spin_box.setMaximum(9999)
- self.main_y_spin_box.setObjectName('main_y_spin_box')
- self.main_position_layout.addRow(self.main_y_label, self.main_y_spin_box)
- self.main_width_label = QtWidgets.QLabel(self.main_position_group_box)
- self.main_width_label.setObjectName('main_width_label')
- self.main_width_spin_box = QtWidgets.QSpinBox(self.main_position_group_box)
- self.main_width_spin_box.setMaximum(9999)
- self.main_width_spin_box.setObjectName('main_width_spin_box')
- self.main_position_layout.addRow(self.main_width_label, self.main_width_spin_box)
- self.main_height_label = QtWidgets.QLabel(self.main_position_group_box)
- self.main_height_label.setObjectName('main_height_label')
- self.main_height_spin_box = QtWidgets.QSpinBox(self.main_position_group_box)
- self.main_height_spin_box.setMaximum(9999)
- self.main_height_spin_box.setObjectName('main_height_spin_box')
- self.main_position_layout.addRow(self.main_height_label, self.main_height_spin_box)
- self.area_position_layout.addWidget(self.main_position_group_box)
- self.footer_position_group_box = QtWidgets.QGroupBox(self.area_position_page)
- self.footer_position_group_box.setObjectName('footer_position_group_box')
- self.footer_position_layout = QtWidgets.QFormLayout(self.footer_position_group_box)
- self.footer_position_layout.setObjectName('footer_position_layout')
- self.footer_position_check_box = QtWidgets.QCheckBox(self.footer_position_group_box)
- self.footer_position_check_box.setObjectName('footer_position_check_box')
- self.footer_position_layout.addRow(self.footer_position_check_box)
- self.footer_x_label = QtWidgets.QLabel(self.footer_position_group_box)
- self.footer_x_label.setObjectName('footer_x_label')
- self.footer_x_spin_box = QtWidgets.QSpinBox(self.footer_position_group_box)
- self.footer_x_spin_box.setMaximum(9999)
- self.footer_x_spin_box.setObjectName('footer_x_spin_box')
- self.footer_position_layout.addRow(self.footer_x_label, self.footer_x_spin_box)
- self.footer_y_label = QtWidgets.QLabel(self.footer_position_group_box)
- self.footer_y_label.setObjectName('footer_y_label')
- self.footer_y_spin_box = QtWidgets.QSpinBox(self.footer_position_group_box)
- self.footer_y_spin_box.setMaximum(9999)
- self.footer_y_spin_box.setObjectName('footer_y_spin_box')
- self.footer_position_layout.addRow(self.footer_y_label, self.footer_y_spin_box)
- self.footer_width_label = QtWidgets.QLabel(self.footer_position_group_box)
- self.footer_width_label.setObjectName('footer_width_label')
- self.footer_width_spin_box = QtWidgets.QSpinBox(self.footer_position_group_box)
- self.footer_width_spin_box.setMaximum(9999)
- self.footer_width_spin_box.setObjectName('footer_width_spin_box')
- self.footer_position_layout.addRow(self.footer_width_label, self.footer_width_spin_box)
- self.footer_height_label = QtWidgets.QLabel(self.footer_position_group_box)
- self.footer_height_label.setObjectName('footer_height_label')
- self.footer_height_spin_box = QtWidgets.QSpinBox(self.footer_position_group_box)
- self.footer_height_spin_box.setMaximum(9999)
- self.footer_height_spin_box.setObjectName('footer_height_spin_box')
- self.footer_position_layout.addRow(self.footer_height_label, self.footer_height_spin_box)
- self.area_position_layout.addWidget(self.footer_position_group_box)
theme_wizard.addPage(self.area_position_page)
# Preview Page
self.preview_page = QtWidgets.QWizardPage()
@@ -313,15 +104,6 @@ class Ui_ThemeWizard(object):
self.preview_layout.addWidget(self.preview_area)
theme_wizard.addPage(self.preview_page)
self.retranslate_ui(theme_wizard)
- self.background_combo_box.currentIndexChanged.connect(self.background_stack.setCurrentIndex)
- self.main_position_check_box.toggled.connect(self.main_x_spin_box.setDisabled)
- self.main_position_check_box.toggled.connect(self.main_y_spin_box.setDisabled)
- self.main_position_check_box.toggled.connect(self.main_width_spin_box.setDisabled)
- self.main_position_check_box.toggled.connect(self.main_height_spin_box.setDisabled)
- self.footer_position_check_box.toggled.connect(self.footer_x_spin_box.setDisabled)
- self.footer_position_check_box.toggled.connect(self.footer_y_spin_box.setDisabled)
- self.footer_position_check_box.toggled.connect(self.footer_width_spin_box.setDisabled)
- self.footer_position_check_box.toggled.connect(self.footer_height_spin_box.setDisabled)
def retranslate_ui(self, theme_wizard):
"""
@@ -336,87 +118,20 @@ class Ui_ThemeWizard(object):
self.background_page.setTitle(translate('OpenLP.ThemeWizard', 'Set Up Background'))
self.background_page.setSubTitle(translate('OpenLP.ThemeWizard', 'Set up your theme\'s background '
'according to the parameters below.'))
- self.background_label.setText(translate('OpenLP.ThemeWizard', 'Background type:'))
- self.background_combo_box.setItemText(BackgroundType.Solid, translate('OpenLP.ThemeWizard', 'Solid color'))
- self.background_combo_box.setItemText(BackgroundType.Gradient, translate('OpenLP.ThemeWizard', 'Gradient'))
- self.background_combo_box.setItemText(BackgroundType.Image, UiStrings().Image)
- self.background_combo_box.setItemText(BackgroundType.Transparent,
- translate('OpenLP.ThemeWizard', 'Transparent'))
- self.color_label.setText(translate('OpenLP.ThemeWizard', 'color:'))
- self.gradient_start_label.setText(translate('OpenLP.ThemeWizard', 'Starting color:'))
- self.gradient_end_label.setText(translate('OpenLP.ThemeWizard', 'Ending color:'))
- self.gradient_type_label.setText(translate('OpenLP.ThemeWizard', 'Gradient:'))
- self.gradient_combo_box.setItemText(BackgroundGradientType.Horizontal,
- translate('OpenLP.ThemeWizard', 'Horizontal'))
- self.gradient_combo_box.setItemText(BackgroundGradientType.Vertical,
- translate('OpenLP.ThemeWizard', 'Vertical'))
- self.gradient_combo_box.setItemText(BackgroundGradientType.Circular,
- translate('OpenLP.ThemeWizard', 'Circular'))
- self.gradient_combo_box.setItemText(BackgroundGradientType.LeftTop,
- translate('OpenLP.ThemeWizard', 'Top Left - Bottom Right'))
- self.gradient_combo_box.setItemText(BackgroundGradientType.LeftBottom,
- translate('OpenLP.ThemeWizard', 'Bottom Left - Top Right'))
- self.image_color_label.setText(translate('OpenLP.ThemeWizard', 'Background color:'))
- self.image_label.setText('{text}:'.format(text=UiStrings().Image))
- self.video_color_label.setText(translate('OpenLP.ThemeWizard', 'Background color:'))
- self.video_label.setText('{text}:'.format(text=UiStrings().Video))
self.main_area_page.setTitle(translate('OpenLP.ThemeWizard', 'Main Area Font Details'))
self.main_area_page.setSubTitle(translate('OpenLP.ThemeWizard', 'Define the font and display '
'characteristics for the Display text'))
self.footer_area_page.setTitle(translate('OpenLP.ThemeWizard', 'Footer Area Font Details'))
self.footer_area_page.setSubTitle(translate('OpenLP.ThemeWizard', 'Define the font and display '
'characteristics for the Footer text'))
- self.footer_font_label.setText(translate('OpenLP.ThemeWizard', 'Font:'))
- self.footer_color_label.setText(translate('OpenLP.ThemeWizard', 'color:'))
- self.footer_size_label.setText(translate('OpenLP.ThemeWizard', 'Size:'))
- self.footer_size_spin_box.setSuffix(' {unit}'.format(unit=UiStrings().FontSizePtUnit))
self.alignment_page.setTitle(translate('OpenLP.ThemeWizard', 'Text Formatting Details'))
self.alignment_page.setSubTitle(translate('OpenLP.ThemeWizard', 'Allows additional display '
'formatting information to be defined'))
- self.horizontal_label.setText(translate('OpenLP.ThemeWizard', 'Horizontal Align:'))
- self.horizontal_combo_box.setItemText(HorizontalType.Left, translate('OpenLP.ThemeWizard', 'Left'))
- self.horizontal_combo_box.setItemText(HorizontalType.Right, translate('OpenLP.ThemeWizard', 'Right'))
- self.horizontal_combo_box.setItemText(HorizontalType.Center, translate('OpenLP.ThemeWizard', 'Center'))
- self.horizontal_combo_box.setItemText(HorizontalType.Justify, translate('OpenLP.ThemeWizard', 'Justify'))
- self.transitions_check_box.setText(translate('OpenLP.ThemeWizard', 'Transitions:'))
- self.transition_combo_box.setItemText(TransitionType.Fade, translate('OpenLP.ThemeWizard', 'Fade'))
- self.transition_combo_box.setItemText(TransitionType.Slide, translate('OpenLP.ThemeWizard', 'Slide'))
- self.transition_combo_box.setItemText(TransitionType.Concave, translate('OpenLP.ThemeWizard', 'Concave'))
- self.transition_combo_box.setItemText(TransitionType.Convex, translate('OpenLP.ThemeWizard', 'Convex'))
- self.transition_combo_box.setItemText(TransitionType.Zoom, translate('OpenLP.ThemeWizard', 'Zoom'))
- self.transition_speed_label.setText(translate('OpenLP.ThemeWizard', 'Speed:'))
- self.transition_speed_combo_box.setItemText(TransitionSpeed.Normal, translate('OpenLP.ThemeWizard', 'Normal'))
- self.transition_speed_combo_box.setItemText(TransitionSpeed.Fast, translate('OpenLP.ThemeWizard', 'Fast'))
- self.transition_speed_combo_box.setItemText(TransitionSpeed.Slow, translate('OpenLP.ThemeWizard', 'Slow'))
self.area_position_page.setTitle(translate('OpenLP.ThemeWizard', 'Output Area Locations'))
self.area_position_page.setSubTitle(translate('OpenLP.ThemeWizard', 'Allows you to change and move the'
' Main and Footer areas.'))
- self.main_position_group_box.setTitle(translate('OpenLP.ThemeWizard', '&Main Area'))
- self.main_position_check_box.setText(translate('OpenLP.ThemeWizard', '&Use default location'))
- self.main_x_label.setText(translate('OpenLP.ThemeWizard', 'X position:'))
- self.main_x_spin_box.setSuffix(translate('OpenLP.ThemeWizard', 'px'))
- self.main_y_spin_box.setSuffix(translate('OpenLP.ThemeWizard', 'px'))
- self.main_y_label.setText(translate('OpenLP.ThemeWizard', 'Y position:'))
- self.main_width_spin_box.setSuffix(translate('OpenLP.ThemeWizard', 'px'))
- self.main_width_label.setText(translate('OpenLP.ThemeWizard', 'Width:'))
- self.main_height_spin_box.setSuffix(translate('OpenLP.ThemeWizard', 'px'))
- self.main_height_label.setText(translate('OpenLP.ThemeWizard', 'Height:'))
- self.footer_position_group_box.setTitle(translate('OpenLP.ThemeWizard', '&Footer Area'))
- self.footer_x_label.setText(translate('OpenLP.ThemeWizard', 'X position:'))
- self.footer_x_spin_box.setSuffix(translate('OpenLP.ThemeWizard', 'px'))
- self.footer_y_label.setText(translate('OpenLP.ThemeWizard', 'Y position:'))
- self.footer_y_spin_box.setSuffix(translate('OpenLP.ThemeWizard', 'px'))
- self.footer_width_label.setText(translate('OpenLP.ThemeWizard', 'Width:'))
- self.footer_width_spin_box.setSuffix(translate('OpenLP.ThemeWizard', 'px'))
- self.footer_height_label.setText(translate('OpenLP.ThemeWizard', 'Height:'))
- self.footer_height_spin_box.setSuffix(translate('OpenLP.ThemeWizard', 'px'))
- self.footer_position_check_box.setText(translate('OpenLP.ThemeWizard', 'Use default location'))
theme_wizard.setOption(QtWidgets.QWizard.HaveCustomButton1, False)
theme_wizard.setButtonText(QtWidgets.QWizard.CustomButton1, translate('OpenLP.ThemeWizard', 'Layout Preview'))
self.preview_page.setTitle(translate('OpenLP.ThemeWizard', 'Preview and Save'))
self.preview_page.setSubTitle(translate('OpenLP.ThemeWizard', 'Preview the theme and save it.'))
self.theme_name_label.setText(translate('OpenLP.ThemeWizard', 'Theme name:'))
- # Align all QFormLayouts towards each other.
- label_width = max(self.background_label.minimumSizeHint().width(),
- self.horizontal_label.minimumSizeHint().width())
- self.spacer.changeSize(label_width, 0, QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
diff --git a/openlp/core/widgets/dialogs.py b/openlp/core/widgets/dialogs.py
old mode 100755
new mode 100644
diff --git a/tests/functional/openlp_core/ui/test_themeform.py b/openlp/core/widgets/labels.py
similarity index 54%
rename from tests/functional/openlp_core/ui/test_themeform.py
rename to openlp/core/widgets/labels.py
index bbb205f0a..65cfdaef6 100644
--- a/tests/functional/openlp_core/ui/test_themeform.py
+++ b/openlp/core/widgets/labels.py
@@ -19,34 +19,15 @@
# along with this program. If not, see . #
##########################################################################
"""
-Package to test the openlp.core.ui.themeform package.
+The :mod:`~openlp.core.widgets.labels` module contains specialised labels
"""
-from pathlib import Path
-from unittest import TestCase
-from unittest.mock import MagicMock, patch
-
-from openlp.core.ui.themeform import ThemeForm
+from PyQt5 import QtCore, QtWidgets
-class TestThemeForm(TestCase):
+class FormLabel(QtWidgets.QLabel):
"""
- Test the functions in the ThemeForm Class
+ A label that is prepped for forms, right aligned and vertically centered
"""
- def setUp(self):
- with patch('openlp.core.ui.themeform.ThemeForm._setup'):
- self.instance = ThemeForm(None)
-
- def test_on_image_path_edit_path_changed(self):
- """
- Test the `image_path_edit.pathChanged` handler
- """
- # GIVEN: An instance of Theme Form
- with patch.object(self.instance, 'set_background_page_values') as mocked_set_background_page_values:
- self.instance.theme = MagicMock()
-
- # WHEN: `on_image_path_edit_path_changed` is clicked
- self.instance.on_image_path_edit_path_changed(Path('/', 'new', 'pat.h'))
-
- # THEN: The theme background file should be set and `set_background_page_values` should have been called
- assert self.instance.theme.background_filename == Path('/', 'new', 'pat.h')
- mocked_set_background_page_values.assert_called_once_with()
+ def __init__(self, parent=None):
+ super().__init__(parent)
+ self.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
diff --git a/openlp/core/widgets/widgets.py b/openlp/core/widgets/widgets.py
index b3ef54ea8..acb280aae 100644
--- a/openlp/core/widgets/widgets.py
+++ b/openlp/core/widgets/widgets.py
@@ -21,13 +21,11 @@
"""
The :mod:`~openlp.core.widgets.widgets` module contains custom widgets used in OpenLP
"""
-from PyQt5 import QtCore, QtGui, QtWidgets
+from PyQt5 import QtCore, QtWidgets
-from openlp.core.common.i18n import UiStrings, translate
+from openlp.core.common.i18n import translate
from openlp.core.common.settings import ProxyMode, Settings
from openlp.core.lib.ui import critical_error_message_box
-from openlp.core.ui.icons import UiIcons
-from openlp.core.widgets.buttons import ColorButton
SCREENS_LAYOUT_STYLE = """
@@ -443,350 +441,3 @@ class ScreenSelectionWidget(QtWidgets.QWidget):
self._setup_spin_box(self.height_spin_box, 0, screen.display_geometry.height(),
screen.display_geometry.height())
self.current_screen = screen
-
-
-class FontSelectWidget(QtWidgets.QWidget):
- """
- A font selection widget
- """
- Outline = 'outline'
- Shadow = 'shadow'
- LineSpacing = 'line_spacing'
-
- font_name_changed = QtCore.pyqtSignal(str)
- font_size_changed = QtCore.pyqtSignal(int)
- font_color_changed = QtCore.pyqtSignal(str)
- is_bold_changed = QtCore.pyqtSignal(bool)
- is_italic_changed = QtCore.pyqtSignal(bool)
- line_spacing_changed = QtCore.pyqtSignal(int)
- is_outline_enabled_changed = QtCore.pyqtSignal(bool)
- outline_color_changed = QtCore.pyqtSignal(str)
- outline_size_changed = QtCore.pyqtSignal(int)
- is_shadow_enabled_changed = QtCore.pyqtSignal(bool)
- shadow_color_changed = QtCore.pyqtSignal(str)
- shadow_size_changed = QtCore.pyqtSignal(int)
-
- def __init__(self, parent=None):
- super().__init__(parent)
- self._column_width = 0
- self.setup_ui()
- self.feature_widgets = {
- FontSelectWidget.Outline: [self.outline_groupbox],
- FontSelectWidget.Shadow: [self.shadow_groupbox],
- FontSelectWidget.LineSpacing: [self.line_spacing_label, self.line_spacing_spinbox]
- }
-
- def setup_ui(self):
- self.layout = QtWidgets.QGridLayout(self)
- # Font name
- self.font_name_label = QtWidgets.QLabel(self)
- self.font_name_label.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
- self.font_name_label.setObjectName('font_name_label')
- self.layout.addWidget(self.font_name_label, 0, 0)
- self.font_name_combobox = QtWidgets.QFontComboBox(self)
- self.font_name_combobox.setObjectName('font_name_combobox')
- self.layout.addWidget(self.font_name_combobox, 0, 1, 1, 3)
- # Font color
- self.font_color_label = QtWidgets.QLabel(self)
- self.font_color_label.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
- self.font_color_label.setObjectName('font_color_label')
- self.layout.addWidget(self.font_color_label, 1, 0)
- self.font_color_button = ColorButton(self)
- self.font_color_button.setObjectName('font_color_button')
- self.layout.addWidget(self.font_color_button, 1, 1)
- # Font style
- self.font_style_label = QtWidgets.QLabel(self)
- self.font_style_label.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
- self.font_style_label.setObjectName('font_style_label')
- self.layout.addWidget(self.font_style_label, 1, 2)
- self.style_layout = QtWidgets.QHBoxLayout()
- self.style_bold_button = QtWidgets.QToolButton(self)
- self.style_bold_button.setIcon(UiIcons().bold)
- self.style_bold_button.setShortcut(QtGui.QKeySequence(QtGui.QKeySequence.Bold))
- self.style_bold_button.setObjectName('style_bold_button')
- self.style_layout.addWidget(self.style_bold_button)
- self.style_italic_button = QtWidgets.QToolButton(self)
- self.style_italic_button.setIcon(UiIcons().italic)
- self.style_italic_button.setShortcut(QtGui.QKeySequence(QtGui.QKeySequence.Italic))
- self.style_italic_button.setObjectName('style_italic_button')
- self.style_layout.addWidget(self.style_italic_button)
- self.style_layout.addStretch(1)
- self.layout.addLayout(self.style_layout, 1, 3)
- # Font size
- self.font_size_label = QtWidgets.QLabel(self)
- self.font_size_label.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
- self.font_size_label.setObjectName('font_size_label')
- self.layout.addWidget(self.font_size_label, 2, 0)
- self.font_size_spinbox = QtWidgets.QSpinBox(self)
- self.font_size_spinbox.setMaximum(999)
- self.font_size_spinbox.setValue(16)
- self.font_size_spinbox.setObjectName('font_size_spinbox')
- self.layout.addWidget(self.font_size_spinbox, 2, 1)
- # Line spacing
- self.line_spacing_label = QtWidgets.QLabel(self)
- self.line_spacing_label.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
- self.line_spacing_label.setObjectName('line_spacing_label')
- self.layout.addWidget(self.line_spacing_label, 2, 2)
- self.line_spacing_spinbox = QtWidgets.QSpinBox(self)
- self.line_spacing_spinbox.setMinimum(-250)
- self.line_spacing_spinbox.setMaximum(250)
- self.line_spacing_spinbox.setObjectName('line_spacing_spinbox')
- self.layout.addWidget(self.line_spacing_spinbox, 2, 3)
- # Outline
- self.outline_groupbox = QtWidgets.QGroupBox(self)
- self.outline_groupbox.setCheckable(True)
- self.outline_groupbox.setChecked(False)
- self.outline_groupbox.setObjectName('outline_groupbox')
- self.outline_layout = QtWidgets.QGridLayout(self.outline_groupbox)
- self.layout.addWidget(self.outline_groupbox, 3, 0, 1, 2)
- # Outline colour
- self.outline_color_label = QtWidgets.QLabel(self.outline_groupbox)
- self.outline_color_label.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
- self.outline_color_label.setObjectName('outline_color_label')
- self.outline_layout.addWidget(self.outline_color_label, 0, 0)
- self.outline_color_button = ColorButton(self.outline_groupbox)
- self.outline_color_button.setObjectName('outline_color_button')
- self.outline_layout.addWidget(self.outline_color_button, 0, 1)
- # Outline size
- self.outline_size_label = QtWidgets.QLabel(self.outline_groupbox)
- self.outline_size_label.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
- self.outline_size_label.setObjectName('outline_size_label')
- self.outline_layout.addWidget(self.outline_size_label, 1, 0)
- self.outline_size_spinbox = QtWidgets.QSpinBox(self.outline_groupbox)
- self.outline_size_spinbox.setMaximum(9999)
- self.outline_size_spinbox.setObjectName('outline_size_spinbox')
- self.outline_layout.addWidget(self.outline_size_spinbox, 1, 1)
- # Shadow
- self.shadow_groupbox = QtWidgets.QGroupBox(self)
- self.shadow_groupbox.setCheckable(True)
- self.shadow_groupbox.setChecked(False)
- self.shadow_groupbox.setObjectName('shadow_groupbox')
- self.shadow_layout = QtWidgets.QGridLayout(self.shadow_groupbox)
- self.layout.addWidget(self.shadow_groupbox, 3, 2, 1, 2)
- # Shadow color
- self.shadow_color_label = QtWidgets.QLabel(self.shadow_groupbox)
- self.shadow_color_label.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
- self.shadow_color_label.setObjectName('shadow_color_label')
- self.shadow_layout.addWidget(self.shadow_color_label, 0, 0)
- self.shadow_color_button = ColorButton(self.shadow_groupbox)
- self.shadow_color_button.setObjectName('shadow_color_button')
- self.shadow_layout.addWidget(self.shadow_color_button, 0, 1)
- # Shadow size
- self.shadow_size_label = QtWidgets.QLabel(self.shadow_groupbox)
- self.shadow_size_label.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
- self.shadow_size_label.setObjectName('shadow_size_label')
- self.shadow_layout.addWidget(self.shadow_size_label, 1, 0)
- self.shadow_size_spinbox = QtWidgets.QSpinBox(self.shadow_groupbox)
- self.shadow_size_spinbox.setMaximum(9999)
- self.shadow_size_spinbox.setObjectName('shadow_size_spinbox')
- self.shadow_layout.addWidget(self.shadow_size_spinbox, 1, 1)
- # Fix the size
- self.resize_widgets()
- # Connect all the signals
- self.font_name_combobox.activated.connect(self._on_font_name_changed)
- self.font_color_button.colorChanged.connect(self._on_font_color_changed)
- self.style_bold_button.toggled.connect(self._on_style_bold_toggled)
- self.style_italic_button.toggled.connect(self._on_style_italic_toggled)
- self.font_size_spinbox.valueChanged.connect(self._on_font_size_changed)
- self.line_spacing_spinbox.valueChanged.connect(self._on_line_spacing_changed)
- self.outline_groupbox.toggled.connect(self._on_outline_toggled)
- self.outline_color_button.colorChanged.connect(self._on_outline_color_changed)
- self.outline_size_spinbox.valueChanged.connect(self._on_outline_size_changed)
- self.shadow_groupbox.toggled.connect(self._on_shadow_toggled)
- self.shadow_color_button.colorChanged.connect(self._on_shadow_color_changed)
- self.shadow_size_spinbox.valueChanged.connect(self._on_shadow_size_changed)
- # Translate everything
- self.retranslate_ui()
-
- def retranslate_ui(self):
- self.font_name_label.setText(translate('OpenLP.FontSelectWidget', 'Font:'))
- self.font_color_label.setText(translate('OpenLP.FontSelectWidget', 'Color:'))
- self.font_style_label.setText(translate('OpenLP.FontSelectWidget', 'Style:'))
- self.style_bold_button.setToolTip('{name} ({shortcut})'.format(
- name=translate('OpenLP.FontSelectWidget', 'Bold'),
- shortcut=QtGui.QKeySequence(QtGui.QKeySequence.Bold).toString()
- ))
- self.style_italic_button.setToolTip('{name} ({shortcut})'.format(
- name=translate('OpenLP.FontSelectWidget', 'Italic'),
- shortcut=QtGui.QKeySequence(QtGui.QKeySequence.Italic).toString()
- ))
- self.font_size_label.setText(translate('OpenLP.FontSelectWidget', 'Size:'))
- self.font_size_spinbox.setSuffix(' {unit}'.format(unit=UiStrings().FontSizePtUnit))
- self.line_spacing_label.setText(translate('OpenLP.FontSelectWidget', 'Line Spacing:'))
- self.outline_groupbox.setTitle(translate('OpenLP.FontSelectWidget', 'Outline'))
- self.outline_color_label.setText(translate('OpenLP.FontSelectWidget', 'Color:'))
- self.outline_size_label.setText(translate('OpenLP.FontSelectWidget', 'Size:'))
- self.shadow_groupbox.setTitle(translate('OpenLP.FontSelectWidget', 'Shadow'))
- self.shadow_color_label.setText(translate('OpenLP.FontSelectWidget', 'Color:'))
- self.shadow_size_label.setText(translate('OpenLP.FontSelectWidget', 'Size:'))
-
- def resizeEvent(self, event):
- """
- Override inherited resize method
- """
- super().resizeEvent(event)
- self.resize_widgets()
-
- def _on_font_name_changed(self, name):
- if isinstance(name, str):
- self.font_name_changed.emit(name)
-
- def _on_font_color_changed(self, color):
- self.font_color_changed.emit(color)
-
- def _on_style_bold_toggled(self, is_bold):
- self.is_bold_changed.emit(is_bold)
-
- def _on_style_italic_toggled(self, is_italic):
- self.is_italic_changed.emit(is_italic)
-
- def _on_font_size_changed(self, size):
- self.font_size_changed.emit(size)
-
- def _on_line_spacing_changed(self, spacing):
- self.line_spacing_changed.emit(spacing)
-
- def _on_outline_toggled(self, is_enabled):
- self.is_outline_enabled_changed.emit(is_enabled)
-
- def _on_outline_color_changed(self, color):
- self.outline_color_changed.emit(color)
-
- def _on_outline_size_changed(self, size):
- self.outline_size_changed.emit(size)
-
- def _on_shadow_toggled(self, is_enabled):
- self.is_shadow_enabled_changed.emit(is_enabled)
-
- def _on_shadow_color_changed(self, color):
- self.shadow_color_changed.emit(color)
-
- def _on_shadow_size_changed(self, size):
- self.shadow_size_changed.emit(size)
-
- def resize_widgets(self):
- """
- Resize all the widgets and set the column widths
- """
- width = self.geometry().width()
- margins = self.layout.contentsMargins()
- spacing = self.layout.horizontalSpacing()
- self._column_width = (width - margins.left() - margins.right() - (spacing * 3)) // 4
- for column_number in range(4):
- self.layout.setColumnMinimumWidth(column_number, self._column_width)
-
- def enable_features(self, *features):
- """
- Enable a feature
- """
- for feature_name in features:
- if feature_name not in self.feature_widgets.keys():
- raise KeyError('No such feature: {feature_name}'.format(feature_name=feature_name))
- for widget in self.feature_widgets[feature_name]:
- widget.show()
-
- def disable_features(self, *features):
- """
- Disable a feature
- """
- for feature_name in features:
- if feature_name not in self.feature_widgets.keys():
- raise KeyError('No such feature: {feature_name}'.format(feature_name=feature_name))
- for widget in self.feature_widgets[feature_name]:
- widget.hide()
-
- @property
- def font_name(self):
- return self.font_name_combobox.currentFont().family()
-
- @font_name.setter
- def font_name(self, font):
- self.font_name_combobox.setCurrentFont(QtGui.QFont(font))
-
- @property
- def font_color(self):
- return self.font_color_button.color
-
- @font_color.setter
- def font_color(self, color):
- self.font_color_button.color = color
-
- @property
- def is_bold(self):
- return self.style_bold_button.isChecked()
-
- @is_bold.setter
- def is_bold(self, is_bold):
- self.style_bold_button.setChecked(is_bold)
-
- @property
- def is_italic(self):
- return self.style_italic_button.isChecked()
-
- @is_italic.setter
- def is_italic(self, is_italic):
- self.style_italic_button.setChecked(is_italic)
-
- @property
- def font_size(self):
- return self.font_size_spinbox.value()
-
- @font_size.setter
- def font_size(self, size):
- self.font_size_spinbox.setValue(size)
-
- @property
- def line_spacing(self):
- return self.line_spacing_spinbox.value()
-
- @line_spacing.setter
- def line_spacing(self, line_spacing):
- self.line_spacing_spinbox.setValue(line_spacing)
-
- @property
- def is_outline_enabled(self):
- return self.outline_groupbox.isChecked()
-
- @is_outline_enabled.setter
- def is_outline_enabled(self, is_enabled):
- self.outline_groupbox.setChecked(is_enabled)
-
- @property
- def outline_color(self):
- return self.outline_color_button.color
-
- @outline_color.setter
- def outline_color(self, color):
- self.outline_color_button.color = color
-
- @property
- def outline_size(self):
- return self.outline_size_spinbox.value()
-
- @outline_size.setter
- def outline_size(self, size):
- self.outline_size_spinbox.setValue(size)
-
- @property
- def is_shadow_enabled(self):
- return self.shadow_groupbox.isChecked()
-
- @is_shadow_enabled.setter
- def is_shadow_enabled(self, is_enabled):
- self.shadow_groupbox.setChecked(is_enabled)
-
- @property
- def shadow_color(self):
- return self.shadow_color_button.color
-
- @shadow_color.setter
- def shadow_color(self, color):
- self.shadow_color_button.color = color
-
- @property
- def shadow_size(self):
- return self.shadow_size_spinbox.value()
-
- @shadow_size.setter
- def shadow_size(self, size):
- self.shadow_size_spinbox.setValue(size)
diff --git a/tests/openlp_core/pages/__init__.py b/tests/openlp_core/pages/__init__.py
new file mode 100644
index 000000000..36a3e2274
--- /dev/null
+++ b/tests/openlp_core/pages/__init__.py
@@ -0,0 +1,23 @@
+# -*- coding: utf-8 -*-
+
+##########################################################################
+# OpenLP - Open Source Lyrics Projection #
+# ---------------------------------------------------------------------- #
+# Copyright (c) 2008-2019 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, either version 3 of the License, or #
+# (at your option) any later version. #
+# #
+# 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, see . #
+##########################################################################
+"""
+Package to test the openlp.core.pages package.
+"""
diff --git a/tests/openlp_core/pages/test_alignment.py b/tests/openlp_core/pages/test_alignment.py
new file mode 100644
index 000000000..2a317d2a0
--- /dev/null
+++ b/tests/openlp_core/pages/test_alignment.py
@@ -0,0 +1,305 @@
+# -*- coding: utf-8 -*-
+
+##########################################################################
+# OpenLP - Open Source Lyrics Projection #
+# ---------------------------------------------------------------------- #
+# Copyright (c) 2008-2019 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, either version 3 of the License, or #
+# (at your option) any later version. #
+# #
+# 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, see . #
+##########################################################################
+"""
+Package to test the openlp.core.pages.alignment package.
+"""
+from unittest import TestCase
+from unittest.mock import MagicMock
+
+import pytest
+
+from openlp.core.lib.theme import HorizontalType, VerticalType, TransitionType, TransitionSpeed
+from openlp.core.pages.alignment import AlignmentTransitionsPage
+from tests.helpers.testmixin import TestMixin
+
+
+class TestAlignmentTransitionsPage(TestCase, TestMixin):
+
+ def setUp(self):
+ """Test setup"""
+ self.setup_application()
+ self.build_settings()
+
+ def tearDown(self):
+ """Tear down tests"""
+ del self.app
+
+ def test_init_(self):
+ """
+ Test the initialisation of AlignmentTransitionsPage
+ """
+ # GIVEN: The AlignmentTransitionsPage class
+ # WHEN: Initialising AlignmentTransitionsPage
+ # THEN: We should have an instance of the widget with no errors
+ AlignmentTransitionsPage()
+
+ def test_on_transition_enabled_changed(self):
+ """
+ Test the _on_transition_enabled_changed() slot
+ """
+ # GIVEN: And instance of AlignmentTransitionsPage and some mock widgets
+ page = AlignmentTransitionsPage()
+
+ # WHEN: _on_transition_enabled_changed
+ page._on_transition_enabled_changed(True)
+
+ # THEN: The correct widgets should be visible
+ assert page.transition_effect_label.isEnabled()
+ assert page.transition_effect_combo_box.isEnabled()
+ assert page.transition_speed_label.isEnabled()
+ assert page.transition_speed_combo_box.isEnabled()
+
+ def test_get_horizontal_align(self):
+ """
+ Test the horizontal_align getter
+ """
+ # GIVEN: A AlignmentTransitionsPage instance with the combobox set to index 1
+ page = AlignmentTransitionsPage()
+ page.horizontal_combo_box.setCurrentIndex(1)
+
+ # WHEN: The property is accessed
+ result = page.horizontal_align
+
+ # THEN: The result should be correct
+ assert result == HorizontalType.Right
+
+ def test_set_horizontal_align_int(self):
+ """
+ Test the horizontal_align setter with an int
+ """
+ # GIVEN: A AlignmentTransitionsPage instance
+ page = AlignmentTransitionsPage()
+
+ # WHEN: The property is set
+ page.horizontal_align = HorizontalType.Center
+
+ # THEN: The combobox should be correct
+ assert page.horizontal_combo_box.currentIndex() == 2
+
+ def test_set_horizontal_align_str(self):
+ """
+ Test the horizontal_align setter with a str
+ """
+ # GIVEN: A AlignmentTransitionsPage instance
+ page = AlignmentTransitionsPage()
+
+ # WHEN: The property is set
+ page.horizontal_align = HorizontalType.to_string(HorizontalType.Justify)
+
+ # THEN: The combobox should be correct
+ assert page.horizontal_combo_box.currentIndex() == 3
+
+ def test_set_horizontal_align_exception(self):
+ """
+ Test the horizontal_align setter with something other than a str or int
+ """
+ # GIVEN: A AlignmentTransitionsPage instance
+ page = AlignmentTransitionsPage()
+
+ # WHEN: The property is set
+ # THEN: An exception is raised
+ with pytest.raises(TypeError, match='horizontal_align must either be a string or an int'):
+ page.horizontal_align = []
+
+ def test_get_vertical_align(self):
+ """
+ Test the vertical_align getter
+ """
+ # GIVEN: A AlignmentTransitionsPage instance with the combobox set to index 1
+ page = AlignmentTransitionsPage()
+ page.vertical_combo_box.setCurrentIndex(1)
+
+ # WHEN: The property is accessed
+ result = page.vertical_align
+
+ # THEN: The result should be correct
+ assert result == VerticalType.Middle
+
+ def test_set_vertical_align_int(self):
+ """
+ Test the vertical_align setter with an int
+ """
+ # GIVEN: A AlignmentTransitionsPage instance
+ page = AlignmentTransitionsPage()
+
+ # WHEN: The property is set
+ page.vertical_align = VerticalType.Bottom
+
+ # THEN: The combobox should be correct
+ assert page.vertical_combo_box.currentIndex() == 2
+
+ def test_set_vertical_align_str(self):
+ """
+ Test the vertical_align setter with a str
+ """
+ # GIVEN: A AlignmentTransitionsPage instance
+ page = AlignmentTransitionsPage()
+
+ # WHEN: The property is set
+ page.vertical_align = VerticalType.to_string(VerticalType.Top)
+
+ # THEN: The combobox should be correct
+ assert page.vertical_combo_box.currentIndex() == 0
+
+ def test_set_vertical_align_exception(self):
+ """
+ Test the vertical_align setter with something other than a str or int
+ """
+ # GIVEN: A AlignmentTransitionsPage instance
+ page = AlignmentTransitionsPage()
+
+ # WHEN: The property is set
+ # THEN: An exception is raised
+ with pytest.raises(TypeError, match='vertical_align must either be a string or an int'):
+ page.vertical_align = []
+
+ def test_get_is_transition_enabled(self):
+ """
+ Test the is_transition_enabled getter
+ """
+ # GIVEN: A AlignmentTransitionsPage instance with the transitions enabled
+ page = AlignmentTransitionsPage()
+ page.transitions_enabled_check_box.setChecked(False)
+
+ # WHEN: The property is accessed
+ result = page.is_transition_enabled
+
+ # THEN: The result should be correct
+ assert result is False
+
+ def test_set_is_transition_enabled(self):
+ """
+ Test the is_transition_enabled setter
+ """
+ # GIVEN: A AlignmentTransitionsPage instance
+ page = AlignmentTransitionsPage()
+ page._on_transition_enabled_changed = MagicMock()
+
+ # WHEN: The property is set
+ page.is_transition_enabled = True
+
+ # THEN: The result should be correct
+ assert page.transitions_enabled_check_box.isChecked() is True
+ page._on_transition_enabled_changed.assert_called_once_with(True)
+
+ def test_get_transition_type(self):
+ """
+ Test the transition_type getter
+ """
+ # GIVEN: A AlignmentTransitionsPage instance with the combobox set to index 1
+ page = AlignmentTransitionsPage()
+ page.transition_effect_combo_box.setCurrentIndex(1)
+
+ # WHEN: The property is accessed
+ result = page.transition_type
+
+ # THEN: The result should be correct
+ assert result == TransitionType.Slide
+
+ def test_set_transition_type_int(self):
+ """
+ Test the transition_type setter with an int
+ """
+ # GIVEN: A AlignmentTransitionsPage instance
+ page = AlignmentTransitionsPage()
+
+ # WHEN: The property is set
+ page.transition_type = TransitionType.Concave
+
+ # THEN: The combobox should be correct
+ assert page.transition_effect_combo_box.currentIndex() == 3
+
+ def test_set_transition_type_str(self):
+ """
+ Test the transition_type setter with a str
+ """
+ # GIVEN: A AlignmentTransitionsPage instance
+ page = AlignmentTransitionsPage()
+
+ # WHEN: The property is set
+ page.transition_type = TransitionType.to_string(TransitionType.Convex)
+
+ # THEN: The combobox should be correct
+ assert page.transition_effect_combo_box.currentIndex() == 2
+
+ def test_set_transition_type_exception(self):
+ """
+ Test the transition_type setter with something other than a str or int
+ """
+ # GIVEN: A AlignmentTransitionsPage instance
+ page = AlignmentTransitionsPage()
+
+ # WHEN: The property is set
+ # THEN: An exception is raised
+ with pytest.raises(TypeError, match='transition_type must either be a string or an int'):
+ page.transition_type = []
+
+ def test_get_transition_speed(self):
+ """
+ Test the transition_speed getter
+ """
+ # GIVEN: A AlignmentTransitionsPage instance with the combobox set to index 1
+ page = AlignmentTransitionsPage()
+ page.transition_speed_combo_box.setCurrentIndex(0)
+
+ # WHEN: The property is accessed
+ result = page.transition_speed
+
+ # THEN: The result should be correct
+ assert result == TransitionSpeed.Normal
+
+ def test_set_transition_speed_int(self):
+ """
+ Test the transition_speed setter with an int
+ """
+ # GIVEN: A AlignmentTransitionsPage instance
+ page = AlignmentTransitionsPage()
+
+ # WHEN: The property is set
+ page.transition_speed = TransitionSpeed.Fast
+
+ # THEN: The combobox should be correct
+ assert page.transition_speed_combo_box.currentIndex() == 1
+
+ def test_set_transition_speed_str(self):
+ """
+ Test the transition_speed setter with a str
+ """
+ # GIVEN: A AlignmentTransitionsPage instance
+ page = AlignmentTransitionsPage()
+
+ # WHEN: The property is set
+ page.transition_speed = TransitionSpeed.to_string(TransitionSpeed.Slow)
+
+ # THEN: The combobox should be correct
+ assert page.transition_speed_combo_box.currentIndex() == 2
+
+ def test_set_transition_speed_exception(self):
+ """
+ Test the transition_speed setter with something other than a str or int
+ """
+ # GIVEN: A AlignmentTransitionsPage instance
+ page = AlignmentTransitionsPage()
+
+ # WHEN: The property is set
+ # THEN: An exception is raised
+ with pytest.raises(TypeError, match='transition_speed must either be a string or an int'):
+ page.transition_speed = []
diff --git a/tests/openlp_core/pages/test_areaposition.py b/tests/openlp_core/pages/test_areaposition.py
new file mode 100644
index 000000000..916221d9c
--- /dev/null
+++ b/tests/openlp_core/pages/test_areaposition.py
@@ -0,0 +1,318 @@
+# -*- coding: utf-8 -*-
+
+##########################################################################
+# OpenLP - Open Source Lyrics Projection #
+# ---------------------------------------------------------------------- #
+# Copyright (c) 2008-2019 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, either version 3 of the License, or #
+# (at your option) any later version. #
+# #
+# 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, see . #
+##########################################################################
+"""
+Package to test the openlp.core.pages.alignment package.
+"""
+from unittest import TestCase
+
+from openlp.core.pages.areaposition import AreaPositionPage
+from tests.helpers.testmixin import TestMixin
+
+
+class TestAreaPositionPage(TestCase, TestMixin):
+
+ def setUp(self):
+ """Test setup"""
+ self.setup_application()
+ self.build_settings()
+
+ def tearDown(self):
+ """Tear down tests"""
+ del self.app
+
+ def test_init_(self):
+ """
+ Test the initialisation of AreaPositionPage
+ """
+ # GIVEN: The AreaPositionPage class
+ # WHEN: Initialising AreaPositionPage
+ # THEN: We should have an instance of the widget with no errors
+ AreaPositionPage()
+
+ def test_get_use_main_default_location(self):
+ """
+ Test the use_main_default_location getter
+ """
+ # GIVEN: A AreaPositionPage instance with the combobox set to index 1
+ page = AreaPositionPage()
+ page.main_position_check_box.setChecked(False)
+
+ # WHEN: The property is accessed
+ result = page.use_main_default_location
+
+ # THEN: The result should be correct
+ assert result is False
+
+ def test_set_use_main_default_location(self):
+ """
+ Test the use_main_default_location setter with an int
+ """
+ # GIVEN: A AreaPositionPage instance
+ page = AreaPositionPage()
+
+ # WHEN: The property is set
+ page.use_main_default_location = True
+
+ # THEN: The combobox should be correct
+ assert page.main_position_check_box.isChecked() is True
+
+ def test_get_main_x(self):
+ """
+ Test the main_x getter
+ """
+ # GIVEN: A AreaPositionPage instance with the combobox set to index 1
+ page = AreaPositionPage()
+ page.main_x_spin_box.setValue(10)
+
+ # WHEN: The property is accessed
+ result = page.main_x
+
+ # THEN: The result should be correct
+ assert result == 10
+
+ def test_set_main_x(self):
+ """
+ Test the main_x setter with an int
+ """
+ # GIVEN: A AreaPositionPage instance
+ page = AreaPositionPage()
+
+ # WHEN: The property is set
+ page.main_x = 20
+
+ # THEN: The combobox should be correct
+ assert page.main_x_spin_box.value() == 20
+
+ def test_get_main_y(self):
+ """
+ Test the main_y getter
+ """
+ # GIVEN: A AreaPositionPage instance with the combobox set to indey 1
+ page = AreaPositionPage()
+ page.main_y_spin_box.setValue(10)
+
+ # WHEN: The property is accessed
+ result = page.main_y
+
+ # THEN: The result should be correct
+ assert result == 10
+
+ def test_set_main_y(self):
+ """
+ Test the main_y setter with an int
+ """
+ # GIVEN: A AreaPositionPage instance
+ page = AreaPositionPage()
+
+ # WHEN: The property is set
+ page.main_y = 20
+
+ # THEN: The combobox should be correct
+ assert page.main_y_spin_box.value() == 20
+
+ def test_get_main_width(self):
+ """
+ Test the main_width getter
+ """
+ # GIVEN: A AreaPositionPage instance with the combobox set to indewidth 1
+ page = AreaPositionPage()
+ page.main_width_spin_box.setValue(10)
+
+ # WHEN: The property is accessed
+ result = page.main_width
+
+ # THEN: The result should be correct
+ assert result == 10
+
+ def test_set_main_width(self):
+ """
+ Test the main_width setter with an int
+ """
+ # GIVEN: A AreaPositionPage instance
+ page = AreaPositionPage()
+
+ # WHEN: The property is set
+ page.main_width = 20
+
+ # THEN: The combobox should be correct
+ assert page.main_width_spin_box.value() == 20
+
+ def test_get_main_height(self):
+ """
+ Test the main_height getter
+ """
+ # GIVEN: A AreaPositionPage instance with the combobox set to indeheight 1
+ page = AreaPositionPage()
+ page.main_height_spin_box.setValue(10)
+
+ # WHEN: The property is accessed
+ result = page.main_height
+
+ # THEN: The result should be correct
+ assert result == 10
+
+ def test_set_main_height(self):
+ """
+ Test the main_height setter with an int
+ """
+ # GIVEN: A AreaPositionPage instance
+ page = AreaPositionPage()
+
+ # WHEN: The property is set
+ page.main_height = 20
+
+ # THEN: The combobox should be correct
+ assert page.main_height_spin_box.value() == 20
+
+ def test_get_footer_x(self):
+ """
+ Test the footer_x getter
+ """
+ # GIVEN: A AreaPositionPage instance with the combobox set to index 1
+ page = AreaPositionPage()
+ page.footer_x_spin_box.setValue(10)
+
+ # WHEN: The property is accessed
+ result = page.footer_x
+
+ # THEN: The result should be correct
+ assert result == 10
+
+ def test_set_footer_x(self):
+ """
+ Test the footer_x setter with an int
+ """
+ # GIVEN: A AreaPositionPage instance
+ page = AreaPositionPage()
+
+ # WHEN: The property is set
+ page.footer_x = 20
+
+ # THEN: The combobox should be correct
+ assert page.footer_x_spin_box.value() == 20
+
+ def test_get_footer_y(self):
+ """
+ Test the footer_y getter
+ """
+ # GIVEN: A AreaPositionPage instance with the combobox set to indey 1
+ page = AreaPositionPage()
+ page.footer_y_spin_box.setValue(10)
+
+ # WHEN: The property is accessed
+ result = page.footer_y
+
+ # THEN: The result should be correct
+ assert result == 10
+
+ def test_set_footer_y(self):
+ """
+ Test the footer_y setter with an int
+ """
+ # GIVEN: A AreaPositionPage instance
+ page = AreaPositionPage()
+
+ # WHEN: The property is set
+ page.footer_y = 20
+
+ # THEN: The combobox should be correct
+ assert page.footer_y_spin_box.value() == 20
+
+ def test_get_footer_width(self):
+ """
+ Test the footer_width getter
+ """
+ # GIVEN: A AreaPositionPage instance with the combobox set to indewidth 1
+ page = AreaPositionPage()
+ page.footer_width_spin_box.setValue(1900)
+
+ # WHEN: The property is accessed
+ result = page.footer_width
+
+ # THEN: The result should be correct
+ assert result == 1900
+
+ def test_set_footer_width(self):
+ """
+ Test the footer_width setter with an int
+ """
+ # GIVEN: A AreaPositionPage instance
+ page = AreaPositionPage()
+
+ # WHEN: The property is set
+ page.footer_width = 1900
+
+ # THEN: The combobox should be correct
+ assert page.footer_width_spin_box.value() == 1900
+
+ def test_get_footer_height(self):
+ """
+ Test the footer_height getter
+ """
+ # GIVEN: A AreaPositionPage instance with the combobox set to indeheight 1
+ page = AreaPositionPage()
+ page.footer_height_spin_box.setValue(1080)
+
+ # WHEN: The property is accessed
+ result = page.footer_height
+
+ # THEN: The result should be correct
+ assert result == 1080
+
+ def test_set_footer_height(self):
+ """
+ Test the footer_height setter with an int
+ """
+ # GIVEN: A AreaPositionPage instance
+ page = AreaPositionPage()
+
+ # WHEN: The property is set
+ page.footer_height = 1080
+
+ # THEN: The combobox should be correct
+ assert page.footer_height_spin_box.value() == 1080
+
+ def test_get_use_footer_default_location(self):
+ """
+ Test the use_footer_default_location getter
+ """
+ # GIVEN: A AreaPositionPage instance with the combobox set to index 1
+ page = AreaPositionPage()
+ page.footer_position_check_box.setChecked(False)
+
+ # WHEN: The property is accessed
+ result = page.use_footer_default_location
+
+ # THEN: The result should be correct
+ assert result is False
+
+ def test_set_use_footer_default_location(self):
+ """
+ Test the use_footer_default_location setter with an int
+ """
+ # GIVEN: A AreaPositionPage instance
+ page = AreaPositionPage()
+
+ # WHEN: The property is set
+ page.use_footer_default_location = True
+
+ # THEN: The combobox should be correct
+ assert page.footer_position_check_box.isChecked() is True
diff --git a/tests/openlp_core/pages/test_background.py b/tests/openlp_core/pages/test_background.py
new file mode 100644
index 000000000..d6090dffa
--- /dev/null
+++ b/tests/openlp_core/pages/test_background.py
@@ -0,0 +1,363 @@
+# -*- coding: utf-8 -*-
+
+##########################################################################
+# OpenLP - Open Source Lyrics Projection #
+# ---------------------------------------------------------------------- #
+# Copyright (c) 2008-2019 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, either version 3 of the License, or #
+# (at your option) any later version. #
+# #
+# 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, see . #
+##########################################################################
+"""
+Package to test the openlp.core.pages.background package.
+"""
+from pathlib import Path
+from unittest import TestCase
+from unittest.mock import MagicMock
+
+import pytest
+
+from openlp.core.lib.theme import BackgroundType, BackgroundGradientType
+from openlp.core.pages.background import BackgroundPage
+from tests.helpers.testmixin import TestMixin
+
+
+class TestBackgroundPage(TestCase, TestMixin):
+
+ def setUp(self):
+ """Test setup"""
+ self.setup_application()
+ self.build_settings()
+
+ def tearDown(self):
+ """Tear down tests"""
+ del self.app
+
+ def test_init_(self):
+ """
+ Test the initialisation of BackgroundPage
+ """
+ # GIVEN: The BackgroundPage class
+ # WHEN: Initialising BackgroundPage
+ # THEN: We should have an instance of the widget with no errors
+ BackgroundPage()
+
+ def test_on_background_type_index_changed(self):
+ """
+ Test the _on_background_type_index_changed() slot
+ """
+ # GIVEN: And instance of BackgroundPage and some mock widgets
+ page = BackgroundPage()
+ page.color_widgets = [MagicMock()]
+ page.gradient_widgets = [MagicMock()]
+
+ # WHEN: _on_background_type_index_changed
+ page._on_background_type_index_changed(1)
+
+ # THEN: The correct widgets should be visible
+ page.color_widgets[0].hide.assert_called_once()
+ page.gradient_widgets[0].hide.assert_called_once()
+ page.gradient_widgets[0].show.assert_called_once()
+
+ def test_get_background_type(self):
+ """
+ Test the background_type getter
+ """
+ # GIVEN: A BackgroundPage instance with the combobox set to index 1
+ page = BackgroundPage()
+ page.background_combo_box.setCurrentIndex(1)
+
+ # WHEN: The property is accessed
+ result = page.background_type
+
+ # THEN: The result should be correct
+ assert result == 'gradient'
+
+ def test_set_background_type_int(self):
+ """
+ Test the background_type setter with an int
+ """
+ # GIVEN: A BackgroundPage instance
+ page = BackgroundPage()
+
+ # WHEN: The property is set
+ page.background_type = BackgroundType.Image
+
+ # THEN: The combobox should be correct
+ assert page.background_combo_box.currentIndex() == 2
+
+ def test_set_background_type_str(self):
+ """
+ Test the background_type setter with a str
+ """
+ # GIVEN: A BackgroundPage instance
+ page = BackgroundPage()
+
+ # WHEN: The property is set
+ page.background_type = BackgroundType.to_string(BackgroundType.Gradient)
+
+ # THEN: The combobox should be correct
+ assert page.background_combo_box.currentIndex() == 1
+
+ def test_set_background_type_exception(self):
+ """
+ Test the background_type setter with something other than a str or int
+ """
+ # GIVEN: A BackgroundPage instance
+ page = BackgroundPage()
+
+ # WHEN: The property is set
+ # THEN: An exception is raised
+ with pytest.raises(TypeError, match='background_type must either be a string or an int'):
+ page.background_type = []
+
+ def test_get_color(self):
+ """
+ Test the color getter
+ """
+ # GIVEN: A BackgroundPage instance with the color button set to #f0f
+ page = BackgroundPage()
+ page.color_button.color = '#f0f'
+
+ # WHEN: The property is accessed
+ result = page.color
+
+ # THEN: The result should be correct
+ assert result == '#f0f'
+
+ def test_set_color(self):
+ """
+ Test the color setter
+ """
+ # GIVEN: A BackgroundPage instance
+ page = BackgroundPage()
+
+ # WHEN: The property is set
+ page.color = '#0f0'
+
+ # THEN: The result should be correct
+ assert page.color_button.color == '#0f0'
+
+ def test_get_gradient_type(self):
+ """
+ Test the gradient_type getter
+ """
+ # GIVEN: A BackgroundPage instance with the combobox set to index 1
+ page = BackgroundPage()
+ page.gradient_combo_box.setCurrentIndex(1)
+
+ # WHEN: The property is accessed
+ result = page.gradient_type
+
+ # THEN: The result should be correct
+ assert result == 'vertical'
+
+ def test_set_gradient_type_int(self):
+ """
+ Test the gradient_type setter with an int
+ """
+ # GIVEN: A BackgroundPage instance
+ page = BackgroundPage()
+
+ # WHEN: The property is set
+ page.gradient_type = BackgroundGradientType.Horizontal
+
+ # THEN: The combobox should be correct
+ assert page.gradient_combo_box.currentIndex() == 0
+
+ def test_set_gradient_type_str(self):
+ """
+ Test the gradient_type setter with a str
+ """
+ # GIVEN: A BackgroundPage instance
+ page = BackgroundPage()
+
+ # WHEN: The property is set
+ page.gradient_type = BackgroundGradientType.to_string(BackgroundGradientType.Circular)
+
+ # THEN: The combobox should be correct
+ assert page.gradient_combo_box.currentIndex() == 2
+
+ def test_set_gradient_type_exception(self):
+ """
+ Test the gradient_type setter with something other than a str or int
+ """
+ # GIVEN: A BackgroundPage instance
+ page = BackgroundPage()
+
+ # WHEN: The property is set
+ # THEN: An exception is raised
+ with pytest.raises(TypeError, match='gradient_type must either be a string or an int'):
+ page.gradient_type = []
+
+ def test_get_gradient_start(self):
+ """
+ Test the gradient_start getter
+ """
+ # GIVEN: A BackgroundPage instance with the gradient_start button set to #f0f
+ page = BackgroundPage()
+ page.gradient_start_button.color = '#f0f'
+
+ # WHEN: The property is accessed
+ result = page.gradient_start
+
+ # THEN: The result should be correct
+ assert result == '#f0f'
+
+ def test_set_gradient_start(self):
+ """
+ Test the gradient_start setter
+ """
+ # GIVEN: A BackgroundPage instance
+ page = BackgroundPage()
+
+ # WHEN: The property is set
+ page.gradient_start = '#0f0'
+
+ # THEN: The result should be correct
+ assert page.gradient_start_button.color == '#0f0'
+
+ def test_get_gradient_end(self):
+ """
+ Test the gradient_end getter
+ """
+ # GIVEN: A BackgroundPage instance with the gradient_end button set to #f0f
+ page = BackgroundPage()
+ page.gradient_end_button.color = '#f0f'
+
+ # WHEN: The property is accessed
+ result = page.gradient_end
+
+ # THEN: The result should be correct
+ assert result == '#f0f'
+
+ def test_set_gradient_end(self):
+ """
+ Test the gradient_end setter
+ """
+ # GIVEN: A BackgroundPage instance
+ page = BackgroundPage()
+
+ # WHEN: The property is set
+ page.gradient_end = '#0f0'
+
+ # THEN: The result should be correct
+ assert page.gradient_end_button.color == '#0f0'
+
+ def test_get_image_color(self):
+ """
+ Test the image_color getter
+ """
+ # GIVEN: A BackgroundPage instance with the image_color button set to #f0f
+ page = BackgroundPage()
+ page.image_color_button.color = '#f0f'
+
+ # WHEN: The property is accessed
+ result = page.image_color
+
+ # THEN: The result should be correct
+ assert result == '#f0f'
+
+ def test_set_image_color(self):
+ """
+ Test the image_color setter
+ """
+ # GIVEN: A BackgroundPage instance
+ page = BackgroundPage()
+
+ # WHEN: The property is set
+ page.image_color = '#0f0'
+
+ # THEN: The result should be correct
+ assert page.image_color_button.color == '#0f0'
+
+ def test_get_image_path(self):
+ """
+ Test the image_path getter
+ """
+ # GIVEN: A BackgroundPage instance with the image_path edit set to a path
+ page = BackgroundPage()
+ page.image_path_edit.path = Path('.')
+
+ # WHEN: The property is accessed
+ result = page.image_path
+
+ # THEN: The result should be correct
+ assert result == Path('.')
+
+ def test_set_image_path(self):
+ """
+ Test the image_path setter
+ """
+ # GIVEN: A BackgroundPage instance
+ page = BackgroundPage()
+
+ # WHEN: The property is set
+ page.image_path = Path('openlp')
+
+ # THEN: The result should be correct
+ assert page.image_path_edit.path == Path('openlp')
+
+ def test_get_video_color(self):
+ """
+ Test the video_color getter
+ """
+ # GIVEN: A BackgroundPage instance with the video_color button set to #f0f
+ page = BackgroundPage()
+ page.video_color_button.color = '#f0f'
+
+ # WHEN: The property is accessed
+ result = page.video_color
+
+ # THEN: The result should be correct
+ assert result == '#f0f'
+
+ def test_set_video_color(self):
+ """
+ Test the video_color setter
+ """
+ # GIVEN: A BackgroundPage instance
+ page = BackgroundPage()
+
+ # WHEN: The property is set
+ page.video_color = '#0f0'
+
+ # THEN: The result should be correct
+ assert page.video_color_button.color == '#0f0'
+
+ def test_get_video_path(self):
+ """
+ Test the video_path getter
+ """
+ # GIVEN: A BackgroundPage instance with the video_path edit set to a path
+ page = BackgroundPage()
+ page.video_path_edit.path = Path('.')
+
+ # WHEN: The property is accessed
+ result = page.video_path
+
+ # THEN: The result should be correct
+ assert result == Path('.')
+
+ def test_set_video_path(self):
+ """
+ Test the video_path setter
+ """
+ # GIVEN: A BackgroundPage instance
+ page = BackgroundPage()
+
+ # WHEN: The property is set
+ page.video_path = Path('openlp')
+
+ # THEN: The result should be correct
+ assert page.video_path_edit.path == Path('openlp')
diff --git a/tests/openlp_core/pages/test_fontselection.py b/tests/openlp_core/pages/test_fontselection.py
new file mode 100644
index 000000000..64ecb7600
--- /dev/null
+++ b/tests/openlp_core/pages/test_fontselection.py
@@ -0,0 +1,594 @@
+# -*- coding: utf-8 -*-
+
+##########################################################################
+# OpenLP - Open Source Lyrics Projection #
+# ---------------------------------------------------------------------- #
+# Copyright (c) 2008-2019 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, either version 3 of the License, or #
+# (at your option) any later version. #
+# #
+# 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, see . #
+##########################################################################
+"""
+Package to test the openlp.core.widgets.fontselect package.
+"""
+from unittest import TestCase
+from unittest.mock import MagicMock, patch
+
+import pytest
+
+from openlp.core.pages.fontselect import FontSelectPage
+from tests.helpers.testmixin import TestMixin
+
+
+class TestFontSelectPage(TestCase, TestMixin):
+
+ def setUp(self):
+ """Test setup"""
+ self.setup_application()
+ self.build_settings()
+
+ def tearDown(self):
+ """Tear down tests"""
+ del self.app
+
+ def test_init_(self):
+ """
+ Test the initialisation of FontSelectPage
+ """
+ # GIVEN: The FontSelectPage class
+ # WHEN: Initialising FontSelectPage
+ # THEN: We should have an instance of the widget with no errors
+ FontSelectPage()
+
+ def test_font_name_changed(self):
+ # GIVEN: An instance of FontSelectPage with a mocked out "font_name_changed" signal
+ instance = FontSelectPage()
+ instance.font_name_changed = MagicMock()
+
+ # WHEN: The font name changes
+ instance._on_font_name_changed('Sans serif')
+
+ # THEN: The signal should be emitted with the correct value
+ instance.font_name_changed.emit.assert_called_once_with('Sans serif')
+
+ def test_font_name_changed_int(self):
+ # GIVEN: An instance of FontSelectPage with a mocked out "font_name_changed" signal
+ instance = FontSelectPage()
+ instance.font_name_changed = MagicMock()
+
+ # WHEN: The font name changes
+ instance._on_font_name_changed(5)
+
+ # THEN: The signal should be emitted with the correct value
+ assert instance.font_name_changed.emit.call_count == 0
+
+ def test_font_color_changed(self):
+ # GIVEN: An instance of FontSelectPage with a mocked out "font_color_changed" signal
+ instance = FontSelectPage()
+ instance.font_color_changed = MagicMock()
+
+ # WHEN: The font color changes
+ instance._on_font_color_changed('#fff')
+
+ # THEN: The signal should be emitted with the correct value
+ instance.font_color_changed.emit.assert_called_once_with('#fff')
+
+ def test_is_bold_changed(self):
+ # GIVEN: An instance of FontSelectPage with a mocked out "is_bold_changed" signal
+ instance = FontSelectPage()
+ instance.is_bold_changed = MagicMock()
+
+ # WHEN: The font name changes
+ instance._on_style_bold_toggled(True)
+
+ # THEN: The signal should be emitted with the correct value
+ instance.is_bold_changed.emit.assert_called_once_with(True)
+
+ def test_is_italic_changed(self):
+ # GIVEN: An instance of FontSelectPage with a mocked out "style_italic_changed" signal
+ instance = FontSelectPage()
+ instance.is_italic_changed = MagicMock()
+
+ # WHEN: The font name changes
+ instance._on_style_italic_toggled(False)
+
+ # THEN: The signal should be emitted with the correct value
+ instance.is_italic_changed.emit.assert_called_once_with(False)
+
+ def test_font_size_changed(self):
+ # GIVEN: An instance of FontSelectPage with a mocked out "font_size_changed" signal
+ instance = FontSelectPage()
+ instance.font_size_changed = MagicMock()
+
+ # WHEN: The font size changes
+ instance._on_font_size_changed(14)
+
+ # THEN: The signal should be emitted with the correct value
+ instance.font_size_changed.emit.assert_called_once_with(14)
+
+ def test_line_spacing_changed(self):
+ # GIVEN: An instance of FontSelectPage with a mocked out "line_spacing_changed" signal
+ instance = FontSelectPage()
+ instance.line_spacing_changed = MagicMock()
+
+ # WHEN: The font name changes
+ instance._on_line_spacing_changed(1)
+
+ # THEN: The signal should be emitted with the correct value
+ instance.line_spacing_changed.emit.assert_called_once_with(1)
+
+ def test_is_outline_enabled_changed(self):
+ # GIVEN: An instance of FontSelectPage with a mocked out "outline_enabled_changed" signal
+ instance = FontSelectPage()
+ instance.is_outline_enabled_changed = MagicMock()
+
+ # WHEN: The font name changes
+ instance._on_outline_toggled(True)
+
+ # THEN: The signal should be emitted with the correct value
+ instance.is_outline_enabled_changed.emit.assert_called_once_with(True)
+
+ def test_outline_color_changed(self):
+ # GIVEN: An instance of FontSelectPage with a mocked out "outline_color_changed" signal
+ instance = FontSelectPage()
+ instance.outline_color_changed = MagicMock()
+
+ # WHEN: The font name changes
+ instance._on_outline_color_changed('#000')
+
+ # THEN: The signal should be emitted with the correct value
+ instance.outline_color_changed.emit.assert_called_once_with('#000')
+
+ def test_outline_size_changed(self):
+ # GIVEN: An instance of FontSelectPage with a mocked out "outline_size_changed" signal
+ instance = FontSelectPage()
+ instance.outline_size_changed = MagicMock()
+
+ # WHEN: The font name changes
+ instance._on_outline_size_changed(2)
+
+ # THEN: The signal should be emitted with the correct value
+ instance.outline_size_changed.emit.assert_called_once_with(2)
+
+ def test_is_shadow_enabled_changed(self):
+ # GIVEN: An instance of FontSelectPage with a mocked out "is_shadow_enabled_changed" signal
+ instance = FontSelectPage()
+ instance.is_shadow_enabled_changed = MagicMock()
+
+ # WHEN: The font name changes
+ instance._on_shadow_toggled(False)
+
+ # THEN: The signal should be emitted with the correct value
+ instance.is_shadow_enabled_changed.emit.assert_called_once_with(False)
+
+ def test_shadow_color_changed(self):
+ # GIVEN: An instance of FontSelectPage with a mocked out "shadow_color_changed" signal
+ instance = FontSelectPage()
+ instance.shadow_color_changed = MagicMock()
+
+ # WHEN: The font name changes
+ instance._on_shadow_color_changed('#000')
+
+ # THEN: The signal should be emitted with the correct value
+ instance.shadow_color_changed.emit.assert_called_once_with('#000')
+
+ def test_shadow_size_changed(self):
+ # GIVEN: An instance of FontSelectPage with a mocked out "shadow_size_changed" signal
+ instance = FontSelectPage()
+ instance.shadow_size_changed = MagicMock()
+
+ # WHEN: The font name changes
+ instance._on_shadow_size_changed(5)
+
+ # THEN: The signal should be emitted with the correct value
+ instance.shadow_size_changed.emit.assert_called_once_with(5)
+
+ def test_enable_features(self):
+ """
+ Test that the `enable_features` method correctly enables widgets based on features
+ """
+ # GIVEN: An instance of FontSelectPage with some mocks
+ instance = FontSelectPage()
+ mock_label = MagicMock()
+ mock_control = MagicMock()
+ instance.feature_widgets = {'test': [mock_label, mock_control]}
+
+ # WHEN: The "test" feature is enabled
+ instance.enable_features('test')
+
+ # THEN: "show()" is called on all the widgets
+ mock_label.show.assert_called_once()
+ mock_control.show.assert_called_once()
+
+ def test_enable_missing_features(self):
+ """
+ Test that the `enable_features` method correctly raises an error on a non-existent feature
+ """
+ # GIVEN: An instance of FontSelectPage with some mocks
+ instance = FontSelectPage()
+ mock_label = MagicMock()
+ mock_control = MagicMock()
+ instance.feature_widgets = {'test1': [mock_label, mock_control]}
+
+ # WHEN: The "test" feature is enabled
+ with pytest.raises(KeyError, match='No such feature'):
+ instance.enable_features('test2')
+
+ def test_disable_features(self):
+ """
+ Test that the `disable_features` method correctly disables widgets based on features
+ """
+ # GIVEN: An instance of FontSelectPage with some mocks
+ instance = FontSelectPage()
+ mock_label = MagicMock()
+ mock_control = MagicMock()
+ instance.feature_widgets = {'test': [mock_label, mock_control]}
+
+ # WHEN: The "test" feature is disabled
+ instance.disable_features('test')
+
+ # THEN: "show()" is called on all the widgets
+ mock_label.hide.assert_called_once()
+ mock_control.hide.assert_called_once()
+
+ def test_disable_missing_features(self):
+ """
+ Test that the `disable_features` method correctly raises an error on a non-existent feature
+ """
+ # GIVEN: An instance of FontSelectPage with some mocks
+ instance = FontSelectPage()
+ mock_label = MagicMock()
+ mock_control = MagicMock()
+ instance.feature_widgets = {'test1': [mock_label, mock_control]}
+
+ # WHEN: The "test" feature is disabled
+ with pytest.raises(KeyError, match='No such feature'):
+ instance.disable_features('test2')
+
+ def test_get_font_name_property(self):
+ """
+ Test the `font_name` property
+ """
+ # GIVEN: An instance of FontSelectPage with some mocks
+ instance = FontSelectPage()
+ instance.font_name_combobox.currentFont = MagicMock(
+ return_value=MagicMock(**{'family.return_value': 'Sans serif'}))
+
+ # WHEN: The `font_name` propert is accessed
+ result = instance.font_name
+
+ # THEN: The value should be correct
+ assert result == 'Sans serif'
+
+ def test_set_font_name_property(self):
+ """
+ Test setting the `font_name` property
+ """
+ # GIVEN: An instance of FontSelectPage with some mocks
+ instance = FontSelectPage()
+ instance.font_name_combobox.setCurrentFont = MagicMock()
+
+ # WHEN: The `font_name` property is set
+ with patch('openlp.core.pages.fontselect.QtGui.QFont') as MockFont:
+ mocked_font = MagicMock()
+ MockFont.return_value = mocked_font
+ instance.font_name = 'Serif'
+
+ # THEN: The correct value should be set
+ MockFont.assert_called_once_with('Serif')
+ instance.font_name_combobox.setCurrentFont.assert_called_once_with(mocked_font)
+
+ def test_get_font_color_property(self):
+ """
+ Test the `font_color` property
+ """
+ # GIVEN: An instance of FontSelectPage with some mocks
+ instance = FontSelectPage()
+ instance.font_color_button.color = '#000'
+
+ # WHEN: The `font_color` propert is accessed
+ result = instance.font_color
+
+ # THEN: The value should be correct
+ assert result == '#000'
+
+ def test_set_font_color_property(self):
+ """
+ Test setting the `font_color` property
+ """
+ # GIVEN: An instance of FontSelectPage with some mocks
+ instance = FontSelectPage()
+
+ # WHEN: The `font_color` property is set
+ instance.font_color = '#fff'
+
+ # THEN: The correct value should be set
+ assert instance.font_color_button.color == '#fff'
+
+ def test_get_is_bold_property(self):
+ """
+ Test the `is_bold` property
+ """
+ # GIVEN: An instance of FontSelectPage with some mocks
+ instance = FontSelectPage()
+ instance.style_bold_button.isChecked = MagicMock(return_value=False)
+
+ # WHEN: The `is_bold` propert is accessed
+ result = instance.is_bold
+
+ # THEN: The value should be correct
+ assert result is False
+
+ def test_set_is_bold_property(self):
+ """
+ Test setting the `is_bold` property
+ """
+ # GIVEN: An instance of FontSelectPage with some mocks
+ instance = FontSelectPage()
+ instance.style_bold_button.setChecked = MagicMock()
+
+ # WHEN: The `is_bold` property is set
+ instance.is_bold = True
+
+ # THEN: The correct value should be set
+ instance.style_bold_button.setChecked.assert_called_once_with(True)
+
+ def test_get_is_italic_property(self):
+ """
+ Test the `is_italic` property
+ """
+ # GIVEN: An instance of FontSelectPage with some mocks
+ instance = FontSelectPage()
+ instance.style_italic_button.isChecked = MagicMock(return_value=True)
+
+ # WHEN: The `is_italic` propert is accessed
+ result = instance.is_italic
+
+ # THEN: The value should be correct
+ assert result is True
+
+ def test_set_is_italic_property(self):
+ """
+ Test setting the `is_italic` property
+ """
+ # GIVEN: An instance of FontSelectPage with some mocks
+ instance = FontSelectPage()
+ instance.style_italic_button.setChecked = MagicMock()
+
+ # WHEN: The `is_italic` property is set
+ instance.is_italic = False
+
+ # THEN: The correct value should be set
+ instance.style_italic_button.setChecked.assert_called_once_with(False)
+
+ def test_get_font_size_property(self):
+ """
+ Test the `font_size` property
+ """
+ # GIVEN: An instance of FontSelectPage with some mocks
+ instance = FontSelectPage()
+ instance.font_size_spinbox.value = MagicMock(return_value=16)
+
+ # WHEN: The `font_size` propert is accessed
+ result = instance.font_size
+
+ # THEN: The value should be correct
+ assert result == 16
+
+ def test_set_font_size_property(self):
+ """
+ Test setting the `font_size` property
+ """
+ # GIVEN: An instance of FontSelectPage with some mocks
+ instance = FontSelectPage()
+ instance.font_size_spinbox.setValue = MagicMock()
+
+ # WHEN: The `font_size` property is set
+ instance.font_size = 18
+
+ # THEN: The correct value should be set
+ instance.font_size_spinbox.setValue.assert_called_once_with(18)
+
+ def test_get_line_spacing_property(self):
+ """
+ Test the `line_spacing` property
+ """
+ # GIVEN: An instance of FontSelectPage with some mocks
+ instance = FontSelectPage()
+ instance.line_spacing_spinbox.value = MagicMock(return_value=1)
+
+ # WHEN: The `line_spacing` propert is accessed
+ result = instance.line_spacing
+
+ # THEN: The value should be correct
+ assert result == 1
+
+ def test_set_line_spacing_property(self):
+ """
+ Test setting the `line_spacing` property
+ """
+ # GIVEN: An instance of FontSelectPage with some mocks
+ instance = FontSelectPage()
+ instance.line_spacing_spinbox.setValue = MagicMock()
+
+ # WHEN: The `line_spacing` property is set
+ instance.line_spacing = 2
+
+ # THEN: The correct value should be set
+ instance.line_spacing_spinbox.setValue.assert_called_once_with(2)
+
+ def test_get_is_outline_enabled_property(self):
+ """
+ Test the `is_outline_enabled` property
+ """
+ # GIVEN: An instance of FontSelectPage with some mocks
+ instance = FontSelectPage()
+ instance.outline_groupbox.isChecked = MagicMock(return_value=True)
+
+ # WHEN: The `is_outline_enabled` propert is accessed
+ result = instance.is_outline_enabled
+
+ # THEN: The value should be correct
+ assert result is True
+
+ def test_set_is_outline_enabled_property(self):
+ """
+ Test setting the `is_outline_enabled` property
+ """
+ # GIVEN: An instance of FontSelectPage with some mocks
+ instance = FontSelectPage()
+ instance.outline_groupbox.setChecked = MagicMock()
+
+ # WHEN: The `is_outline_enabled` property is set
+ instance.is_outline_enabled = False
+
+ # THEN: The correct value should be set
+ instance.outline_groupbox.setChecked.assert_called_once_with(False)
+
+ def test_get_outline_color_property(self):
+ """
+ Test the `outline_color` property
+ """
+ # GIVEN: An instance of FontSelectPage with some mocks
+ instance = FontSelectPage()
+ instance.outline_color_button.color = '#fff'
+
+ # WHEN: The `outline_color` propert is accessed
+ result = instance.outline_color
+
+ # THEN: The value should be correct
+ assert result == '#fff'
+
+ def test_set_outline_color_property(self):
+ """
+ Test setting the `outline_color` property
+ """
+ # GIVEN: An instance of FontSelectPage with some mocks
+ instance = FontSelectPage()
+
+ # WHEN: The `outline_color` property is set
+ instance.outline_color = '#000'
+
+ # THEN: The correct value should be set
+ assert instance.outline_color_button.color == '#000'
+
+ def test_get_outline_size_property(self):
+ """
+ Test the `outline_size` property
+ """
+ # GIVEN: An instance of FontSelectPage with some mocks
+ instance = FontSelectPage()
+ instance.outline_size_spinbox.value = MagicMock(return_value=2)
+
+ # WHEN: The `outline_size` propert is accessed
+ result = instance.outline_size
+
+ # THEN: The value should be correct
+ assert result == 2
+
+ def test_set_outline_size_property(self):
+ """
+ Test setting the `outline_size` property
+ """
+ # GIVEN: An instance of FontSelectPage with some mocks
+ instance = FontSelectPage()
+ instance.outline_size_spinbox.setValue = MagicMock()
+
+ # WHEN: The `outline_size` property is set
+ instance.outline_size = 1
+
+ # THEN: The correct value should be set
+ instance.outline_size_spinbox.setValue.assert_called_once_with(1)
+
+ def test_get_is_shadow_enabled_property(self):
+ """
+ Test the `is_shadow_enabled` property
+ """
+ # GIVEN: An instance of FontSelectPage with some mocks
+ instance = FontSelectPage()
+ instance.shadow_groupbox.isChecked = MagicMock(return_value=False)
+
+ # WHEN: The `is_shadow_enabled` propert is accessed
+ result = instance.is_shadow_enabled
+
+ # THEN: The value should be correct
+ assert result is False
+
+ def test_set_is_shadow_enabled_property(self):
+ """
+ Test setting the `is_shadow_enabled` property
+ """
+ # GIVEN: An instance of FontSelectPage with some mocks
+ instance = FontSelectPage()
+ instance.shadow_groupbox.setChecked = MagicMock()
+
+ # WHEN: The `is_shadow_enabled` property is set
+ instance.is_shadow_enabled = True
+
+ # THEN: The correct value should be set
+ instance.shadow_groupbox.setChecked.assert_called_once_with(True)
+
+ def test_get_shadow_color_property(self):
+ """
+ Test the `shadow_color` property
+ """
+ # GIVEN: An instance of FontSelectPage with some mocks
+ instance = FontSelectPage()
+ instance.shadow_color_button.color = '#000'
+
+ # WHEN: The `shadow_color` propert is accessed
+ result = instance.shadow_color
+
+ # THEN: The value should be correct
+ assert result == '#000'
+
+ def test_set_shadow_color_property(self):
+ """
+ Test setting the `shadow_color` property
+ """
+ # GIVEN: An instance of FontSelectPage with some mocks
+ instance = FontSelectPage()
+
+ # WHEN: The `shadow_color` property is set
+ instance.shadow_color = '#fff'
+
+ # THEN: The correct value should be set
+ instance.shadow_color_button.color == '#fff'
+
+ def test_get_shadow_size_property(self):
+ """
+ Test the `shadow_size` property
+ """
+ # GIVEN: An instance of FontSelectPage with some mocks
+ instance = FontSelectPage()
+ instance.shadow_size_spinbox.value = MagicMock(return_value=5)
+
+ # WHEN: The `shadow_size` propert is accessed
+ result = instance.shadow_size
+
+ # THEN: The value should be correct
+ assert result == 5
+
+ def test_set_shadow_size_property(self):
+ """
+ Test setting the `shadow_size` property
+ """
+ # GIVEN: An instance of FontSelectPage with some mocks
+ instance = FontSelectPage()
+ instance.shadow_size_spinbox.setValue = MagicMock()
+
+ # WHEN: The `shadow_size` property is set
+ instance.shadow_size = 10
+
+ # THEN: The correct value should be set
+ instance.shadow_size_spinbox.setValue.assert_called_once_with(10)
diff --git a/tests/openlp_core/pages/test_gridlayoutpage.py b/tests/openlp_core/pages/test_gridlayoutpage.py
new file mode 100644
index 000000000..f813586c2
--- /dev/null
+++ b/tests/openlp_core/pages/test_gridlayoutpage.py
@@ -0,0 +1,97 @@
+# -*- coding: utf-8 -*-
+
+##########################################################################
+# OpenLP - Open Source Lyrics Projection #
+# ---------------------------------------------------------------------- #
+# Copyright (c) 2008-2019 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, either version 3 of the License, or #
+# (at your option) any later version. #
+# #
+# 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, see . #
+##########################################################################
+"""
+Package to test the openlp.core.pages package.
+"""
+from unittest import TestCase
+from unittest.mock import MagicMock, call, patch
+
+import pytest
+
+from openlp.core.pages import GridLayoutPage
+from tests.helpers.testmixin import TestMixin
+
+
+class TestGridLayoutPage(TestCase, TestMixin):
+
+ def setUp(self):
+ """Test setup"""
+ self.setup_application()
+ self.build_settings()
+
+ def tearDown(self):
+ """Tear down tests"""
+ del self.app
+
+ @patch('openlp.core.pages.GridLayoutPage.setup_ui')
+ @patch('openlp.core.pages.GridLayoutPage.retranslate_ui')
+ def test_resize_event(self, mocked_retranslate_ui, mocked_setup_ui):
+ """
+ Test that the `resizeEvent()` method called the `resize_columns()` method.
+ """
+ # GIVEN: An instance of GridLayoutPage with a mocked out "resize_columns" method
+ instance = GridLayoutPage()
+ instance.resize_columns = MagicMock()
+
+ # WHEN: resizeEvent is called
+ instance.resizeEvent(None)
+
+ # THEN: resize_widgets should have been called
+ instance.resize_columns.assert_called_once()
+
+ def test_unimplemented_setup_ui(self):
+ """
+ Test that setup_ui() throws a NotImplementedError
+ """
+ with pytest.raises(NotImplementedError, match='Descendant pages need to implement setup_ui'):
+ GridLayoutPage()
+
+ @patch('openlp.core.pages.GridLayoutPage.setup_ui')
+ def test_unimplemented_retranslate_ui(self, mocked_setup_ui):
+ """
+ Test that retranslate_ui() throws a NotImplementedError
+ """
+ with pytest.raises(NotImplementedError, match='Descendant pages need to implement retranslate_ui'):
+ GridLayoutPage()
+
+ @patch('openlp.core.pages.GridLayoutPage.setup_ui')
+ @patch('openlp.core.pages.GridLayoutPage.retranslate_ui')
+ def test_resize_columns(self, mocked_retranslate_ui, mocked_setup_ui):
+ """
+ Test the `resize_columns()` method with an implemented page
+ """
+ # GIVEN: An instance of GridLayoutPage and various mocked out methods
+ instance = GridLayoutPage()
+ instance.layout.contentsRect = MagicMock(return_value=MagicMock(**{'width.return_value': 100}))
+ instance.layout.horizontalSpacing = MagicMock(return_value=6)
+ instance.layout.columnCount = MagicMock(return_value=4)
+ instance.layout.setColumnMinimumWidth = MagicMock()
+
+ # WHEN: `resize_columns()` is called
+ instance.resize_columns()
+
+ # THEN: The column widths should be set to 16
+ instance.layout.contentsRect.assert_called_once()
+ instance.layout.horizontalSpacing.assert_called_once()
+ instance.layout.columnCount.assert_called_once()
+ assert instance._column_width == 20
+ assert instance.layout.setColumnMinimumWidth.call_args_list == [call(0, 20), call(1, 20),
+ call(2, 20), call(3, 20)]
diff --git a/tests/openlp_core/ui/test_themeform.py b/tests/openlp_core/ui/test_themeform.py
index 54a434729..7fdc69bd7 100644
--- a/tests/openlp_core/ui/test_themeform.py
+++ b/tests/openlp_core/ui/test_themeform.py
@@ -19,19 +19,22 @@
# along with this program. If not, see . #
##########################################################################
"""
-Interface tests to test the ThemeWizard class and related methods.
+Test the ThemeForm class and related methods.
"""
+from pathlib import Path
from unittest import TestCase
from unittest.mock import patch, MagicMock
from openlp.core.common.registry import Registry
+from openlp.core.lib.theme import BackgroundType
from openlp.core.ui.themeform import ThemeForm
+from openlp.core.ui.themelayoutform import ThemeLayoutForm
from tests.helpers.testmixin import TestMixin
-class TestThemeManager(TestCase, TestMixin):
+class TestThemeForm(TestCase, TestMixin):
"""
- Test the functions in the ThemeManager module
+ Test the functions in the ThemeForm Class
"""
def setUp(self):
"""
@@ -41,8 +44,8 @@ class TestThemeManager(TestCase, TestMixin):
mocked_renderer = MagicMock()
Registry().register('renderer', mocked_renderer)
- @patch('openlp.core.display.window.QtWidgets.QVBoxLayout')
- def test_create_theme_wizard(self, mocked_qvboxlayout):
+ @patch('openlp.core.ui.themeform.ThemeForm._setup')
+ def test_create_theme_wizard(self, mocked_setup):
"""
Test creating a ThemeForm instance
"""
@@ -50,3 +53,358 @@ class TestThemeManager(TestCase, TestMixin):
# WHEN: An object is created
# THEN: There should be no problems
ThemeForm(None)
+
+ def test_setup(self):
+ """
+ Test the _setup method
+ """
+ # GIVEN: A ThemeForm instance
+ with patch('openlp.core.ui.themeform.ThemeForm._setup'):
+ theme_form = ThemeForm(None)
+ theme_form.setup_ui = MagicMock()
+ theme_form.main_area_page = MagicMock()
+ theme_form.footer_area_page = MagicMock()
+
+ # WHEN: _setup() is called
+ theme_form._setup()
+
+ # THEN: The right calls should have been made
+ theme_form.setup_ui.assert_called_once_with(theme_form)
+ assert theme_form.can_update_theme is True
+ assert theme_form.temp_background_filename is None
+ assert isinstance(theme_form.theme_layout_form, ThemeLayoutForm)
+ theme_form.main_area_page.font_name_changed.connect.assert_called_once_with(theme_form.calculate_lines)
+ theme_form.main_area_page.font_size_changed.connect.assert_called_once_with(theme_form.calculate_lines)
+ theme_form.main_area_page.line_spacing_changed.connect.assert_called_once_with(theme_form.calculate_lines)
+ theme_form.main_area_page.is_outline_enabled_changed.connect.assert_called_once_with(
+ theme_form.on_outline_toggled)
+ theme_form.main_area_page.outline_size_changed.connect.assert_called_once_with(theme_form.calculate_lines)
+ theme_form.main_area_page.is_shadow_enabled_changed.connect.assert_called_once_with(
+ theme_form.on_shadow_toggled)
+ theme_form.main_area_page.shadow_size_changed.connect.assert_called_once_with(theme_form.calculate_lines)
+ theme_form.footer_area_page.font_name_changed.connect.assert_called_once_with(theme_form.update_theme)
+ theme_form.footer_area_page.font_size_changed.connect.assert_called_once_with(theme_form.update_theme)
+
+ @patch('openlp.core.ui.themeform.ThemeForm._setup')
+ def test_set_defaults(self, mocked_setup):
+ """
+ Test that the right methods are called by the set_defaults() method
+ """
+ # GIVEN: A ThemeForm instance with mocked methods
+ theme_form = ThemeForm(None)
+ theme_form.restart = MagicMock()
+ theme_form.set_background_page_values = MagicMock()
+ theme_form.set_main_area_page_values = MagicMock()
+ theme_form.set_footer_area_page_values = MagicMock()
+ theme_form.set_alignment_page_values = MagicMock()
+ theme_form.set_position_page_values = MagicMock()
+ theme_form.set_preview_page_values = MagicMock()
+
+ # WHEN: set_defaults() is called
+ theme_form.set_defaults()
+
+ # THEN: all the mocks are called
+ theme_form.restart.assert_called_once()
+ theme_form.set_background_page_values.assert_called_once()
+ theme_form.set_main_area_page_values.assert_called_once()
+ theme_form.set_footer_area_page_values.assert_called_once()
+ theme_form.set_alignment_page_values.assert_called_once()
+ theme_form.set_position_page_values.assert_called_once()
+ theme_form.set_preview_page_values.assert_called_once()
+
+ @patch('openlp.core.ui.themeform.ThemeForm._setup')
+ def test_calculate_lines(self, mocked_setup):
+ """
+ Test the calculate_lines() method
+ """
+ # GIVEN: A ThemeForm instance with some mocked methods
+ theme_form = ThemeForm(None)
+ theme_form.theme = None
+ theme_form.welcome_page = None
+ theme_form.currentPage = MagicMock()
+ theme_form.update_theme = MagicMock()
+ mocked_theme_manager = MagicMock()
+ Registry().register('theme_manager', mocked_theme_manager)
+
+ # WHEN: calculate_lines() is called
+ theme_form.calculate_lines()
+
+ # THEN: The mocks should have been called correctly
+ theme_form.currentPage.assert_called_once()
+ theme_form.update_theme.assert_called_once()
+ mocked_theme_manager.generate_image.assert_called_once_with(None, True)
+
+ @patch('openlp.core.ui.themeform.ThemeForm._setup')
+ def test_update_lines_text(self, mocked_setup):
+ """
+ Test the update_lines_text() method
+ """
+ # GIVEN: A ThemeForm instance with some mocked methods
+ theme_form = ThemeForm(None)
+ theme_form.main_line_count_label = MagicMock()
+
+ # WHEN: calculate_lines() is called
+ theme_form.update_lines_text(10)
+
+ # THEN: The mocks should have been called correctly
+ theme_form.main_line_count_label.setText.assert_called_once_with('(approximately 10 lines per slide)')
+
+ @patch('openlp.core.ui.themeform.QtWidgets.QWizard.resizeEvent')
+ @patch('openlp.core.ui.themeform.QtGui.QResizeEvent')
+ @patch('openlp.core.ui.themeform.ThemeForm._setup')
+ def test_resize_event(self, mocked_setup, MockResizeEvent, mocked_resizeEvent):
+ """
+ Test that the resizeEvent method handles resizing correctly
+ """
+ # GIVEN: A ThemeForm instance with a number of mocked methods
+ mocked_event = MagicMock()
+ MockResizeEvent.return_value = mocked_event
+ theme_form = ThemeForm(None)
+ theme_form.size = MagicMock(return_value=1920)
+ theme_form.preview_area_layout = MagicMock()
+ theme_form.preview_box = MagicMock(**{'width.return_value': 300})
+ mocked_renderer = MagicMock(**{'width.return_value': 1920, 'height.return_value': 1080})
+ Registry().remove('renderer')
+ Registry().register('renderer', mocked_renderer)
+
+ # WHEN: resizeEvent() is called
+ theme_form.resizeEvent()
+
+ # THEN: The correct calls should have been made
+ MockResizeEvent.assert_called_once_with(1920, 1920)
+ mocked_resizeEvent.assert_called_once_with(theme_form, mocked_event)
+ assert mocked_renderer.width.call_count == 2
+ mocked_renderer.height.assert_called_once()
+ theme_form.preview_area_layout.set_aspect_ratio.assert_called_once_with(16 / 9)
+ theme_form.preview_box.set_scale.assert_called_once_with(float(300 / 1920))
+
+ @patch('openlp.core.ui.themeform.QtWidgets.QWizard.resizeEvent')
+ @patch('openlp.core.ui.themeform.QtGui.QResizeEvent')
+ @patch('openlp.core.ui.themeform.ThemeForm._setup')
+ def test_resize_event_dbze(self, mocked_setup, MockResizeEvent, mocked_resizeEvent):
+ """
+ Test that the resizeEvent method handles a divide by zero exception correctly
+ """
+ # GIVEN: A ThemeForm instance with a number of mocked methods
+ mocked_event = MagicMock()
+ MockResizeEvent.return_value = mocked_event
+ theme_form = ThemeForm(None)
+ theme_form.size = MagicMock(return_value=1920)
+ theme_form.preview_area_layout = MagicMock()
+ theme_form.preview_box = MagicMock(**{'width.return_value': 300})
+ mocked_renderer = MagicMock(**{'width.return_value': 1920, 'height.return_value': 0})
+ Registry().remove('renderer')
+ Registry().register('renderer', mocked_renderer)
+
+ # WHEN: resizeEvent() is called
+ theme_form.resizeEvent()
+
+ # THEN: The correct calls should have been made
+ MockResizeEvent.assert_called_once_with(1920, 1920)
+ mocked_resizeEvent.assert_called_once_with(theme_form, mocked_event)
+ assert mocked_renderer.width.call_count == 2
+ mocked_renderer.height.assert_called_once()
+ theme_form.preview_area_layout.set_aspect_ratio.assert_called_once_with(1)
+ theme_form.preview_box.set_scale.assert_called_once_with(float(300 / 1920))
+
+ @patch('openlp.core.ui.themeform.QtWidgets.QMessageBox.critical')
+ @patch('openlp.core.ui.themeform.is_not_image_file')
+ @patch('openlp.core.ui.themeform.ThemeForm._setup')
+ def test_validate_current_page_with_image(self, mocked_setup, mocked_is_not_image_file, mocked_critical):
+ """
+ Test the validateCurrentPage() method
+ """
+ # GIVEN: An instance of ThemeForm with some mocks
+ theme_form = ThemeForm(None)
+ theme_form.background_page = MagicMock(background_type=BackgroundType.to_string(BackgroundType.Image),
+ image_path=Path('picture.jpg'))
+ theme_form.page = MagicMock(return_value=theme_form.background_page)
+ mocked_is_not_image_file.return_value = True
+
+ # WHEN: validateCurrentPage() is called
+ result = theme_form.validateCurrentPage()
+
+ # THEN: The right methods were called, and the result is False
+ mocked_critical.assert_called_once_with(theme_form, 'Background Image Empty',
+ 'You have not selected a background image. '
+ 'Please select one before continuing.')
+ assert result is False
+
+ @patch('openlp.core.ui.themeform.ThemeForm._setup')
+ def test_validate_current_page(self, mocked_setup):
+ """
+ Test the validateCurrentPage() method
+ """
+ # GIVEN: An instance of ThemeForm with some mocks
+ theme_form = ThemeForm(None)
+ theme_form.background_page = MagicMock()
+ theme_form.page = MagicMock()
+
+ # WHEN: validateCurrentPage() is called
+ result = theme_form.validateCurrentPage()
+
+ # THEN: The right methods were called, and the result is False
+ assert result is True
+
+ @patch('openlp.core.ui.themeform.ThemeForm._setup')
+ def test_on_current_id_changed_preview(self, mocked_setup):
+ """
+ Test the on_current_id_changed() method
+ """
+ # GIVEN: An instance of ThemeForm with some mocks
+ theme_form = ThemeForm(None)
+ theme_form.theme = 'my fake theme'
+ theme_form.area_position_page = MagicMock()
+ theme_form.preview_page = MagicMock()
+ theme_form.page = MagicMock(return_value=theme_form.preview_page)
+ theme_form.update_theme = MagicMock()
+ theme_form.preview_box = MagicMock(**{'width.return_value': 300})
+ theme_form.preview_area_layout = MagicMock()
+ theme_form.resizeEvent = MagicMock()
+ mocked_renderer = MagicMock(**{'width.return_value': 1920, 'height.return_value': 0})
+ Registry().remove('renderer')
+ Registry().register('renderer', mocked_renderer)
+
+ # WHEN: on_current_id_changed() is called
+ theme_form.on_current_id_changed(1)
+
+ # THEN: The right options should have been set
+ theme_form.update_theme.assert_called_once()
+ theme_form.preview_box.set_theme.assert_called_once_with('my fake theme')
+ theme_form.preview_box.clear_slides.assert_called_once()
+ theme_form.preview_box.set_scale.assert_called_once_with(float(300 / 1920))
+ theme_form.preview_area_layout.set_aspect_ratio(16 / 9)
+ theme_form.resizeEvent.assert_called_once()
+ theme_form.preview_box.show.assert_called_once()
+ theme_form.preview_box.generate_preview.assert_called_once_with('my fake theme', False, False)
+
+ @patch('openlp.core.ui.themeform.ThemeForm._setup')
+ def test_on_outline_toggled(self, mocked_setup):
+ """
+ Test the on_outline_toggled() method
+ """
+ # GIVEN: An instance of ThemeForm with some mocks
+ theme_form = ThemeForm(None)
+ theme_form.can_update_theme = True
+ theme_form.theme = MagicMock()
+ theme_form.calculate_lines = MagicMock()
+
+ # WHEN: on_outline_toggled is called
+ theme_form.on_outline_toggled(True)
+
+ # THEN: Everything is working right
+ assert theme_form.theme.font_main_outline is True
+ theme_form.calculate_lines.assert_called_once()
+
+ @patch('openlp.core.ui.themeform.ThemeForm._setup')
+ def test_on_shadow_toggled(self, mocked_setup):
+ """
+ Test the on_shadow_toggled() method
+ """
+ # GIVEN: An instance of ThemeForm with some mocks
+ theme_form = ThemeForm(None)
+ theme_form.can_update_theme = True
+ theme_form.theme = MagicMock()
+ theme_form.calculate_lines = MagicMock()
+
+ # WHEN: on_shadow_toggled is called
+ theme_form.on_shadow_toggled(True)
+
+ # THEN: Everything is working right
+ assert theme_form.theme.font_main_shadow is True
+ theme_form.calculate_lines.assert_called_once()
+
+ @patch('openlp.core.ui.themeform.ThemeForm._setup')
+ def test_initialise_page_background(self, mocked_setup):
+ """
+ Test the initializePage() method with the background page
+ """
+ # GIVEN: An instance of ThemeForm with some mocks
+ theme_form = ThemeForm(None)
+ theme_form.background_page = MagicMock()
+ theme_form.page = MagicMock(return_value=theme_form.background_page)
+ theme_form.set_background_page_values = MagicMock()
+
+ # WHEN: on_shadow_toggled is called
+ theme_form.initializePage(0)
+
+ # THEN: Everything is working right
+ theme_form.set_background_page_values.assert_called_once()
+
+ @patch('openlp.core.ui.themeform.ThemeForm._setup')
+ def test_initialise_page_main_area(self, mocked_setup):
+ """
+ Test the initializePage() method with the main_area page
+ """
+ # GIVEN: An instance of ThemeForm with some mocks
+ theme_form = ThemeForm(None)
+ theme_form.background_page = MagicMock()
+ theme_form.main_area_page = MagicMock()
+ theme_form.page = MagicMock(return_value=theme_form.main_area_page)
+ theme_form.set_main_area_page_values = MagicMock()
+
+ # WHEN: on_shadow_toggled is called
+ theme_form.initializePage(0)
+
+ # THEN: Everything is working right
+ theme_form.set_main_area_page_values.assert_called_once()
+
+ @patch('openlp.core.ui.themeform.ThemeForm._setup')
+ def test_initialise_page_footer_area(self, mocked_setup):
+ """
+ Test the initializePage() method with the footer_area page
+ """
+ # GIVEN: An instance of ThemeForm with some mocks
+ theme_form = ThemeForm(None)
+ theme_form.background_page = MagicMock()
+ theme_form.main_area_page = MagicMock()
+ theme_form.footer_area_page = MagicMock()
+ theme_form.page = MagicMock(return_value=theme_form.footer_area_page)
+ theme_form.set_footer_area_page_values = MagicMock()
+
+ # WHEN: on_shadow_toggled is called
+ theme_form.initializePage(0)
+
+ # THEN: Everything is working right
+ theme_form.set_footer_area_page_values.assert_called_once()
+
+ @patch('openlp.core.ui.themeform.ThemeForm._setup')
+ def test_initialise_page_alignment(self, mocked_setup):
+ """
+ Test the initializePage() method with the alignment page
+ """
+ # GIVEN: An instance of ThemeForm with some mocks
+ theme_form = ThemeForm(None)
+ theme_form.background_page = MagicMock()
+ theme_form.main_area_page = MagicMock()
+ theme_form.footer_area_page = MagicMock()
+ theme_form.alignment_page = MagicMock()
+ theme_form.page = MagicMock(return_value=theme_form.alignment_page)
+ theme_form.set_alignment_page_values = MagicMock()
+
+ # WHEN: on_shadow_toggled is called
+ theme_form.initializePage(0)
+
+ # THEN: Everything is working right
+ theme_form.set_alignment_page_values.assert_called_once()
+
+ @patch('openlp.core.ui.themeform.ThemeForm._setup')
+ def test_initialise_page_area_position(self, mocked_setup):
+ """
+ Test the initializePage() method with the area_position page
+ """
+ # GIVEN: An instance of ThemeForm with some mocks
+ theme_form = ThemeForm(None)
+ theme_form.background_page = MagicMock()
+ theme_form.main_area_page = MagicMock()
+ theme_form.footer_area_page = MagicMock()
+ theme_form.alignment_page = MagicMock()
+ theme_form.area_position_page = MagicMock()
+ theme_form.page = MagicMock(return_value=theme_form.area_position_page)
+ theme_form.set_position_page_values = MagicMock()
+
+ # WHEN: on_shadow_toggled is called
+ theme_form.initializePage(0)
+
+ # THEN: Everything is working right
+ theme_form.set_position_page_values.assert_called_once()
diff --git a/tests/openlp_core/widgets/test_widgets.py b/tests/openlp_core/widgets/test_widgets.py
index 796021807..5754e4346 100644
--- a/tests/openlp_core/widgets/test_widgets.py
+++ b/tests/openlp_core/widgets/test_widgets.py
@@ -24,12 +24,11 @@ Package to test the openlp.core.widgets.widgets package.
from unittest import TestCase
from unittest.mock import MagicMock, call, patch
-import pytest
from PyQt5 import QtCore, QtWidgets
from openlp.core.common.settings import ProxyMode
from openlp.core.display.screens import Screen
-from openlp.core.widgets.widgets import ProxyWidget, ProxyDialog, ScreenButton, ScreenSelectionWidget, FontSelectWidget
+from openlp.core.widgets.widgets import ProxyWidget, ProxyDialog, ScreenButton, ScreenSelectionWidget
from tests.helpers.testmixin import TestMixin
@@ -526,604 +525,3 @@ class TestScreenSelectionWidget(TestCase, TestMixin):
'checkbox for that screen.', parent=instance, question=False)
assert instance.use_screen_check_box.isChecked() is True
assert instance.display_group_box.isChecked() is True
-
-
-class TestFontSelectWidget(TestCase, TestMixin):
-
- def setUp(self):
- """Test setup"""
- self.setup_application()
- self.build_settings()
-
- def tearDown(self):
- """Tear down tests"""
- del self.app
-
- def test_init_(self):
- """
- Test the initialisation of FontSelectWidget
- """
- # GIVEN: The FontSelectWidget class
- # WHEN: Initialising FontSelectWidget
- # THEN: We should have an instance of the widget with no errors
- FontSelectWidget()
-
- def test_resize_event(self):
- """
- Test that the `resizeEvent()` method called the `resize_widgets()` method.
- """
- # GIVEN: An instance of FontSelectWidget with a mocked out "resize_widgets" method
- instance = FontSelectWidget()
- instance.resize_widgets = MagicMock()
-
- # WHEN: resizeEvent is called
- instance.resizeEvent(None)
-
- # THEN: resize_widgets should have been called
- instance.resize_widgets.assert_called_once()
-
- def test_font_name_changed(self):
- # GIVEN: An instance of FontSelectWidget with a mocked out "font_name_changed" signal
- instance = FontSelectWidget()
- instance.font_name_changed = MagicMock()
-
- # WHEN: The font name changes
- instance._on_font_name_changed('Sans serif')
-
- # THEN: The signal should be emitted with the correct value
- instance.font_name_changed.emit.assert_called_once_with('Sans serif')
-
- def test_font_name_changed_int(self):
- # GIVEN: An instance of FontSelectWidget with a mocked out "font_name_changed" signal
- instance = FontSelectWidget()
- instance.font_name_changed = MagicMock()
-
- # WHEN: The font name changes
- instance._on_font_name_changed(5)
-
- # THEN: The signal should be emitted with the correct value
- assert instance.font_name_changed.emit.call_count == 0
-
- def test_font_color_changed(self):
- # GIVEN: An instance of FontSelectWidget with a mocked out "font_color_changed" signal
- instance = FontSelectWidget()
- instance.font_color_changed = MagicMock()
-
- # WHEN: The font color changes
- instance._on_font_color_changed('#fff')
-
- # THEN: The signal should be emitted with the correct value
- instance.font_color_changed.emit.assert_called_once_with('#fff')
-
- def test_is_bold_changed(self):
- # GIVEN: An instance of FontSelectWidget with a mocked out "is_bold_changed" signal
- instance = FontSelectWidget()
- instance.is_bold_changed = MagicMock()
-
- # WHEN: The font name changes
- instance._on_style_bold_toggled(True)
-
- # THEN: The signal should be emitted with the correct value
- instance.is_bold_changed.emit.assert_called_once_with(True)
-
- def test_is_italic_changed(self):
- # GIVEN: An instance of FontSelectWidget with a mocked out "style_italic_changed" signal
- instance = FontSelectWidget()
- instance.is_italic_changed = MagicMock()
-
- # WHEN: The font name changes
- instance._on_style_italic_toggled(False)
-
- # THEN: The signal should be emitted with the correct value
- instance.is_italic_changed.emit.assert_called_once_with(False)
-
- def test_font_size_changed(self):
- # GIVEN: An instance of FontSelectWidget with a mocked out "font_size_changed" signal
- instance = FontSelectWidget()
- instance.font_size_changed = MagicMock()
-
- # WHEN: The font size changes
- instance._on_font_size_changed(14)
-
- # THEN: The signal should be emitted with the correct value
- instance.font_size_changed.emit.assert_called_once_with(14)
-
- def test_line_spacing_changed(self):
- # GIVEN: An instance of FontSelectWidget with a mocked out "line_spacing_changed" signal
- instance = FontSelectWidget()
- instance.line_spacing_changed = MagicMock()
-
- # WHEN: The font name changes
- instance._on_line_spacing_changed(1)
-
- # THEN: The signal should be emitted with the correct value
- instance.line_spacing_changed.emit.assert_called_once_with(1)
-
- def test_is_outline_enabled_changed(self):
- # GIVEN: An instance of FontSelectWidget with a mocked out "outline_enabled_changed" signal
- instance = FontSelectWidget()
- instance.is_outline_enabled_changed = MagicMock()
-
- # WHEN: The font name changes
- instance._on_outline_toggled(True)
-
- # THEN: The signal should be emitted with the correct value
- instance.is_outline_enabled_changed.emit.assert_called_once_with(True)
-
- def test_outline_color_changed(self):
- # GIVEN: An instance of FontSelectWidget with a mocked out "outline_color_changed" signal
- instance = FontSelectWidget()
- instance.outline_color_changed = MagicMock()
-
- # WHEN: The font name changes
- instance._on_outline_color_changed('#000')
-
- # THEN: The signal should be emitted with the correct value
- instance.outline_color_changed.emit.assert_called_once_with('#000')
-
- def test_outline_size_changed(self):
- # GIVEN: An instance of FontSelectWidget with a mocked out "outline_size_changed" signal
- instance = FontSelectWidget()
- instance.outline_size_changed = MagicMock()
-
- # WHEN: The font name changes
- instance._on_outline_size_changed(2)
-
- # THEN: The signal should be emitted with the correct value
- instance.outline_size_changed.emit.assert_called_once_with(2)
-
- def test_is_shadow_enabled_changed(self):
- # GIVEN: An instance of FontSelectWidget with a mocked out "is_shadow_enabled_changed" signal
- instance = FontSelectWidget()
- instance.is_shadow_enabled_changed = MagicMock()
-
- # WHEN: The font name changes
- instance._on_shadow_toggled(False)
-
- # THEN: The signal should be emitted with the correct value
- instance.is_shadow_enabled_changed.emit.assert_called_once_with(False)
-
- def test_shadow_color_changed(self):
- # GIVEN: An instance of FontSelectWidget with a mocked out "shadow_color_changed" signal
- instance = FontSelectWidget()
- instance.shadow_color_changed = MagicMock()
-
- # WHEN: The font name changes
- instance._on_shadow_color_changed('#000')
-
- # THEN: The signal should be emitted with the correct value
- instance.shadow_color_changed.emit.assert_called_once_with('#000')
-
- def test_shadow_size_changed(self):
- # GIVEN: An instance of FontSelectWidget with a mocked out "shadow_size_changed" signal
- instance = FontSelectWidget()
- instance.shadow_size_changed = MagicMock()
-
- # WHEN: The font name changes
- instance._on_shadow_size_changed(5)
-
- # THEN: The signal should be emitted with the correct value
- instance.shadow_size_changed.emit.assert_called_once_with(5)
-
- def test_resize_widgets(self):
- """
- Test the `resize_widgets()` method
- """
- # GIVEN: An instance of FontSelectWidget and various mocked out methods
- instance = FontSelectWidget()
- instance.geometry = MagicMock(return_value=MagicMock(**{'width.return_value': 100}))
- instance.layout.contentsMargins = MagicMock(return_value=MagicMock(**{'left.return_value': 8,
- 'right.return_value': 8}))
- instance.layout.horizontalSpacing = MagicMock(return_value=6)
- instance.layout.setColumnMinimumWidth = MagicMock()
-
- # WHEN: `resize_widgets()` is called
- instance.resize_widgets()
-
- # THEN: The column widths should be set to 16
- instance.geometry.assert_called_once()
- instance.layout.contentsMargins.assert_called_once()
- instance.layout.horizontalSpacing.assert_called_once()
- assert instance._column_width == 16
- assert instance.layout.setColumnMinimumWidth.call_args_list == [call(0, 16), call(1, 16),
- call(2, 16), call(3, 16)]
-
- def test_enable_features(self):
- """
- Test that the `enable_features` method correctly enables widgets based on features
- """
- # GIVEN: An instance of FontSelectWidget with some mocks
- instance = FontSelectWidget()
- mock_label = MagicMock()
- mock_control = MagicMock()
- instance.feature_widgets = {'test': [mock_label, mock_control]}
-
- # WHEN: The "test" feature is enabled
- instance.enable_features('test')
-
- # THEN: "show()" is called on all the widgets
- mock_label.show.assert_called_once()
- mock_control.show.assert_called_once()
-
- def test_enable_missing_features(self):
- """
- Test that the `enable_features` method correctly raises an error on a non-existent feature
- """
- # GIVEN: An instance of FontSelectWidget with some mocks
- instance = FontSelectWidget()
- mock_label = MagicMock()
- mock_control = MagicMock()
- instance.feature_widgets = {'test1': [mock_label, mock_control]}
-
- # WHEN: The "test" feature is enabled
- with pytest.raises(KeyError, match='No such feature'):
- instance.enable_features('test2')
-
- def test_disable_features(self):
- """
- Test that the `disable_features` method correctly disables widgets based on features
- """
- # GIVEN: An instance of FontSelectWidget with some mocks
- instance = FontSelectWidget()
- mock_label = MagicMock()
- mock_control = MagicMock()
- instance.feature_widgets = {'test': [mock_label, mock_control]}
-
- # WHEN: The "test" feature is disabled
- instance.disable_features('test')
-
- # THEN: "show()" is called on all the widgets
- mock_label.hide.assert_called_once()
- mock_control.hide.assert_called_once()
-
- def test_disable_missing_features(self):
- """
- Test that the `disable_features` method correctly raises an error on a non-existent feature
- """
- # GIVEN: An instance of FontSelectWidget with some mocks
- instance = FontSelectWidget()
- mock_label = MagicMock()
- mock_control = MagicMock()
- instance.feature_widgets = {'test1': [mock_label, mock_control]}
-
- # WHEN: The "test" feature is disabled
- with pytest.raises(KeyError, match='No such feature'):
- instance.disable_features('test2')
-
- def test_get_font_name_property(self):
- """
- Test the `font_name` property
- """
- # GIVEN: An instance of FontSelectWidget with some mocks
- instance = FontSelectWidget()
- instance.font_name_combobox.currentFont = MagicMock(
- return_value=MagicMock(**{'family.return_value': 'Sans serif'}))
-
- # WHEN: The `font_name` propert is accessed
- result = instance.font_name
-
- # THEN: The value should be correct
- assert result == 'Sans serif'
-
- def test_set_font_name_property(self):
- """
- Test setting the `font_name` property
- """
- # GIVEN: An instance of FontSelectWidget with some mocks
- instance = FontSelectWidget()
- instance.font_name_combobox.setCurrentFont = MagicMock()
-
- # WHEN: The `font_name` property is set
- with patch('openlp.core.widgets.widgets.QtGui.QFont') as MockFont:
- mocked_font = MagicMock()
- MockFont.return_value = mocked_font
- instance.font_name = 'Serif'
-
- # THEN: The correct value should be set
- MockFont.assert_called_once_with('Serif')
- instance.font_name_combobox.setCurrentFont.assert_called_once_with(mocked_font)
-
- def test_get_font_color_property(self):
- """
- Test the `font_color` property
- """
- # GIVEN: An instance of FontSelectWidget with some mocks
- instance = FontSelectWidget()
- instance.font_color_button.color = '#000'
-
- # WHEN: The `font_color` propert is accessed
- result = instance.font_color
-
- # THEN: The value should be correct
- assert result == '#000'
-
- def test_set_font_color_property(self):
- """
- Test setting the `font_color` property
- """
- # GIVEN: An instance of FontSelectWidget with some mocks
- instance = FontSelectWidget()
-
- # WHEN: The `font_color` property is set
- instance.font_color = '#fff'
-
- # THEN: The correct value should be set
- assert instance.font_color_button.color == '#fff'
-
- def test_get_is_bold_property(self):
- """
- Test the `is_bold` property
- """
- # GIVEN: An instance of FontSelectWidget with some mocks
- instance = FontSelectWidget()
- instance.style_bold_button.isChecked = MagicMock(return_value=False)
-
- # WHEN: The `is_bold` propert is accessed
- result = instance.is_bold
-
- # THEN: The value should be correct
- assert result is False
-
- def test_set_is_bold_property(self):
- """
- Test setting the `is_bold` property
- """
- # GIVEN: An instance of FontSelectWidget with some mocks
- instance = FontSelectWidget()
- instance.style_bold_button.setChecked = MagicMock()
-
- # WHEN: The `is_bold` property is set
- instance.is_bold = True
-
- # THEN: The correct value should be set
- instance.style_bold_button.setChecked.assert_called_once_with(True)
-
- def test_get_is_italic_property(self):
- """
- Test the `is_italic` property
- """
- # GIVEN: An instance of FontSelectWidget with some mocks
- instance = FontSelectWidget()
- instance.style_italic_button.isChecked = MagicMock(return_value=True)
-
- # WHEN: The `is_italic` propert is accessed
- result = instance.is_italic
-
- # THEN: The value should be correct
- assert result is True
-
- def test_set_is_italic_property(self):
- """
- Test setting the `is_italic` property
- """
- # GIVEN: An instance of FontSelectWidget with some mocks
- instance = FontSelectWidget()
- instance.style_italic_button.setChecked = MagicMock()
-
- # WHEN: The `is_italic` property is set
- instance.is_italic = False
-
- # THEN: The correct value should be set
- instance.style_italic_button.setChecked.assert_called_once_with(False)
-
- def test_get_font_size_property(self):
- """
- Test the `font_size` property
- """
- # GIVEN: An instance of FontSelectWidget with some mocks
- instance = FontSelectWidget()
- instance.font_size_spinbox.value = MagicMock(return_value=16)
-
- # WHEN: The `font_size` propert is accessed
- result = instance.font_size
-
- # THEN: The value should be correct
- assert result == 16
-
- def test_set_font_size_property(self):
- """
- Test setting the `font_size` property
- """
- # GIVEN: An instance of FontSelectWidget with some mocks
- instance = FontSelectWidget()
- instance.font_size_spinbox.setValue = MagicMock()
-
- # WHEN: The `font_size` property is set
- instance.font_size = 18
-
- # THEN: The correct value should be set
- instance.font_size_spinbox.setValue.assert_called_once_with(18)
-
- def test_get_line_spacing_property(self):
- """
- Test the `line_spacing` property
- """
- # GIVEN: An instance of FontSelectWidget with some mocks
- instance = FontSelectWidget()
- instance.line_spacing_spinbox.value = MagicMock(return_value=1)
-
- # WHEN: The `line_spacing` propert is accessed
- result = instance.line_spacing
-
- # THEN: The value should be correct
- assert result == 1
-
- def test_set_line_spacing_property(self):
- """
- Test setting the `line_spacing` property
- """
- # GIVEN: An instance of FontSelectWidget with some mocks
- instance = FontSelectWidget()
- instance.line_spacing_spinbox.setValue = MagicMock()
-
- # WHEN: The `line_spacing` property is set
- instance.line_spacing = 2
-
- # THEN: The correct value should be set
- instance.line_spacing_spinbox.setValue.assert_called_once_with(2)
-
- def test_get_is_outline_enabled_property(self):
- """
- Test the `is_outline_enabled` property
- """
- # GIVEN: An instance of FontSelectWidget with some mocks
- instance = FontSelectWidget()
- instance.outline_groupbox.isChecked = MagicMock(return_value=True)
-
- # WHEN: The `is_outline_enabled` propert is accessed
- result = instance.is_outline_enabled
-
- # THEN: The value should be correct
- assert result is True
-
- def test_set_is_outline_enabled_property(self):
- """
- Test setting the `is_outline_enabled` property
- """
- # GIVEN: An instance of FontSelectWidget with some mocks
- instance = FontSelectWidget()
- instance.outline_groupbox.setChecked = MagicMock()
-
- # WHEN: The `is_outline_enabled` property is set
- instance.is_outline_enabled = False
-
- # THEN: The correct value should be set
- instance.outline_groupbox.setChecked.assert_called_once_with(False)
-
- def test_get_outline_color_property(self):
- """
- Test the `outline_color` property
- """
- # GIVEN: An instance of FontSelectWidget with some mocks
- instance = FontSelectWidget()
- instance.outline_color_button.color = '#fff'
-
- # WHEN: The `outline_color` propert is accessed
- result = instance.outline_color
-
- # THEN: The value should be correct
- assert result == '#fff'
-
- def test_set_outline_color_property(self):
- """
- Test setting the `outline_color` property
- """
- # GIVEN: An instance of FontSelectWidget with some mocks
- instance = FontSelectWidget()
-
- # WHEN: The `outline_color` property is set
- instance.outline_color = '#000'
-
- # THEN: The correct value should be set
- assert instance.outline_color_button.color == '#000'
-
- def test_get_outline_size_property(self):
- """
- Test the `outline_size` property
- """
- # GIVEN: An instance of FontSelectWidget with some mocks
- instance = FontSelectWidget()
- instance.outline_size_spinbox.value = MagicMock(return_value=2)
-
- # WHEN: The `outline_size` propert is accessed
- result = instance.outline_size
-
- # THEN: The value should be correct
- assert result == 2
-
- def test_set_outline_size_property(self):
- """
- Test setting the `outline_size` property
- """
- # GIVEN: An instance of FontSelectWidget with some mocks
- instance = FontSelectWidget()
- instance.outline_size_spinbox.setValue = MagicMock()
-
- # WHEN: The `outline_size` property is set
- instance.outline_size = 1
-
- # THEN: The correct value should be set
- instance.outline_size_spinbox.setValue.assert_called_once_with(1)
-
- def test_get_is_shadow_enabled_property(self):
- """
- Test the `is_shadow_enabled` property
- """
- # GIVEN: An instance of FontSelectWidget with some mocks
- instance = FontSelectWidget()
- instance.shadow_groupbox.isChecked = MagicMock(return_value=False)
-
- # WHEN: The `is_shadow_enabled` propert is accessed
- result = instance.is_shadow_enabled
-
- # THEN: The value should be correct
- assert result is False
-
- def test_set_is_shadow_enabled_property(self):
- """
- Test setting the `is_shadow_enabled` property
- """
- # GIVEN: An instance of FontSelectWidget with some mocks
- instance = FontSelectWidget()
- instance.shadow_groupbox.setChecked = MagicMock()
-
- # WHEN: The `is_shadow_enabled` property is set
- instance.is_shadow_enabled = True
-
- # THEN: The correct value should be set
- instance.shadow_groupbox.setChecked.assert_called_once_with(True)
-
- def test_get_shadow_color_property(self):
- """
- Test the `shadow_color` property
- """
- # GIVEN: An instance of FontSelectWidget with some mocks
- instance = FontSelectWidget()
- instance.shadow_color_button.color = '#000'
-
- # WHEN: The `shadow_color` propert is accessed
- result = instance.shadow_color
-
- # THEN: The value should be correct
- assert result == '#000'
-
- def test_set_shadow_color_property(self):
- """
- Test setting the `shadow_color` property
- """
- # GIVEN: An instance of FontSelectWidget with some mocks
- instance = FontSelectWidget()
-
- # WHEN: The `shadow_color` property is set
- instance.shadow_color = '#fff'
-
- # THEN: The correct value should be set
- instance.shadow_color_button.color == '#fff'
-
- def test_get_shadow_size_property(self):
- """
- Test the `shadow_size` property
- """
- # GIVEN: An instance of FontSelectWidget with some mocks
- instance = FontSelectWidget()
- instance.shadow_size_spinbox.value = MagicMock(return_value=5)
-
- # WHEN: The `shadow_size` propert is accessed
- result = instance.shadow_size
-
- # THEN: The value should be correct
- assert result == 5
-
- def test_set_shadow_size_property(self):
- """
- Test setting the `shadow_size` property
- """
- # GIVEN: An instance of FontSelectWidget with some mocks
- instance = FontSelectWidget()
- instance.shadow_size_spinbox.setValue = MagicMock()
-
- # WHEN: The `shadow_size` property is set
- instance.shadow_size = 10
-
- # THEN: The correct value should be set
- instance.shadow_size_spinbox.setValue.assert_called_once_with(10)