- Merged trunk on 18.5.2016 20:57

This commit is contained in:
suutari-olli 2016-05-18 20:56:50 +03:00
commit 165d8cd4ba
52 changed files with 1153 additions and 276 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

@ -114,7 +114,7 @@ class CategoryActionList(object):
if item[1] == action:
self.actions.remove(item)
return
raise ValueError('Action "%s" does not exist.' % action)
raise ValueError('Action "{action}" does not exist.'.format(action=action))
class CategoryList(object):
@ -138,7 +138,7 @@ class CategoryList(object):
for category in self.categories:
if category.name == key:
return category
raise KeyError('Category "%s" does not exist.' % key)
raise KeyError('Category "{keY}" does not exist.'.format(key=key))
def __len__(self):
"""
@ -203,7 +203,7 @@ class CategoryList(object):
if category.name == name:
self.categories.remove(category)
return
raise ValueError('Category "%s" does not exist.' % name)
raise ValueError('Category "{name}" does not exist.'.format(name=name))
class ActionList(object):
@ -272,8 +272,9 @@ class ActionList(object):
actions.append(action)
ActionList.shortcut_map[shortcuts[1]] = actions
else:
log.warning('Shortcut "%s" is removed from "%s" because another action already uses this shortcut.' %
(shortcuts[1], action.objectName()))
log.warning('Shortcut "{shortcut}" is removed from "{action}" because another '
'action already uses this shortcut.'.format(shortcut=shortcuts[1],
action=action.objectName()))
shortcuts.remove(shortcuts[1])
# Check the primary shortcut.
existing_actions = ActionList.shortcut_map.get(shortcuts[0], [])
@ -283,8 +284,9 @@ class ActionList(object):
actions.append(action)
ActionList.shortcut_map[shortcuts[0]] = actions
else:
log.warning('Shortcut "%s" is removed from "%s" because another action already uses this shortcut.' %
(shortcuts[0], action.objectName()))
log.warning('Shortcut "{shortcut}" is removed from "{action}" '
'because another action already uses this shortcut.'.format(shortcut=shortcuts[0],
action=action.objectName()))
shortcuts.remove(shortcuts[0])
action.setShortcuts([QtGui.QKeySequence(shortcut) for shortcut in shortcuts])

View File

@ -68,7 +68,7 @@ class LanguageManager(object):
"""
Find all available language files in this OpenLP install
"""
log.debug('Translation files: %s', AppLocation.get_directory(AppLocation.LanguageDir))
log.debug('Translation files: {files}'.format(files=AppLocation.get_directory(AppLocation.LanguageDir)))
trans_dir = QtCore.QDir(AppLocation.get_directory(AppLocation.LanguageDir))
file_names = trans_dir.entryList(['*.qm'], QtCore.QDir.Files, QtCore.QDir.Name)
# Remove qm files from the list which start with "qt_".
@ -93,7 +93,7 @@ class LanguageManager(object):
"""
language = Settings().value('core/language')
language = str(language)
log.info('Language file: \'%s\' Loaded from conf file' % language)
log.info("Language file: '{language}' Loaded from conf file".format(language=language))
if re.match(r'[[].*[]]', language):
LanguageManager.auto_language = True
language = re.sub(r'[\[\]]', '', language)
@ -117,9 +117,9 @@ class LanguageManager(object):
qm_list = LanguageManager.get_qm_list()
language = str(qm_list[action_name])
if LanguageManager.auto_language:
language = '[%s]' % language
language = '[{language}]'.format(language=language)
Settings().setValue('core/language', language)
log.info('Language file: \'%s\' written to conf file' % language)
log.info("Language file: '{language}' written to conf file".format(language=language))
if message:
QtWidgets.QMessageBox.information(None,
translate('OpenLP.LanguageManager', 'Language'),
@ -136,7 +136,8 @@ class LanguageManager(object):
for counter, qmf in enumerate(qm_files):
reg_ex = QtCore.QRegExp("^.*i18n/(.*).qm")
if reg_ex.exactMatch(qmf):
name = '%s' % reg_ex.cap(1)
name = '{regex}'.format(regex=reg_ex.cap(1))
# TODO: Test before converting to python3 string format
LanguageManager.__qm_list__['%#2i %s' % (counter + 1, LanguageManager.language_name(qmf))] = name
@staticmethod

View File

@ -49,12 +49,13 @@ class OpenLPMixin(object):
Code to added debug wrapper to work on called functions within a decorated class.
"""
def wrapped(*args, **kwargs):
parent.logger.debug("Entering %s" % func.__name__)
parent.logger.debug("Entering {function}".format(function=func.__name__))
try:
return func(*args, **kwargs)
except Exception as e:
if parent.logger.getEffectiveLevel() <= logging.ERROR:
parent.logger.error('Exception in %s : %s' % (func.__name__, e))
parent.logger.error('Exception in {function} : {error}'.format(function=func.__name__,
error=e))
raise e
return wrapped

View File

@ -71,8 +71,8 @@ class Registry(object):
else:
if not self.initialising:
trace_error_handler(log)
log.error('Service %s not found in list' % key)
raise KeyError('Service %s not found in list' % key)
log.error('Service {key} not found in list'.format(key=key))
raise KeyError('Service {key} not found in list'.format(key=key))
def register(self, key, reference):
"""
@ -83,8 +83,8 @@ class Registry(object):
"""
if key in self.service_list:
trace_error_handler(log)
log.error('Duplicate service exception %s' % key)
raise KeyError('Duplicate service exception %s' % key)
log.error('Duplicate service exception {key}'.format(key=key))
raise KeyError('Duplicate service exception {key}'.format(key=key))
else:
self.service_list[key] = reference
@ -140,8 +140,8 @@ class Registry(object):
except TypeError:
# Who has called me can help in debugging
trace_error_handler(log)
log.exception('Exception for function %s', function)
log.exception('Exception for function {function}'.format(function=function))
else:
trace_error_handler(log)
log.error("Event %s called but not registered" % event)
log.error("Event {event} called but not registered".format(event=event))
return results

View File

@ -487,16 +487,16 @@ class Settings(QtCore.QSettings):
# Do NOT do this anywhere else!
settings = QtCore.QSettings(self.fileName(), Settings.IniFormat)
settings.beginGroup(plugin.settings_section)
if settings.contains('%s count' % plugin.name):
if settings.contains('{name} count'.format(name=plugin.name)):
# Get the count.
list_count = int(settings.value('%s count' % plugin.name, 0))
list_count = int(settings.value('{name} count'.format(name=plugin.name), 0))
if list_count:
for counter in range(list_count):
# The keys were named e. g.: "image 0"
item = settings.value('%s %d' % (plugin.name, counter), '')
item = settings.value('{name} {counter:d}'.format(name=plugin.name, counter=counter), '')
if item:
files_list.append(item)
settings.remove('%s %d' % (plugin.name, counter))
settings.remove('%s count' % plugin.name)
settings.remove('{name} {counter:d}'.format(name=plugin.name, counter=counter))
settings.remove('{name} count'.format(name=plugin.name))
settings.endGroup()
return files_list

View File

@ -81,6 +81,7 @@ class UiStrings(object):
self.Export = translate('OpenLP.Ui', 'Export')
self.File = translate('OpenLP.Ui', 'File')
self.FileNotFound = translate('OpenLP.Ui', 'File Not Found')
# TODO: Check before converting to python3 string
self.FileNotFoundMessage = translate('OpenLP.Ui', 'File %s not found.\nPlease try selecting it individually.')
self.FontSizePtUnit = translate('OpenLP.Ui', 'pt', 'Abbreviated font pointsize unit')
self.Help = translate('OpenLP.Ui', 'Help')
@ -111,8 +112,8 @@ class UiStrings(object):
self.NISs = translate('OpenLP.Ui', 'No Item Selected', 'Singular')
self.NISp = translate('OpenLP.Ui', 'No Items Selected', 'Plural')
self.OLP = translate('OpenLP.Ui', 'OpenLP')
self.OLPV2 = "%s %s" % (self.OLP, "2")
self.OLPV2x = "%s %s" % (self.OLP, "2.4")
self.OLPV2 = "{name} {version}".format(name=self.OLP, version="2")
self.OLPV2x = "{name} {version}".format(name=self.OLP, version="2.4")
self.OpenLPStart = translate('OpenLP.Ui', 'OpenLP is already running. Do you wish to continue?')
self.OpenService = translate('OpenLP.Ui', 'Open service.')
self.PlaySlidesInLoop = translate('OpenLP.Ui', 'Play Slides in Loop')
@ -140,6 +141,7 @@ class UiStrings(object):
self.Split = translate('OpenLP.Ui', 'Optional &Split')
self.SplitToolTip = translate('OpenLP.Ui',
'Split a slide into two only if it does not fit on the screen as one slide.')
# TODO: Check before converting to python3 string
self.StartTimeCode = translate('OpenLP.Ui', 'Start %s')
self.StopPlaySlidesInLoop = translate('OpenLP.Ui', 'Stop Play Slides in Loop')
self.StopPlaySlidesToEnd = translate('OpenLP.Ui', 'Stop Play Slides to End')
@ -153,3 +155,4 @@ 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')

View File

@ -44,9 +44,10 @@ class VersionThread(QtCore.QThread):
log.debug('Version thread - run')
app_version = get_application_version()
version = check_latest_version(app_version)
log.debug("Versions %s and %s " % (LooseVersion(str(version)), LooseVersion(str(app_version['full']))))
log.debug("Versions {version1} and {version2} ".format(version1=LooseVersion(str(version)),
version2=LooseVersion(str(app_version['full']))))
if LooseVersion(str(version)) > LooseVersion(str(app_version['full'])):
self.main_window.openlp_version_check.emit('%s' % version)
self.main_window.openlp_version_check.emit('{version}'.format(version=version))
def get_application_version():
@ -91,7 +92,7 @@ def get_application_version():
if tree_revision == tag_revision:
full_version = tag_version.strip()
else:
full_version = '%s-bzr%s' % (tag_version.strip(), tree_revision.strip())
full_version = '{tag}-bzr{tree}'.format(tag=tag_version.strip(), tree=tree_revision.strip())
else:
# We're not running the development version, let's use the file.
file_path = AppLocation.get_directory(AppLocation.VersionDir)
@ -113,9 +114,10 @@ def get_application_version():
'build': bits[1] if len(bits) > 1 else None
}
if APPLICATION_VERSION['build']:
log.info('Openlp version %s build %s', APPLICATION_VERSION['version'], APPLICATION_VERSION['build'])
log.info('Openlp version {version} build {build}'.format(version=APPLICATION_VERSION['version'],
build=APPLICATION_VERSION['build']))
else:
log.info('Openlp version %s' % APPLICATION_VERSION['version'])
log.info('Openlp version {version}'.format(version=APPLICATION_VERSION['version']))
return APPLICATION_VERSION
@ -149,8 +151,9 @@ def check_latest_version(current_version):
req = urllib.request.Request('http://www.openlp.org/files/dev_version.txt')
else:
req = urllib.request.Request('http://www.openlp.org/files/version.txt')
req.add_header('User-Agent', 'OpenLP/%s %s/%s; ' % (current_version['full'], platform.system(),
platform.release()))
req.add_header('User-Agent', 'OpenLP/{version} {system}/{release}; '.format(version=current_version['full'],
system=platform.system(),
release=platform.release()))
remote_version = None
retries = 0
while True:

View File

@ -55,9 +55,13 @@ class ImageSource(object):
``Theme``
This says, that the image is used by a theme.
``CommandPlugins``
This states that an image is being used by a command plugin.
"""
ImagePlugin = 1
Theme = 2
CommandPlugins = 3
class MediaType(object):
@ -97,7 +101,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()
@ -174,10 +178,30 @@ def create_thumb(image_path, thumb_path, return_icon=True, size=None):
ext = os.path.splitext(thumb_path)[1].lower()
reader = QtGui.QImageReader(image_path)
if size is None:
ratio = reader.size().width() / reader.size().height()
# No size given; use default height of 88
if reader.size().isEmpty():
ratio = 1
else:
ratio = reader.size().width() / reader.size().height()
reader.setScaledSize(QtCore.QSize(int(ratio * 88), 88))
else:
elif size.isValid():
# Complete size given
reader.setScaledSize(size)
else:
# Invalid size given
if reader.size().isEmpty():
ratio = 1
else:
ratio = reader.size().width() / reader.size().height()
if size.width() >= 0:
# Valid width; scale height
reader.setScaledSize(QtCore.QSize(size.width(), int(size.width() / ratio)))
elif size.height() >= 0:
# Valid height; scale width
reader.setScaledSize(QtCore.QSize(int(ratio * size.height()), size.height()))
else:
# Invalid; use default height of 88
reader.setScaledSize(QtCore.QSize(int(ratio * 88), 88))
thumb = reader.read()
thumb.save(thumb_path, ext[1:])
if not return_icon:
@ -300,6 +324,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

@ -68,9 +68,11 @@ def get_db_path(plugin_name, db_file_name=None):
:return: The path to the database as type str
"""
if db_file_name is None:
return 'sqlite:///%s/%s.sqlite' % (AppLocation.get_section_data_path(plugin_name), plugin_name)
return 'sqlite:///{path}/{plugin}.sqlite'.format(path=AppLocation.get_section_data_path(plugin_name),
plugin=plugin_name)
else:
return 'sqlite:///%s/%s' % (AppLocation.get_section_data_path(plugin_name), db_file_name)
return 'sqlite:///{path}/{name}'.format(path=AppLocation.get_section_data_path(plugin_name),
name=db_file_name)
def handle_db_error(plugin_name, db_file_name):
@ -82,10 +84,10 @@ def handle_db_error(plugin_name, db_file_name):
:return: None
"""
db_path = get_db_path(plugin_name, db_file_name)
log.exception('Error loading database: %s', db_path)
log.exception('Error loading database: {db}'.format(db=db_path))
critical_error_message_box(translate('OpenLP.Manager', 'Database Error'),
translate('OpenLP.Manager', 'OpenLP cannot load your database.\n\nDatabase: %s')
% db_path)
translate('OpenLP.Manager',
'OpenLP cannot load your database.\n\nDatabase: {db}').format(db=db_path))
def init_url(plugin_name, db_file_name=None):
@ -101,10 +103,11 @@ def init_url(plugin_name, db_file_name=None):
if db_type == 'sqlite':
db_url = get_db_path(plugin_name, db_file_name)
else:
db_url = '%s://%s:%s@%s/%s' % (db_type, urlquote(settings.value('db username')),
urlquote(settings.value('db password')),
urlquote(settings.value('db hostname')),
urlquote(settings.value('db database')))
db_url = '{type}://{user}:{password}@{host}/{db}'.format(type=db_type,
user=urlquote(settings.value('db username')),
password=urlquote(settings.value('db password')),
host=urlquote(settings.value('db hostname')),
db=urlquote(settings.value('db database')))
settings.endGroup()
return db_url
@ -157,10 +160,10 @@ def upgrade_db(url, upgrade):
return version, upgrade.__version__
version += 1
try:
while hasattr(upgrade, 'upgrade_%d' % version):
log.debug('Running upgrade_%d', version)
while hasattr(upgrade, 'upgrade_{version:d}'.format(version=version)):
log.debug('Running upgrade_{version:d}'.format(version=version))
try:
upgrade_func = getattr(upgrade, 'upgrade_%d' % version)
upgrade_func = getattr(upgrade, 'upgrade_{version:d}'.format(version=version))
upgrade_func(session, metadata)
session.commit()
# Update the version number AFTER a commit so that we are sure the previous transaction happened
@ -168,8 +171,8 @@ def upgrade_db(url, upgrade):
session.commit()
version += 1
except (SQLAlchemyError, DBAPIError):
log.exception('Could not run database upgrade script "upgrade_%s", upgrade process has been halted.',
version)
log.exception('Could not run database upgrade script '
'"upgrade_{version:d}", upgrade process has been halted.'.format(version=version))
break
except (SQLAlchemyError, DBAPIError):
version_meta = Metadata.populate(key='version', value=int(upgrade.__version__))
@ -242,9 +245,10 @@ class Manager(object):
critical_error_message_box(
translate('OpenLP.Manager', 'Database Error'),
translate('OpenLP.Manager', 'The database being loaded was created in a more recent version of '
'OpenLP. The database is version %d, while OpenLP expects version %d. The database will '
'not be loaded.\n\nDatabase: %s') % (db_ver, up_ver, self.db_url)
)
'OpenLP. The database is version {db_ver}, while OpenLP expects version {db_up}. '
'The database will not be loaded.\n\nDatabase: {db_name}').format(db_ver=db_ver,
db_up=up_ver,
db_name=self.db_url))
return
if not session:
try:
@ -460,7 +464,7 @@ class Manager(object):
raise
except InvalidRequestError:
self.session.rollback()
log.exception('Failed to delete %s records', object_class.__name__)
log.exception('Failed to delete {name} records'.format(name=object_class.__name__))
return False
except:
self.session.rollback()

View File

@ -50,7 +50,8 @@ class FileDialog(QtWidgets.QFileDialog):
log.info('File not found. Attempting to unquote.')
file = parse.unquote(file)
if not os.path.exists(file):
log.error('File %s not found.' % file)
log.error('File {text} not found.'.format(text=file))
# TODO: Test with UiStrings() before converting to python3 strings
QtWidgets.QMessageBox.information(parent, UiStrings().FileNotFound,
UiStrings().FileNotFoundMessage % file)
continue

View File

@ -396,6 +396,7 @@ from openlp.core.lib.theme import BackgroundType, BackgroundGradientType, Vertic
log = logging.getLogger(__name__)
# TODO: Verify where this is used before converting to python3
HTMLSRC = """
<!DOCTYPE html>
<html>
@ -564,13 +565,13 @@ def build_html(item, screen, is_live, background, image=None, plugins=None):
theme_data = item.theme_data
# Image generated and poked in
if background:
bgimage_src = 'src="data:image/png;base64,%s"' % background
bgimage_src = 'src="data:image/png;base64,{image}"'.format(image=background)
elif item.bg_image_bytes:
bgimage_src = 'src="data:image/png;base64,%s"' % item.bg_image_bytes
bgimage_src = 'src="data:image/png;base64,{image}"'.format(image=item.bg_image_bytes)
else:
bgimage_src = 'style="display:none;"'
if image:
image_src = 'src="data:image/png;base64,%s"' % image
image_src = 'src="data:image/png;base64,{image}"'.format(image=image)
else:
image_src = 'style="display:none;"'
css_additions = ''
@ -601,7 +602,7 @@ def webkit_version():
"""
try:
webkit_ver = float(QtWebKit.qWebKitVersion())
log.debug('Webkit version = %s' % webkit_ver)
log.debug('Webkit version = {version}'.format(version=webkit_ver))
except AttributeError:
webkit_ver = 0
return webkit_ver
@ -621,23 +622,25 @@ def build_background_css(item, width):
if theme.background_type == BackgroundType.to_string(BackgroundType.Transparent):
background = ''
elif theme.background_type == BackgroundType.to_string(BackgroundType.Solid):
background = 'background-color: %s' % theme.background_color
background = 'background-color: {theme}'.format(theme=theme.background_color)
else:
if theme.background_direction == BackgroundGradientType.to_string(BackgroundGradientType.Horizontal):
background = 'background: -webkit-gradient(linear, left top, left bottom, from(%s), to(%s)) fixed' \
% (theme.background_start_color, theme.background_end_color)
background = 'background: -webkit-gradient(linear, left top, left bottom, from({start}), to({end})) ' \
'fixed'.format(start=theme.background_start_color, end=theme.background_end_color)
elif theme.background_direction == BackgroundGradientType.to_string(BackgroundGradientType.LeftTop):
background = 'background: -webkit-gradient(linear, left top, right bottom, from(%s), to(%s)) fixed' \
% (theme.background_start_color, theme.background_end_color)
background = 'background: -webkit-gradient(linear, left top, right bottom, from({start}), to({end})) ' \
'fixed'.format(start=theme.background_start_color, end=theme.background_end_color)
elif theme.background_direction == BackgroundGradientType.to_string(BackgroundGradientType.LeftBottom):
background = 'background: -webkit-gradient(linear, left bottom, right top, from(%s), to(%s)) fixed' \
% (theme.background_start_color, theme.background_end_color)
background = 'background: -webkit-gradient(linear, left bottom, right top, from({start}), to({end})) ' \
'fixed'.format(start=theme.background_start_color, end=theme.background_end_color)
elif theme.background_direction == BackgroundGradientType.to_string(BackgroundGradientType.Vertical):
background = 'background: -webkit-gradient(linear, left top, right top, from(%s), to(%s)) fixed' % \
(theme.background_start_color, theme.background_end_color)
background = 'background: -webkit-gradient(linear, left top, right top, from({start}), to({end})) ' \
'fixed'.format(start=theme.background_start_color, end=theme.background_end_color)
else:
background = 'background: -webkit-gradient(radial, %s 50%%, 100, %s 50%%, %s, from(%s), to(%s)) fixed'\
% (width, width, width, theme.background_start_color, theme.background_end_color)
background = 'background: -webkit-gradient(radial, {width} 50%, 100, {width} 50%, {width}, ' \
'from({start}), to({end})) fixed'.format(width=width,
start=theme.background_start_color,
end=theme.background_end_color)
return background
@ -647,6 +650,7 @@ def build_lyrics_css(item):
:param item: Service Item containing theme and location information
"""
# TODO: Verify this before converting to python3
style = """
.lyricstable {
z-index: 5;
@ -669,12 +673,13 @@ def build_lyrics_css(item):
lyrics = ''
lyricsmain = ''
if theme_data and item.main:
lyricstable = 'left: %spx; top: %spx;' % (item.main.x(), item.main.y())
lyricstable = 'left: {left}px; top: {top}px;'.format(left=item.main.x(), top=item.main.y())
lyrics = build_lyrics_format_css(theme_data, item.main.width(), item.main.height())
lyricsmain += build_lyrics_outline_css(theme_data)
if theme_data.font_main_shadow:
lyricsmain += ' text-shadow: %s %spx %spx;' % \
(theme_data.font_main_shadow_color, theme_data.font_main_shadow_size, theme_data.font_main_shadow_size)
lyricsmain += ' text-shadow: {theme} {shadow}px ' \
'{shadow}px;'.format(theme=theme_data.font_main_shadow_color,
shadow=theme_data.font_main_shadow_size)
lyrics_css = style % (lyricstable, lyrics, lyricsmain)
return lyrics_css
@ -689,7 +694,9 @@ def build_lyrics_outline_css(theme_data):
size = float(theme_data.font_main_outline_size) / 16
fill_color = theme_data.font_main_color
outline_color = theme_data.font_main_outline_color
return ' -webkit-text-stroke: %sem %s; -webkit-text-fill-color: %s; ' % (size, outline_color, fill_color)
return ' -webkit-text-stroke: {size}em {color}; -webkit-text-fill-color: {fill}; '.format(size=size,
color=outline_color,
fill=fill_color)
return ''
@ -715,13 +722,21 @@ def build_lyrics_format_css(theme_data, width, height):
padding_bottom = '0.5em'
else:
padding_bottom = '0'
lyrics = '%s word-wrap: break-word; ' \
'text-align: %s; vertical-align: %s; font-family: %s; ' \
'font-size: %spt; color: %s; line-height: %d%%; margin: 0;' \
'padding: 0; padding-bottom: %s; padding-left: %spx; width: %spx; height: %spx; ' % \
(justify, align, valign, theme_data.font_main_name, theme_data.font_main_size,
theme_data.font_main_color, 100 + int(theme_data.font_main_line_adjustment), padding_bottom,
left_margin, width, height)
lyrics = '{justify} word-wrap: break-word; ' \
'text-align: {align}; vertical-align: {valign}; font-family: {font}; ' \
'font-size: {size}pt; color: {color}; line-height: {line:d}%; margin: 0;' \
'padding: 0; padding-bottom: {bottom}; padding-left: {left}px; width: {width}px; ' \
'height: {height}px; '.format(justify=justify,
align=align,
valign=valign,
font=theme_data.font_main_name,
size=theme_data.font_main_size,
color=theme_data.font_main_color,
line=100 + int(theme_data.font_main_line_adjustment),
bottom=padding_bottom,
left=left_margin,
width=width,
height=height)
if theme_data.font_main_italics:
lyrics += 'font-style:italic; '
if theme_data.font_main_bold:
@ -737,20 +752,21 @@ def build_footer_css(item, height):
:param height:
"""
style = """
left: %spx;
bottom: %spx;
width: %spx;
font-family: %s;
font-size: %spt;
color: %s;
left: {left}px;
bottom: {bottom}px;
width: {width}px;
font-family: {family};
font-size: {size}pt;
color: {color};
text-align: left;
white-space: %s;
white-space: {space};
"""
theme = item.theme_data
if not theme or not item.footer:
return ''
bottom = height - int(item.footer.y()) - int(item.footer.height())
whitespace = 'normal' if Settings().value('themes/wrap footer') else 'nowrap'
lyrics_html = style % (item.footer.x(), bottom, item.footer.width(),
theme.font_footer_name, theme.font_footer_size, theme.font_footer_color, whitespace)
lyrics_html = style.format(left=item.footer.x(), bottom=bottom, width=item.footer.width(),
family=theme.font_footer_name, size=theme.font_footer_size,
color=theme.font_footer_color, space=whitespace)
return lyrics_html

View File

@ -236,7 +236,7 @@ class ImageManager(QtCore.QObject):
"""
Return the ``QImage`` from the cache. If not present wait for the background thread to process it.
"""
log.debug('getImage %s' % path)
log.debug('getImage {path}'.format(path=path))
image = self._cache[(path, source, width, height)]
if image.image is None:
self._conversion_queue.modify_priority(image, Priority.High)
@ -256,7 +256,7 @@ class ImageManager(QtCore.QObject):
"""
Returns the byte string for an image. If not present wait for the background thread to process it.
"""
log.debug('get_image_bytes %s' % path)
log.debug('get_image_bytes {path}'.format(path=path))
image = self._cache[(path, source, width, height)]
if image.image_bytes is None:
self._conversion_queue.modify_priority(image, Priority.Urgent)
@ -271,7 +271,7 @@ class ImageManager(QtCore.QObject):
"""
Add image to cache if it is not already there.
"""
log.debug('add_image %s' % path)
log.debug('add_image {path}'.format(path=path))
if not (path, source, width, height) in self._cache:
image = Image(path, source, background, width, height)
self._cache[(path, source, width, height)] = image

View File

@ -186,7 +186,7 @@ class MediaManagerItem(QtWidgets.QWidget, RegistryProperties):
for action in toolbar_actions:
if action[0] == StringContent.Preview:
self.toolbar.addSeparator()
self.toolbar.add_toolbar_action('%s%sAction' % (self.plugin.name, action[0]),
self.toolbar.add_toolbar_action('{name}{action}Action'.format(name=self.plugin.name, action=action[0]),
text=self.plugin.get_string(action[1])['title'], icon=action[2],
tooltip=self.plugin.get_string(action[1])['tooltip'],
triggers=action[3])
@ -200,7 +200,7 @@ class MediaManagerItem(QtWidgets.QWidget, RegistryProperties):
self.list_view.setSpacing(1)
self.list_view.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
self.list_view.setAlternatingRowColors(True)
self.list_view.setObjectName('%sListView' % self.plugin.name)
self.list_view.setObjectName('{name}ListView'.format(name=self.plugin.name))
# Add to page_layout
self.page_layout.addWidget(self.list_view)
# define and add the context menu
@ -212,19 +212,22 @@ class MediaManagerItem(QtWidgets.QWidget, RegistryProperties):
triggers=self.on_edit_click)
create_widget_action(self.list_view, separator=True)
create_widget_action(self.list_view,
'listView%s%sItem' % (self.plugin.name.title(), StringContent.Preview.title()),
'listView{plugin}{preview}Item'.format(plugin=self.plugin.name.title(),
preview=StringContent.Preview.title()),
text=self.plugin.get_string(StringContent.Preview)['title'],
icon=':/general/general_preview.png',
can_shortcuts=True,
triggers=self.on_preview_click)
create_widget_action(self.list_view,
'listView%s%sItem' % (self.plugin.name.title(), StringContent.Live.title()),
'listView{plugin}{live}Item'.format(plugin=self.plugin.name.title(),
live=StringContent.Live.title()),
text=self.plugin.get_string(StringContent.Live)['title'],
icon=':/general/general_live.png',
can_shortcuts=True,
triggers=self.on_live_click)
create_widget_action(self.list_view,
'listView%s%sItem' % (self.plugin.name.title(), StringContent.Service.title()),
'listView{plugin}{service}Item'.format(plugin=self.plugin.name.title(),
service=StringContent.Service.title()),
can_shortcuts=True,
text=self.plugin.get_string(StringContent.Service)['title'],
icon=':/general/general_add.png',
@ -232,7 +235,8 @@ class MediaManagerItem(QtWidgets.QWidget, RegistryProperties):
if self.has_delete_icon:
create_widget_action(self.list_view, separator=True)
create_widget_action(self.list_view,
'listView%s%sItem' % (self.plugin.name.title(), StringContent.Delete.title()),
'listView{plugin}{delete}Item'.format(plugin=self.plugin.name.title(),
delete=StringContent.Delete.title()),
text=self.plugin.get_string(StringContent.Delete)['title'],
icon=':/general/general_delete.png',
can_shortcuts=True, triggers=self.on_delete_click)
@ -313,7 +317,7 @@ class MediaManagerItem(QtWidgets.QWidget, RegistryProperties):
files = FileDialog.getOpenFileNames(self, self.on_new_prompt,
Settings().value(self.settings_section + '/last directory'),
self.on_new_file_masks)
log.info('New files(s) %s' % files)
log.info('New files(s) {files}'.format(files=files))
if files:
self.application.set_busy_cursor()
self.validate_and_load(files)
@ -333,7 +337,8 @@ class MediaManagerItem(QtWidgets.QWidget, RegistryProperties):
if not error_shown:
critical_error_message_box(translate('OpenLP.MediaManagerItem', 'Invalid File Type'),
translate('OpenLP.MediaManagerItem',
'Invalid File %s.\nSuffix not supported') % file_name)
'Invalid File {name}.\n'
'Suffix not supported').format(name=file_name))
error_shown = True
else:
new_files.append(file_name)
@ -375,7 +380,8 @@ class MediaManagerItem(QtWidgets.QWidget, RegistryProperties):
self.load_list(full_list, target_group)
last_dir = os.path.split(files[0])[0]
Settings().setValue(self.settings_section + '/last directory', last_dir)
Settings().setValue('%s/%s files' % (self.settings_section, self.settings_section), self.get_file_list())
Settings().setValue('{section}/{section} files'.format(section=self.settings_section),
self.get_file_list())
if duplicates_found:
critical_error_message_box(UiStrings().Duplicate,
translate('OpenLP.MediaManagerItem',
@ -550,7 +556,7 @@ class MediaManagerItem(QtWidgets.QWidget, RegistryProperties):
# Is it possible to process multiple list items to generate
# multiple service items?
if self.single_service_item:
log.debug('%s Add requested', self.plugin.name)
log.debug('{plugin} Add requested'.format(plugin=self.plugin.name))
self.add_to_service(replace=self.remote_triggered)
else:
items = self.list_view.selectedIndexes()
@ -591,7 +597,7 @@ class MediaManagerItem(QtWidgets.QWidget, RegistryProperties):
translate('OpenLP.MediaManagerItem',
'You must select one or more items.'))
else:
log.debug('%s Add requested', self.plugin.name)
log.debug('{plugin} Add requested'.format(plugin=self.plugin.name))
service_item = self.service_manager.get_service_item()
if not service_item:
QtWidgets.QMessageBox.information(self, UiStrings().NISs,
@ -604,7 +610,8 @@ class MediaManagerItem(QtWidgets.QWidget, RegistryProperties):
# Turn off the remote edit update message indicator
QtWidgets.QMessageBox.information(self, translate('OpenLP.MediaManagerItem', 'Invalid Service Item'),
translate('OpenLP.MediaManagerItem',
'You must select a %s service item.') % self.title)
'You must select a {title} '
'service item.').format(title=self.title))
def build_service_item(self, item=None, xml_version=False, remote=False, context=ServiceItemContext.Live):
"""

View File

@ -130,7 +130,7 @@ class Plugin(QtCore.QObject, RegistryProperties):
:param settings_tab_class: The class name of the plugin's settings tab.
:param version: Defaults to *None*, which means that the same version number is used as OpenLP's version number.
"""
log.debug('Plugin %s initialised' % name)
log.debug('Plugin {plugin} initialised'.format(plugin=name))
super(Plugin, self).__init__()
self.name = name
self.text_strings = {}
@ -154,11 +154,11 @@ class Plugin(QtCore.QObject, RegistryProperties):
# Append a setting for files in the mediamanager (note not all plugins
# which have a mediamanager need this).
if media_item_class is not None:
default_settings['%s/%s files' % (name, name)] = []
default_settings['{name}/{name} files'.format(name=name)] = []
# Add settings to the dict of all settings.
Settings.extend_default_settings(default_settings)
Registry().register_function('%s_add_service_item' % self.name, self.process_add_service_event)
Registry().register_function('%s_config_updated' % self.name, self.config_update)
Registry().register_function('{name}_add_service_item'.format(name=self.name), self.process_add_service_event)
Registry().register_function('{name}_config_updated'.format(name=self.name), self.config_update)
def check_pre_conditions(self):
"""
@ -256,7 +256,7 @@ class Plugin(QtCore.QObject, RegistryProperties):
"""
Generic Drag and drop handler triggered from service_manager.
"""
log.debug('process_add_service_event event called for plugin %s' % self.name)
log.debug('process_add_service_event event called for plugin {name}'.format(name=self.name))
if replace:
self.media_item.on_add_edit_click()
else:

View File

@ -43,7 +43,7 @@ class PluginManager(RegistryMixin, OpenLPMixin, RegistryProperties):
super(PluginManager, self).__init__(parent)
self.log_info('Plugin manager Initialising')
self.base_path = os.path.abspath(AppLocation.get_directory(AppLocation.PluginsDir))
self.log_debug('Base path %s ' % self.base_path)
self.log_debug('Base path {path}'.format(path=self.base_path))
self.plugins = []
self.log_info('Plugin manager Initialised')
@ -73,7 +73,7 @@ class PluginManager(RegistryMixin, OpenLPMixin, RegistryProperties):
"""
start_depth = len(os.path.abspath(self.base_path).split(os.sep))
present_plugin_dir = os.path.join(self.base_path, 'presentations')
self.log_debug('finding plugins in %s at depth %d' % (self.base_path, start_depth))
self.log_debug('finding plugins in {path} at depth {depth:d}'.format(path=self.base_path, depth=start_depth))
for root, dirs, files in os.walk(self.base_path):
for name in files:
if name.endswith('.py') and not name.startswith('__'):
@ -84,7 +84,9 @@ class PluginManager(RegistryMixin, OpenLPMixin, RegistryProperties):
break
module_name = name[:-3]
# import the modules
self.log_debug('Importing %s from %s. Depth %d' % (module_name, root, this_depth))
self.log_debug('Importing {name} from {root}. Depth {depth:d}'.format(name=module_name,
root=root,
depth=this_depth))
try:
# Use the "imp" library to try to get around a problem with the PyUNO library which
# monkey-patches the __import__ function to do some magic. This causes issues with our tests.
@ -93,21 +95,21 @@ class PluginManager(RegistryMixin, OpenLPMixin, RegistryProperties):
# Then load the module (do the actual import) using the details from find_module()
imp.load_module(module_name, fp, path_name, description)
except ImportError as e:
self.log_exception('Failed to import module %s on path %s: %s'
% (module_name, path, e.args[0]))
self.log_exception('Failed to import module {name} on path {path}: '
'{args}'.format(name=module_name, path=path, args=e.args[0]))
plugin_classes = Plugin.__subclasses__()
plugin_objects = []
for p in plugin_classes:
try:
plugin = p()
self.log_debug('Loaded plugin %s' % str(p))
self.log_debug('Loaded plugin {plugin}'.format(plugin=str(p)))
plugin_objects.append(plugin)
except TypeError:
self.log_exception('Failed to load plugin %s' % str(p))
self.log_exception('Failed to load plugin {plugin}'.format(plugin=str(p)))
plugins_list = sorted(plugin_objects, key=lambda plugin: plugin.weight)
for plugin in plugins_list:
if plugin.check_pre_conditions():
self.log_debug('Plugin %s active' % str(plugin.name))
self.log_debug('Plugin {plugin} active'.format(plugin=str(plugin.name)))
plugin.set_status()
else:
plugin.status = PluginStatus.Disabled
@ -175,10 +177,11 @@ class PluginManager(RegistryMixin, OpenLPMixin, RegistryProperties):
Loop through all the plugins and give them an opportunity to initialise themselves.
"""
for plugin in self.plugins:
self.log_info('initialising plugins %s in a %s state' % (plugin.name, plugin.is_active()))
self.log_info('initialising plugins {plugin} in a {state} state'.format(plugin=plugin.name,
state=plugin.is_active()))
if plugin.is_active():
plugin.initialise()
self.log_info('Initialisation Complete for %s ' % plugin.name)
self.log_info('Initialisation Complete for {plugin}'.format(plugin=plugin.name))
def finalise_plugins(self):
"""
@ -187,7 +190,7 @@ class PluginManager(RegistryMixin, OpenLPMixin, RegistryProperties):
for plugin in self.plugins:
if plugin.is_active():
plugin.finalise()
self.log_info('Finalisation Complete for %s ' % plugin.name)
self.log_info('Finalisation Complete for {plugin}'.format(plugin=plugin.name))
def get_plugin_by_name(self, name):
"""

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

@ -107,7 +107,7 @@ class Renderer(OpenLPMixin, RegistryMixin, RegistryProperties):
:param theme_name: The theme name
"""
self.log_debug("_set_theme with theme %s" % theme_name)
self.log_debug("_set_theme with theme {theme}".format(theme=theme_name))
if theme_name not in self._theme_dimensions:
theme_data = self.theme_manager.get_theme_data(theme_name)
main_rect = self.get_main_rectangle(theme_data)
@ -183,7 +183,7 @@ class Renderer(OpenLPMixin, RegistryMixin, RegistryProperties):
:param item_theme_name: The item theme's name.
"""
self.log_debug("set_item_theme with theme %s" % item_theme_name)
self.log_debug("set_item_theme with theme {theme}".format(theme=item_theme_name))
self._set_theme(item_theme_name)
self.item_theme_name = item_theme_name
@ -317,7 +317,7 @@ class Renderer(OpenLPMixin, RegistryMixin, RegistryProperties):
self.width = screen_size.width()
self.height = screen_size.height()
self.screen_ratio = self.height / self.width
self.log_debug('_calculate default %s, %f' % (screen_size, self.screen_ratio))
self.log_debug('_calculate default {size}, {ratio:f}'.format(size=screen_size, ratio=self.screen_ratio))
# 90% is start of footer
self.footer_start = int(self.height * 0.90)
@ -354,7 +354,7 @@ class Renderer(OpenLPMixin, RegistryMixin, RegistryProperties):
:param rect_main: The main text block.
:param rect_footer: The footer text block.
"""
self.log_debug('_set_text_rectangle %s , %s' % (rect_main, rect_footer))
self.log_debug('_set_text_rectangle {main} , {footer}'.format(main=rect_main, footer=rect_footer))
self._rect = rect_main
self._rect_footer = rect_footer
self.page_width = self._rect.width()
@ -370,6 +370,7 @@ class Renderer(OpenLPMixin, RegistryMixin, RegistryProperties):
self.web.resize(self.page_width, self.page_height)
self.web_frame = self.web.page().mainFrame()
# Adjust width and height to account for shadow. outline done in css.
# TODO: Verify before converting to python3 strings
html = """<!DOCTYPE html><html><head><script>
function show_text(newtext) {
var main = document.getElementById('main');
@ -518,7 +519,8 @@ class Renderer(OpenLPMixin, RegistryMixin, RegistryProperties):
:param text: The text to check. It may contain HTML tags.
"""
self.web_frame.evaluateJavaScript('show_text("%s")' % text.replace('\\', '\\\\').replace('\"', '\\\"'))
self.web_frame.evaluateJavaScript('show_text'
'("{text}")'.format(text=text.replace('\\', '\\\\').replace('\"', '\\\"')))
return self.web_frame.contentsSize().height() <= self.empty_height

View File

@ -78,7 +78,7 @@ class ScreenList(object):
``number``
The number of the screen, which size has changed.
"""
log.info('screen_resolution_changed %d' % number)
log.info('screen_resolution_changed {number:d}'.format(number=number))
for screen in self.screen_list:
if number == screen['number']:
new_screen = {
@ -105,7 +105,7 @@ class ScreenList(object):
"""
# Do not log at start up.
if changed_screen != -1:
log.info('screen_count_changed %d' % self.desktop.screenCount())
log.info('screen_count_changed {count:d}'.format(count=self.desktop.screenCount()))
# Remove unplugged screens.
for screen in copy.deepcopy(self.screen_list):
if screen['number'] == self.desktop.screenCount():
@ -132,9 +132,11 @@ class ScreenList(object):
"""
screen_list = []
for screen in self.screen_list:
screen_name = '%s %d' % (translate('OpenLP.ScreenList', 'Screen'), screen['number'] + 1)
screen_name = '{name} {number:d}'.format(name=translate('OpenLP.ScreenList', 'Screen'),
number=screen['number'] + 1)
if screen['primary']:
screen_name = '%s (%s)' % (screen_name, translate('OpenLP.ScreenList', 'primary'))
screen_name = '{name} ({primary})'.format(name=screen_name,
primary=translate('OpenLP.ScreenList', 'primary'))
screen_list.append(screen_name)
return screen_list
@ -152,7 +154,7 @@ class ScreenList(object):
'size': PyQt5.QtCore.QRect(0, 0, 1024, 768)
}
"""
log.info('Screen %d found with resolution %s' % (screen['number'], screen['size']))
log.info('Screen {number:d} found with resolution {size}'.format(number=screen['number'], size=screen['size']))
if screen['primary']:
self.current = screen
self.override = copy.deepcopy(self.current)
@ -165,7 +167,7 @@ class ScreenList(object):
:param number: The screen number (int).
"""
log.info('remove_screen %d' % number)
log.info('remove_screen {number:d}'.forma(number=number))
for screen in self.screen_list:
if screen['number'] == number:
self.screen_list.remove(screen)
@ -189,7 +191,7 @@ class ScreenList(object):
:param number: The screen number (int).
"""
log.debug('set_current_display %s' % number)
log.debug('set_current_display {number}'.format(number=number))
if number + 1 > self.display_count:
self.current = self.screen_list[0]
else:

View File

@ -62,9 +62,10 @@ class SearchEdit(QtWidgets.QLineEdit):
right_padding = self.clear_button.width() + frame_width
if hasattr(self, 'menu_button'):
left_padding = self.menu_button.width()
stylesheet = 'QLineEdit { padding-left: %spx; padding-right: %spx; } ' % (left_padding, right_padding)
stylesheet = 'QLineEdit {{ padding-left:{left}px; padding-right: {right}px; }} '.format(left=left_padding,
right=right_padding)
else:
stylesheet = 'QLineEdit { padding-right: %spx; } ' % right_padding
stylesheet = 'QLineEdit {{ padding-right: {right}px; }} '.format(right=right_padding)
self.setStyleSheet(stylesheet)
msz = self.minimumSizeHint()
self.setMinimumSize(max(msz.width(), self.clear_button.width() + (frame_width * 2) + 2),

View File

@ -247,7 +247,7 @@ class ServiceItem(RegistryProperties):
self.renderer.set_item_theme(self.theme)
self.theme_data, self.main, self.footer = self.renderer.pre_render()
if self.service_item_type == ServiceItemType.Text:
log.debug('Formatting slides: %s' % self.title)
log.debug('Formatting slides: {title}'.format(title=self.title))
# Save rendered pages to this dict. In the case that a slide is used twice we can use the pages saved to
# the dict instead of rendering them again.
previous_pages = {}
@ -270,7 +270,7 @@ class ServiceItem(RegistryProperties):
elif self.service_item_type == ServiceItemType.Image or self.service_item_type == ServiceItemType.Command:
pass
else:
log.error('Invalid value renderer: %s' % self.service_item_type)
log.error('Invalid value renderer: {item}'.format(item=self.service_item_type))
self.title = clean_tags(self.title)
# The footer should never be None, but to be compatible with a few
# nightly builds between 1.9.4 and 1.9.5, we have to correct this to
@ -325,7 +325,8 @@ class ServiceItem(RegistryProperties):
self.service_item_type = ServiceItemType.Command
# If the item should have a display title but this frame doesn't have one, we make one up
if self.is_capable(ItemCapabilities.HasDisplayTitle) and not display_title:
display_title = translate('OpenLP.ServiceItem', '[slide %d]') % (len(self._raw_frames) + 1)
display_title = translate('OpenLP.ServiceItem',
'[slide {frame:d}]').format(frame=len(self._raw_frames) + 1)
# Update image path to match servicemanager location if file was loaded from service
if image and not self.has_original_files and self.name == 'presentations':
file_location = os.path.join(path, file_name)
@ -334,6 +335,8 @@ class ServiceItem(RegistryProperties):
file_location_hash, ntpath.basename(image))
self._raw_frames.append({'title': file_name, 'image': image, 'path': path,
'display_title': display_title, 'notes': notes})
if self.is_capable(ItemCapabilities.HasThumbnails):
self.image_manager.add_image(image, ImageSource.CommandPlugins, '#000000')
self._new_item()
def get_service_repr(self, lite_save):
@ -390,7 +393,7 @@ class ServiceItem(RegistryProperties):
:param path: Defaults to *None*. This is the service manager path for things which have their files saved
with them or None when the saved service is lite and the original file paths need to be preserved.
"""
log.debug('set_from_service called with path %s' % path)
log.debug('set_from_service called with path {path}'.format(path=path))
header = service_item['serviceitem']['header']
self.title = header['title']
self.name = header['name']
@ -606,11 +609,13 @@ class ServiceItem(RegistryProperties):
start = None
end = None
if self.start_time != 0:
start = translate('OpenLP.ServiceItem', '<strong>Start</strong>: %s') % \
str(datetime.timedelta(seconds=self.start_time))
time = str(datetime.timedelta(seconds=self.start_time))
start = translate('OpenLP.ServiceItem',
'<strong>Start</strong>: {start}').format(start=time)
if self.media_length != 0:
end = translate('OpenLP.ServiceItem', '<strong>Length</strong>: %s') % \
str(datetime.timedelta(seconds=self.media_length // 1000))
length = str(datetime.timedelta(seconds=self.media_length // 1000))
end = translate('OpenLP.ServiceItem', '<strong>Length</strong>: {length}').format(length=length)
if not start and not end:
return ''
elif start and not end:
@ -618,7 +623,7 @@ class ServiceItem(RegistryProperties):
elif not start and end:
return end
else:
return '%s <br>%s' % (start, end)
return '{start} <br>{end}'.format(start=start, end=end)
def update_theme(self, theme):
"""

View File

@ -135,4 +135,4 @@ class SettingsTab(QtWidgets.QWidget, RegistryProperties):
"""
Tab has just been made visible to the user
"""
self.tab_visited = True
pass

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):
@ -407,7 +427,7 @@ class ThemeXML(object):
try:
theme_xml = objectify.fromstring(xml)
except etree.XMLSyntaxError:
log.exception('Invalid xml %s', xml)
log.exception('Invalid xml {text}'.format(text=xml))
return
xml_iter = theme_xml.getiterator()
for element in xml_iter:
@ -493,6 +513,7 @@ class ThemeXML(object):
theme_strings = []
for key in dir(self):
if key[0:1] != '_':
# TODO: Verify spacing format before converting to python3 string
theme_strings.append('%30s: %s' % (key, getattr(self, key)))
return '\n'.join(theme_strings)
@ -512,6 +533,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

@ -165,7 +165,7 @@ def create_button(parent, name, **kwargs):
kwargs.setdefault('icon', ':/services/service_down.png')
kwargs.setdefault('tooltip', translate('OpenLP.Ui', 'Move selection down one position.'))
else:
log.warning('The role "%s" is not defined in create_push_button().', role)
log.warning('The role "{role}" is not defined in create_push_button().'.format(role=role))
if kwargs.pop('btn_class', '') == 'toolbutton':
button = QtWidgets.QToolButton(parent)
else:
@ -183,7 +183,7 @@ def create_button(parent, name, **kwargs):
button.clicked.connect(kwargs.pop('click'))
for key in list(kwargs.keys()):
if key not in ['text', 'icon', 'tooltip', 'click']:
log.warning('Parameter %s was not consumed in create_button().', key)
log.warning('Parameter {key} was not consumed in create_button().'.format(key=key))
return button
@ -270,7 +270,7 @@ def create_action(parent, name, **kwargs):
action.triggered.connect(kwargs.pop('triggers'))
for key in list(kwargs.keys()):
if key not in ['text', 'icon', 'tooltip', 'statustip', 'checked', 'can_shortcuts', 'category', 'triggers']:
log.warning('Parameter %s was not consumed in create_action().' % key)
log.warning('Parameter {key} was not consumed in create_action().'.format(key=key))
return action

View File

@ -133,37 +133,37 @@ def get_web_page(url, header=None, update_openlp=False):
time.sleep(0.1)
try:
page = urllib.request.urlopen(req, timeout=CONNECTION_TIMEOUT)
log.debug('Downloaded page {}'.format(page.geturl()))
log.debug('Downloaded page {text}'.format(text=page.geturl()))
break
except urllib.error.URLError as err:
log.exception('URLError on {}'.format(url))
log.exception('URLError: {}'.format(err.reason))
log.exception('URLError on {text}'.format(text=url))
log.exception('URLError: {text}'.format(text=err.reason))
page = None
if retries > CONNECTION_RETRIES:
raise
except socket.timeout:
log.exception('Socket timeout: {}'.format(url))
log.exception('Socket timeout: {text}'.format(text=url))
page = None
if retries > CONNECTION_RETRIES:
raise
except socket.gaierror:
log.exception('Socket gaierror: {}'.format(url))
log.exception('Socket gaierror: {text}'.format(text=url))
page = None
if retries > CONNECTION_RETRIES:
raise
except ConnectionRefusedError:
log.exception('ConnectionRefused: {}'.format(url))
log.exception('ConnectionRefused: {text}'.format(text=url))
page = None
if retries > CONNECTION_RETRIES:
raise
break
except ConnectionError:
log.exception('Connection error: {}'.format(url))
log.exception('Connection error: {text}'.format(text=url))
page = None
if retries > CONNECTION_RETRIES:
raise
except HTTPException:
log.exception('HTTPException error: {}'.format(url))
log.exception('HTTPException error: {text}'.format(text=url))
page = None
if retries > CONNECTION_RETRIES:
raise
@ -173,7 +173,7 @@ def get_web_page(url, header=None, update_openlp=False):
if update_openlp:
Registry().get('application').process_events()
if not page:
log.exception('{} could not be downloaded'.format(url))
log.exception('{text} could not be downloaded'.format(text=url))
return None
log.debug(page)
return page

View File

@ -27,7 +27,7 @@ It is based on a QTableWidget but represents its contents in list form.
from PyQt5 import QtCore, QtGui, QtWidgets
from openlp.core.common import RegistryProperties, Settings
from openlp.core.lib import ImageSource, ServiceItem
from openlp.core.lib import ImageSource, ItemCapabilities, ServiceItem
class ListPreviewWidget(QtWidgets.QTableWidget, RegistryProperties):
@ -152,14 +152,16 @@ class ListPreviewWidget(QtWidgets.QTableWidget, RegistryProperties):
else:
label.setScaledContents(True)
if self.service_item.is_command():
pixmap = QtGui.QPixmap(frame['image'])
pixmap.setDevicePixelRatio(label.devicePixelRatio())
label.setPixmap(pixmap)
if self.service_item.is_capable(ItemCapabilities.HasThumbnails):
image = self.image_manager.get_image(frame['image'], ImageSource.CommandPlugins)
pixmap = QtGui.QPixmap.fromImage(image)
else:
pixmap = QtGui.QPixmap(frame['image'])
else:
image = self.image_manager.get_image(frame['path'], ImageSource.ImagePlugin)
pixmap = QtGui.QPixmap.fromImage(image)
pixmap.setDevicePixelRatio(label.devicePixelRatio())
label.setPixmap(pixmap)
pixmap.setDevicePixelRatio(label.devicePixelRatio())
label.setPixmap(pixmap)
slide_height = width // self.screen_ratio
# Setup and validate row height cap if in use.
max_img_row_height = Settings().value('advanced/slide max height')

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

@ -53,15 +53,9 @@ class Ui_PluginViewDialog(object):
self.plugin_info_layout.setObjectName('plugin_info_layout')
self.status_label = QtWidgets.QLabel(self.plugin_info_group_box)
self.status_label.setObjectName('status_label')
self.status_combo_box = QtWidgets.QComboBox(self.plugin_info_group_box)
self.status_combo_box.addItems(('', ''))
self.status_combo_box.setObjectName('status_combo_box')
self.plugin_info_layout.addRow(self.status_label, self.status_combo_box)
self.version_label = QtWidgets.QLabel(self.plugin_info_group_box)
self.version_label.setObjectName('version_label')
self.version_number_label = QtWidgets.QLabel(self.plugin_info_group_box)
self.version_number_label.setObjectName('version_number_label')
self.plugin_info_layout.addRow(self.version_label, self.version_number_label)
self.status_checkbox = QtWidgets.QCheckBox(self.plugin_info_group_box)
self.status_checkbox.setObjectName('status_checkbox')
self.plugin_info_layout.addRow(self.status_label, self.status_checkbox)
self.about_label = QtWidgets.QLabel(self.plugin_info_group_box)
self.about_label.setObjectName('about_label')
self.about_text_browser = QtWidgets.QTextBrowser(self.plugin_info_group_box)
@ -80,8 +74,6 @@ class Ui_PluginViewDialog(object):
"""
plugin_view_dialog.setWindowTitle(translate('OpenLP.PluginForm', 'Manage Plugins'))
self.plugin_info_group_box.setTitle(translate('OpenLP.PluginForm', 'Plugin Details'))
self.version_label.setText('%s:' % UiStrings().Version)
self.about_label.setText('%s:' % UiStrings().About)
self.status_label.setText(translate('OpenLP.PluginForm', 'Status:'))
self.status_combo_box.setItemText(0, translate('OpenLP.PluginForm', 'Active'))
self.status_combo_box.setItemText(1, translate('OpenLP.PluginForm', 'Inactive'))
self.status_checkbox.setText(translate('OpenLP.PluginForm', 'Active'))

View File

@ -49,7 +49,7 @@ class PluginForm(QtWidgets.QDialog, Ui_PluginViewDialog, RegistryProperties):
self._clear_details()
# Right, now let's put some signals and slots together!
self.plugin_list_widget.itemSelectionChanged.connect(self.on_plugin_list_widget_selection_changed)
self.status_combo_box.currentIndexChanged.connect(self.on_status_combo_box_changed)
self.status_checkbox.stateChanged.connect(self.on_status_checkbox_changed)
def load(self):
"""
@ -86,24 +86,23 @@ class PluginForm(QtWidgets.QDialog, Ui_PluginViewDialog, RegistryProperties):
"""
Clear the plugin details widgets
"""
self.status_combo_box.setCurrentIndex(-1)
self.version_number_label.setText('')
self.status_checkbox.setChecked(False)
self.about_text_browser.setHtml('')
self.status_combo_box.setEnabled(False)
self.status_checkbox.setEnabled(False)
def _set_details(self):
"""
Set the details of the currently selected plugin
"""
log.debug('PluginStatus: %s', str(self.active_plugin.status))
self.version_number_label.setText(self.active_plugin.version)
self.about_text_browser.setHtml(self.active_plugin.about())
self.programatic_change = True
status = PluginStatus.Active
if self.active_plugin.status == PluginStatus.Active:
status = PluginStatus.Inactive
self.status_combo_box.setCurrentIndex(status)
self.status_combo_box.setEnabled(True)
if self.active_plugin.status != PluginStatus.Disabled:
self.status_checkbox.setChecked(self.active_plugin.status == PluginStatus.Active)
self.status_checkbox.setEnabled(True)
else:
self.status_checkbox.setChecked(False)
self.status_checkbox.setEnabled(False)
self.programatic_change = False
def on_plugin_list_widget_selection_changed(self):
@ -116,22 +115,21 @@ class PluginForm(QtWidgets.QDialog, Ui_PluginViewDialog, RegistryProperties):
plugin_name_singular = self.plugin_list_widget.currentItem().text().split('(')[0][:-1]
self.active_plugin = None
for plugin in self.plugin_manager.plugins:
if plugin.status != PluginStatus.Disabled:
if plugin.name_strings['singular'] == plugin_name_singular:
self.active_plugin = plugin
break
if plugin.name_strings['singular'] == plugin_name_singular:
self.active_plugin = plugin
break
if self.active_plugin:
self._set_details()
else:
self._clear_details()
def on_status_combo_box_changed(self, status):
def on_status_checkbox_changed(self, status):
"""
If the status of a plugin is altered, apply the change
"""
if self.programatic_change or status == PluginStatus.Disabled:
if self.programatic_change or self.active_plugin is None:
return
if status == PluginStatus.Inactive:
if status:
self.application.set_busy_cursor()
self.active_plugin.toggle_status(PluginStatus.Active)
self.application.set_normal_cursor()

View File

@ -1323,9 +1323,10 @@ 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)
self.regenerate_service_items()
def regenerate_service_items(self, changed=False):
"""

View File

@ -60,7 +60,8 @@ class SettingsForm(QtWidgets.QDialog, Ui_SettingsDialog, RegistryProperties):
"""
Execute the form
"""
# load all the settings
# load all the
self.setting_list_widget.blockSignals(True)
self.setting_list_widget.clear()
while self.stacked_layout.count():
# take at 0 and the rest shuffle up.
@ -74,6 +75,7 @@ class SettingsForm(QtWidgets.QDialog, Ui_SettingsDialog, RegistryProperties):
if plugin.settings_tab:
self.insert_tab(plugin.settings_tab, plugin.is_active())
self.setting_list_widget.setCurrentRow(0)
self.setting_list_widget.blockSignals(False)
return QtWidgets.QDialog.exec(self)
def insert_tab(self, tab_widget, is_visible=True):
@ -177,6 +179,7 @@ class SettingsForm(QtWidgets.QDialog, Ui_SettingsDialog, RegistryProperties):
# Check that the title of the tab (i.e. plugin name) is the same as the data in the list item
if tab_widget.tab_title == list_item.data(QtCore.Qt.UserRole):
# Make the matching tab visible
tab_widget.tab_visited = True
self.stacked_layout.setCurrentIndex(tab_index)
self.stacked_layout.currentWidget().tab_visible()

View File

@ -1135,9 +1135,21 @@ class SlideController(DisplayController, RegistryProperties):
"""
self.log_debug('update_preview %s ' % self.screens.current['primary'])
if self.service_item and self.service_item.is_capable(ItemCapabilities.ProvidesOwnDisplay):
# Grab now, but try again in a couple of seconds if slide change is slow
QtCore.QTimer.singleShot(500, self.grab_maindisplay)
QtCore.QTimer.singleShot(2500, self.grab_maindisplay)
if self.is_live:
# If live, grab screen-cap of main display now
QtCore.QTimer.singleShot(500, self.grab_maindisplay)
# but take another in a couple of seconds in case slide change is slow
QtCore.QTimer.singleShot(2500, self.grab_maindisplay)
else:
# If not live, use the slide's thumbnail/icon instead
image_path = self.service_item.get_rendered_frame(self.selected_row)
if self.service_item.is_capable(ItemCapabilities.HasThumbnails):
image = self.image_manager.get_image(image_path, ImageSource.CommandPlugins)
self.slide_image = QtGui.QPixmap.fromImage(image)
else:
self.slide_image = QtGui.QPixmap(image_path)
self.slide_image.setDevicePixelRatio(self.main_window.devicePixelRatio())
self.slide_preview.setPixmap(self.slide_image)
else:
self.slide_image = self.display.preview()
self.slide_image.setDevicePixelRatio(self.main_window.devicePixelRatio())

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

@ -242,13 +242,13 @@ class PresentationDocument(object):
def convert_thumbnail(self, file, idx):
"""
Convert the slide image the application made to a standard 320x240 .png image.
Convert the slide image the application made to a scaled 360px height .png image.
"""
if self.check_thumbnails():
return
if os.path.isfile(file):
thumb_path = self.get_thumbnail_path(idx, False)
create_thumb(file, thumb_path, False, QtCore.QSize(320, 240))
create_thumb(file, thumb_path, False, QtCore.QSize(-1, 360))
def get_thumbnail_path(self, slide_no, check_exists):
"""

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

@ -111,6 +111,21 @@ class TestCategoryActionList(TestCase):
self.assertEqual(self.list.actions[0], (41, self.action2))
self.assertEqual(self.list.actions[1], (42, self.action1))
def iterator_test(self):
"""
Test the __iter__ and __next__ methods
"""
# GIVEN: The list including two actions
self.list.add(self.action1)
self.list.add(self.action2)
# WHEN: Iterating over the list
l = [a for a in self.list]
# THEN: Make sure they are returned in correct order
self.assertEquals(len(self.list), 2)
self.assertIs(l[0], self.action1)
self.assertIs(l[1], self.action2)
def remove_test(self):
"""
Test the remove() method

View File

@ -250,7 +250,7 @@ class TestLib(TestCase):
def create_thumb_with_size_test(self):
"""
Test the create_thumb() function
Test the create_thumb() function with a given size.
"""
# GIVEN: An image to create a thumb of.
image_path = os.path.join(TEST_PATH, 'church.jpg')
@ -270,7 +270,7 @@ class TestLib(TestCase):
# WHEN: Create the thumb.
icon = create_thumb(image_path, thumb_path, size=thumb_size)
# THEN: Check if the thumb was created.
# THEN: Check if the thumb was created and scaled to the given size.
self.assertTrue(os.path.exists(thumb_path), 'Test was not ran, because the thumb already exists')
self.assertIsInstance(icon, QtGui.QIcon, 'The icon should be a QIcon')
self.assertFalse(icon.isNull(), 'The icon should not be null')
@ -282,6 +282,194 @@ class TestLib(TestCase):
except:
pass
def create_thumb_no_size_test(self):
"""
Test the create_thumb() function with no size specified.
"""
# GIVEN: An image to create a thumb of.
image_path = os.path.join(TEST_PATH, 'church.jpg')
thumb_path = os.path.join(TEST_PATH, 'church_thumb.jpg')
expected_size = QtCore.QSize(63, 88)
# Remove the thumb so that the test actually tests if the thumb will be created. Maybe it was not deleted in the
# last test.
try:
os.remove(thumb_path)
except:
pass
# Only continue when the thumb does not exist.
self.assertFalse(os.path.exists(thumb_path), 'Test was not run, because the thumb already exists.')
# WHEN: Create the thumb.
icon = create_thumb(image_path, thumb_path)
# THEN: Check if the thumb was created, retaining its aspect ratio.
self.assertTrue(os.path.exists(thumb_path), 'Test was not ran, because the thumb already exists')
self.assertIsInstance(icon, QtGui.QIcon, 'The icon should be a QIcon')
self.assertFalse(icon.isNull(), 'The icon should not be null')
self.assertEqual(expected_size, QtGui.QImageReader(thumb_path).size(), 'The thumb should have the given size')
# Remove the thumb so that the test actually tests if the thumb will be created.
try:
os.remove(thumb_path)
except:
pass
def create_thumb_invalid_size_test(self):
"""
Test the create_thumb() function with invalid size specified.
"""
# GIVEN: An image to create a thumb of.
image_path = os.path.join(TEST_PATH, 'church.jpg')
thumb_path = os.path.join(TEST_PATH, 'church_thumb.jpg')
thumb_size = QtCore.QSize(-1, -1)
expected_size = QtCore.QSize(63, 88)
# Remove the thumb so that the test actually tests if the thumb will be created. Maybe it was not deleted in the
# last test.
try:
os.remove(thumb_path)
except:
pass
# Only continue when the thumb does not exist.
self.assertFalse(os.path.exists(thumb_path), 'Test was not run, because the thumb already exists.')
# WHEN: Create the thumb.
icon = create_thumb(image_path, thumb_path, size=thumb_size)
# THEN: Check if the thumb was created, retaining its aspect ratio.
self.assertTrue(os.path.exists(thumb_path), 'Test was not ran, because the thumb already exists')
self.assertIsInstance(icon, QtGui.QIcon, 'The icon should be a QIcon')
self.assertFalse(icon.isNull(), 'The icon should not be null')
self.assertEqual(expected_size, QtGui.QImageReader(thumb_path).size(), 'The thumb should have the given size')
# Remove the thumb so that the test actually tests if the thumb will be created.
try:
os.remove(thumb_path)
except:
pass
def create_thumb_width_only_test(self):
"""
Test the create_thumb() function with a size of only width specified.
"""
# GIVEN: An image to create a thumb of.
image_path = os.path.join(TEST_PATH, 'church.jpg')
thumb_path = os.path.join(TEST_PATH, 'church_thumb.jpg')
thumb_size = QtCore.QSize(100, -1)
expected_size = QtCore.QSize(100, 137)
# Remove the thumb so that the test actually tests if the thumb will be created. Maybe it was not deleted in the
# last test.
try:
os.remove(thumb_path)
except:
pass
# Only continue when the thumb does not exist.
self.assertFalse(os.path.exists(thumb_path), 'Test was not run, because the thumb already exists.')
# WHEN: Create the thumb.
icon = create_thumb(image_path, thumb_path, size=thumb_size)
# THEN: Check if the thumb was created, retaining its aspect ratio.
self.assertTrue(os.path.exists(thumb_path), 'Test was not ran, because the thumb already exists')
self.assertIsInstance(icon, QtGui.QIcon, 'The icon should be a QIcon')
self.assertFalse(icon.isNull(), 'The icon should not be null')
self.assertEqual(expected_size, QtGui.QImageReader(thumb_path).size(), 'The thumb should have the given size')
# Remove the thumb so that the test actually tests if the thumb will be created.
try:
os.remove(thumb_path)
except:
pass
def create_thumb_height_only_test(self):
"""
Test the create_thumb() function with a size of only height specified.
"""
# GIVEN: An image to create a thumb of.
image_path = os.path.join(TEST_PATH, 'church.jpg')
thumb_path = os.path.join(TEST_PATH, 'church_thumb.jpg')
thumb_size = QtCore.QSize(-1, 100)
expected_size = QtCore.QSize(72, 100)
# Remove the thumb so that the test actually tests if the thumb will be created. Maybe it was not deleted in the
# last test.
try:
os.remove(thumb_path)
except:
pass
# Only continue when the thumb does not exist.
self.assertFalse(os.path.exists(thumb_path), 'Test was not run, because the thumb already exists.')
# WHEN: Create the thumb.
icon = create_thumb(image_path, thumb_path, size=thumb_size)
# THEN: Check if the thumb was created, retaining its aspect ratio.
self.assertTrue(os.path.exists(thumb_path), 'Test was not ran, because the thumb already exists')
self.assertIsInstance(icon, QtGui.QIcon, 'The icon should be a QIcon')
self.assertFalse(icon.isNull(), 'The icon should not be null')
self.assertEqual(expected_size, QtGui.QImageReader(thumb_path).size(), 'The thumb should have the given size')
# Remove the thumb so that the test actually tests if the thumb will be created.
try:
os.remove(thumb_path)
except:
pass
def create_thumb_empty_img_test(self):
"""
Test the create_thumb() function with a size of only height specified.
"""
# GIVEN: An image to create a thumb of.
image_path = os.path.join(TEST_PATH, 'church.jpg')
thumb_path = os.path.join(TEST_PATH, 'church_thumb.jpg')
thumb_size = QtCore.QSize(-1, 100)
expected_size_1 = QtCore.QSize(88, 88)
expected_size_2 = QtCore.QSize(100, 100)
# Remove the thumb so that the test actually tests if the thumb will be created. Maybe it was not deleted in the
# last test.
try:
os.remove(thumb_path)
except:
pass
# Only continue when the thumb does not exist.
self.assertFalse(os.path.exists(thumb_path), 'Test was not run, because the thumb already exists.')
# WHEN: Create the thumb.
with patch('openlp.core.lib.QtGui.QImageReader.size') as mocked_size:
mocked_size.return_value = QtCore.QSize(0, 0)
icon = create_thumb(image_path, thumb_path, size=None)
# THEN: Check if the thumb was created with aspect ratio of 1.
self.assertTrue(os.path.exists(thumb_path), 'Test was not ran, because the thumb already exists')
self.assertIsInstance(icon, QtGui.QIcon, 'The icon should be a QIcon')
self.assertFalse(icon.isNull(), 'The icon should not be null')
self.assertEqual(expected_size_1, QtGui.QImageReader(thumb_path).size(), 'The thumb should have the given size')
# WHEN: Create the thumb.
with patch('openlp.core.lib.QtGui.QImageReader.size') as mocked_size:
mocked_size.return_value = QtCore.QSize(0, 0)
icon = create_thumb(image_path, thumb_path, size=thumb_size)
# THEN: Check if the thumb was created with aspect ratio of 1.
self.assertIsInstance(icon, QtGui.QIcon, 'The icon should be a QIcon')
self.assertFalse(icon.isNull(), 'The icon should not be null')
self.assertEqual(expected_size_2, QtGui.QImageReader(thumb_path).size(), 'The thumb should have the given size')
# Remove the thumb so that the test actually tests if the thumb will be created.
try:
os.remove(thumb_path)
except:
pass
def check_item_selected_true_test(self):
"""
Test that the check_item_selected() function returns True when there are selected indexes

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
@ -111,7 +112,7 @@ class TestPJLink(TestCase):
@patch.object(pjlink_test, 'projectorReceivedData')
def projector_process_lamp_test(self, mock_projectorReceivedData):
"""
Test setting lamp on/off and hours
Test status lamp on/off and hours
"""
# GIVEN: Test object
pjlink = pjlink_test
@ -128,7 +129,7 @@ class TestPJLink(TestCase):
@patch.object(pjlink_test, 'projectorReceivedData')
def projector_process_multiple_lamp_test(self, mock_projectorReceivedData):
"""
Test setting multiple lamp on/off and hours
Test status multiple lamp on/off and hours
"""
# GIVEN: Test object
pjlink = pjlink_test
@ -151,3 +152,101 @@ 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 status 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 status 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')
@patch.object(pjlink_test, 'projectorUpdateIcons')
def projector_process_avmt_closed_unmuted_test(self, mock_projectorReceivedData):
"""
Test avmt status shutter closed and audio muted
"""
# GIVEN: Test object
pjlink = pjlink_test
pjlink.shutter = False
pjlink.mute = True
# WHEN: Called with setting shutter closed and mute off
pjlink.process_avmt('11')
# THEN: Shutter should be True and mute should be False
self.assertTrue(pjlink.shutter, 'Shutter should have been set to closed')
self.assertFalse(pjlink.mute, 'Audio should be off')
@patch.object(pjlink_test, 'projectorUpdateIcons')
def projector_process_avmt_open_muted_test(self, mock_projectorReceivedData):
"""
Test avmt status shutter open and mute on
"""
# GIVEN: Test object
pjlink = pjlink_test
pjlink.shutter = True
pjlink.mute = False
# WHEN: Called with setting shutter closed and mute on
pjlink.process_avmt('21')
# THEN: Shutter should be closed and mute should be True
self.assertFalse(pjlink.shutter, 'Shutter should have been set to closed')
self.assertTrue(pjlink.mute, 'Audio should be off')
@patch.object(pjlink_test, 'projectorUpdateIcons')
def projector_process_avmt_open_unmuted_test(self, mock_projectorReceivedData):
"""
Test avmt status shutter open and mute off off
"""
# GIVEN: Test object
pjlink = pjlink_test
pjlink.shutter = True
pjlink.mute = True
# WHEN: Called with setting shutter to closed and mute on
pjlink.process_avmt('30')
# THEN: Shutter should be closed and mute should be True
self.assertFalse(pjlink.shutter, 'Shutter should have been set to open')
self.assertFalse(pjlink.mute, 'Audio should be on')
@patch.object(pjlink_test, 'projectorUpdateIcons')
def projector_process_avmt_closed_muted_test(self, mock_projectorReceivedData):
"""
Test avmt status shutter closed and mute off
"""
# GIVEN: Test object
pjlink = pjlink_test
pjlink.shutter = False
pjlink.mute = False
# WHEN: Called with setting shutter to closed and mute on
pjlink.process_avmt('31')
# THEN: Shutter should be closed and mute should be True
self.assertTrue(pjlink.shutter, 'Shutter should have been set to closed')
self.assertTrue(pjlink.mute, 'Audio should be on')

View File

@ -206,3 +206,17 @@ class TestProjectorDB(TestCase):
# THEN: __repr__ should return a proper string
self.assertEqual(str(manufacturer), '<Manufacturer(name="OpenLP Test")>',
'Manufacturer.__repr__() should have returned a proper representation string')
def model_repr_test(self):
"""
Test model class __repr__ text
"""
# GIVEN: Test object
model = Model()
# WHEN: Name is set
model.name = 'OpenLP Test'
# THEN: __repr__ should return a proper string
self.assertEqual(str(model), '<Model(name='"OpenLP Test"')>',
'Model.__repr__() should have returned a proper representation string')

View File

@ -244,14 +244,16 @@ class TestServiceItem(TestCase):
self.assertEqual(service_item.service_item_type, ServiceItemType.Command, 'It should be a Command')
self.assertEqual(service_item.get_frames()[0], frame, 'Frames should match')
@patch(u'openlp.core.lib.serviceitem.ServiceItem.image_manager')
@patch('openlp.core.lib.serviceitem.AppLocation.get_section_data_path')
def add_from_command_for_a_presentation_thumb_test(self, mocked_get_section_data_path):
def add_from_command_for_a_presentation_thumb_test(self, mocked_get_section_data_path, mocked_image_manager):
"""
Test the Service Item - adding a presentation, and updating the thumb path
Test the Service Item - adding a presentation, updating the thumb path & adding the thumb to image_manager
"""
# GIVEN: A service item, a mocked AppLocation and presentation data
mocked_get_section_data_path.return_value = os.path.join('mocked', 'section', 'path')
service_item = ServiceItem(None)
service_item.add_capability(ItemCapabilities.HasThumbnails)
service_item.has_original_files = False
service_item.name = 'presentations'
presentation_name = 'test.pptx'
@ -270,6 +272,7 @@ class TestServiceItem(TestCase):
# THEN: verify that it is setup as a Command and that the frame data matches
self.assertEqual(service_item.service_item_type, ServiceItemType.Command, 'It should be a Command')
self.assertEqual(service_item.get_frames()[0], frame, 'Frames should match')
self.assertEqual(1, mocked_image_manager.add_image.call_count, 'image_manager should be used')
def service_item_load_optical_media_from_service_test(self):
"""

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

@ -26,7 +26,7 @@ from PyQt5 import QtCore, QtGui
from unittest import TestCase
from openlp.core import Registry
from openlp.core.lib import ServiceItemAction
from openlp.core.lib import ImageSource, ServiceItemAction
from openlp.core.ui import SlideController, LiveController, PreviewController
from openlp.core.ui.slidecontroller import InfoLabel, WIDE_MENU, NON_TEXT_MENU
@ -713,6 +713,175 @@ class TestSlideController(TestCase):
slide_controller.theme_screen, slide_controller.blank_screen
])
@patch(u'openlp.core.ui.slidecontroller.SlideController.image_manager')
@patch(u'PyQt5.QtCore.QTimer.singleShot')
def update_preview_test_live(self, mocked_singleShot, mocked_image_manager):
"""
Test that the preview screen is updated with a screen grab for live service items
"""
# GIVEN: A mocked live service item, a mocked image_manager, a mocked Registry,
# and a slide controller with many mocks.
# Mocked Live Item
mocked_live_item = MagicMock()
mocked_live_item.get_rendered_frame.return_value = ''
mocked_live_item.is_capable = MagicMock()
mocked_live_item.is_capable.side_effect = [True, True]
# Mock image_manager
mocked_image_manager.get_image.return_value = QtGui.QImage()
# Mock Registry
Registry.create()
mocked_main_window = MagicMock()
Registry().register('main_window', mocked_main_window)
# Mock SlideController
slide_controller = SlideController(None)
slide_controller.service_item = mocked_live_item
slide_controller.is_live = True
slide_controller.log_debug = MagicMock()
slide_controller.selected_row = MagicMock()
slide_controller.screens = MagicMock()
slide_controller.screens.current = {'primary': ''}
slide_controller.display = MagicMock()
slide_controller.display.preview.return_value = QtGui.QImage()
slide_controller.grab_maindisplay = MagicMock()
slide_controller.slide_preview = MagicMock()
slide_controller.slide_count = 0
# WHEN: update_preview is called
slide_controller.update_preview()
# THEN: A screen_grab should have been called
self.assertEqual(0, slide_controller.slide_preview.setPixmap.call_count, 'setPixmap should not be called')
self.assertEqual(0, slide_controller.display.preview.call_count, 'display.preview() should not be called')
self.assertEqual(2, mocked_singleShot.call_count,
'Timer to grab_maindisplay should have been called 2 times')
self.assertEqual(0, mocked_image_manager.get_image.call_count, 'image_manager not be called')
@patch(u'openlp.core.ui.slidecontroller.SlideController.image_manager')
@patch(u'PyQt5.QtCore.QTimer.singleShot')
def update_preview_test_pres(self, mocked_singleShot, mocked_image_manager):
"""
Test that the preview screen is updated with the correct preview for presentation service items
"""
# GIVEN: A mocked presentation service item, a mocked image_manager, a mocked Registry,
# and a slide controller with many mocks.
# Mocked Presentation Item
mocked_pres_item = MagicMock()
mocked_pres_item.get_rendered_frame.return_value = ''
mocked_pres_item.is_capable = MagicMock()
mocked_pres_item.is_capable.side_effect = [True, True]
# Mock image_manager
mocked_image_manager.get_image.return_value = QtGui.QImage()
# Mock Registry
Registry.create()
mocked_main_window = MagicMock()
Registry().register('main_window', mocked_main_window)
# Mock SlideController
slide_controller = SlideController(None)
slide_controller.service_item = mocked_pres_item
slide_controller.is_live = False
slide_controller.log_debug = MagicMock()
slide_controller.selected_row = MagicMock()
slide_controller.screens = MagicMock()
slide_controller.screens.current = {'primary': ''}
slide_controller.display = MagicMock()
slide_controller.display.preview.return_value = QtGui.QImage()
slide_controller.grab_maindisplay = MagicMock()
slide_controller.slide_preview = MagicMock()
slide_controller.slide_count = 0
# WHEN: update_preview is called
slide_controller.update_preview()
# THEN: setPixmap and the image_manager should have been called
self.assertEqual(1, slide_controller.slide_preview.setPixmap.call_count, 'setPixmap should be called')
self.assertEqual(0, slide_controller.display.preview.call_count, 'display.preview() should not be called')
self.assertEqual(0, mocked_singleShot.call_count, 'Timer to grab_maindisplay should not be called')
self.assertEqual(1, mocked_image_manager.get_image.call_count, 'image_manager should be called')
@patch(u'openlp.core.ui.slidecontroller.SlideController.image_manager')
@patch(u'PyQt5.QtCore.QTimer.singleShot')
def update_preview_test_media(self, mocked_singleShot, mocked_image_manager):
"""
Test that the preview screen is updated with the correct preview for media service items
"""
# GIVEN: A mocked media service item, a mocked image_manager, a mocked Registry,
# and a slide controller with many mocks.
# Mocked Media Item
mocked_media_item = MagicMock()
mocked_media_item.get_rendered_frame.return_value = ''
mocked_media_item.is_capable = MagicMock()
mocked_media_item.is_capable.side_effect = [True, False]
# Mock image_manager
mocked_image_manager.get_image.return_value = QtGui.QImage()
# Mock Registry
Registry.create()
mocked_main_window = MagicMock()
Registry().register('main_window', mocked_main_window)
# Mock SlideController
slide_controller = SlideController(None)
slide_controller.service_item = mocked_media_item
slide_controller.is_live = False
slide_controller.log_debug = MagicMock()
slide_controller.selected_row = MagicMock()
slide_controller.screens = MagicMock()
slide_controller.screens.current = {'primary': ''}
slide_controller.display = MagicMock()
slide_controller.display.preview.return_value = QtGui.QImage()
slide_controller.grab_maindisplay = MagicMock()
slide_controller.slide_preview = MagicMock()
slide_controller.slide_count = 0
# WHEN: update_preview is called
slide_controller.update_preview()
# THEN: setPixmap should have been called
self.assertEqual(1, slide_controller.slide_preview.setPixmap.call_count, 'setPixmap should be called')
self.assertEqual(0, slide_controller.display.preview.call_count, 'display.preview() should not be called')
self.assertEqual(0, mocked_singleShot.call_count, 'Timer to grab_maindisplay should not be called')
self.assertEqual(0, mocked_image_manager.get_image.call_count, 'image_manager should not be called')
@patch(u'openlp.core.ui.slidecontroller.SlideController.image_manager')
@patch(u'PyQt5.QtCore.QTimer.singleShot')
def update_preview_test_image(self, mocked_singleShot, mocked_image_manager):
"""
Test that the preview screen is updated with the correct preview for image service items
"""
# GIVEN: A mocked image service item, a mocked image_manager, a mocked Registry,
# and a slide controller with many mocks.
# Mocked Image Item
mocked_img_item = MagicMock()
mocked_img_item.get_rendered_frame.return_value = ''
mocked_img_item.is_capable = MagicMock()
mocked_img_item.is_capable.side_effect = [False, True]
# Mock image_manager
mocked_image_manager.get_image.return_value = QtGui.QImage()
# Mock Registry
Registry.create()
mocked_main_window = MagicMock()
Registry().register('main_window', mocked_main_window)
# Mock SlideController
slide_controller = SlideController(None)
slide_controller.service_item = mocked_img_item
slide_controller.is_live = False
slide_controller.log_debug = MagicMock()
slide_controller.selected_row = MagicMock()
slide_controller.screens = MagicMock()
slide_controller.screens.current = {'primary': ''}
slide_controller.display = MagicMock()
slide_controller.display.preview.return_value = QtGui.QImage()
slide_controller.grab_maindisplay = MagicMock()
slide_controller.slide_preview = MagicMock()
slide_controller.slide_count = 0
# WHEN: update_preview is called
slide_controller.update_preview()
# THEN: setPixmap and display.preview should have been called
self.assertEqual(1, slide_controller.slide_preview.setPixmap.call_count, 'setPixmap should be called')
self.assertEqual(1, slide_controller.display.preview.call_count, 'display.preview() should be called')
self.assertEqual(0, mocked_singleShot.call_count, 'Timer to grab_maindisplay should not be called')
self.assertEqual(0, mocked_image_manager.get_image.call_count, 'image_manager should not be called')
class TestInfoLabel(TestCase):

View File

@ -0,0 +1,84 @@
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
###############################################################################
# OpenLP - Open Source Lyrics Projection #
# --------------------------------------------------------------------------- #
# Copyright (c) 2008-2016 OpenLP Developers #
# --------------------------------------------------------------------------- #
# This program is free software; you can redistribute it and/or modify it #
# under the terms of the GNU General Public License as published by the Free #
# Software Foundation; version 2 of the License. #
# #
# This program is distributed in the hope that it will be useful, but WITHOUT #
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
# more details. #
# #
# You should have received a copy of the GNU General Public License along #
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
###############################################################################
"""
Package to test the openlp.core.ui.ThemeTab package.
"""
from unittest import TestCase
from openlp.core.common import Registry
from openlp.core.ui.themestab import ThemesTab
from openlp.core.ui.settingsform import SettingsForm
from tests.helpers.testmixin import TestMixin
from tests.functional import MagicMock
class TestThemeTab(TestCase, TestMixin):
def setUp(self):
"""
Set up a few things for the tests
"""
Registry.create()
def test_creation(self):
"""
Test that Themes Tab is created.
"""
# GIVEN: A new Advanced Tab
settings_form = SettingsForm(None)
# WHEN: I create an advanced tab
themes_tab = ThemesTab(settings_form)
# THEN:
self.assertEqual("Themes", themes_tab.tab_title, 'The tab title should be Theme')
def test_save_triggers_processes_true(self):
"""
Test that the global theme event is triggered when the tab is visited.
"""
# GIVEN: A new Advanced Tab
settings_form = SettingsForm(None)
themes_tab = ThemesTab(settings_form)
Registry().register('renderer', MagicMock())
themes_tab.tab_visited = True
# WHEN: I change search as type check box
themes_tab.save()
# THEN: we should have two post save processed to run
self.assertEqual(1, len(settings_form.processes), 'One post save processes should be created')
def test_save_triggers_processes_false(self):
"""
Test that the global theme event is not triggered when the tab is not visited.
"""
# GIVEN: A new Advanced Tab
settings_form = SettingsForm(None)
themes_tab = ThemesTab(settings_form)
Registry().register('renderer', MagicMock())
themes_tab.tab_visited = False
# WHEN: I change search as type check box
themes_tab.save()
# THEN: we should have two post save processed to run
self.assertEqual(0, len(settings_form.processes), 'No post save processes should be created')

View File

@ -24,9 +24,11 @@ Package to test the openlp.core.ui.lib.listpreviewwidget package.
"""
from unittest import TestCase
from PyQt5 import QtGui
from openlp.core.common import Settings
from openlp.core.ui.lib.listpreviewwidget import ListPreviewWidget
from openlp.core.lib import ServiceItem
from openlp.core.lib import ImageSource, ServiceItem
from tests.functional import MagicMock, patch, call
@ -72,6 +74,53 @@ class TestListPreviewWidget(TestCase):
self.assertIsNotNone(list_preview_widget, 'The ListPreviewWidget object should not be None')
self.assertEquals(list_preview_widget.screen_ratio, 1, 'Should not be called')
@patch(u'openlp.core.ui.lib.listpreviewwidget.ListPreviewWidget.image_manager')
@patch(u'openlp.core.ui.lib.listpreviewwidget.ListPreviewWidget.resizeRowsToContents')
@patch(u'openlp.core.ui.lib.listpreviewwidget.ListPreviewWidget.setRowHeight')
def replace_service_item_test_thumbs(self, mocked_setRowHeight, mocked_resizeRowsToContents,
mocked_image_manager):
"""
Test that thubmails for different slides are loaded properly in replace_service_item.
"""
# GIVEN: A setting to adjust "Max height for non-text slides in slide controller",
# different ServiceItem(s), an ImageManager, and a ListPreviewWidget.
# Mock Settings().value('advanced/slide max height')
self.mocked_Settings_obj.value.return_value = 0
# Mock self.viewport().width()
self.mocked_viewport_obj.width.return_value = 200
# Mock Image service item
mocked_img_service_item = MagicMock()
mocked_img_service_item.is_text.return_value = False
mocked_img_service_item.is_media.return_value = False
mocked_img_service_item.is_command.return_value = False
mocked_img_service_item.is_capable.return_value = False
mocked_img_service_item.get_frames.return_value = [{'title': None, 'path': 'TEST1', 'image': 'FAIL'},
{'title': None, 'path': 'TEST2', 'image': 'FAIL'}]
# Mock Command service item
mocked_cmd_service_item = MagicMock()
mocked_cmd_service_item.is_text.return_value = False
mocked_cmd_service_item.is_media.return_value = False
mocked_cmd_service_item.is_command.return_value = True
mocked_cmd_service_item.is_capable.return_value = True
mocked_cmd_service_item.get_frames.return_value = [{'title': None, 'path': 'FAIL', 'image': 'TEST3'},
{'title': None, 'path': 'FAIL', 'image': 'TEST4'}]
# Mock image_manager
mocked_image_manager.get_image.return_value = QtGui.QImage()
# init ListPreviewWidget and load service item
list_preview_widget = ListPreviewWidget(None, 1)
# WHEN: replace_service_item is called
list_preview_widget.replace_service_item(mocked_img_service_item, 200, 0)
list_preview_widget.replace_service_item(mocked_cmd_service_item, 200, 0)
# THEN: The ImageManager should be called in the appriopriate manner for each service item.
self.assertEquals(mocked_image_manager.get_image.call_count, 4, 'Should be called once for each slide')
calls = [call('TEST1', ImageSource.ImagePlugin), call('TEST2', ImageSource.ImagePlugin),
call('TEST3', ImageSource.CommandPlugins), call('TEST4', ImageSource.CommandPlugins)]
mocked_image_manager.get_image.assert_has_calls(calls)
@patch(u'openlp.core.ui.lib.listpreviewwidget.ListPreviewWidget.resizeRowsToContents')
@patch(u'openlp.core.ui.lib.listpreviewwidget.ListPreviewWidget.setRowHeight')
def replace_recalculate_layout_test_text(self, mocked_setRowHeight, mocked_resizeRowsToContents):
@ -120,6 +169,7 @@ class TestListPreviewWidget(TestCase):
# Mock image service item
service_item = MagicMock()
service_item.is_text.return_value = False
service_item.is_capable.return_value = False
service_item.get_frames.return_value = [{'title': None, 'path': None, 'image': None},
{'title': None, 'path': None, 'image': None}]
# init ListPreviewWidget and load service item
@ -156,6 +206,7 @@ class TestListPreviewWidget(TestCase):
# Mock image service item
service_item = MagicMock()
service_item.is_text.return_value = False
service_item.is_capable.return_value = False
service_item.get_frames.return_value = [{'title': None, 'path': None, 'image': None},
{'title': None, 'path': None, 'image': None}]
# init ListPreviewWidget and load service item
@ -225,6 +276,7 @@ class TestListPreviewWidget(TestCase):
# Mock image service item
service_item = MagicMock()
service_item.is_text.return_value = False
service_item.is_capable.return_value = False
service_item.get_frames.return_value = [{'title': None, 'path': None, 'image': None},
{'title': None, 'path': None, 'image': None}]
# Mock self.cellWidget().children().setMaximumWidth()
@ -261,6 +313,7 @@ class TestListPreviewWidget(TestCase):
# Mock image service item
service_item = MagicMock()
service_item.is_text.return_value = False
service_item.is_capable.return_value = False
service_item.get_frames.return_value = [{'title': None, 'path': None, 'image': None},
{'title': None, 'path': None, 'image': None}]
# Mock self.cellWidget().children().setMaximumWidth()

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)