- Noticed this had a conflict, fixed it, improved comments

This commit is contained in:
suutari-olli 2016-05-15 22:41:13 +03:00
commit 334d16d81c
20 changed files with 282 additions and 77 deletions

View File

@ -222,10 +222,11 @@ class OpenLP(OpenLPMixin, QtWidgets.QApplication):
QtWidgets.QMessageBox.warning(None, translate('OpenLP', 'Backup'),
translate('OpenLP', 'Backup of the data folder failed!'))
return
QtWidgets.QMessageBox.information(None, translate('OpenLP', 'Backup'),
translate('OpenLP',
'A backup of the data folder has been created at %s')
% data_folder_backup_path)
message = translate('OpenLP',
'A backup of the data folder has been created'
'at {text}').format(text=data_folder_backup_path)
QtWidgets.QMessageBox.information(None, translate('OpenLP', 'Backup'), message)
# Update the version in the settings
Settings().setValue('core/application version', openlp_version)
@ -257,7 +258,7 @@ class OpenLP(OpenLPMixin, QtWidgets.QApplication):
"""
if event.type() == QtCore.QEvent.FileOpen:
file_name = event.file()
log.debug('Got open file event for %s!', file_name)
log.debug('Got open file event for {name}!'.format(name=file_name))
self.args.insert(0, file_name)
return True
# Mac OS X should restore app window when user clicked on the OpenLP icon
@ -311,7 +312,7 @@ def set_up_logging(log_path):
logfile.setFormatter(logging.Formatter('%(asctime)s %(name)-55s %(levelname)-8s %(message)s'))
log.addHandler(logfile)
if log.isEnabledFor(logging.DEBUG):
print('Logging to: %s' % filename)
print('Logging to: {name}'.format(name=filename))
def main(args=None):
@ -351,12 +352,12 @@ def main(args=None):
log.info('Running portable')
portable_settings_file = os.path.abspath(os.path.join(application_path, '..', '..', 'Data', 'OpenLP.ini'))
# Make this our settings file
log.info('INI file: %s', portable_settings_file)
log.info('INI file: {name}'.format(name=portable_settings_file))
Settings.set_filename(portable_settings_file)
portable_settings = Settings()
# Set our data path
data_path = os.path.abspath(os.path.join(application_path, '..', '..', 'Data',))
log.info('Data path: %s', data_path)
log.info('Data path: {name}'.format(name=data_path))
# Point to our data path
portable_settings.setValue('advanced/data path', data_path)
portable_settings.setValue('advanced/is portable', True)

View File

@ -55,7 +55,9 @@ def trace_error_handler(logger):
"""
log_string = "OpenLP Error trace"
for tb in traceback.extract_stack():
log_string = '%s\n File %s at line %d \n\t called %s' % (log_string, tb[0], tb[1], tb[3])
log_string += '\n File {file} at line {line} \n\t called {data}'.format(file=tb[0],
line=tb[1],
data=tb[3])
logger.error(log_string)
@ -67,7 +69,7 @@ def check_directory_exists(directory, do_not_log=False):
:param do_not_log: To not log anything. This is need for the start up, when the log isn't ready.
"""
if not do_not_log:
log.debug('check_directory_exists %s' % directory)
log.debug('check_directory_exists {text}'.format(text=directory))
try:
if not os.path.exists(directory):
os.makedirs(directory)
@ -202,13 +204,13 @@ def md5_hash(salt, data=None):
:param data: OPTIONAL Data to hash
:returns: str
"""
log.debug('md5_hash(salt="%s")' % salt)
log.debug('md5_hash(salt="{text}")'.format(text=salt))
hash_obj = hashlib.new('md5')
hash_obj.update(salt)
if data:
hash_obj.update(data)
hash_value = hash_obj.hexdigest()
log.debug('md5_hash() returning "%s"' % hash_value)
log.debug('md5_hash() returning "{text}"'.format(text=hash_value))
return hash_value
@ -221,12 +223,12 @@ def qmd5_hash(salt, data=None):
:param data: OPTIONAL Data to hash
:returns: str
"""
log.debug('qmd5_hash(salt="%s"' % salt)
log.debug('qmd5_hash(salt="{text}"'.format(text=salt))
hash_obj = QHash(QHash.Md5)
hash_obj.addData(salt)
hash_obj.addData(data)
hash_value = hash_obj.result().toHex()
log.debug('qmd5_hash() returning "%s"' % hash_value)
log.debug('qmd5_hash() returning "{text}"'.format(text=hash_value))
return hash_value.data()
@ -283,7 +285,7 @@ def get_uno_command(connection_type='pipe'):
CONNECTION = '"--accept=pipe,name=openlp_pipe;urp;"'
else:
CONNECTION = '"--accept=socket,host=localhost,port=2002;urp;"'
return '%s %s %s' % (command, OPTIONS, CONNECTION)
return '{cmd} {opt} {conn}'.format(cmd=command, opt=OPTIONS, conn=CONNECTION)
def get_uno_instance(resolver, connection_type='pipe'):
@ -333,7 +335,7 @@ def delete_file(file_path_name):
os.remove(file_path_name)
return True
except (IOError, OSError):
log.exception("Unable to delete file %s" % file_path_name)
log.exception("Unable to delete file {text}".format(text=file_path_name))
return False
@ -345,9 +347,11 @@ def get_images_filter():
if not IMAGES_FILTER:
log.debug('Generating images filter.')
formats = list(map(bytes.decode, list(map(bytes, QtGui.QImageReader.supportedImageFormats()))))
visible_formats = '(*.%s)' % '; *.'.join(formats)
actual_formats = '(*.%s)' % ' *.'.join(formats)
IMAGES_FILTER = '%s %s %s' % (translate('OpenLP', 'Image Files'), visible_formats, actual_formats)
visible_formats = '(*.{text})'.format(text='; *.'.join(formats))
actual_formats = '(*.{text})'.format(text=' *.'.join(formats))
IMAGES_FILTER = '{text} {visible} {actual}'.format(text=translate('OpenLP', 'Image Files'),
visible=visible_formats,
actual=actual_formats)
return IMAGES_FILTER
@ -385,7 +389,7 @@ def check_binary_exists(program_path):
:param program_path:The full path to the binary to check.
:return: program output to be parsed
"""
log.debug('testing program_path: %s', program_path)
log.debug('testing program_path: {text}'.format(text=program_path))
try:
# Setup startupinfo options for check_output to avoid console popping up on windows
if is_win():
@ -399,5 +403,5 @@ def check_binary_exists(program_path):
except Exception:
trace_error_handler(log)
runlog = ''
log.debug('check_output returned: %s' % runlog)
log.debug('check_output returned: {text}'.format(text=runlog))
return runlog

View File

@ -153,6 +153,7 @@ class UiStrings(object):
self.Version = translate('OpenLP.Ui', 'Version')
self.View = translate('OpenLP.Ui', 'View')
self.ViewMode = translate('OpenLP.Ui', 'View Mode')
self.Video = translate('OpenLP.Ui', 'Video')
self.BibleShortSearchTitle = translate('OpenLP.Ui', 'Search is Empty or too Short')
self.BibleShortSearch = translate('OpenLP.Ui', '<strong>The search you have entered is empty or shorter '
'than 3 characters long.<br>Please try again with '
@ -182,3 +183,4 @@ class UiStrings(object):
itertools.chain.from_iterable(itertools.repeat(strings, 1) if isinstance(strings, str)
else strings for strings in bible_scripture_items)
self.BibleScriptureError = ''.join(str(joined) for joined in bible_scripture_items)

View File

@ -97,7 +97,7 @@ def get_text_file_string(text_file):
file_handle.seek(0)
content = file_handle.read()
except (IOError, UnicodeError):
log.exception('Failed to open text file %s' % text_file)
log.exception('Failed to open text file {text}'.format(text=text_file))
finally:
if file_handle:
file_handle.close()
@ -300,6 +300,8 @@ def create_separated_list(string_list):
return ''
elif len(string_list) == 1:
return string_list[0]
# TODO:
# Cannot convert these strings to python3 yet until I can figure out how to mock translate() with the new format
elif len(string_list) == 2:
return translate('OpenLP.core.lib', '%s and %s',
'Locale list separator: 2 items') % (string_list[0], string_list[1])

View File

@ -297,7 +297,11 @@ PJLINK_ERST_STATUS = {'0': ERROR_STRING[E_OK],
PJLINK_POWR_STATUS = {'0': S_STANDBY,
'1': S_ON,
'2': S_COOLDOWN,
'3': S_WARMUP}
'3': S_WARMUP,
S_STANDBY: '0',
S_ON: '1',
S_COOLDOWN: '2',
S_WARMUP: '3'}
PJLINK_DEFAULT_SOURCES = {'1': translate('OpenLP.DB', 'RGB'),
'2': translate('OpenLP.DB', 'Video'),

View File

@ -44,6 +44,7 @@ class BackgroundType(object):
Gradient = 1
Image = 2
Transparent = 3
Video = 4
@staticmethod
def to_string(background_type):
@ -58,6 +59,8 @@ class BackgroundType(object):
return 'image'
elif background_type == BackgroundType.Transparent:
return 'transparent'
elif background_type == BackgroundType.Video:
return 'video'
@staticmethod
def from_string(type_string):
@ -72,6 +75,8 @@ class BackgroundType(object):
return BackgroundType.Image
elif type_string == 'transparent':
return BackgroundType.Transparent
elif type_string == 'video':
return BackgroundType.Video
class BackgroundGradientType(object):
@ -184,7 +189,7 @@ class ThemeXML(object):
:param path: The path name to be added.
"""
if self.background_type == 'image':
if self.background_type == 'image' or self.background_type == 'video':
if self.background_filename and path:
self.theme_name = self.theme_name.strip()
self.background_filename = self.background_filename.strip()
@ -255,6 +260,21 @@ class ThemeXML(object):
# Create endColor element
self.child_element(background, 'borderColor', str(border_color))
def add_background_video(self, filename, border_color):
"""
Add a video background.
:param filename: The file name of the video.
:param border_color:
"""
background = self.theme_xml.createElement('background')
background.setAttribute('type', 'video')
self.theme.appendChild(background)
# Create Filename element
self.child_element(background, 'filename', filename)
# Create endColor element
self.child_element(background, 'borderColor', str(border_color))
def add_font(self, name, color, size, override, fonttype='main', bold='False', italics='False',
line_adjustment=0, xpos=0, ypos=0, width=0, height=0, outline='False', outline_color='#ffffff',
outline_pixel=2, shadow='False', shadow_color='#ffffff', shadow_pixel=5):
@ -512,6 +532,9 @@ class ThemeXML(object):
elif self.background_type == BackgroundType.to_string(BackgroundType.Image):
filename = os.path.split(self.background_filename)[1]
self.add_background_image(filename, self.background_border_color)
elif self.background_type == BackgroundType.to_string(BackgroundType.Video):
filename = os.path.split(self.background_filename)[1]
self.add_background_video(filename, self.background_border_color)
elif self.background_type == BackgroundType.to_string(BackgroundType.Transparent):
self.add_background_transparent()
self.add_font(

View File

@ -31,13 +31,15 @@ Some of the code for this form is based on the examples at:
import html
import logging
import os
from PyQt5 import QtCore, QtWidgets, QtWebKit, QtWebKitWidgets, QtOpenGL, QtGui, QtMultimedia
from openlp.core.common import Registry, RegistryProperties, OpenLPMixin, Settings, translate, is_macosx, is_win
from openlp.core.common import AppLocation, Registry, RegistryProperties, OpenLPMixin, Settings, translate,\
is_macosx, is_win
from openlp.core.lib import ServiceItem, ImageSource, ScreenList, build_html, expand_tags, image_to_byte
from openlp.core.lib.theme import BackgroundType
from openlp.core.ui import HideMode, AlertLocation
from openlp.core.ui import HideMode, AlertLocation, DisplayControllerType
if is_macosx():
from ctypes import pythonapi, c_void_p, c_char_p, py_object
@ -459,13 +461,13 @@ class MainDisplay(OpenLPMixin, Display, RegistryProperties):
background = self.image_manager.get_image_bytes(self.override['image'], ImageSource.ImagePlugin)
self.set_transparency(self.service_item.theme_data.background_type ==
BackgroundType.to_string(BackgroundType.Transparent))
if self.service_item.theme_data.background_filename:
self.service_item.bg_image_bytes = self.image_manager.get_image_bytes(
self.service_item.theme_data.background_filename, ImageSource.Theme)
if image_path:
image_bytes = self.image_manager.get_image_bytes(image_path, ImageSource.ImagePlugin)
else:
image_bytes = None
image_bytes = None
if self.service_item.theme_data.background_type == 'image':
if self.service_item.theme_data.background_filename:
self.service_item.bg_image_bytes = self.image_manager.get_image_bytes(
self.service_item.theme_data.background_filename, ImageSource.Theme)
if image_path:
image_bytes = self.image_manager.get_image_bytes(image_path, ImageSource.ImagePlugin)
html = build_html(self.service_item, self.screen, self.is_live, background, image_bytes,
plugins=self.plugin_manager.plugins)
self.web_view.setHtml(html)
@ -477,6 +479,17 @@ class MainDisplay(OpenLPMixin, Display, RegistryProperties):
Registry().execute('slidecontroller_live_unblank')
else:
self.hide_display(self.hide_mode)
if self.service_item.theme_data.background_type == 'video' and self.is_live:
if self.service_item.theme_data.background_filename:
service_item = ServiceItem()
service_item.title = 'webkit'
service_item.processor = 'webkit'
path = os.path.join(AppLocation.get_section_data_path('themes'),
self.service_item.theme_data.theme_name)
service_item.add_from_command(path,
self.service_item.theme_data.background_filename,
':/media/slidecontroller_multimedia.png')
self.media_controller.video(DisplayControllerType.Live, service_item, video_behind_text=True)
self._hide_mouse()
def footer(self, text):

View File

@ -83,7 +83,7 @@ def get_media_players():
reg_ex = QtCore.QRegExp(".*\[(.*)\].*")
if Settings().value('media/override player') == QtCore.Qt.Checked:
if reg_ex.exactMatch(saved_players):
overridden_player = '%s' % reg_ex.cap(1)
overridden_player = '{text}'.format(text=reg_ex.cap(1))
else:
overridden_player = 'auto'
else:
@ -102,7 +102,7 @@ def set_media_players(players_list, overridden_player='auto'):
log.debug('set_media_players')
players = ','.join(players_list)
if Settings().value('media/override player') == QtCore.Qt.Checked and overridden_player != 'auto':
players = players.replace(overridden_player, '[%s]' % overridden_player)
players = players.replace(overridden_player, '[{text}]'.format(text=overridden_player))
Settings().setValue('media/players', players)
@ -113,7 +113,7 @@ def parse_optical_path(input_string):
:param input_string: The string to parse
:return: The elements extracted from the string: filename, title, audio_track, subtitle_track, start, end
"""
log.debug('parse_optical_path, about to parse: "%s"' % input_string)
log.debug('parse_optical_path, about to parse: "{text}"'.format(text=input_string))
clip_info = input_string.split(sep=':')
title = int(clip_info[1])
audio_track = int(clip_info[2])
@ -137,7 +137,10 @@ def format_milliseconds(milliseconds):
seconds, millis = divmod(milliseconds, 1000)
minutes, seconds = divmod(seconds, 60)
hours, minutes = divmod(minutes, 60)
return "%02d:%02d:%02d,%03d" % (hours, minutes, seconds, millis)
return "{hours:02d}:{minutes:02d}:{seconds:02d},{millis:03d}".format(hours=hours,
minutes=minutes,
seconds=seconds,
millis=millis)
from .mediacontroller import MediaController
from .playertab import PlayerTab

View File

@ -45,7 +45,7 @@ VIDEO_CSS = """
"""
VIDEO_JS = """
function show_video(state, path, volume, loop, variable_value){
function show_video(state, path, volume, variable_value){
// Sometimes video.currentTime stops slightly short of video.duration and video.ended is intermittent!
var video = document.getElementById('video');
@ -55,9 +55,6 @@ VIDEO_JS = """
switch(state){
case 'load':
video.src = 'file:///' + path;
if(loop == true) {
video.loop = true;
}
video.load();
break;
case 'play':
@ -180,12 +177,8 @@ class WebkitPlayer(MediaPlayer):
else:
vol = 0
path = controller.media_info.file_info.absoluteFilePath()
if controller.media_info.is_background:
loop = 'true'
else:
loop = 'false'
display.web_view.setVisible(True)
js = 'show_video("load", "%s", %s, %s);' % (path.replace('\\', '\\\\'), str(vol), loop)
js = 'show_video("load", "{path}", {vol});'.format(path=path.replace('\\', '\\\\'), vol=str(vol))
display.frame.evaluateJavaScript(js)
return True

View File

@ -1323,7 +1323,7 @@ class ServiceManager(OpenLPMixin, RegistryMixin, QtWidgets.QWidget, Ui_ServiceMa
"""
The theme may have changed in the settings dialog so make sure the theme combo box is in the correct state.
"""
visible = self.renderer.theme_level == ThemeLevel.Global
visible = not self.renderer.theme_level == ThemeLevel.Global
self.theme_label.setVisible(visible)
self.theme_combo_box.setVisible(visible)

View File

@ -31,7 +31,7 @@ from openlp.core.common import Registry, RegistryProperties, UiStrings, translat
from openlp.core.lib.theme import BackgroundType, BackgroundGradientType
from openlp.core.lib.ui import critical_error_message_box
from openlp.core.ui import ThemeLayoutForm
from openlp.core.ui.lib.colorbutton import ColorButton
from openlp.core.ui.media.webkitplayer import VIDEO_EXT
from .themewizard import Ui_ThemeWizard
log = logging.getLogger(__name__)
@ -66,10 +66,13 @@ class ThemeForm(QtWidgets.QWizard, Ui_ThemeWizard, RegistryProperties):
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_browse_button.clicked.connect(self.on_image_browse_button_clicked)
self.image_file_edit.editingFinished.connect(self.on_image_file_edit_editing_finished)
self.video_browse_button.clicked.connect(self.on_video_browse_button_clicked)
self.video_file_edit.editingFinished.connect(self.on_video_file_edit_editing_finished)
self.main_color_button.colorChanged.connect(self.on_main_color_changed)
self.outline_color_button.colorChanged.connect(self.on_outline_color_changed)
self.shadow_color_button.colorChanged.connect(self.on_shadow_color_changed)
@ -307,6 +310,10 @@ class ThemeForm(QtWidgets.QWizard, Ui_ThemeWizard, RegistryProperties):
self.image_color_button.color = self.theme.background_border_color
self.image_file_edit.setText(self.theme.background_filename)
self.setField('background_type', 2)
elif self.theme.background_type == BackgroundType.to_string(BackgroundType.Video):
self.video_color_button.color = self.theme.background_border_color
self.video_file_edit.setText(self.theme.background_filename)
self.setField('background_type', 4)
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):
@ -384,10 +391,12 @@ class ThemeForm(QtWidgets.QWizard, Ui_ThemeWizard, RegistryProperties):
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 == '':
self.temp_background_filename = self.theme.background_filename
self.theme.background_filename = ''
if self.theme.background_type == BackgroundType.to_string(BackgroundType.Image) and \
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 != '':
self.theme.background_filename = self.temp_background_filename
self.temp_background_filename = ''
@ -413,6 +422,12 @@ class ThemeForm(QtWidgets.QWizard, Ui_ThemeWizard, RegistryProperties):
"""
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.
@ -444,6 +459,28 @@ class ThemeForm(QtWidgets.QWizard, Ui_ThemeWizard, RegistryProperties):
"""
self.theme.background_filename = str(self.image_file_edit.text())
def on_video_browse_button_clicked(self):
"""
Background video button pushed.
"""
visible_formats = '(%s)' % '; '.join(VIDEO_EXT)
actual_formats = '(%s)' % ' '.join(VIDEO_EXT)
video_filter = '{trans} {visible} {actual}'.format(trans=translate('OpenLP', 'Video Files'),
visible=visible_formats, actual=actual_formats)
video_filter = '{video};;{ui} (*.*)'.format(video=video_filter, ui=UiStrings().AllFiles)
filename, filter_used = QtWidgets.QFileDialog.getOpenFileName(
self, translate('OpenLP.ThemeWizard', 'Select Video'),
self.video_file_edit.text(), video_filter)
if filename:
self.theme.background_filename = filename
self.set_background_page_values()
def on_video_file_edit_editing_finished(self):
"""
Background video path edited
"""
self.theme.background_filename = str(self.image_file_edit.text())
def on_main_color_changed(self, color):
"""
Set the main colour value
@ -519,7 +556,8 @@ class ThemeForm(QtWidgets.QWizard, Ui_ThemeWizard, RegistryProperties):
return
save_from = None
save_to = None
if self.theme.background_type == BackgroundType.to_string(BackgroundType.Image):
if self.theme.background_type == BackgroundType.to_string(BackgroundType.Image) or \
self.theme.background_type == BackgroundType.to_string(BackgroundType.Video):
filename = os.path.split(str(self.theme.background_filename))[1]
save_to = os.path.join(self.path, self.theme.theme_name, filename)
save_from = self.theme.background_filename

View File

@ -300,7 +300,7 @@ class ThemeManager(OpenLPMixin, RegistryMixin, QtWidgets.QWidget, Ui_ThemeManage
"""
save_to = None
save_from = None
if theme_data.background_type == 'image':
if theme_data.background_type == 'image' or theme_data.background_type == 'video':
save_to = os.path.join(self.path, new_theme_name, os.path.split(str(theme_data.background_filename))[1])
save_from = theme_data.background_filename
theme_data.theme_name = new_theme_name
@ -318,7 +318,7 @@ class ThemeManager(OpenLPMixin, RegistryMixin, QtWidgets.QWidget, Ui_ThemeManage
translate('OpenLP.ThemeManager', 'You must select a theme to edit.')):
item = self.theme_list_widget.currentItem()
theme = self.get_theme_data(item.data(QtCore.Qt.UserRole))
if theme.background_type == 'image':
if theme.background_type == 'image' or theme.background_type == 'video':
self.old_background_image = theme.background_filename
self.theme_form.theme = theme
self.theme_form.exec(True)

View File

@ -62,7 +62,7 @@ class Ui_ThemeWizard(object):
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.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)
@ -135,6 +135,30 @@ class Ui_ThemeWizard(object):
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_file_layout = QtWidgets.QHBoxLayout()
self.video_file_layout.setObjectName('video_file_layout')
self.video_file_edit = QtWidgets.QLineEdit(self.video_widget)
self.video_file_edit.setObjectName('video_file_edit')
self.video_file_layout.addWidget(self.video_file_edit)
self.video_browse_button = QtWidgets.QToolButton(self.video_widget)
self.video_browse_button.setObjectName('video_browse_button')
self.video_browse_button.setIcon(build_icon(':/general/general_open.png'))
self.video_file_layout.addWidget(self.video_browse_button)
self.video_layout.addRow(self.video_label, self.video_file_layout)
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()
@ -390,11 +414,10 @@ class Ui_ThemeWizard(object):
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.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.Video, UiStrings().Video)
self.background_combo_box.setItemText(BackgroundType.Transparent,
translate('OpenLP.ThemeWizard', 'Transparent'))
self.color_label.setText(translate('OpenLP.ThemeWizard', 'color:'))
@ -413,6 +436,8 @@ class Ui_ThemeWizard(object):
translate('OpenLP.ThemeWizard', 'Bottom Left - Top Right'))
self.image_color_label.setText(translate('OpenLP.ThemeWizard', 'Background color:'))
self.image_label.setText('%s:' % UiStrings().Image)
self.video_color_label.setText(translate('OpenLP.ThemeWizard', 'Background color:'))
self.video_label.setText('%s:' % 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'))

View File

@ -211,19 +211,21 @@ def update_reference_separators():
while '||' in source_string:
source_string = source_string.replace('||', '|')
if role != 'e':
REFERENCE_SEPARATORS['sep_%s_display' % role] = source_string.split('|')[0]
REFERENCE_SEPARATORS['sep_{role}_display'.format(role=role)] = source_string.split('|')[0]
# escape reserved characters
for character in '\\.^$*+?{}[]()':
source_string = source_string.replace(character, '\\' + character)
# add various unicode alternatives
source_string = source_string.replace('-', '(?:[-\u00AD\u2010\u2011\u2012\u2014\u2014\u2212\uFE63\uFF0D])')
source_string = source_string.replace(',', '(?:[,\u201A])')
REFERENCE_SEPARATORS['sep_%s' % role] = '\s*(?:%s)\s*' % source_string
REFERENCE_SEPARATORS['sep_%s_default' % role] = default_separators[index]
REFERENCE_SEPARATORS['sep_{role}'.format(role=role)] = '\s*(?:{source})\s*'.format(source=source_string)
REFERENCE_SEPARATORS['sep_{role}_default'.format(role=role)] = default_separators[index]
# verse range match: (<chapter>:)?<verse>(-((<chapter>:)?<verse>|end)?)?
# TODO: Check before converting this string
range_regex = '(?:(?P<from_chapter>[0-9]+)%(sep_v)s)?' \
'(?P<from_verse>[0-9]+)(?P<range_to>%(sep_r)s(?:(?:(?P<to_chapter>' \
'[0-9]+)%(sep_v)s)?(?P<to_verse>[0-9]+)|%(sep_e)s)?)?' % REFERENCE_SEPARATORS
# TODO: Test before converting re.compile strings
REFERENCE_MATCHES['range'] = re.compile('^\s*%s\s*$' % range_regex, re.UNICODE)
REFERENCE_MATCHES['range_separator'] = re.compile(REFERENCE_SEPARATORS['sep_l'], re.UNICODE)
# full reference match: <book>(<range>(,(?!$)|(?=$)))+
@ -331,10 +333,10 @@ def parse_reference(reference, bible, language_selection, book_ref_id=False):
separator.
"""
log.debug('parse_reference("%s")', reference)
log.debug('parse_reference("{text}")'.format(text=reference))
match = get_reference_match('full').match(reference)
if match:
log.debug('Matched reference %s' % reference)
log.debug('Matched reference {text}'.format(text=reference))
book = match.group('book')
if not book_ref_id:
book_ref_id = bible.get_book_ref_id_by_localised_name(book, language_selection)
@ -400,7 +402,7 @@ def parse_reference(reference, bible, language_selection, book_ref_id=False):
ref_list.append((book_ref_id, from_chapter, 1, -1))
return ref_list
else:
log.debug('Invalid reference: %s' % reference)
log.debug('Invalid reference: {text}'.format(text=reference))
return None

View File

@ -669,6 +669,8 @@ class BibleMediaItem(MediaManagerItem):
second_bible = self.quickSecondComboBox.currentText()
# Get input from field and replace 'A-Z + . ' with ''
# This will check if field has any '.' after A-Z and removes them. Eg. Gen. 1 = Gen 1 = Genesis 1
# If Book name has '.' after number. eg. 1. Genesis, the search fails without the dot, and vice versa.
# A better solution would be to make '.' optional in the search results. Current solution was easier to code.
text = self.quick_search_edit.text()
text = re.sub('\D[.]\s', ' ', text)
# This is triggered on reference search, use the search from manager.py
@ -730,7 +732,8 @@ class BibleMediaItem(MediaManagerItem):
"""
log.debug('Quick Search Button clicked')
# If we are performing "Search while typing", this setting is set to True, here it's reset to "False"
Settings().setValue('bibles/hide web bible error if searching while typing', False)
if Settings().Value('bibles/hide web bible error if searching while typing'):
Settings().setValue('bibles/hide web bible error if searching while typing', False)
self.quickSearchButton.setEnabled(False)
self.application.process_events()
bible = self.quickVersionComboBox.currentText()
@ -811,6 +814,7 @@ class BibleMediaItem(MediaManagerItem):
"""
This function is called when "Search as you type" is enabled for Bibles.
It is basically the same thing as "on_quick_search_search" but all the error messages are removed.
This also has increased min len for text search for performance reasons.
For commented version, please visit def on_quick_search_button.
"""
bible = self.quickVersionComboBox.currentText()
@ -858,14 +862,14 @@ class BibleMediaItem(MediaManagerItem):
This would result in seeing the same message multiple times.
This message is located in lib\manager.py, so the setting is required.
"""
Settings().setValue('bibles/hide web bible error if searching while typing', True)
# Regex for finding space + any non whitemark character. (Prevents search from starting on 1 word searches)
space_and_any = re.compile(' \S')
# Turn this into a format that may be used in if statement.
count_space_any = space_and_any.findall(text)
# Start searching if this behaviour is not disabled in settings and conditions are met.
if Settings().value('bibles/is search while typing enabled'):
# If text length is less than the mininum and results are not locked, clear the results.
Settings().setValue('bibles/hide web bible error if searching while typing', True)
# Regex for finding space + any non whitemark character. (Prevents search from starting on 1 word searches)
space_and_any = re.compile(' \S')
# Turn this into a format that may be used in if statement.
count_space_any = space_and_any.findall(text)
# Start searching if this behaviour is not disabled in settings and conditions are met.
# If text does not have 'count_space_any' and results are not locked, clear the results.
if len(count_space_any) == 0:
if not self.quickLockButton.isChecked():
self.list_view.clear()

View File

@ -60,7 +60,7 @@ import webbrowser
from PyQt5 import QtCore
from lxml import etree, objectify
SERVER_URL = 'http://www.transifex.net/api/2/project/openlp/resource/openlp-24x/'
SERVER_URL = 'http://www.transifex.com/api/2/project/openlp/resource/openlp-26x/'
IGNORED_PATHS = ['scripts']
IGNORED_FILES = ['setup.py']
@ -270,7 +270,7 @@ def update_translations():
return
else:
os.chdir(os.path.abspath('..'))
run('pylupdate4 -verbose -noobsolete openlp.pro')
run('pylupdate5 -verbose -noobsolete openlp.pro')
os.chdir(os.path.abspath('scripts'))

View File

@ -26,7 +26,8 @@ Package to test the openlp.core.lib.projector.pjlink1 package.
from unittest import TestCase
from openlp.core.lib.projector.pjlink1 import PJLink1
from openlp.core.lib.projector.constants import E_PARAMETER, ERROR_STRING
from openlp.core.lib.projector.constants import E_PARAMETER, ERROR_STRING, S_OFF, S_STANDBY, S_WARMUP, S_ON, \
S_COOLDOWN, PJLINK_POWR_STATUS
from tests.functional import patch
from tests.resources.projector.data import TEST_PIN, TEST_SALT, TEST_CONNECT_AUTHENTICATE
@ -151,3 +152,33 @@ class TestPJLink(TestCase):
'Lamp 3 power status should have been set to TRUE')
self.assertEquals(pjlink.lamp[2]['Hours'], 33333,
'Lamp 3 hours should have been set to 33333')
@patch.object(pjlink_test, 'projectorReceivedData')
def projector_process_power_on_test(self, mock_projectorReceivedData):
"""
Test setting power to ON
"""
# GIVEN: Test object and preset
pjlink = pjlink_test
pjlink.power = S_STANDBY
# WHEN: Call process_command with turn power on command
pjlink.process_command('POWR', PJLINK_POWR_STATUS[S_ON])
# THEN: Power should be set to ON
self.assertEquals(pjlink.power, S_ON, 'Power should have been set to ON')
@patch.object(pjlink_test, 'projectorReceivedData')
def projector_process_power_off_test(self, mock_projectorReceivedData):
"""
Test setting power to STANDBY
"""
# GIVEN: Test object and preset
pjlink = pjlink_test
pjlink.power = S_ON
# WHEN: Call process_command with turn power on command
pjlink.process_command('POWR', PJLINK_POWR_STATUS[S_STANDBY])
# THEN: Power should be set to STANDBY
self.assertEquals(pjlink.power, S_STANDBY, 'Power should have been set to STANDBY')

View File

@ -27,8 +27,9 @@ from unittest import TestCase, skipUnless
from PyQt5 import QtCore
from openlp.core.common import Registry, is_macosx, Settings
from openlp.core.lib import ScreenList
from openlp.core.lib import ScreenList, PluginManager
from openlp.core.ui import MainDisplay
from openlp.core.ui.media import MediaController
from openlp.core.ui.maindisplay import TRANSPARENT_STYLESHEET, OPAQUE_STYLESHEET
from tests.helpers.testmixin import TestMixin
@ -223,3 +224,61 @@ class TestMainDisplay(TestCase, TestMixin):
# THEN: setVisible should had not been called
main_display.setVisible.assert_not_called()
@patch(u'openlp.core.ui.maindisplay.Settings')
@patch(u'openlp.core.ui.maindisplay.build_html')
def build_html_no_video_test(self, MockedSettings, Mocked_build_html):
# GIVEN: Mocked display
display = MagicMock()
mocked_media_controller = MagicMock()
Registry.create()
Registry().register('media_controller', mocked_media_controller)
main_display = MainDisplay(display)
main_display.frame = MagicMock()
mocked_settings = MagicMock()
mocked_settings.value.return_value = False
MockedSettings.return_value = mocked_settings
main_display.shake_web_view = MagicMock()
service_item = MagicMock()
mocked_plugin = MagicMock()
display.plugin_manager = PluginManager()
display.plugin_manager.plugins = [mocked_plugin]
main_display.web_view = MagicMock()
# WHEN: build_html is called with a normal service item and a non video theme.
main_display.build_html(service_item)
# THEN: the following should had not been called
self.assertEquals(main_display.web_view.setHtml.call_count, 1, 'setHTML should be called once')
self.assertEquals(main_display.media_controller.video.call_count, 0,
'Media Controller video should not have been called')
@patch(u'openlp.core.ui.maindisplay.Settings')
@patch(u'openlp.core.ui.maindisplay.build_html')
def build_html_video_test(self, MockedSettings, Mocked_build_html):
# GIVEN: Mocked display
display = MagicMock()
mocked_media_controller = MagicMock()
Registry.create()
Registry().register('media_controller', mocked_media_controller)
main_display = MainDisplay(display)
main_display.frame = MagicMock()
mocked_settings = MagicMock()
mocked_settings.value.return_value = False
MockedSettings.return_value = mocked_settings
main_display.shake_web_view = MagicMock()
service_item = MagicMock()
service_item.theme_data = MagicMock()
service_item.theme_data.background_type = 'video'
mocked_plugin = MagicMock()
display.plugin_manager = PluginManager()
display.plugin_manager.plugins = [mocked_plugin]
main_display.web_view = MagicMock()
# WHEN: build_html is called with a normal service item and a video theme.
main_display.build_html(service_item)
# THEN: the following should had not been called
self.assertEquals(main_display.web_view.setHtml.call_count, 1, 'setHTML should be called once')
self.assertEquals(main_display.media_controller.video.call_count, 1,
'Media Controller video should have been called once')

View File

@ -22,7 +22,7 @@
"""
Package to test the openlp.plugin.bible.lib.https package.
"""
from unittest import TestCase
from unittest import TestCase, skip
from openlp.core.common import Registry
from openlp.plugins.bibles.lib.http import BGExtract, CWExtract, BSExtract
@ -146,6 +146,7 @@ class TestBibleHTTP(TestCase):
self.assertIsNotNone(bibles)
self.assertIn(('Holman Christian Standard Bible', 'HCSB', 'en'), bibles)
@skip("Waiting for Crosswalk to fix their server")
def crosswalk_get_bibles_test(self):
"""
Test getting list of bibles from Crosswalk.com

View File

@ -26,7 +26,7 @@ import json
def assert_length(expected, iterable, msg=None):
if len(iterable) != expected:
if not msg:
msg = 'Expected length %s, got %s' % (expected, len(iterable))
msg = 'Expected length {expected}, got {got}'.format(expected=expected, got=len(iterable))
raise AssertionError(msg)