Refactor the theme wizard into separate pages

- Refactor wizard pages into external page components
- Refactor form labels into a central label with the alignment already set
- Fix some tests
- Write more tests
This commit is contained in:
Raoul Snyman 2019-12-11 19:31:48 +00:00 committed by Tim Bentley
parent 97ba02d1f1
commit ec329eff2f
20 changed files with 3277 additions and 1543 deletions

View File

@ -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']

View File

@ -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])

View File

@ -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 <https://www.gnu.org/licenses/>. #
##########################################################################
"""
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')

View File

@ -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 <https://www.gnu.org/licenses/>. #
##########################################################################
"""
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')

View File

@ -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 <https://www.gnu.org/licenses/>. #
##########################################################################
"""
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)

View File

@ -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 <https://www.gnu.org/licenses/>. #
##########################################################################
"""
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

View File

@ -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 <https://www.gnu.org/licenses/>. #
##########################################################################
"""
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)

View File

@ -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):

View File

@ -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)

0
openlp/core/widgets/dialogs.py Executable file → Normal file
View File

View File

@ -19,34 +19,15 @@
# along with this program. If not, see <https://www.gnu.org/licenses/>. #
##########################################################################
"""
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)

View File

@ -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)

View File

@ -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 <https://www.gnu.org/licenses/>. #
##########################################################################
"""
Package to test the openlp.core.pages package.
"""

View File

@ -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 <https://www.gnu.org/licenses/>. #
##########################################################################
"""
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 = []

View File

@ -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 <https://www.gnu.org/licenses/>. #
##########################################################################
"""
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

View File

@ -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 <https://www.gnu.org/licenses/>. #
##########################################################################
"""
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')

View File

@ -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 <https://www.gnu.org/licenses/>. #
##########################################################################
"""
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)

View File

@ -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 <https://www.gnu.org/licenses/>. #
##########################################################################
"""
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)]

View File

@ -19,19 +19,22 @@
# along with this program. If not, see <https://www.gnu.org/licenses/>. #
##########################################################################
"""
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()

View File

@ -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)