forked from openlp/openlp
- Merged trunk on 18.5.2016 20:57
This commit is contained in:
commit
165d8cd4ba
|
@ -222,10 +222,11 @@ class OpenLP(OpenLPMixin, QtWidgets.QApplication):
|
||||||
QtWidgets.QMessageBox.warning(None, translate('OpenLP', 'Backup'),
|
QtWidgets.QMessageBox.warning(None, translate('OpenLP', 'Backup'),
|
||||||
translate('OpenLP', 'Backup of the data folder failed!'))
|
translate('OpenLP', 'Backup of the data folder failed!'))
|
||||||
return
|
return
|
||||||
QtWidgets.QMessageBox.information(None, translate('OpenLP', 'Backup'),
|
message = translate('OpenLP',
|
||||||
translate('OpenLP',
|
'A backup of the data folder has been created'
|
||||||
'A backup of the data folder has been created at %s')
|
'at {text}').format(text=data_folder_backup_path)
|
||||||
% data_folder_backup_path)
|
QtWidgets.QMessageBox.information(None, translate('OpenLP', 'Backup'), message)
|
||||||
|
|
||||||
# Update the version in the settings
|
# Update the version in the settings
|
||||||
Settings().setValue('core/application version', openlp_version)
|
Settings().setValue('core/application version', openlp_version)
|
||||||
|
|
||||||
|
@ -257,7 +258,7 @@ class OpenLP(OpenLPMixin, QtWidgets.QApplication):
|
||||||
"""
|
"""
|
||||||
if event.type() == QtCore.QEvent.FileOpen:
|
if event.type() == QtCore.QEvent.FileOpen:
|
||||||
file_name = event.file()
|
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)
|
self.args.insert(0, file_name)
|
||||||
return True
|
return True
|
||||||
# Mac OS X should restore app window when user clicked on the OpenLP icon
|
# 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'))
|
logfile.setFormatter(logging.Formatter('%(asctime)s %(name)-55s %(levelname)-8s %(message)s'))
|
||||||
log.addHandler(logfile)
|
log.addHandler(logfile)
|
||||||
if log.isEnabledFor(logging.DEBUG):
|
if log.isEnabledFor(logging.DEBUG):
|
||||||
print('Logging to: %s' % filename)
|
print('Logging to: {name}'.format(name=filename))
|
||||||
|
|
||||||
|
|
||||||
def main(args=None):
|
def main(args=None):
|
||||||
|
@ -351,12 +352,12 @@ def main(args=None):
|
||||||
log.info('Running portable')
|
log.info('Running portable')
|
||||||
portable_settings_file = os.path.abspath(os.path.join(application_path, '..', '..', 'Data', 'OpenLP.ini'))
|
portable_settings_file = os.path.abspath(os.path.join(application_path, '..', '..', 'Data', 'OpenLP.ini'))
|
||||||
# Make this our settings file
|
# 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)
|
Settings.set_filename(portable_settings_file)
|
||||||
portable_settings = Settings()
|
portable_settings = Settings()
|
||||||
# Set our data path
|
# Set our data path
|
||||||
data_path = os.path.abspath(os.path.join(application_path, '..', '..', 'Data',))
|
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
|
# Point to our data path
|
||||||
portable_settings.setValue('advanced/data path', data_path)
|
portable_settings.setValue('advanced/data path', data_path)
|
||||||
portable_settings.setValue('advanced/is portable', True)
|
portable_settings.setValue('advanced/is portable', True)
|
||||||
|
|
|
@ -55,7 +55,9 @@ def trace_error_handler(logger):
|
||||||
"""
|
"""
|
||||||
log_string = "OpenLP Error trace"
|
log_string = "OpenLP Error trace"
|
||||||
for tb in traceback.extract_stack():
|
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)
|
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.
|
: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:
|
if not do_not_log:
|
||||||
log.debug('check_directory_exists %s' % directory)
|
log.debug('check_directory_exists {text}'.format(text=directory))
|
||||||
try:
|
try:
|
||||||
if not os.path.exists(directory):
|
if not os.path.exists(directory):
|
||||||
os.makedirs(directory)
|
os.makedirs(directory)
|
||||||
|
@ -202,13 +204,13 @@ def md5_hash(salt, data=None):
|
||||||
:param data: OPTIONAL Data to hash
|
:param data: OPTIONAL Data to hash
|
||||||
:returns: str
|
: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 = hashlib.new('md5')
|
||||||
hash_obj.update(salt)
|
hash_obj.update(salt)
|
||||||
if data:
|
if data:
|
||||||
hash_obj.update(data)
|
hash_obj.update(data)
|
||||||
hash_value = hash_obj.hexdigest()
|
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
|
return hash_value
|
||||||
|
|
||||||
|
|
||||||
|
@ -221,12 +223,12 @@ def qmd5_hash(salt, data=None):
|
||||||
:param data: OPTIONAL Data to hash
|
:param data: OPTIONAL Data to hash
|
||||||
:returns: str
|
: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 = QHash(QHash.Md5)
|
||||||
hash_obj.addData(salt)
|
hash_obj.addData(salt)
|
||||||
hash_obj.addData(data)
|
hash_obj.addData(data)
|
||||||
hash_value = hash_obj.result().toHex()
|
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()
|
return hash_value.data()
|
||||||
|
|
||||||
|
|
||||||
|
@ -283,7 +285,7 @@ def get_uno_command(connection_type='pipe'):
|
||||||
CONNECTION = '"--accept=pipe,name=openlp_pipe;urp;"'
|
CONNECTION = '"--accept=pipe,name=openlp_pipe;urp;"'
|
||||||
else:
|
else:
|
||||||
CONNECTION = '"--accept=socket,host=localhost,port=2002;urp;"'
|
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'):
|
def get_uno_instance(resolver, connection_type='pipe'):
|
||||||
|
@ -333,7 +335,7 @@ def delete_file(file_path_name):
|
||||||
os.remove(file_path_name)
|
os.remove(file_path_name)
|
||||||
return True
|
return True
|
||||||
except (IOError, OSError):
|
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
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
@ -345,9 +347,11 @@ def get_images_filter():
|
||||||
if not IMAGES_FILTER:
|
if not IMAGES_FILTER:
|
||||||
log.debug('Generating images filter.')
|
log.debug('Generating images filter.')
|
||||||
formats = list(map(bytes.decode, list(map(bytes, QtGui.QImageReader.supportedImageFormats()))))
|
formats = list(map(bytes.decode, list(map(bytes, QtGui.QImageReader.supportedImageFormats()))))
|
||||||
visible_formats = '(*.%s)' % '; *.'.join(formats)
|
visible_formats = '(*.{text})'.format(text='; *.'.join(formats))
|
||||||
actual_formats = '(*.%s)' % ' *.'.join(formats)
|
actual_formats = '(*.{text})'.format(text=' *.'.join(formats))
|
||||||
IMAGES_FILTER = '%s %s %s' % (translate('OpenLP', 'Image Files'), visible_formats, actual_formats)
|
IMAGES_FILTER = '{text} {visible} {actual}'.format(text=translate('OpenLP', 'Image Files'),
|
||||||
|
visible=visible_formats,
|
||||||
|
actual=actual_formats)
|
||||||
return IMAGES_FILTER
|
return IMAGES_FILTER
|
||||||
|
|
||||||
|
|
||||||
|
@ -385,7 +389,7 @@ def check_binary_exists(program_path):
|
||||||
:param program_path:The full path to the binary to check.
|
:param program_path:The full path to the binary to check.
|
||||||
:return: program output to be parsed
|
: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:
|
try:
|
||||||
# Setup startupinfo options for check_output to avoid console popping up on windows
|
# Setup startupinfo options for check_output to avoid console popping up on windows
|
||||||
if is_win():
|
if is_win():
|
||||||
|
@ -399,5 +403,5 @@ def check_binary_exists(program_path):
|
||||||
except Exception:
|
except Exception:
|
||||||
trace_error_handler(log)
|
trace_error_handler(log)
|
||||||
runlog = ''
|
runlog = ''
|
||||||
log.debug('check_output returned: %s' % runlog)
|
log.debug('check_output returned: {text}'.format(text=runlog))
|
||||||
return runlog
|
return runlog
|
||||||
|
|
|
@ -114,7 +114,7 @@ class CategoryActionList(object):
|
||||||
if item[1] == action:
|
if item[1] == action:
|
||||||
self.actions.remove(item)
|
self.actions.remove(item)
|
||||||
return
|
return
|
||||||
raise ValueError('Action "%s" does not exist.' % action)
|
raise ValueError('Action "{action}" does not exist.'.format(action=action))
|
||||||
|
|
||||||
|
|
||||||
class CategoryList(object):
|
class CategoryList(object):
|
||||||
|
@ -138,7 +138,7 @@ class CategoryList(object):
|
||||||
for category in self.categories:
|
for category in self.categories:
|
||||||
if category.name == key:
|
if category.name == key:
|
||||||
return category
|
return category
|
||||||
raise KeyError('Category "%s" does not exist.' % key)
|
raise KeyError('Category "{keY}" does not exist.'.format(key=key))
|
||||||
|
|
||||||
def __len__(self):
|
def __len__(self):
|
||||||
"""
|
"""
|
||||||
|
@ -203,7 +203,7 @@ class CategoryList(object):
|
||||||
if category.name == name:
|
if category.name == name:
|
||||||
self.categories.remove(category)
|
self.categories.remove(category)
|
||||||
return
|
return
|
||||||
raise ValueError('Category "%s" does not exist.' % name)
|
raise ValueError('Category "{name}" does not exist.'.format(name=name))
|
||||||
|
|
||||||
|
|
||||||
class ActionList(object):
|
class ActionList(object):
|
||||||
|
@ -272,8 +272,9 @@ class ActionList(object):
|
||||||
actions.append(action)
|
actions.append(action)
|
||||||
ActionList.shortcut_map[shortcuts[1]] = actions
|
ActionList.shortcut_map[shortcuts[1]] = actions
|
||||||
else:
|
else:
|
||||||
log.warning('Shortcut "%s" is removed from "%s" because another action already uses this shortcut.' %
|
log.warning('Shortcut "{shortcut}" is removed from "{action}" because another '
|
||||||
(shortcuts[1], action.objectName()))
|
'action already uses this shortcut.'.format(shortcut=shortcuts[1],
|
||||||
|
action=action.objectName()))
|
||||||
shortcuts.remove(shortcuts[1])
|
shortcuts.remove(shortcuts[1])
|
||||||
# Check the primary shortcut.
|
# Check the primary shortcut.
|
||||||
existing_actions = ActionList.shortcut_map.get(shortcuts[0], [])
|
existing_actions = ActionList.shortcut_map.get(shortcuts[0], [])
|
||||||
|
@ -283,8 +284,9 @@ class ActionList(object):
|
||||||
actions.append(action)
|
actions.append(action)
|
||||||
ActionList.shortcut_map[shortcuts[0]] = actions
|
ActionList.shortcut_map[shortcuts[0]] = actions
|
||||||
else:
|
else:
|
||||||
log.warning('Shortcut "%s" is removed from "%s" because another action already uses this shortcut.' %
|
log.warning('Shortcut "{shortcut}" is removed from "{action}" '
|
||||||
(shortcuts[0], action.objectName()))
|
'because another action already uses this shortcut.'.format(shortcut=shortcuts[0],
|
||||||
|
action=action.objectName()))
|
||||||
shortcuts.remove(shortcuts[0])
|
shortcuts.remove(shortcuts[0])
|
||||||
action.setShortcuts([QtGui.QKeySequence(shortcut) for shortcut in shortcuts])
|
action.setShortcuts([QtGui.QKeySequence(shortcut) for shortcut in shortcuts])
|
||||||
|
|
||||||
|
|
|
@ -68,7 +68,7 @@ class LanguageManager(object):
|
||||||
"""
|
"""
|
||||||
Find all available language files in this OpenLP install
|
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))
|
trans_dir = QtCore.QDir(AppLocation.get_directory(AppLocation.LanguageDir))
|
||||||
file_names = trans_dir.entryList(['*.qm'], QtCore.QDir.Files, QtCore.QDir.Name)
|
file_names = trans_dir.entryList(['*.qm'], QtCore.QDir.Files, QtCore.QDir.Name)
|
||||||
# Remove qm files from the list which start with "qt_".
|
# Remove qm files from the list which start with "qt_".
|
||||||
|
@ -93,7 +93,7 @@ class LanguageManager(object):
|
||||||
"""
|
"""
|
||||||
language = Settings().value('core/language')
|
language = Settings().value('core/language')
|
||||||
language = str(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):
|
if re.match(r'[[].*[]]', language):
|
||||||
LanguageManager.auto_language = True
|
LanguageManager.auto_language = True
|
||||||
language = re.sub(r'[\[\]]', '', language)
|
language = re.sub(r'[\[\]]', '', language)
|
||||||
|
@ -117,9 +117,9 @@ class LanguageManager(object):
|
||||||
qm_list = LanguageManager.get_qm_list()
|
qm_list = LanguageManager.get_qm_list()
|
||||||
language = str(qm_list[action_name])
|
language = str(qm_list[action_name])
|
||||||
if LanguageManager.auto_language:
|
if LanguageManager.auto_language:
|
||||||
language = '[%s]' % language
|
language = '[{language}]'.format(language=language)
|
||||||
Settings().setValue('core/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:
|
if message:
|
||||||
QtWidgets.QMessageBox.information(None,
|
QtWidgets.QMessageBox.information(None,
|
||||||
translate('OpenLP.LanguageManager', 'Language'),
|
translate('OpenLP.LanguageManager', 'Language'),
|
||||||
|
@ -136,7 +136,8 @@ class LanguageManager(object):
|
||||||
for counter, qmf in enumerate(qm_files):
|
for counter, qmf in enumerate(qm_files):
|
||||||
reg_ex = QtCore.QRegExp("^.*i18n/(.*).qm")
|
reg_ex = QtCore.QRegExp("^.*i18n/(.*).qm")
|
||||||
if reg_ex.exactMatch(qmf):
|
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
|
LanguageManager.__qm_list__['%#2i %s' % (counter + 1, LanguageManager.language_name(qmf))] = name
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
|
|
@ -49,12 +49,13 @@ class OpenLPMixin(object):
|
||||||
Code to added debug wrapper to work on called functions within a decorated class.
|
Code to added debug wrapper to work on called functions within a decorated class.
|
||||||
"""
|
"""
|
||||||
def wrapped(*args, **kwargs):
|
def wrapped(*args, **kwargs):
|
||||||
parent.logger.debug("Entering %s" % func.__name__)
|
parent.logger.debug("Entering {function}".format(function=func.__name__))
|
||||||
try:
|
try:
|
||||||
return func(*args, **kwargs)
|
return func(*args, **kwargs)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
if parent.logger.getEffectiveLevel() <= logging.ERROR:
|
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
|
raise e
|
||||||
return wrapped
|
return wrapped
|
||||||
|
|
||||||
|
|
|
@ -71,8 +71,8 @@ class Registry(object):
|
||||||
else:
|
else:
|
||||||
if not self.initialising:
|
if not self.initialising:
|
||||||
trace_error_handler(log)
|
trace_error_handler(log)
|
||||||
log.error('Service %s not found in list' % key)
|
log.error('Service {key} not found in list'.format(key=key))
|
||||||
raise KeyError('Service %s not found in list' % key)
|
raise KeyError('Service {key} not found in list'.format(key=key))
|
||||||
|
|
||||||
def register(self, key, reference):
|
def register(self, key, reference):
|
||||||
"""
|
"""
|
||||||
|
@ -83,8 +83,8 @@ class Registry(object):
|
||||||
"""
|
"""
|
||||||
if key in self.service_list:
|
if key in self.service_list:
|
||||||
trace_error_handler(log)
|
trace_error_handler(log)
|
||||||
log.error('Duplicate service exception %s' % key)
|
log.error('Duplicate service exception {key}'.format(key=key))
|
||||||
raise KeyError('Duplicate service exception %s' % key)
|
raise KeyError('Duplicate service exception {key}'.format(key=key))
|
||||||
else:
|
else:
|
||||||
self.service_list[key] = reference
|
self.service_list[key] = reference
|
||||||
|
|
||||||
|
@ -140,8 +140,8 @@ class Registry(object):
|
||||||
except TypeError:
|
except TypeError:
|
||||||
# Who has called me can help in debugging
|
# Who has called me can help in debugging
|
||||||
trace_error_handler(log)
|
trace_error_handler(log)
|
||||||
log.exception('Exception for function %s', function)
|
log.exception('Exception for function {function}'.format(function=function))
|
||||||
else:
|
else:
|
||||||
trace_error_handler(log)
|
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
|
return results
|
||||||
|
|
|
@ -487,16 +487,16 @@ class Settings(QtCore.QSettings):
|
||||||
# Do NOT do this anywhere else!
|
# Do NOT do this anywhere else!
|
||||||
settings = QtCore.QSettings(self.fileName(), Settings.IniFormat)
|
settings = QtCore.QSettings(self.fileName(), Settings.IniFormat)
|
||||||
settings.beginGroup(plugin.settings_section)
|
settings.beginGroup(plugin.settings_section)
|
||||||
if settings.contains('%s count' % plugin.name):
|
if settings.contains('{name} count'.format(name=plugin.name)):
|
||||||
# Get the count.
|
# 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:
|
if list_count:
|
||||||
for counter in range(list_count):
|
for counter in range(list_count):
|
||||||
# The keys were named e. g.: "image 0"
|
# 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:
|
if item:
|
||||||
files_list.append(item)
|
files_list.append(item)
|
||||||
settings.remove('%s %d' % (plugin.name, counter))
|
settings.remove('{name} {counter:d}'.format(name=plugin.name, counter=counter))
|
||||||
settings.remove('%s count' % plugin.name)
|
settings.remove('{name} count'.format(name=plugin.name))
|
||||||
settings.endGroup()
|
settings.endGroup()
|
||||||
return files_list
|
return files_list
|
||||||
|
|
|
@ -81,6 +81,7 @@ class UiStrings(object):
|
||||||
self.Export = translate('OpenLP.Ui', 'Export')
|
self.Export = translate('OpenLP.Ui', 'Export')
|
||||||
self.File = translate('OpenLP.Ui', 'File')
|
self.File = translate('OpenLP.Ui', 'File')
|
||||||
self.FileNotFound = translate('OpenLP.Ui', 'File Not Found')
|
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.FileNotFoundMessage = translate('OpenLP.Ui', 'File %s not found.\nPlease try selecting it individually.')
|
||||||
self.FontSizePtUnit = translate('OpenLP.Ui', 'pt', 'Abbreviated font pointsize unit')
|
self.FontSizePtUnit = translate('OpenLP.Ui', 'pt', 'Abbreviated font pointsize unit')
|
||||||
self.Help = translate('OpenLP.Ui', 'Help')
|
self.Help = translate('OpenLP.Ui', 'Help')
|
||||||
|
@ -111,8 +112,8 @@ class UiStrings(object):
|
||||||
self.NISs = translate('OpenLP.Ui', 'No Item Selected', 'Singular')
|
self.NISs = translate('OpenLP.Ui', 'No Item Selected', 'Singular')
|
||||||
self.NISp = translate('OpenLP.Ui', 'No Items Selected', 'Plural')
|
self.NISp = translate('OpenLP.Ui', 'No Items Selected', 'Plural')
|
||||||
self.OLP = translate('OpenLP.Ui', 'OpenLP')
|
self.OLP = translate('OpenLP.Ui', 'OpenLP')
|
||||||
self.OLPV2 = "%s %s" % (self.OLP, "2")
|
self.OLPV2 = "{name} {version}".format(name=self.OLP, version="2")
|
||||||
self.OLPV2x = "%s %s" % (self.OLP, "2.4")
|
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.OpenLPStart = translate('OpenLP.Ui', 'OpenLP is already running. Do you wish to continue?')
|
||||||
self.OpenService = translate('OpenLP.Ui', 'Open service.')
|
self.OpenService = translate('OpenLP.Ui', 'Open service.')
|
||||||
self.PlaySlidesInLoop = translate('OpenLP.Ui', 'Play Slides in Loop')
|
self.PlaySlidesInLoop = translate('OpenLP.Ui', 'Play Slides in Loop')
|
||||||
|
@ -140,6 +141,7 @@ class UiStrings(object):
|
||||||
self.Split = translate('OpenLP.Ui', 'Optional &Split')
|
self.Split = translate('OpenLP.Ui', 'Optional &Split')
|
||||||
self.SplitToolTip = translate('OpenLP.Ui',
|
self.SplitToolTip = translate('OpenLP.Ui',
|
||||||
'Split a slide into two only if it does not fit on the screen as one slide.')
|
'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.StartTimeCode = translate('OpenLP.Ui', 'Start %s')
|
||||||
self.StopPlaySlidesInLoop = translate('OpenLP.Ui', 'Stop Play Slides in Loop')
|
self.StopPlaySlidesInLoop = translate('OpenLP.Ui', 'Stop Play Slides in Loop')
|
||||||
self.StopPlaySlidesToEnd = translate('OpenLP.Ui', 'Stop Play Slides to End')
|
self.StopPlaySlidesToEnd = translate('OpenLP.Ui', 'Stop Play Slides to End')
|
||||||
|
@ -153,3 +155,4 @@ class UiStrings(object):
|
||||||
self.Version = translate('OpenLP.Ui', 'Version')
|
self.Version = translate('OpenLP.Ui', 'Version')
|
||||||
self.View = translate('OpenLP.Ui', 'View')
|
self.View = translate('OpenLP.Ui', 'View')
|
||||||
self.ViewMode = translate('OpenLP.Ui', 'View Mode')
|
self.ViewMode = translate('OpenLP.Ui', 'View Mode')
|
||||||
|
self.Video = translate('OpenLP.Ui', 'Video')
|
||||||
|
|
|
@ -44,9 +44,10 @@ class VersionThread(QtCore.QThread):
|
||||||
log.debug('Version thread - run')
|
log.debug('Version thread - run')
|
||||||
app_version = get_application_version()
|
app_version = get_application_version()
|
||||||
version = check_latest_version(app_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'])):
|
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():
|
def get_application_version():
|
||||||
|
@ -91,7 +92,7 @@ def get_application_version():
|
||||||
if tree_revision == tag_revision:
|
if tree_revision == tag_revision:
|
||||||
full_version = tag_version.strip()
|
full_version = tag_version.strip()
|
||||||
else:
|
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:
|
else:
|
||||||
# We're not running the development version, let's use the file.
|
# We're not running the development version, let's use the file.
|
||||||
file_path = AppLocation.get_directory(AppLocation.VersionDir)
|
file_path = AppLocation.get_directory(AppLocation.VersionDir)
|
||||||
|
@ -113,9 +114,10 @@ def get_application_version():
|
||||||
'build': bits[1] if len(bits) > 1 else None
|
'build': bits[1] if len(bits) > 1 else None
|
||||||
}
|
}
|
||||||
if APPLICATION_VERSION['build']:
|
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:
|
else:
|
||||||
log.info('Openlp version %s' % APPLICATION_VERSION['version'])
|
log.info('Openlp version {version}'.format(version=APPLICATION_VERSION['version']))
|
||||||
return APPLICATION_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')
|
req = urllib.request.Request('http://www.openlp.org/files/dev_version.txt')
|
||||||
else:
|
else:
|
||||||
req = urllib.request.Request('http://www.openlp.org/files/version.txt')
|
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(),
|
req.add_header('User-Agent', 'OpenLP/{version} {system}/{release}; '.format(version=current_version['full'],
|
||||||
platform.release()))
|
system=platform.system(),
|
||||||
|
release=platform.release()))
|
||||||
remote_version = None
|
remote_version = None
|
||||||
retries = 0
|
retries = 0
|
||||||
while True:
|
while True:
|
||||||
|
|
|
@ -55,9 +55,13 @@ class ImageSource(object):
|
||||||
|
|
||||||
``Theme``
|
``Theme``
|
||||||
This says, that the image is used by a 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
|
ImagePlugin = 1
|
||||||
Theme = 2
|
Theme = 2
|
||||||
|
CommandPlugins = 3
|
||||||
|
|
||||||
|
|
||||||
class MediaType(object):
|
class MediaType(object):
|
||||||
|
@ -97,7 +101,7 @@ def get_text_file_string(text_file):
|
||||||
file_handle.seek(0)
|
file_handle.seek(0)
|
||||||
content = file_handle.read()
|
content = file_handle.read()
|
||||||
except (IOError, UnicodeError):
|
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:
|
finally:
|
||||||
if file_handle:
|
if file_handle:
|
||||||
file_handle.close()
|
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()
|
ext = os.path.splitext(thumb_path)[1].lower()
|
||||||
reader = QtGui.QImageReader(image_path)
|
reader = QtGui.QImageReader(image_path)
|
||||||
if size is None:
|
if size is None:
|
||||||
|
# No size given; use default height of 88
|
||||||
|
if reader.size().isEmpty():
|
||||||
|
ratio = 1
|
||||||
|
else:
|
||||||
ratio = reader.size().width() / reader.size().height()
|
ratio = reader.size().width() / reader.size().height()
|
||||||
reader.setScaledSize(QtCore.QSize(int(ratio * 88), 88))
|
reader.setScaledSize(QtCore.QSize(int(ratio * 88), 88))
|
||||||
else:
|
elif size.isValid():
|
||||||
|
# Complete size given
|
||||||
reader.setScaledSize(size)
|
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 = reader.read()
|
||||||
thumb.save(thumb_path, ext[1:])
|
thumb.save(thumb_path, ext[1:])
|
||||||
if not return_icon:
|
if not return_icon:
|
||||||
|
@ -300,6 +324,8 @@ def create_separated_list(string_list):
|
||||||
return ''
|
return ''
|
||||||
elif len(string_list) == 1:
|
elif len(string_list) == 1:
|
||||||
return string_list[0]
|
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:
|
elif len(string_list) == 2:
|
||||||
return translate('OpenLP.core.lib', '%s and %s',
|
return translate('OpenLP.core.lib', '%s and %s',
|
||||||
'Locale list separator: 2 items') % (string_list[0], string_list[1])
|
'Locale list separator: 2 items') % (string_list[0], string_list[1])
|
||||||
|
|
|
@ -68,9 +68,11 @@ def get_db_path(plugin_name, db_file_name=None):
|
||||||
:return: The path to the database as type str
|
:return: The path to the database as type str
|
||||||
"""
|
"""
|
||||||
if db_file_name is None:
|
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:
|
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):
|
def handle_db_error(plugin_name, db_file_name):
|
||||||
|
@ -82,10 +84,10 @@ def handle_db_error(plugin_name, db_file_name):
|
||||||
:return: None
|
:return: None
|
||||||
"""
|
"""
|
||||||
db_path = get_db_path(plugin_name, db_file_name)
|
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'),
|
critical_error_message_box(translate('OpenLP.Manager', 'Database Error'),
|
||||||
translate('OpenLP.Manager', 'OpenLP cannot load your database.\n\nDatabase: %s')
|
translate('OpenLP.Manager',
|
||||||
% db_path)
|
'OpenLP cannot load your database.\n\nDatabase: {db}').format(db=db_path))
|
||||||
|
|
||||||
|
|
||||||
def init_url(plugin_name, db_file_name=None):
|
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':
|
if db_type == 'sqlite':
|
||||||
db_url = get_db_path(plugin_name, db_file_name)
|
db_url = get_db_path(plugin_name, db_file_name)
|
||||||
else:
|
else:
|
||||||
db_url = '%s://%s:%s@%s/%s' % (db_type, urlquote(settings.value('db username')),
|
db_url = '{type}://{user}:{password}@{host}/{db}'.format(type=db_type,
|
||||||
urlquote(settings.value('db password')),
|
user=urlquote(settings.value('db username')),
|
||||||
urlquote(settings.value('db hostname')),
|
password=urlquote(settings.value('db password')),
|
||||||
urlquote(settings.value('db database')))
|
host=urlquote(settings.value('db hostname')),
|
||||||
|
db=urlquote(settings.value('db database')))
|
||||||
settings.endGroup()
|
settings.endGroup()
|
||||||
return db_url
|
return db_url
|
||||||
|
|
||||||
|
@ -157,10 +160,10 @@ def upgrade_db(url, upgrade):
|
||||||
return version, upgrade.__version__
|
return version, upgrade.__version__
|
||||||
version += 1
|
version += 1
|
||||||
try:
|
try:
|
||||||
while hasattr(upgrade, 'upgrade_%d' % version):
|
while hasattr(upgrade, 'upgrade_{version:d}'.format(version=version)):
|
||||||
log.debug('Running upgrade_%d', version)
|
log.debug('Running upgrade_{version:d}'.format(version=version))
|
||||||
try:
|
try:
|
||||||
upgrade_func = getattr(upgrade, 'upgrade_%d' % version)
|
upgrade_func = getattr(upgrade, 'upgrade_{version:d}'.format(version=version))
|
||||||
upgrade_func(session, metadata)
|
upgrade_func(session, metadata)
|
||||||
session.commit()
|
session.commit()
|
||||||
# Update the version number AFTER a commit so that we are sure the previous transaction happened
|
# 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()
|
session.commit()
|
||||||
version += 1
|
version += 1
|
||||||
except (SQLAlchemyError, DBAPIError):
|
except (SQLAlchemyError, DBAPIError):
|
||||||
log.exception('Could not run database upgrade script "upgrade_%s", upgrade process has been halted.',
|
log.exception('Could not run database upgrade script '
|
||||||
version)
|
'"upgrade_{version:d}", upgrade process has been halted.'.format(version=version))
|
||||||
break
|
break
|
||||||
except (SQLAlchemyError, DBAPIError):
|
except (SQLAlchemyError, DBAPIError):
|
||||||
version_meta = Metadata.populate(key='version', value=int(upgrade.__version__))
|
version_meta = Metadata.populate(key='version', value=int(upgrade.__version__))
|
||||||
|
@ -242,9 +245,10 @@ class Manager(object):
|
||||||
critical_error_message_box(
|
critical_error_message_box(
|
||||||
translate('OpenLP.Manager', 'Database Error'),
|
translate('OpenLP.Manager', 'Database Error'),
|
||||||
translate('OpenLP.Manager', 'The database being loaded was created in a more recent version of '
|
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 '
|
'OpenLP. The database is version {db_ver}, while OpenLP expects version {db_up}. '
|
||||||
'not be loaded.\n\nDatabase: %s') % (db_ver, up_ver, self.db_url)
|
'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
|
return
|
||||||
if not session:
|
if not session:
|
||||||
try:
|
try:
|
||||||
|
@ -460,7 +464,7 @@ class Manager(object):
|
||||||
raise
|
raise
|
||||||
except InvalidRequestError:
|
except InvalidRequestError:
|
||||||
self.session.rollback()
|
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
|
return False
|
||||||
except:
|
except:
|
||||||
self.session.rollback()
|
self.session.rollback()
|
||||||
|
|
|
@ -50,7 +50,8 @@ class FileDialog(QtWidgets.QFileDialog):
|
||||||
log.info('File not found. Attempting to unquote.')
|
log.info('File not found. Attempting to unquote.')
|
||||||
file = parse.unquote(file)
|
file = parse.unquote(file)
|
||||||
if not os.path.exists(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,
|
QtWidgets.QMessageBox.information(parent, UiStrings().FileNotFound,
|
||||||
UiStrings().FileNotFoundMessage % file)
|
UiStrings().FileNotFoundMessage % file)
|
||||||
continue
|
continue
|
||||||
|
|
|
@ -396,6 +396,7 @@ from openlp.core.lib.theme import BackgroundType, BackgroundGradientType, Vertic
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
# TODO: Verify where this is used before converting to python3
|
||||||
HTMLSRC = """
|
HTMLSRC = """
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
|
@ -564,13 +565,13 @@ def build_html(item, screen, is_live, background, image=None, plugins=None):
|
||||||
theme_data = item.theme_data
|
theme_data = item.theme_data
|
||||||
# Image generated and poked in
|
# Image generated and poked in
|
||||||
if background:
|
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:
|
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:
|
else:
|
||||||
bgimage_src = 'style="display:none;"'
|
bgimage_src = 'style="display:none;"'
|
||||||
if image:
|
if image:
|
||||||
image_src = 'src="data:image/png;base64,%s"' % image
|
image_src = 'src="data:image/png;base64,{image}"'.format(image=image)
|
||||||
else:
|
else:
|
||||||
image_src = 'style="display:none;"'
|
image_src = 'style="display:none;"'
|
||||||
css_additions = ''
|
css_additions = ''
|
||||||
|
@ -601,7 +602,7 @@ def webkit_version():
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
webkit_ver = float(QtWebKit.qWebKitVersion())
|
webkit_ver = float(QtWebKit.qWebKitVersion())
|
||||||
log.debug('Webkit version = %s' % webkit_ver)
|
log.debug('Webkit version = {version}'.format(version=webkit_ver))
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
webkit_ver = 0
|
webkit_ver = 0
|
||||||
return webkit_ver
|
return webkit_ver
|
||||||
|
@ -621,23 +622,25 @@ def build_background_css(item, width):
|
||||||
if theme.background_type == BackgroundType.to_string(BackgroundType.Transparent):
|
if theme.background_type == BackgroundType.to_string(BackgroundType.Transparent):
|
||||||
background = ''
|
background = ''
|
||||||
elif theme.background_type == BackgroundType.to_string(BackgroundType.Solid):
|
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:
|
else:
|
||||||
if theme.background_direction == BackgroundGradientType.to_string(BackgroundGradientType.Horizontal):
|
if theme.background_direction == BackgroundGradientType.to_string(BackgroundGradientType.Horizontal):
|
||||||
background = 'background: -webkit-gradient(linear, left top, left bottom, from(%s), to(%s)) fixed' \
|
background = 'background: -webkit-gradient(linear, left top, left bottom, from({start}), to({end})) ' \
|
||||||
% (theme.background_start_color, theme.background_end_color)
|
'fixed'.format(start=theme.background_start_color, end=theme.background_end_color)
|
||||||
elif theme.background_direction == BackgroundGradientType.to_string(BackgroundGradientType.LeftTop):
|
elif theme.background_direction == BackgroundGradientType.to_string(BackgroundGradientType.LeftTop):
|
||||||
background = 'background: -webkit-gradient(linear, left top, right bottom, from(%s), to(%s)) fixed' \
|
background = 'background: -webkit-gradient(linear, left top, right bottom, from({start}), to({end})) ' \
|
||||||
% (theme.background_start_color, theme.background_end_color)
|
'fixed'.format(start=theme.background_start_color, end=theme.background_end_color)
|
||||||
elif theme.background_direction == BackgroundGradientType.to_string(BackgroundGradientType.LeftBottom):
|
elif theme.background_direction == BackgroundGradientType.to_string(BackgroundGradientType.LeftBottom):
|
||||||
background = 'background: -webkit-gradient(linear, left bottom, right top, from(%s), to(%s)) fixed' \
|
background = 'background: -webkit-gradient(linear, left bottom, right top, from({start}), to({end})) ' \
|
||||||
% (theme.background_start_color, theme.background_end_color)
|
'fixed'.format(start=theme.background_start_color, end=theme.background_end_color)
|
||||||
elif theme.background_direction == BackgroundGradientType.to_string(BackgroundGradientType.Vertical):
|
elif theme.background_direction == BackgroundGradientType.to_string(BackgroundGradientType.Vertical):
|
||||||
background = 'background: -webkit-gradient(linear, left top, right top, from(%s), to(%s)) fixed' % \
|
background = 'background: -webkit-gradient(linear, left top, right top, from({start}), to({end})) ' \
|
||||||
(theme.background_start_color, theme.background_end_color)
|
'fixed'.format(start=theme.background_start_color, end=theme.background_end_color)
|
||||||
else:
|
else:
|
||||||
background = 'background: -webkit-gradient(radial, %s 50%%, 100, %s 50%%, %s, from(%s), to(%s)) fixed'\
|
background = 'background: -webkit-gradient(radial, {width} 50%, 100, {width} 50%, {width}, ' \
|
||||||
% (width, width, width, theme.background_start_color, theme.background_end_color)
|
'from({start}), to({end})) fixed'.format(width=width,
|
||||||
|
start=theme.background_start_color,
|
||||||
|
end=theme.background_end_color)
|
||||||
return background
|
return background
|
||||||
|
|
||||||
|
|
||||||
|
@ -647,6 +650,7 @@ def build_lyrics_css(item):
|
||||||
|
|
||||||
:param item: Service Item containing theme and location information
|
:param item: Service Item containing theme and location information
|
||||||
"""
|
"""
|
||||||
|
# TODO: Verify this before converting to python3
|
||||||
style = """
|
style = """
|
||||||
.lyricstable {
|
.lyricstable {
|
||||||
z-index: 5;
|
z-index: 5;
|
||||||
|
@ -669,12 +673,13 @@ def build_lyrics_css(item):
|
||||||
lyrics = ''
|
lyrics = ''
|
||||||
lyricsmain = ''
|
lyricsmain = ''
|
||||||
if theme_data and item.main:
|
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())
|
lyrics = build_lyrics_format_css(theme_data, item.main.width(), item.main.height())
|
||||||
lyricsmain += build_lyrics_outline_css(theme_data)
|
lyricsmain += build_lyrics_outline_css(theme_data)
|
||||||
if theme_data.font_main_shadow:
|
if theme_data.font_main_shadow:
|
||||||
lyricsmain += ' text-shadow: %s %spx %spx;' % \
|
lyricsmain += ' text-shadow: {theme} {shadow}px ' \
|
||||||
(theme_data.font_main_shadow_color, theme_data.font_main_shadow_size, theme_data.font_main_shadow_size)
|
'{shadow}px;'.format(theme=theme_data.font_main_shadow_color,
|
||||||
|
shadow=theme_data.font_main_shadow_size)
|
||||||
lyrics_css = style % (lyricstable, lyrics, lyricsmain)
|
lyrics_css = style % (lyricstable, lyrics, lyricsmain)
|
||||||
return lyrics_css
|
return lyrics_css
|
||||||
|
|
||||||
|
@ -689,7 +694,9 @@ def build_lyrics_outline_css(theme_data):
|
||||||
size = float(theme_data.font_main_outline_size) / 16
|
size = float(theme_data.font_main_outline_size) / 16
|
||||||
fill_color = theme_data.font_main_color
|
fill_color = theme_data.font_main_color
|
||||||
outline_color = theme_data.font_main_outline_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 ''
|
return ''
|
||||||
|
|
||||||
|
|
||||||
|
@ -715,13 +722,21 @@ def build_lyrics_format_css(theme_data, width, height):
|
||||||
padding_bottom = '0.5em'
|
padding_bottom = '0.5em'
|
||||||
else:
|
else:
|
||||||
padding_bottom = '0'
|
padding_bottom = '0'
|
||||||
lyrics = '%s word-wrap: break-word; ' \
|
lyrics = '{justify} word-wrap: break-word; ' \
|
||||||
'text-align: %s; vertical-align: %s; font-family: %s; ' \
|
'text-align: {align}; vertical-align: {valign}; font-family: {font}; ' \
|
||||||
'font-size: %spt; color: %s; line-height: %d%%; margin: 0;' \
|
'font-size: {size}pt; color: {color}; line-height: {line:d}%; margin: 0;' \
|
||||||
'padding: 0; padding-bottom: %s; padding-left: %spx; width: %spx; height: %spx; ' % \
|
'padding: 0; padding-bottom: {bottom}; padding-left: {left}px; width: {width}px; ' \
|
||||||
(justify, align, valign, theme_data.font_main_name, theme_data.font_main_size,
|
'height: {height}px; '.format(justify=justify,
|
||||||
theme_data.font_main_color, 100 + int(theme_data.font_main_line_adjustment), padding_bottom,
|
align=align,
|
||||||
left_margin, width, height)
|
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:
|
if theme_data.font_main_italics:
|
||||||
lyrics += 'font-style:italic; '
|
lyrics += 'font-style:italic; '
|
||||||
if theme_data.font_main_bold:
|
if theme_data.font_main_bold:
|
||||||
|
@ -737,20 +752,21 @@ def build_footer_css(item, height):
|
||||||
:param height:
|
:param height:
|
||||||
"""
|
"""
|
||||||
style = """
|
style = """
|
||||||
left: %spx;
|
left: {left}px;
|
||||||
bottom: %spx;
|
bottom: {bottom}px;
|
||||||
width: %spx;
|
width: {width}px;
|
||||||
font-family: %s;
|
font-family: {family};
|
||||||
font-size: %spt;
|
font-size: {size}pt;
|
||||||
color: %s;
|
color: {color};
|
||||||
text-align: left;
|
text-align: left;
|
||||||
white-space: %s;
|
white-space: {space};
|
||||||
"""
|
"""
|
||||||
theme = item.theme_data
|
theme = item.theme_data
|
||||||
if not theme or not item.footer:
|
if not theme or not item.footer:
|
||||||
return ''
|
return ''
|
||||||
bottom = height - int(item.footer.y()) - int(item.footer.height())
|
bottom = height - int(item.footer.y()) - int(item.footer.height())
|
||||||
whitespace = 'normal' if Settings().value('themes/wrap footer') else 'nowrap'
|
whitespace = 'normal' if Settings().value('themes/wrap footer') else 'nowrap'
|
||||||
lyrics_html = style % (item.footer.x(), bottom, item.footer.width(),
|
lyrics_html = style.format(left=item.footer.x(), bottom=bottom, width=item.footer.width(),
|
||||||
theme.font_footer_name, theme.font_footer_size, theme.font_footer_color, whitespace)
|
family=theme.font_footer_name, size=theme.font_footer_size,
|
||||||
|
color=theme.font_footer_color, space=whitespace)
|
||||||
return lyrics_html
|
return lyrics_html
|
||||||
|
|
|
@ -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.
|
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)]
|
image = self._cache[(path, source, width, height)]
|
||||||
if image.image is None:
|
if image.image is None:
|
||||||
self._conversion_queue.modify_priority(image, Priority.High)
|
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.
|
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)]
|
image = self._cache[(path, source, width, height)]
|
||||||
if image.image_bytes is None:
|
if image.image_bytes is None:
|
||||||
self._conversion_queue.modify_priority(image, Priority.Urgent)
|
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.
|
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:
|
if not (path, source, width, height) in self._cache:
|
||||||
image = Image(path, source, background, width, height)
|
image = Image(path, source, background, width, height)
|
||||||
self._cache[(path, source, width, height)] = image
|
self._cache[(path, source, width, height)] = image
|
||||||
|
|
|
@ -186,7 +186,7 @@ class MediaManagerItem(QtWidgets.QWidget, RegistryProperties):
|
||||||
for action in toolbar_actions:
|
for action in toolbar_actions:
|
||||||
if action[0] == StringContent.Preview:
|
if action[0] == StringContent.Preview:
|
||||||
self.toolbar.addSeparator()
|
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],
|
text=self.plugin.get_string(action[1])['title'], icon=action[2],
|
||||||
tooltip=self.plugin.get_string(action[1])['tooltip'],
|
tooltip=self.plugin.get_string(action[1])['tooltip'],
|
||||||
triggers=action[3])
|
triggers=action[3])
|
||||||
|
@ -200,7 +200,7 @@ class MediaManagerItem(QtWidgets.QWidget, RegistryProperties):
|
||||||
self.list_view.setSpacing(1)
|
self.list_view.setSpacing(1)
|
||||||
self.list_view.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
|
self.list_view.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
|
||||||
self.list_view.setAlternatingRowColors(True)
|
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
|
# Add to page_layout
|
||||||
self.page_layout.addWidget(self.list_view)
|
self.page_layout.addWidget(self.list_view)
|
||||||
# define and add the context menu
|
# define and add the context menu
|
||||||
|
@ -212,19 +212,22 @@ class MediaManagerItem(QtWidgets.QWidget, RegistryProperties):
|
||||||
triggers=self.on_edit_click)
|
triggers=self.on_edit_click)
|
||||||
create_widget_action(self.list_view, separator=True)
|
create_widget_action(self.list_view, separator=True)
|
||||||
create_widget_action(self.list_view,
|
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'],
|
text=self.plugin.get_string(StringContent.Preview)['title'],
|
||||||
icon=':/general/general_preview.png',
|
icon=':/general/general_preview.png',
|
||||||
can_shortcuts=True,
|
can_shortcuts=True,
|
||||||
triggers=self.on_preview_click)
|
triggers=self.on_preview_click)
|
||||||
create_widget_action(self.list_view,
|
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'],
|
text=self.plugin.get_string(StringContent.Live)['title'],
|
||||||
icon=':/general/general_live.png',
|
icon=':/general/general_live.png',
|
||||||
can_shortcuts=True,
|
can_shortcuts=True,
|
||||||
triggers=self.on_live_click)
|
triggers=self.on_live_click)
|
||||||
create_widget_action(self.list_view,
|
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,
|
can_shortcuts=True,
|
||||||
text=self.plugin.get_string(StringContent.Service)['title'],
|
text=self.plugin.get_string(StringContent.Service)['title'],
|
||||||
icon=':/general/general_add.png',
|
icon=':/general/general_add.png',
|
||||||
|
@ -232,7 +235,8 @@ class MediaManagerItem(QtWidgets.QWidget, RegistryProperties):
|
||||||
if self.has_delete_icon:
|
if self.has_delete_icon:
|
||||||
create_widget_action(self.list_view, separator=True)
|
create_widget_action(self.list_view, separator=True)
|
||||||
create_widget_action(self.list_view,
|
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'],
|
text=self.plugin.get_string(StringContent.Delete)['title'],
|
||||||
icon=':/general/general_delete.png',
|
icon=':/general/general_delete.png',
|
||||||
can_shortcuts=True, triggers=self.on_delete_click)
|
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,
|
files = FileDialog.getOpenFileNames(self, self.on_new_prompt,
|
||||||
Settings().value(self.settings_section + '/last directory'),
|
Settings().value(self.settings_section + '/last directory'),
|
||||||
self.on_new_file_masks)
|
self.on_new_file_masks)
|
||||||
log.info('New files(s) %s' % files)
|
log.info('New files(s) {files}'.format(files=files))
|
||||||
if files:
|
if files:
|
||||||
self.application.set_busy_cursor()
|
self.application.set_busy_cursor()
|
||||||
self.validate_and_load(files)
|
self.validate_and_load(files)
|
||||||
|
@ -333,7 +337,8 @@ class MediaManagerItem(QtWidgets.QWidget, RegistryProperties):
|
||||||
if not error_shown:
|
if not error_shown:
|
||||||
critical_error_message_box(translate('OpenLP.MediaManagerItem', 'Invalid File Type'),
|
critical_error_message_box(translate('OpenLP.MediaManagerItem', 'Invalid File Type'),
|
||||||
translate('OpenLP.MediaManagerItem',
|
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
|
error_shown = True
|
||||||
else:
|
else:
|
||||||
new_files.append(file_name)
|
new_files.append(file_name)
|
||||||
|
@ -375,7 +380,8 @@ class MediaManagerItem(QtWidgets.QWidget, RegistryProperties):
|
||||||
self.load_list(full_list, target_group)
|
self.load_list(full_list, target_group)
|
||||||
last_dir = os.path.split(files[0])[0]
|
last_dir = os.path.split(files[0])[0]
|
||||||
Settings().setValue(self.settings_section + '/last directory', last_dir)
|
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:
|
if duplicates_found:
|
||||||
critical_error_message_box(UiStrings().Duplicate,
|
critical_error_message_box(UiStrings().Duplicate,
|
||||||
translate('OpenLP.MediaManagerItem',
|
translate('OpenLP.MediaManagerItem',
|
||||||
|
@ -550,7 +556,7 @@ class MediaManagerItem(QtWidgets.QWidget, RegistryProperties):
|
||||||
# Is it possible to process multiple list items to generate
|
# Is it possible to process multiple list items to generate
|
||||||
# multiple service items?
|
# multiple service items?
|
||||||
if self.single_service_item:
|
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)
|
self.add_to_service(replace=self.remote_triggered)
|
||||||
else:
|
else:
|
||||||
items = self.list_view.selectedIndexes()
|
items = self.list_view.selectedIndexes()
|
||||||
|
@ -591,7 +597,7 @@ class MediaManagerItem(QtWidgets.QWidget, RegistryProperties):
|
||||||
translate('OpenLP.MediaManagerItem',
|
translate('OpenLP.MediaManagerItem',
|
||||||
'You must select one or more items.'))
|
'You must select one or more items.'))
|
||||||
else:
|
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()
|
service_item = self.service_manager.get_service_item()
|
||||||
if not service_item:
|
if not service_item:
|
||||||
QtWidgets.QMessageBox.information(self, UiStrings().NISs,
|
QtWidgets.QMessageBox.information(self, UiStrings().NISs,
|
||||||
|
@ -604,7 +610,8 @@ class MediaManagerItem(QtWidgets.QWidget, RegistryProperties):
|
||||||
# Turn off the remote edit update message indicator
|
# Turn off the remote edit update message indicator
|
||||||
QtWidgets.QMessageBox.information(self, translate('OpenLP.MediaManagerItem', 'Invalid Service Item'),
|
QtWidgets.QMessageBox.information(self, translate('OpenLP.MediaManagerItem', 'Invalid Service Item'),
|
||||||
translate('OpenLP.MediaManagerItem',
|
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):
|
def build_service_item(self, item=None, xml_version=False, remote=False, context=ServiceItemContext.Live):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -130,7 +130,7 @@ class Plugin(QtCore.QObject, RegistryProperties):
|
||||||
:param settings_tab_class: The class name of the plugin's settings tab.
|
: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.
|
: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__()
|
super(Plugin, self).__init__()
|
||||||
self.name = name
|
self.name = name
|
||||||
self.text_strings = {}
|
self.text_strings = {}
|
||||||
|
@ -154,11 +154,11 @@ class Plugin(QtCore.QObject, RegistryProperties):
|
||||||
# Append a setting for files in the mediamanager (note not all plugins
|
# Append a setting for files in the mediamanager (note not all plugins
|
||||||
# which have a mediamanager need this).
|
# which have a mediamanager need this).
|
||||||
if media_item_class is not None:
|
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.
|
# Add settings to the dict of all settings.
|
||||||
Settings.extend_default_settings(default_settings)
|
Settings.extend_default_settings(default_settings)
|
||||||
Registry().register_function('%s_add_service_item' % self.name, self.process_add_service_event)
|
Registry().register_function('{name}_add_service_item'.format(name=self.name), self.process_add_service_event)
|
||||||
Registry().register_function('%s_config_updated' % self.name, self.config_update)
|
Registry().register_function('{name}_config_updated'.format(name=self.name), self.config_update)
|
||||||
|
|
||||||
def check_pre_conditions(self):
|
def check_pre_conditions(self):
|
||||||
"""
|
"""
|
||||||
|
@ -256,7 +256,7 @@ class Plugin(QtCore.QObject, RegistryProperties):
|
||||||
"""
|
"""
|
||||||
Generic Drag and drop handler triggered from service_manager.
|
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:
|
if replace:
|
||||||
self.media_item.on_add_edit_click()
|
self.media_item.on_add_edit_click()
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -43,7 +43,7 @@ class PluginManager(RegistryMixin, OpenLPMixin, RegistryProperties):
|
||||||
super(PluginManager, self).__init__(parent)
|
super(PluginManager, self).__init__(parent)
|
||||||
self.log_info('Plugin manager Initialising')
|
self.log_info('Plugin manager Initialising')
|
||||||
self.base_path = os.path.abspath(AppLocation.get_directory(AppLocation.PluginsDir))
|
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.plugins = []
|
||||||
self.log_info('Plugin manager Initialised')
|
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))
|
start_depth = len(os.path.abspath(self.base_path).split(os.sep))
|
||||||
present_plugin_dir = os.path.join(self.base_path, 'presentations')
|
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 root, dirs, files in os.walk(self.base_path):
|
||||||
for name in files:
|
for name in files:
|
||||||
if name.endswith('.py') and not name.startswith('__'):
|
if name.endswith('.py') and not name.startswith('__'):
|
||||||
|
@ -84,7 +84,9 @@ class PluginManager(RegistryMixin, OpenLPMixin, RegistryProperties):
|
||||||
break
|
break
|
||||||
module_name = name[:-3]
|
module_name = name[:-3]
|
||||||
# import the modules
|
# 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:
|
try:
|
||||||
# Use the "imp" library to try to get around a problem with the PyUNO library which
|
# 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.
|
# 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()
|
# Then load the module (do the actual import) using the details from find_module()
|
||||||
imp.load_module(module_name, fp, path_name, description)
|
imp.load_module(module_name, fp, path_name, description)
|
||||||
except ImportError as e:
|
except ImportError as e:
|
||||||
self.log_exception('Failed to import module %s on path %s: %s'
|
self.log_exception('Failed to import module {name} on path {path}: '
|
||||||
% (module_name, path, e.args[0]))
|
'{args}'.format(name=module_name, path=path, args=e.args[0]))
|
||||||
plugin_classes = Plugin.__subclasses__()
|
plugin_classes = Plugin.__subclasses__()
|
||||||
plugin_objects = []
|
plugin_objects = []
|
||||||
for p in plugin_classes:
|
for p in plugin_classes:
|
||||||
try:
|
try:
|
||||||
plugin = p()
|
plugin = p()
|
||||||
self.log_debug('Loaded plugin %s' % str(p))
|
self.log_debug('Loaded plugin {plugin}'.format(plugin=str(p)))
|
||||||
plugin_objects.append(plugin)
|
plugin_objects.append(plugin)
|
||||||
except TypeError:
|
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)
|
plugins_list = sorted(plugin_objects, key=lambda plugin: plugin.weight)
|
||||||
for plugin in plugins_list:
|
for plugin in plugins_list:
|
||||||
if plugin.check_pre_conditions():
|
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()
|
plugin.set_status()
|
||||||
else:
|
else:
|
||||||
plugin.status = PluginStatus.Disabled
|
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.
|
Loop through all the plugins and give them an opportunity to initialise themselves.
|
||||||
"""
|
"""
|
||||||
for plugin in self.plugins:
|
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():
|
if plugin.is_active():
|
||||||
plugin.initialise()
|
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):
|
def finalise_plugins(self):
|
||||||
"""
|
"""
|
||||||
|
@ -187,7 +190,7 @@ class PluginManager(RegistryMixin, OpenLPMixin, RegistryProperties):
|
||||||
for plugin in self.plugins:
|
for plugin in self.plugins:
|
||||||
if plugin.is_active():
|
if plugin.is_active():
|
||||||
plugin.finalise()
|
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):
|
def get_plugin_by_name(self, name):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -297,7 +297,11 @@ PJLINK_ERST_STATUS = {'0': ERROR_STRING[E_OK],
|
||||||
PJLINK_POWR_STATUS = {'0': S_STANDBY,
|
PJLINK_POWR_STATUS = {'0': S_STANDBY,
|
||||||
'1': S_ON,
|
'1': S_ON,
|
||||||
'2': S_COOLDOWN,
|
'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'),
|
PJLINK_DEFAULT_SOURCES = {'1': translate('OpenLP.DB', 'RGB'),
|
||||||
'2': translate('OpenLP.DB', 'Video'),
|
'2': translate('OpenLP.DB', 'Video'),
|
||||||
|
|
|
@ -107,7 +107,7 @@ class Renderer(OpenLPMixin, RegistryMixin, RegistryProperties):
|
||||||
|
|
||||||
:param theme_name: The theme name
|
: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:
|
if theme_name not in self._theme_dimensions:
|
||||||
theme_data = self.theme_manager.get_theme_data(theme_name)
|
theme_data = self.theme_manager.get_theme_data(theme_name)
|
||||||
main_rect = self.get_main_rectangle(theme_data)
|
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.
|
: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._set_theme(item_theme_name)
|
||||||
self.item_theme_name = 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.width = screen_size.width()
|
||||||
self.height = screen_size.height()
|
self.height = screen_size.height()
|
||||||
self.screen_ratio = self.height / self.width
|
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
|
# 90% is start of footer
|
||||||
self.footer_start = int(self.height * 0.90)
|
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_main: The main text block.
|
||||||
:param rect_footer: The footer 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 = rect_main
|
||||||
self._rect_footer = rect_footer
|
self._rect_footer = rect_footer
|
||||||
self.page_width = self._rect.width()
|
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.resize(self.page_width, self.page_height)
|
||||||
self.web_frame = self.web.page().mainFrame()
|
self.web_frame = self.web.page().mainFrame()
|
||||||
# Adjust width and height to account for shadow. outline done in css.
|
# 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>
|
html = """<!DOCTYPE html><html><head><script>
|
||||||
function show_text(newtext) {
|
function show_text(newtext) {
|
||||||
var main = document.getElementById('main');
|
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.
|
: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
|
return self.web_frame.contentsSize().height() <= self.empty_height
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -78,7 +78,7 @@ class ScreenList(object):
|
||||||
``number``
|
``number``
|
||||||
The number of the screen, which size has changed.
|
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:
|
for screen in self.screen_list:
|
||||||
if number == screen['number']:
|
if number == screen['number']:
|
||||||
new_screen = {
|
new_screen = {
|
||||||
|
@ -105,7 +105,7 @@ class ScreenList(object):
|
||||||
"""
|
"""
|
||||||
# Do not log at start up.
|
# Do not log at start up.
|
||||||
if changed_screen != -1:
|
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.
|
# Remove unplugged screens.
|
||||||
for screen in copy.deepcopy(self.screen_list):
|
for screen in copy.deepcopy(self.screen_list):
|
||||||
if screen['number'] == self.desktop.screenCount():
|
if screen['number'] == self.desktop.screenCount():
|
||||||
|
@ -132,9 +132,11 @@ class ScreenList(object):
|
||||||
"""
|
"""
|
||||||
screen_list = []
|
screen_list = []
|
||||||
for screen in self.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']:
|
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)
|
screen_list.append(screen_name)
|
||||||
return screen_list
|
return screen_list
|
||||||
|
|
||||||
|
@ -152,7 +154,7 @@ class ScreenList(object):
|
||||||
'size': PyQt5.QtCore.QRect(0, 0, 1024, 768)
|
'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']:
|
if screen['primary']:
|
||||||
self.current = screen
|
self.current = screen
|
||||||
self.override = copy.deepcopy(self.current)
|
self.override = copy.deepcopy(self.current)
|
||||||
|
@ -165,7 +167,7 @@ class ScreenList(object):
|
||||||
|
|
||||||
:param number: The screen number (int).
|
: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:
|
for screen in self.screen_list:
|
||||||
if screen['number'] == number:
|
if screen['number'] == number:
|
||||||
self.screen_list.remove(screen)
|
self.screen_list.remove(screen)
|
||||||
|
@ -189,7 +191,7 @@ class ScreenList(object):
|
||||||
|
|
||||||
:param number: The screen number (int).
|
: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:
|
if number + 1 > self.display_count:
|
||||||
self.current = self.screen_list[0]
|
self.current = self.screen_list[0]
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -62,9 +62,10 @@ class SearchEdit(QtWidgets.QLineEdit):
|
||||||
right_padding = self.clear_button.width() + frame_width
|
right_padding = self.clear_button.width() + frame_width
|
||||||
if hasattr(self, 'menu_button'):
|
if hasattr(self, 'menu_button'):
|
||||||
left_padding = self.menu_button.width()
|
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:
|
else:
|
||||||
stylesheet = 'QLineEdit { padding-right: %spx; } ' % right_padding
|
stylesheet = 'QLineEdit {{ padding-right: {right}px; }} '.format(right=right_padding)
|
||||||
self.setStyleSheet(stylesheet)
|
self.setStyleSheet(stylesheet)
|
||||||
msz = self.minimumSizeHint()
|
msz = self.minimumSizeHint()
|
||||||
self.setMinimumSize(max(msz.width(), self.clear_button.width() + (frame_width * 2) + 2),
|
self.setMinimumSize(max(msz.width(), self.clear_button.width() + (frame_width * 2) + 2),
|
||||||
|
|
|
@ -247,7 +247,7 @@ class ServiceItem(RegistryProperties):
|
||||||
self.renderer.set_item_theme(self.theme)
|
self.renderer.set_item_theme(self.theme)
|
||||||
self.theme_data, self.main, self.footer = self.renderer.pre_render()
|
self.theme_data, self.main, self.footer = self.renderer.pre_render()
|
||||||
if self.service_item_type == ServiceItemType.Text:
|
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
|
# 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.
|
# the dict instead of rendering them again.
|
||||||
previous_pages = {}
|
previous_pages = {}
|
||||||
|
@ -270,7 +270,7 @@ class ServiceItem(RegistryProperties):
|
||||||
elif self.service_item_type == ServiceItemType.Image or self.service_item_type == ServiceItemType.Command:
|
elif self.service_item_type == ServiceItemType.Image or self.service_item_type == ServiceItemType.Command:
|
||||||
pass
|
pass
|
||||||
else:
|
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)
|
self.title = clean_tags(self.title)
|
||||||
# The footer should never be None, but to be compatible with a few
|
# 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
|
# 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
|
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 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:
|
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
|
# 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':
|
if image and not self.has_original_files and self.name == 'presentations':
|
||||||
file_location = os.path.join(path, file_name)
|
file_location = os.path.join(path, file_name)
|
||||||
|
@ -334,6 +335,8 @@ class ServiceItem(RegistryProperties):
|
||||||
file_location_hash, ntpath.basename(image))
|
file_location_hash, ntpath.basename(image))
|
||||||
self._raw_frames.append({'title': file_name, 'image': image, 'path': path,
|
self._raw_frames.append({'title': file_name, 'image': image, 'path': path,
|
||||||
'display_title': display_title, 'notes': notes})
|
'display_title': display_title, 'notes': notes})
|
||||||
|
if self.is_capable(ItemCapabilities.HasThumbnails):
|
||||||
|
self.image_manager.add_image(image, ImageSource.CommandPlugins, '#000000')
|
||||||
self._new_item()
|
self._new_item()
|
||||||
|
|
||||||
def get_service_repr(self, lite_save):
|
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
|
: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.
|
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']
|
header = service_item['serviceitem']['header']
|
||||||
self.title = header['title']
|
self.title = header['title']
|
||||||
self.name = header['name']
|
self.name = header['name']
|
||||||
|
@ -606,11 +609,13 @@ class ServiceItem(RegistryProperties):
|
||||||
start = None
|
start = None
|
||||||
end = None
|
end = None
|
||||||
if self.start_time != 0:
|
if self.start_time != 0:
|
||||||
start = translate('OpenLP.ServiceItem', '<strong>Start</strong>: %s') % \
|
time = str(datetime.timedelta(seconds=self.start_time))
|
||||||
str(datetime.timedelta(seconds=self.start_time))
|
start = translate('OpenLP.ServiceItem',
|
||||||
|
'<strong>Start</strong>: {start}').format(start=time)
|
||||||
if self.media_length != 0:
|
if self.media_length != 0:
|
||||||
end = translate('OpenLP.ServiceItem', '<strong>Length</strong>: %s') % \
|
length = str(datetime.timedelta(seconds=self.media_length // 1000))
|
||||||
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:
|
if not start and not end:
|
||||||
return ''
|
return ''
|
||||||
elif start and not end:
|
elif start and not end:
|
||||||
|
@ -618,7 +623,7 @@ class ServiceItem(RegistryProperties):
|
||||||
elif not start and end:
|
elif not start and end:
|
||||||
return end
|
return end
|
||||||
else:
|
else:
|
||||||
return '%s <br>%s' % (start, end)
|
return '{start} <br>{end}'.format(start=start, end=end)
|
||||||
|
|
||||||
def update_theme(self, theme):
|
def update_theme(self, theme):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -135,4 +135,4 @@ class SettingsTab(QtWidgets.QWidget, RegistryProperties):
|
||||||
"""
|
"""
|
||||||
Tab has just been made visible to the user
|
Tab has just been made visible to the user
|
||||||
"""
|
"""
|
||||||
self.tab_visited = True
|
pass
|
||||||
|
|
|
@ -44,6 +44,7 @@ class BackgroundType(object):
|
||||||
Gradient = 1
|
Gradient = 1
|
||||||
Image = 2
|
Image = 2
|
||||||
Transparent = 3
|
Transparent = 3
|
||||||
|
Video = 4
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def to_string(background_type):
|
def to_string(background_type):
|
||||||
|
@ -58,6 +59,8 @@ class BackgroundType(object):
|
||||||
return 'image'
|
return 'image'
|
||||||
elif background_type == BackgroundType.Transparent:
|
elif background_type == BackgroundType.Transparent:
|
||||||
return 'transparent'
|
return 'transparent'
|
||||||
|
elif background_type == BackgroundType.Video:
|
||||||
|
return 'video'
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def from_string(type_string):
|
def from_string(type_string):
|
||||||
|
@ -72,6 +75,8 @@ class BackgroundType(object):
|
||||||
return BackgroundType.Image
|
return BackgroundType.Image
|
||||||
elif type_string == 'transparent':
|
elif type_string == 'transparent':
|
||||||
return BackgroundType.Transparent
|
return BackgroundType.Transparent
|
||||||
|
elif type_string == 'video':
|
||||||
|
return BackgroundType.Video
|
||||||
|
|
||||||
|
|
||||||
class BackgroundGradientType(object):
|
class BackgroundGradientType(object):
|
||||||
|
@ -184,7 +189,7 @@ class ThemeXML(object):
|
||||||
|
|
||||||
:param path: The path name to be added.
|
: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:
|
if self.background_filename and path:
|
||||||
self.theme_name = self.theme_name.strip()
|
self.theme_name = self.theme_name.strip()
|
||||||
self.background_filename = self.background_filename.strip()
|
self.background_filename = self.background_filename.strip()
|
||||||
|
@ -255,6 +260,21 @@ class ThemeXML(object):
|
||||||
# Create endColor element
|
# Create endColor element
|
||||||
self.child_element(background, 'borderColor', str(border_color))
|
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',
|
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',
|
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):
|
outline_pixel=2, shadow='False', shadow_color='#ffffff', shadow_pixel=5):
|
||||||
|
@ -407,7 +427,7 @@ class ThemeXML(object):
|
||||||
try:
|
try:
|
||||||
theme_xml = objectify.fromstring(xml)
|
theme_xml = objectify.fromstring(xml)
|
||||||
except etree.XMLSyntaxError:
|
except etree.XMLSyntaxError:
|
||||||
log.exception('Invalid xml %s', xml)
|
log.exception('Invalid xml {text}'.format(text=xml))
|
||||||
return
|
return
|
||||||
xml_iter = theme_xml.getiterator()
|
xml_iter = theme_xml.getiterator()
|
||||||
for element in xml_iter:
|
for element in xml_iter:
|
||||||
|
@ -493,6 +513,7 @@ class ThemeXML(object):
|
||||||
theme_strings = []
|
theme_strings = []
|
||||||
for key in dir(self):
|
for key in dir(self):
|
||||||
if key[0:1] != '_':
|
if key[0:1] != '_':
|
||||||
|
# TODO: Verify spacing format before converting to python3 string
|
||||||
theme_strings.append('%30s: %s' % (key, getattr(self, key)))
|
theme_strings.append('%30s: %s' % (key, getattr(self, key)))
|
||||||
return '\n'.join(theme_strings)
|
return '\n'.join(theme_strings)
|
||||||
|
|
||||||
|
@ -512,6 +533,9 @@ class ThemeXML(object):
|
||||||
elif self.background_type == BackgroundType.to_string(BackgroundType.Image):
|
elif self.background_type == BackgroundType.to_string(BackgroundType.Image):
|
||||||
filename = os.path.split(self.background_filename)[1]
|
filename = os.path.split(self.background_filename)[1]
|
||||||
self.add_background_image(filename, self.background_border_color)
|
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):
|
elif self.background_type == BackgroundType.to_string(BackgroundType.Transparent):
|
||||||
self.add_background_transparent()
|
self.add_background_transparent()
|
||||||
self.add_font(
|
self.add_font(
|
||||||
|
|
|
@ -165,7 +165,7 @@ def create_button(parent, name, **kwargs):
|
||||||
kwargs.setdefault('icon', ':/services/service_down.png')
|
kwargs.setdefault('icon', ':/services/service_down.png')
|
||||||
kwargs.setdefault('tooltip', translate('OpenLP.Ui', 'Move selection down one position.'))
|
kwargs.setdefault('tooltip', translate('OpenLP.Ui', 'Move selection down one position.'))
|
||||||
else:
|
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':
|
if kwargs.pop('btn_class', '') == 'toolbutton':
|
||||||
button = QtWidgets.QToolButton(parent)
|
button = QtWidgets.QToolButton(parent)
|
||||||
else:
|
else:
|
||||||
|
@ -183,7 +183,7 @@ def create_button(parent, name, **kwargs):
|
||||||
button.clicked.connect(kwargs.pop('click'))
|
button.clicked.connect(kwargs.pop('click'))
|
||||||
for key in list(kwargs.keys()):
|
for key in list(kwargs.keys()):
|
||||||
if key not in ['text', 'icon', 'tooltip', 'click']:
|
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
|
return button
|
||||||
|
|
||||||
|
|
||||||
|
@ -270,7 +270,7 @@ def create_action(parent, name, **kwargs):
|
||||||
action.triggered.connect(kwargs.pop('triggers'))
|
action.triggered.connect(kwargs.pop('triggers'))
|
||||||
for key in list(kwargs.keys()):
|
for key in list(kwargs.keys()):
|
||||||
if key not in ['text', 'icon', 'tooltip', 'statustip', 'checked', 'can_shortcuts', 'category', 'triggers']:
|
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
|
return action
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -133,37 +133,37 @@ def get_web_page(url, header=None, update_openlp=False):
|
||||||
time.sleep(0.1)
|
time.sleep(0.1)
|
||||||
try:
|
try:
|
||||||
page = urllib.request.urlopen(req, timeout=CONNECTION_TIMEOUT)
|
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
|
break
|
||||||
except urllib.error.URLError as err:
|
except urllib.error.URLError as err:
|
||||||
log.exception('URLError on {}'.format(url))
|
log.exception('URLError on {text}'.format(text=url))
|
||||||
log.exception('URLError: {}'.format(err.reason))
|
log.exception('URLError: {text}'.format(text=err.reason))
|
||||||
page = None
|
page = None
|
||||||
if retries > CONNECTION_RETRIES:
|
if retries > CONNECTION_RETRIES:
|
||||||
raise
|
raise
|
||||||
except socket.timeout:
|
except socket.timeout:
|
||||||
log.exception('Socket timeout: {}'.format(url))
|
log.exception('Socket timeout: {text}'.format(text=url))
|
||||||
page = None
|
page = None
|
||||||
if retries > CONNECTION_RETRIES:
|
if retries > CONNECTION_RETRIES:
|
||||||
raise
|
raise
|
||||||
except socket.gaierror:
|
except socket.gaierror:
|
||||||
log.exception('Socket gaierror: {}'.format(url))
|
log.exception('Socket gaierror: {text}'.format(text=url))
|
||||||
page = None
|
page = None
|
||||||
if retries > CONNECTION_RETRIES:
|
if retries > CONNECTION_RETRIES:
|
||||||
raise
|
raise
|
||||||
except ConnectionRefusedError:
|
except ConnectionRefusedError:
|
||||||
log.exception('ConnectionRefused: {}'.format(url))
|
log.exception('ConnectionRefused: {text}'.format(text=url))
|
||||||
page = None
|
page = None
|
||||||
if retries > CONNECTION_RETRIES:
|
if retries > CONNECTION_RETRIES:
|
||||||
raise
|
raise
|
||||||
break
|
break
|
||||||
except ConnectionError:
|
except ConnectionError:
|
||||||
log.exception('Connection error: {}'.format(url))
|
log.exception('Connection error: {text}'.format(text=url))
|
||||||
page = None
|
page = None
|
||||||
if retries > CONNECTION_RETRIES:
|
if retries > CONNECTION_RETRIES:
|
||||||
raise
|
raise
|
||||||
except HTTPException:
|
except HTTPException:
|
||||||
log.exception('HTTPException error: {}'.format(url))
|
log.exception('HTTPException error: {text}'.format(text=url))
|
||||||
page = None
|
page = None
|
||||||
if retries > CONNECTION_RETRIES:
|
if retries > CONNECTION_RETRIES:
|
||||||
raise
|
raise
|
||||||
|
@ -173,7 +173,7 @@ def get_web_page(url, header=None, update_openlp=False):
|
||||||
if update_openlp:
|
if update_openlp:
|
||||||
Registry().get('application').process_events()
|
Registry().get('application').process_events()
|
||||||
if not page:
|
if not page:
|
||||||
log.exception('{} could not be downloaded'.format(url))
|
log.exception('{text} could not be downloaded'.format(text=url))
|
||||||
return None
|
return None
|
||||||
log.debug(page)
|
log.debug(page)
|
||||||
return page
|
return page
|
||||||
|
|
|
@ -27,7 +27,7 @@ It is based on a QTableWidget but represents its contents in list form.
|
||||||
from PyQt5 import QtCore, QtGui, QtWidgets
|
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||||
|
|
||||||
from openlp.core.common import RegistryProperties, Settings
|
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):
|
class ListPreviewWidget(QtWidgets.QTableWidget, RegistryProperties):
|
||||||
|
@ -152,9 +152,11 @@ class ListPreviewWidget(QtWidgets.QTableWidget, RegistryProperties):
|
||||||
else:
|
else:
|
||||||
label.setScaledContents(True)
|
label.setScaledContents(True)
|
||||||
if self.service_item.is_command():
|
if self.service_item.is_command():
|
||||||
|
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'])
|
pixmap = QtGui.QPixmap(frame['image'])
|
||||||
pixmap.setDevicePixelRatio(label.devicePixelRatio())
|
|
||||||
label.setPixmap(pixmap)
|
|
||||||
else:
|
else:
|
||||||
image = self.image_manager.get_image(frame['path'], ImageSource.ImagePlugin)
|
image = self.image_manager.get_image(frame['path'], ImageSource.ImagePlugin)
|
||||||
pixmap = QtGui.QPixmap.fromImage(image)
|
pixmap = QtGui.QPixmap.fromImage(image)
|
||||||
|
|
|
@ -31,13 +31,15 @@ Some of the code for this form is based on the examples at:
|
||||||
|
|
||||||
import html
|
import html
|
||||||
import logging
|
import logging
|
||||||
|
import os
|
||||||
|
|
||||||
from PyQt5 import QtCore, QtWidgets, QtWebKit, QtWebKitWidgets, QtOpenGL, QtGui, QtMultimedia
|
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 import ServiceItem, ImageSource, ScreenList, build_html, expand_tags, image_to_byte
|
||||||
from openlp.core.lib.theme import BackgroundType
|
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():
|
if is_macosx():
|
||||||
from ctypes import pythonapi, c_void_p, c_char_p, py_object
|
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)
|
background = self.image_manager.get_image_bytes(self.override['image'], ImageSource.ImagePlugin)
|
||||||
self.set_transparency(self.service_item.theme_data.background_type ==
|
self.set_transparency(self.service_item.theme_data.background_type ==
|
||||||
BackgroundType.to_string(BackgroundType.Transparent))
|
BackgroundType.to_string(BackgroundType.Transparent))
|
||||||
|
image_bytes = None
|
||||||
|
if self.service_item.theme_data.background_type == 'image':
|
||||||
if self.service_item.theme_data.background_filename:
|
if self.service_item.theme_data.background_filename:
|
||||||
self.service_item.bg_image_bytes = self.image_manager.get_image_bytes(
|
self.service_item.bg_image_bytes = self.image_manager.get_image_bytes(
|
||||||
self.service_item.theme_data.background_filename, ImageSource.Theme)
|
self.service_item.theme_data.background_filename, ImageSource.Theme)
|
||||||
if image_path:
|
if image_path:
|
||||||
image_bytes = self.image_manager.get_image_bytes(image_path, ImageSource.ImagePlugin)
|
image_bytes = self.image_manager.get_image_bytes(image_path, ImageSource.ImagePlugin)
|
||||||
else:
|
|
||||||
image_bytes = None
|
|
||||||
html = build_html(self.service_item, self.screen, self.is_live, background, image_bytes,
|
html = build_html(self.service_item, self.screen, self.is_live, background, image_bytes,
|
||||||
plugins=self.plugin_manager.plugins)
|
plugins=self.plugin_manager.plugins)
|
||||||
self.web_view.setHtml(html)
|
self.web_view.setHtml(html)
|
||||||
|
@ -477,6 +479,17 @@ class MainDisplay(OpenLPMixin, Display, RegistryProperties):
|
||||||
Registry().execute('slidecontroller_live_unblank')
|
Registry().execute('slidecontroller_live_unblank')
|
||||||
else:
|
else:
|
||||||
self.hide_display(self.hide_mode)
|
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()
|
self._hide_mouse()
|
||||||
|
|
||||||
def footer(self, text):
|
def footer(self, text):
|
||||||
|
|
|
@ -83,7 +83,7 @@ def get_media_players():
|
||||||
reg_ex = QtCore.QRegExp(".*\[(.*)\].*")
|
reg_ex = QtCore.QRegExp(".*\[(.*)\].*")
|
||||||
if Settings().value('media/override player') == QtCore.Qt.Checked:
|
if Settings().value('media/override player') == QtCore.Qt.Checked:
|
||||||
if reg_ex.exactMatch(saved_players):
|
if reg_ex.exactMatch(saved_players):
|
||||||
overridden_player = '%s' % reg_ex.cap(1)
|
overridden_player = '{text}'.format(text=reg_ex.cap(1))
|
||||||
else:
|
else:
|
||||||
overridden_player = 'auto'
|
overridden_player = 'auto'
|
||||||
else:
|
else:
|
||||||
|
@ -102,7 +102,7 @@ def set_media_players(players_list, overridden_player='auto'):
|
||||||
log.debug('set_media_players')
|
log.debug('set_media_players')
|
||||||
players = ','.join(players_list)
|
players = ','.join(players_list)
|
||||||
if Settings().value('media/override player') == QtCore.Qt.Checked and overridden_player != 'auto':
|
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)
|
Settings().setValue('media/players', players)
|
||||||
|
|
||||||
|
|
||||||
|
@ -113,7 +113,7 @@ def parse_optical_path(input_string):
|
||||||
:param input_string: The string to parse
|
:param input_string: The string to parse
|
||||||
:return: The elements extracted from the string: filename, title, audio_track, subtitle_track, start, end
|
: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=':')
|
clip_info = input_string.split(sep=':')
|
||||||
title = int(clip_info[1])
|
title = int(clip_info[1])
|
||||||
audio_track = int(clip_info[2])
|
audio_track = int(clip_info[2])
|
||||||
|
@ -137,7 +137,10 @@ def format_milliseconds(milliseconds):
|
||||||
seconds, millis = divmod(milliseconds, 1000)
|
seconds, millis = divmod(milliseconds, 1000)
|
||||||
minutes, seconds = divmod(seconds, 60)
|
minutes, seconds = divmod(seconds, 60)
|
||||||
hours, minutes = divmod(minutes, 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 .mediacontroller import MediaController
|
||||||
from .playertab import PlayerTab
|
from .playertab import PlayerTab
|
||||||
|
|
|
@ -45,7 +45,7 @@ VIDEO_CSS = """
|
||||||
"""
|
"""
|
||||||
|
|
||||||
VIDEO_JS = """
|
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!
|
// Sometimes video.currentTime stops slightly short of video.duration and video.ended is intermittent!
|
||||||
|
|
||||||
var video = document.getElementById('video');
|
var video = document.getElementById('video');
|
||||||
|
@ -55,9 +55,6 @@ VIDEO_JS = """
|
||||||
switch(state){
|
switch(state){
|
||||||
case 'load':
|
case 'load':
|
||||||
video.src = 'file:///' + path;
|
video.src = 'file:///' + path;
|
||||||
if(loop == true) {
|
|
||||||
video.loop = true;
|
|
||||||
}
|
|
||||||
video.load();
|
video.load();
|
||||||
break;
|
break;
|
||||||
case 'play':
|
case 'play':
|
||||||
|
@ -180,12 +177,8 @@ class WebkitPlayer(MediaPlayer):
|
||||||
else:
|
else:
|
||||||
vol = 0
|
vol = 0
|
||||||
path = controller.media_info.file_info.absoluteFilePath()
|
path = controller.media_info.file_info.absoluteFilePath()
|
||||||
if controller.media_info.is_background:
|
|
||||||
loop = 'true'
|
|
||||||
else:
|
|
||||||
loop = 'false'
|
|
||||||
display.web_view.setVisible(True)
|
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)
|
display.frame.evaluateJavaScript(js)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
|
@ -53,15 +53,9 @@ class Ui_PluginViewDialog(object):
|
||||||
self.plugin_info_layout.setObjectName('plugin_info_layout')
|
self.plugin_info_layout.setObjectName('plugin_info_layout')
|
||||||
self.status_label = QtWidgets.QLabel(self.plugin_info_group_box)
|
self.status_label = QtWidgets.QLabel(self.plugin_info_group_box)
|
||||||
self.status_label.setObjectName('status_label')
|
self.status_label.setObjectName('status_label')
|
||||||
self.status_combo_box = QtWidgets.QComboBox(self.plugin_info_group_box)
|
self.status_checkbox = QtWidgets.QCheckBox(self.plugin_info_group_box)
|
||||||
self.status_combo_box.addItems(('', ''))
|
self.status_checkbox.setObjectName('status_checkbox')
|
||||||
self.status_combo_box.setObjectName('status_combo_box')
|
self.plugin_info_layout.addRow(self.status_label, self.status_checkbox)
|
||||||
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.about_label = QtWidgets.QLabel(self.plugin_info_group_box)
|
self.about_label = QtWidgets.QLabel(self.plugin_info_group_box)
|
||||||
self.about_label.setObjectName('about_label')
|
self.about_label.setObjectName('about_label')
|
||||||
self.about_text_browser = QtWidgets.QTextBrowser(self.plugin_info_group_box)
|
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'))
|
plugin_view_dialog.setWindowTitle(translate('OpenLP.PluginForm', 'Manage Plugins'))
|
||||||
self.plugin_info_group_box.setTitle(translate('OpenLP.PluginForm', 'Plugin Details'))
|
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.about_label.setText('%s:' % UiStrings().About)
|
||||||
self.status_label.setText(translate('OpenLP.PluginForm', 'Status:'))
|
self.status_label.setText(translate('OpenLP.PluginForm', 'Status:'))
|
||||||
self.status_combo_box.setItemText(0, translate('OpenLP.PluginForm', 'Active'))
|
self.status_checkbox.setText(translate('OpenLP.PluginForm', 'Active'))
|
||||||
self.status_combo_box.setItemText(1, translate('OpenLP.PluginForm', 'Inactive'))
|
|
||||||
|
|
|
@ -49,7 +49,7 @@ class PluginForm(QtWidgets.QDialog, Ui_PluginViewDialog, RegistryProperties):
|
||||||
self._clear_details()
|
self._clear_details()
|
||||||
# Right, now let's put some signals and slots together!
|
# Right, now let's put some signals and slots together!
|
||||||
self.plugin_list_widget.itemSelectionChanged.connect(self.on_plugin_list_widget_selection_changed)
|
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):
|
def load(self):
|
||||||
"""
|
"""
|
||||||
|
@ -86,24 +86,23 @@ class PluginForm(QtWidgets.QDialog, Ui_PluginViewDialog, RegistryProperties):
|
||||||
"""
|
"""
|
||||||
Clear the plugin details widgets
|
Clear the plugin details widgets
|
||||||
"""
|
"""
|
||||||
self.status_combo_box.setCurrentIndex(-1)
|
self.status_checkbox.setChecked(False)
|
||||||
self.version_number_label.setText('')
|
|
||||||
self.about_text_browser.setHtml('')
|
self.about_text_browser.setHtml('')
|
||||||
self.status_combo_box.setEnabled(False)
|
self.status_checkbox.setEnabled(False)
|
||||||
|
|
||||||
def _set_details(self):
|
def _set_details(self):
|
||||||
"""
|
"""
|
||||||
Set the details of the currently selected plugin
|
Set the details of the currently selected plugin
|
||||||
"""
|
"""
|
||||||
log.debug('PluginStatus: %s', str(self.active_plugin.status))
|
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.about_text_browser.setHtml(self.active_plugin.about())
|
||||||
self.programatic_change = True
|
self.programatic_change = True
|
||||||
status = PluginStatus.Active
|
if self.active_plugin.status != PluginStatus.Disabled:
|
||||||
if self.active_plugin.status == PluginStatus.Active:
|
self.status_checkbox.setChecked(self.active_plugin.status == PluginStatus.Active)
|
||||||
status = PluginStatus.Inactive
|
self.status_checkbox.setEnabled(True)
|
||||||
self.status_combo_box.setCurrentIndex(status)
|
else:
|
||||||
self.status_combo_box.setEnabled(True)
|
self.status_checkbox.setChecked(False)
|
||||||
|
self.status_checkbox.setEnabled(False)
|
||||||
self.programatic_change = False
|
self.programatic_change = False
|
||||||
|
|
||||||
def on_plugin_list_widget_selection_changed(self):
|
def on_plugin_list_widget_selection_changed(self):
|
||||||
|
@ -116,7 +115,6 @@ class PluginForm(QtWidgets.QDialog, Ui_PluginViewDialog, RegistryProperties):
|
||||||
plugin_name_singular = self.plugin_list_widget.currentItem().text().split('(')[0][:-1]
|
plugin_name_singular = self.plugin_list_widget.currentItem().text().split('(')[0][:-1]
|
||||||
self.active_plugin = None
|
self.active_plugin = None
|
||||||
for plugin in self.plugin_manager.plugins:
|
for plugin in self.plugin_manager.plugins:
|
||||||
if plugin.status != PluginStatus.Disabled:
|
|
||||||
if plugin.name_strings['singular'] == plugin_name_singular:
|
if plugin.name_strings['singular'] == plugin_name_singular:
|
||||||
self.active_plugin = plugin
|
self.active_plugin = plugin
|
||||||
break
|
break
|
||||||
|
@ -125,13 +123,13 @@ class PluginForm(QtWidgets.QDialog, Ui_PluginViewDialog, RegistryProperties):
|
||||||
else:
|
else:
|
||||||
self._clear_details()
|
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 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
|
return
|
||||||
if status == PluginStatus.Inactive:
|
if status:
|
||||||
self.application.set_busy_cursor()
|
self.application.set_busy_cursor()
|
||||||
self.active_plugin.toggle_status(PluginStatus.Active)
|
self.active_plugin.toggle_status(PluginStatus.Active)
|
||||||
self.application.set_normal_cursor()
|
self.application.set_normal_cursor()
|
||||||
|
|
|
@ -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.
|
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_label.setVisible(visible)
|
||||||
self.theme_combo_box.setVisible(visible)
|
self.theme_combo_box.setVisible(visible)
|
||||||
|
self.regenerate_service_items()
|
||||||
|
|
||||||
def regenerate_service_items(self, changed=False):
|
def regenerate_service_items(self, changed=False):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -60,7 +60,8 @@ class SettingsForm(QtWidgets.QDialog, Ui_SettingsDialog, RegistryProperties):
|
||||||
"""
|
"""
|
||||||
Execute the form
|
Execute the form
|
||||||
"""
|
"""
|
||||||
# load all the settings
|
# load all the
|
||||||
|
self.setting_list_widget.blockSignals(True)
|
||||||
self.setting_list_widget.clear()
|
self.setting_list_widget.clear()
|
||||||
while self.stacked_layout.count():
|
while self.stacked_layout.count():
|
||||||
# take at 0 and the rest shuffle up.
|
# take at 0 and the rest shuffle up.
|
||||||
|
@ -74,6 +75,7 @@ class SettingsForm(QtWidgets.QDialog, Ui_SettingsDialog, RegistryProperties):
|
||||||
if plugin.settings_tab:
|
if plugin.settings_tab:
|
||||||
self.insert_tab(plugin.settings_tab, plugin.is_active())
|
self.insert_tab(plugin.settings_tab, plugin.is_active())
|
||||||
self.setting_list_widget.setCurrentRow(0)
|
self.setting_list_widget.setCurrentRow(0)
|
||||||
|
self.setting_list_widget.blockSignals(False)
|
||||||
return QtWidgets.QDialog.exec(self)
|
return QtWidgets.QDialog.exec(self)
|
||||||
|
|
||||||
def insert_tab(self, tab_widget, is_visible=True):
|
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
|
# 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):
|
if tab_widget.tab_title == list_item.data(QtCore.Qt.UserRole):
|
||||||
# Make the matching tab visible
|
# Make the matching tab visible
|
||||||
|
tab_widget.tab_visited = True
|
||||||
self.stacked_layout.setCurrentIndex(tab_index)
|
self.stacked_layout.setCurrentIndex(tab_index)
|
||||||
self.stacked_layout.currentWidget().tab_visible()
|
self.stacked_layout.currentWidget().tab_visible()
|
||||||
|
|
||||||
|
|
|
@ -1135,9 +1135,21 @@ class SlideController(DisplayController, RegistryProperties):
|
||||||
"""
|
"""
|
||||||
self.log_debug('update_preview %s ' % self.screens.current['primary'])
|
self.log_debug('update_preview %s ' % self.screens.current['primary'])
|
||||||
if self.service_item and self.service_item.is_capable(ItemCapabilities.ProvidesOwnDisplay):
|
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
|
if self.is_live:
|
||||||
|
# If live, grab screen-cap of main display now
|
||||||
QtCore.QTimer.singleShot(500, self.grab_maindisplay)
|
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)
|
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:
|
else:
|
||||||
self.slide_image = self.display.preview()
|
self.slide_image = self.display.preview()
|
||||||
self.slide_image.setDevicePixelRatio(self.main_window.devicePixelRatio())
|
self.slide_image.setDevicePixelRatio(self.main_window.devicePixelRatio())
|
||||||
|
|
|
@ -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.theme import BackgroundType, BackgroundGradientType
|
||||||
from openlp.core.lib.ui import critical_error_message_box
|
from openlp.core.lib.ui import critical_error_message_box
|
||||||
from openlp.core.ui import ThemeLayoutForm
|
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
|
from .themewizard import Ui_ThemeWizard
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
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.gradient_combo_box.currentIndexChanged.connect(self.on_gradient_combo_box_current_index_changed)
|
||||||
self.color_button.colorChanged.connect(self.on_color_changed)
|
self.color_button.colorChanged.connect(self.on_color_changed)
|
||||||
self.image_color_button.colorChanged.connect(self.on_image_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_start_button.colorChanged.connect(self.on_gradient_start_color_changed)
|
||||||
self.gradient_end_button.colorChanged.connect(self.on_gradient_end_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_browse_button.clicked.connect(self.on_image_browse_button_clicked)
|
||||||
self.image_file_edit.editingFinished.connect(self.on_image_file_edit_editing_finished)
|
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.main_color_button.colorChanged.connect(self.on_main_color_changed)
|
||||||
self.outline_color_button.colorChanged.connect(self.on_outline_color_changed)
|
self.outline_color_button.colorChanged.connect(self.on_outline_color_changed)
|
||||||
self.shadow_color_button.colorChanged.connect(self.on_shadow_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_color_button.color = self.theme.background_border_color
|
||||||
self.image_file_edit.setText(self.theme.background_filename)
|
self.image_file_edit.setText(self.theme.background_filename)
|
||||||
self.setField('background_type', 2)
|
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):
|
elif self.theme.background_type == BackgroundType.to_string(BackgroundType.Transparent):
|
||||||
self.setField('background_type', 3)
|
self.setField('background_type', 3)
|
||||||
if self.theme.background_direction == BackgroundGradientType.to_string(BackgroundGradientType.Horizontal):
|
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:
|
if self.update_theme_allowed:
|
||||||
self.theme.background_type = BackgroundType.to_string(index)
|
self.theme.background_type = BackgroundType.to_string(index)
|
||||||
if self.theme.background_type != BackgroundType.to_string(BackgroundType.Image) and \
|
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.temp_background_filename = self.theme.background_filename
|
self.temp_background_filename = self.theme.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.temp_background_filename != '':
|
||||||
self.theme.background_filename = self.temp_background_filename
|
self.theme.background_filename = self.temp_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
|
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):
|
def on_gradient_start_color_changed(self, color):
|
||||||
"""
|
"""
|
||||||
Gradient 2 _color button pushed.
|
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())
|
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):
|
def on_main_color_changed(self, color):
|
||||||
"""
|
"""
|
||||||
Set the main colour value
|
Set the main colour value
|
||||||
|
@ -519,7 +556,8 @@ class ThemeForm(QtWidgets.QWizard, Ui_ThemeWizard, RegistryProperties):
|
||||||
return
|
return
|
||||||
save_from = None
|
save_from = None
|
||||||
save_to = 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]
|
filename = os.path.split(str(self.theme.background_filename))[1]
|
||||||
save_to = os.path.join(self.path, self.theme.theme_name, filename)
|
save_to = os.path.join(self.path, self.theme.theme_name, filename)
|
||||||
save_from = self.theme.background_filename
|
save_from = self.theme.background_filename
|
||||||
|
|
|
@ -300,7 +300,7 @@ class ThemeManager(OpenLPMixin, RegistryMixin, QtWidgets.QWidget, Ui_ThemeManage
|
||||||
"""
|
"""
|
||||||
save_to = None
|
save_to = None
|
||||||
save_from = 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_to = os.path.join(self.path, new_theme_name, os.path.split(str(theme_data.background_filename))[1])
|
||||||
save_from = theme_data.background_filename
|
save_from = theme_data.background_filename
|
||||||
theme_data.theme_name = new_theme_name
|
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.')):
|
translate('OpenLP.ThemeManager', 'You must select a theme to edit.')):
|
||||||
item = self.theme_list_widget.currentItem()
|
item = self.theme_list_widget.currentItem()
|
||||||
theme = self.get_theme_data(item.data(QtCore.Qt.UserRole))
|
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.old_background_image = theme.background_filename
|
||||||
self.theme_form.theme = theme
|
self.theme_form.theme = theme
|
||||||
self.theme_form.exec(True)
|
self.theme_form.exec(True)
|
||||||
|
|
|
@ -62,7 +62,7 @@ class Ui_ThemeWizard(object):
|
||||||
self.background_label = QtWidgets.QLabel(self.background_page)
|
self.background_label = QtWidgets.QLabel(self.background_page)
|
||||||
self.background_label.setObjectName('background_label')
|
self.background_label.setObjectName('background_label')
|
||||||
self.background_combo_box = QtWidgets.QComboBox(self.background_page)
|
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_combo_box.setObjectName('background_combo_box')
|
||||||
self.background_type_layout.addRow(self.background_label, self.background_combo_box)
|
self.background_type_layout.addRow(self.background_label, self.background_combo_box)
|
||||||
self.background_type_layout.setItem(1, QtWidgets.QFormLayout.LabelRole, self.spacer)
|
self.background_type_layout.setItem(1, QtWidgets.QFormLayout.LabelRole, self.spacer)
|
||||||
|
@ -135,6 +135,30 @@ class Ui_ThemeWizard(object):
|
||||||
self.transparent_layout.setObjectName('Transparent_layout')
|
self.transparent_layout.setObjectName('Transparent_layout')
|
||||||
self.background_stack.addWidget(self.transparent_widget)
|
self.background_stack.addWidget(self.transparent_widget)
|
||||||
self.background_layout.addLayout(self.background_stack)
|
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)
|
theme_wizard.addPage(self.background_page)
|
||||||
# Main Area Page
|
# Main Area Page
|
||||||
self.main_area_page = QtWidgets.QWizardPage()
|
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 '
|
self.background_page.setSubTitle(translate('OpenLP.ThemeWizard', 'Set up your theme\'s background '
|
||||||
'according to the parameters below.'))
|
'according to the parameters below.'))
|
||||||
self.background_label.setText(translate('OpenLP.ThemeWizard', 'Background type:'))
|
self.background_label.setText(translate('OpenLP.ThemeWizard', 'Background type:'))
|
||||||
self.background_combo_box.setItemText(BackgroundType.Solid,
|
self.background_combo_box.setItemText(BackgroundType.Solid, translate('OpenLP.ThemeWizard', 'Solid color'))
|
||||||
translate('OpenLP.ThemeWizard', 'Solid color'))
|
self.background_combo_box.setItemText(BackgroundType.Gradient, translate('OpenLP.ThemeWizard', 'Gradient'))
|
||||||
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.Image, UiStrings().Image)
|
||||||
|
self.background_combo_box.setItemText(BackgroundType.Video, UiStrings().Video)
|
||||||
self.background_combo_box.setItemText(BackgroundType.Transparent,
|
self.background_combo_box.setItemText(BackgroundType.Transparent,
|
||||||
translate('OpenLP.ThemeWizard', 'Transparent'))
|
translate('OpenLP.ThemeWizard', 'Transparent'))
|
||||||
self.color_label.setText(translate('OpenLP.ThemeWizard', 'color:'))
|
self.color_label.setText(translate('OpenLP.ThemeWizard', 'color:'))
|
||||||
|
@ -413,6 +436,8 @@ class Ui_ThemeWizard(object):
|
||||||
translate('OpenLP.ThemeWizard', 'Bottom Left - Top Right'))
|
translate('OpenLP.ThemeWizard', 'Bottom Left - Top Right'))
|
||||||
self.image_color_label.setText(translate('OpenLP.ThemeWizard', 'Background color:'))
|
self.image_color_label.setText(translate('OpenLP.ThemeWizard', 'Background color:'))
|
||||||
self.image_label.setText('%s:' % UiStrings().Image)
|
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.setTitle(translate('OpenLP.ThemeWizard', 'Main Area Font Details'))
|
||||||
self.main_area_page.setSubTitle(translate('OpenLP.ThemeWizard', 'Define the font and display '
|
self.main_area_page.setSubTitle(translate('OpenLP.ThemeWizard', 'Define the font and display '
|
||||||
'characteristics for the Display text'))
|
'characteristics for the Display text'))
|
||||||
|
|
|
@ -211,19 +211,21 @@ def update_reference_separators():
|
||||||
while '||' in source_string:
|
while '||' in source_string:
|
||||||
source_string = source_string.replace('||', '|')
|
source_string = source_string.replace('||', '|')
|
||||||
if role != 'e':
|
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
|
# escape reserved characters
|
||||||
for character in '\\.^$*+?{}[]()':
|
for character in '\\.^$*+?{}[]()':
|
||||||
source_string = source_string.replace(character, '\\' + character)
|
source_string = source_string.replace(character, '\\' + character)
|
||||||
# add various unicode alternatives
|
# add various unicode alternatives
|
||||||
source_string = source_string.replace('-', '(?:[-\u00AD\u2010\u2011\u2012\u2014\u2014\u2212\uFE63\uFF0D])')
|
source_string = source_string.replace('-', '(?:[-\u00AD\u2010\u2011\u2012\u2014\u2014\u2212\uFE63\uFF0D])')
|
||||||
source_string = source_string.replace(',', '(?:[,\u201A])')
|
source_string = source_string.replace(',', '(?:[,\u201A])')
|
||||||
REFERENCE_SEPARATORS['sep_%s' % role] = '\s*(?:%s)\s*' % source_string
|
REFERENCE_SEPARATORS['sep_{role}'.format(role=role)] = '\s*(?:{source})\s*'.format(source=source_string)
|
||||||
REFERENCE_SEPARATORS['sep_%s_default' % role] = default_separators[index]
|
REFERENCE_SEPARATORS['sep_{role}_default'.format(role=role)] = default_separators[index]
|
||||||
# verse range match: (<chapter>:)?<verse>(-((<chapter>:)?<verse>|end)?)?
|
# verse range match: (<chapter>:)?<verse>(-((<chapter>:)?<verse>|end)?)?
|
||||||
|
# TODO: Check before converting this string
|
||||||
range_regex = '(?:(?P<from_chapter>[0-9]+)%(sep_v)s)?' \
|
range_regex = '(?:(?P<from_chapter>[0-9]+)%(sep_v)s)?' \
|
||||||
'(?P<from_verse>[0-9]+)(?P<range_to>%(sep_r)s(?:(?:(?P<to_chapter>' \
|
'(?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
|
'[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'] = re.compile('^\s*%s\s*$' % range_regex, re.UNICODE)
|
||||||
REFERENCE_MATCHES['range_separator'] = re.compile(REFERENCE_SEPARATORS['sep_l'], re.UNICODE)
|
REFERENCE_MATCHES['range_separator'] = re.compile(REFERENCE_SEPARATORS['sep_l'], re.UNICODE)
|
||||||
# full reference match: <book>(<range>(,(?!$)|(?=$)))+
|
# full reference match: <book>(<range>(,(?!$)|(?=$)))+
|
||||||
|
@ -331,10 +333,10 @@ def parse_reference(reference, bible, language_selection, book_ref_id=False):
|
||||||
separator.
|
separator.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
log.debug('parse_reference("%s")', reference)
|
log.debug('parse_reference("{text}")'.format(text=reference))
|
||||||
match = get_reference_match('full').match(reference)
|
match = get_reference_match('full').match(reference)
|
||||||
if match:
|
if match:
|
||||||
log.debug('Matched reference %s' % reference)
|
log.debug('Matched reference {text}'.format(text=reference))
|
||||||
book = match.group('book')
|
book = match.group('book')
|
||||||
if not book_ref_id:
|
if not book_ref_id:
|
||||||
book_ref_id = bible.get_book_ref_id_by_localised_name(book, language_selection)
|
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))
|
ref_list.append((book_ref_id, from_chapter, 1, -1))
|
||||||
return ref_list
|
return ref_list
|
||||||
else:
|
else:
|
||||||
log.debug('Invalid reference: %s' % reference)
|
log.debug('Invalid reference: {text}'.format(text=reference))
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -242,13 +242,13 @@ class PresentationDocument(object):
|
||||||
|
|
||||||
def convert_thumbnail(self, file, idx):
|
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():
|
if self.check_thumbnails():
|
||||||
return
|
return
|
||||||
if os.path.isfile(file):
|
if os.path.isfile(file):
|
||||||
thumb_path = self.get_thumbnail_path(idx, False)
|
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):
|
def get_thumbnail_path(self, slide_no, check_exists):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -60,7 +60,7 @@ import webbrowser
|
||||||
from PyQt5 import QtCore
|
from PyQt5 import QtCore
|
||||||
from lxml import etree, objectify
|
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_PATHS = ['scripts']
|
||||||
IGNORED_FILES = ['setup.py']
|
IGNORED_FILES = ['setup.py']
|
||||||
|
|
||||||
|
@ -270,7 +270,7 @@ def update_translations():
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
os.chdir(os.path.abspath('..'))
|
os.chdir(os.path.abspath('..'))
|
||||||
run('pylupdate4 -verbose -noobsolete openlp.pro')
|
run('pylupdate5 -verbose -noobsolete openlp.pro')
|
||||||
os.chdir(os.path.abspath('scripts'))
|
os.chdir(os.path.abspath('scripts'))
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -111,6 +111,21 @@ class TestCategoryActionList(TestCase):
|
||||||
self.assertEqual(self.list.actions[0], (41, self.action2))
|
self.assertEqual(self.list.actions[0], (41, self.action2))
|
||||||
self.assertEqual(self.list.actions[1], (42, self.action1))
|
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):
|
def remove_test(self):
|
||||||
"""
|
"""
|
||||||
Test the remove() method
|
Test the remove() method
|
||||||
|
|
|
@ -250,7 +250,7 @@ class TestLib(TestCase):
|
||||||
|
|
||||||
def create_thumb_with_size_test(self):
|
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.
|
# GIVEN: An image to create a thumb of.
|
||||||
image_path = os.path.join(TEST_PATH, 'church.jpg')
|
image_path = os.path.join(TEST_PATH, 'church.jpg')
|
||||||
|
@ -270,7 +270,7 @@ class TestLib(TestCase):
|
||||||
# WHEN: Create the thumb.
|
# WHEN: Create the thumb.
|
||||||
icon = create_thumb(image_path, thumb_path, size=thumb_size)
|
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.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.assertIsInstance(icon, QtGui.QIcon, 'The icon should be a QIcon')
|
||||||
self.assertFalse(icon.isNull(), 'The icon should not be null')
|
self.assertFalse(icon.isNull(), 'The icon should not be null')
|
||||||
|
@ -282,6 +282,194 @@ class TestLib(TestCase):
|
||||||
except:
|
except:
|
||||||
pass
|
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):
|
def check_item_selected_true_test(self):
|
||||||
"""
|
"""
|
||||||
Test that the check_item_selected() function returns True when there are selected indexes
|
Test that the check_item_selected() function returns True when there are selected indexes
|
||||||
|
|
|
@ -26,7 +26,8 @@ Package to test the openlp.core.lib.projector.pjlink1 package.
|
||||||
from unittest import TestCase
|
from unittest import TestCase
|
||||||
|
|
||||||
from openlp.core.lib.projector.pjlink1 import PJLink1
|
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.functional import patch
|
||||||
from tests.resources.projector.data import TEST_PIN, TEST_SALT, TEST_CONNECT_AUTHENTICATE
|
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')
|
@patch.object(pjlink_test, 'projectorReceivedData')
|
||||||
def projector_process_lamp_test(self, mock_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
|
# GIVEN: Test object
|
||||||
pjlink = pjlink_test
|
pjlink = pjlink_test
|
||||||
|
@ -128,7 +129,7 @@ class TestPJLink(TestCase):
|
||||||
@patch.object(pjlink_test, 'projectorReceivedData')
|
@patch.object(pjlink_test, 'projectorReceivedData')
|
||||||
def projector_process_multiple_lamp_test(self, mock_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
|
# GIVEN: Test object
|
||||||
pjlink = pjlink_test
|
pjlink = pjlink_test
|
||||||
|
@ -151,3 +152,101 @@ class TestPJLink(TestCase):
|
||||||
'Lamp 3 power status should have been set to TRUE')
|
'Lamp 3 power status should have been set to TRUE')
|
||||||
self.assertEquals(pjlink.lamp[2]['Hours'], 33333,
|
self.assertEquals(pjlink.lamp[2]['Hours'], 33333,
|
||||||
'Lamp 3 hours should have been set to 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')
|
||||||
|
|
|
@ -206,3 +206,17 @@ class TestProjectorDB(TestCase):
|
||||||
# THEN: __repr__ should return a proper string
|
# THEN: __repr__ should return a proper string
|
||||||
self.assertEqual(str(manufacturer), '<Manufacturer(name="OpenLP Test")>',
|
self.assertEqual(str(manufacturer), '<Manufacturer(name="OpenLP Test")>',
|
||||||
'Manufacturer.__repr__() should have returned a proper representation string')
|
'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')
|
||||||
|
|
|
@ -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.service_item_type, ServiceItemType.Command, 'It should be a Command')
|
||||||
self.assertEqual(service_item.get_frames()[0], frame, 'Frames should match')
|
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')
|
@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
|
# GIVEN: A service item, a mocked AppLocation and presentation data
|
||||||
mocked_get_section_data_path.return_value = os.path.join('mocked', 'section', 'path')
|
mocked_get_section_data_path.return_value = os.path.join('mocked', 'section', 'path')
|
||||||
service_item = ServiceItem(None)
|
service_item = ServiceItem(None)
|
||||||
|
service_item.add_capability(ItemCapabilities.HasThumbnails)
|
||||||
service_item.has_original_files = False
|
service_item.has_original_files = False
|
||||||
service_item.name = 'presentations'
|
service_item.name = 'presentations'
|
||||||
presentation_name = 'test.pptx'
|
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
|
# 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.service_item_type, ServiceItemType.Command, 'It should be a Command')
|
||||||
self.assertEqual(service_item.get_frames()[0], frame, 'Frames should match')
|
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):
|
def service_item_load_optical_media_from_service_test(self):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -27,8 +27,9 @@ from unittest import TestCase, skipUnless
|
||||||
from PyQt5 import QtCore
|
from PyQt5 import QtCore
|
||||||
|
|
||||||
from openlp.core.common import Registry, is_macosx, Settings
|
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 import MainDisplay
|
||||||
|
from openlp.core.ui.media import MediaController
|
||||||
from openlp.core.ui.maindisplay import TRANSPARENT_STYLESHEET, OPAQUE_STYLESHEET
|
from openlp.core.ui.maindisplay import TRANSPARENT_STYLESHEET, OPAQUE_STYLESHEET
|
||||||
|
|
||||||
from tests.helpers.testmixin import TestMixin
|
from tests.helpers.testmixin import TestMixin
|
||||||
|
@ -223,3 +224,61 @@ class TestMainDisplay(TestCase, TestMixin):
|
||||||
|
|
||||||
# THEN: setVisible should had not been called
|
# THEN: setVisible should had not been called
|
||||||
main_display.setVisible.assert_not_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')
|
||||||
|
|
|
@ -26,7 +26,7 @@ from PyQt5 import QtCore, QtGui
|
||||||
|
|
||||||
from unittest import TestCase
|
from unittest import TestCase
|
||||||
from openlp.core import Registry
|
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 import SlideController, LiveController, PreviewController
|
||||||
from openlp.core.ui.slidecontroller import InfoLabel, WIDE_MENU, NON_TEXT_MENU
|
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
|
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):
|
class TestInfoLabel(TestCase):
|
||||||
|
|
||||||
|
|
|
@ -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')
|
|
@ -24,9 +24,11 @@ Package to test the openlp.core.ui.lib.listpreviewwidget package.
|
||||||
"""
|
"""
|
||||||
from unittest import TestCase
|
from unittest import TestCase
|
||||||
|
|
||||||
|
from PyQt5 import QtGui
|
||||||
|
|
||||||
from openlp.core.common import Settings
|
from openlp.core.common import Settings
|
||||||
from openlp.core.ui.lib.listpreviewwidget import ListPreviewWidget
|
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
|
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.assertIsNotNone(list_preview_widget, 'The ListPreviewWidget object should not be None')
|
||||||
self.assertEquals(list_preview_widget.screen_ratio, 1, 'Should not be called')
|
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.resizeRowsToContents')
|
||||||
@patch(u'openlp.core.ui.lib.listpreviewwidget.ListPreviewWidget.setRowHeight')
|
@patch(u'openlp.core.ui.lib.listpreviewwidget.ListPreviewWidget.setRowHeight')
|
||||||
def replace_recalculate_layout_test_text(self, mocked_setRowHeight, mocked_resizeRowsToContents):
|
def replace_recalculate_layout_test_text(self, mocked_setRowHeight, mocked_resizeRowsToContents):
|
||||||
|
@ -120,6 +169,7 @@ class TestListPreviewWidget(TestCase):
|
||||||
# Mock image service item
|
# Mock image service item
|
||||||
service_item = MagicMock()
|
service_item = MagicMock()
|
||||||
service_item.is_text.return_value = False
|
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},
|
service_item.get_frames.return_value = [{'title': None, 'path': None, 'image': None},
|
||||||
{'title': None, 'path': None, 'image': None}]
|
{'title': None, 'path': None, 'image': None}]
|
||||||
# init ListPreviewWidget and load service item
|
# init ListPreviewWidget and load service item
|
||||||
|
@ -156,6 +206,7 @@ class TestListPreviewWidget(TestCase):
|
||||||
# Mock image service item
|
# Mock image service item
|
||||||
service_item = MagicMock()
|
service_item = MagicMock()
|
||||||
service_item.is_text.return_value = False
|
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},
|
service_item.get_frames.return_value = [{'title': None, 'path': None, 'image': None},
|
||||||
{'title': None, 'path': None, 'image': None}]
|
{'title': None, 'path': None, 'image': None}]
|
||||||
# init ListPreviewWidget and load service item
|
# init ListPreviewWidget and load service item
|
||||||
|
@ -225,6 +276,7 @@ class TestListPreviewWidget(TestCase):
|
||||||
# Mock image service item
|
# Mock image service item
|
||||||
service_item = MagicMock()
|
service_item = MagicMock()
|
||||||
service_item.is_text.return_value = False
|
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},
|
service_item.get_frames.return_value = [{'title': None, 'path': None, 'image': None},
|
||||||
{'title': None, 'path': None, 'image': None}]
|
{'title': None, 'path': None, 'image': None}]
|
||||||
# Mock self.cellWidget().children().setMaximumWidth()
|
# Mock self.cellWidget().children().setMaximumWidth()
|
||||||
|
@ -261,6 +313,7 @@ class TestListPreviewWidget(TestCase):
|
||||||
# Mock image service item
|
# Mock image service item
|
||||||
service_item = MagicMock()
|
service_item = MagicMock()
|
||||||
service_item.is_text.return_value = False
|
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},
|
service_item.get_frames.return_value = [{'title': None, 'path': None, 'image': None},
|
||||||
{'title': None, 'path': None, 'image': None}]
|
{'title': None, 'path': None, 'image': None}]
|
||||||
# Mock self.cellWidget().children().setMaximumWidth()
|
# Mock self.cellWidget().children().setMaximumWidth()
|
||||||
|
|
|
@ -22,7 +22,7 @@
|
||||||
"""
|
"""
|
||||||
Package to test the openlp.plugin.bible.lib.https package.
|
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.core.common import Registry
|
||||||
from openlp.plugins.bibles.lib.http import BGExtract, CWExtract, BSExtract
|
from openlp.plugins.bibles.lib.http import BGExtract, CWExtract, BSExtract
|
||||||
|
@ -146,6 +146,7 @@ class TestBibleHTTP(TestCase):
|
||||||
self.assertIsNotNone(bibles)
|
self.assertIsNotNone(bibles)
|
||||||
self.assertIn(('Holman Christian Standard Bible', 'HCSB', 'en'), bibles)
|
self.assertIn(('Holman Christian Standard Bible', 'HCSB', 'en'), bibles)
|
||||||
|
|
||||||
|
@skip("Waiting for Crosswalk to fix their server")
|
||||||
def crosswalk_get_bibles_test(self):
|
def crosswalk_get_bibles_test(self):
|
||||||
"""
|
"""
|
||||||
Test getting list of bibles from Crosswalk.com
|
Test getting list of bibles from Crosswalk.com
|
||||||
|
|
|
@ -26,7 +26,7 @@ import json
|
||||||
def assert_length(expected, iterable, msg=None):
|
def assert_length(expected, iterable, msg=None):
|
||||||
if len(iterable) != expected:
|
if len(iterable) != expected:
|
||||||
if not msg:
|
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)
|
raise AssertionError(msg)
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue