forked from openlp/openlp
Merged with Tim's refactoring changes
This commit is contained in:
commit
d2782e099f
@ -137,6 +137,7 @@ class OpenLP(QtGui.QApplication):
|
||||
self.main_window = MainWindow()
|
||||
Registry().execute('bootstrap_initialise')
|
||||
Registry().execute('bootstrap_post_set_up')
|
||||
Registry().initialise = False
|
||||
self.main_window.show()
|
||||
if show_splash:
|
||||
# now kill the splashscreen
|
||||
|
@ -4,8 +4,8 @@
|
||||
###############################################################################
|
||||
# OpenLP - Open Source Lyrics Projection #
|
||||
# --------------------------------------------------------------------------- #
|
||||
# Copyright (c) 2008-2013 Raoul Snyman #
|
||||
# Portions copyright (c) 2008-2013 Tim Bentley, Gerald Britton, Jonathan #
|
||||
# Copyright (c) 2008-2014 Raoul Snyman #
|
||||
# Portions copyright (c) 2008-2014 Tim Bentley, Gerald Britton, Jonathan #
|
||||
# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, #
|
||||
# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. #
|
||||
# Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru, #
|
||||
@ -33,7 +33,7 @@ import logging
|
||||
import inspect
|
||||
|
||||
from openlp.core.common import trace_error_handler
|
||||
DO_NOT_TRACE_EVENTS = ['timerEvent', 'paintEvent']
|
||||
DO_NOT_TRACE_EVENTS = ['timerEvent', 'paintEvent', 'drag_enter_event', 'drop_event']
|
||||
|
||||
|
||||
class OpenLPMixin(object):
|
||||
@ -68,13 +68,13 @@ class OpenLPMixin(object):
|
||||
|
||||
def log_debug(self, message):
|
||||
"""
|
||||
Common log debug handler which prints the calling path
|
||||
Common log debug handler
|
||||
"""
|
||||
self.logger.debug(message)
|
||||
|
||||
def log_info(self, message):
|
||||
"""
|
||||
Common log info handler which prints the calling path
|
||||
Common log info handler
|
||||
"""
|
||||
self.logger.info(message)
|
||||
|
||||
|
@ -63,6 +63,7 @@ class Registry(object):
|
||||
registry.service_list = {}
|
||||
registry.functions_list = {}
|
||||
registry.running_under_test = False
|
||||
registry.initialising = True
|
||||
# Allow the tests to remove Registry entries but not the live system
|
||||
if 'nose' in sys.argv[0]:
|
||||
registry.running_under_test = True
|
||||
@ -78,9 +79,10 @@ class Registry(object):
|
||||
if key in self.service_list:
|
||||
return self.service_list[key]
|
||||
else:
|
||||
trace_error_handler(log)
|
||||
log.error('Service %s not found in list' % key)
|
||||
#raise KeyError('Service %s not found in list' % key)
|
||||
if not self.initialising:
|
||||
trace_error_handler(log)
|
||||
log.error('Service %s not found in list' % key)
|
||||
raise KeyError('Service %s not found in list' % key)
|
||||
|
||||
def register(self, key, reference):
|
||||
"""
|
||||
@ -170,5 +172,5 @@ class Registry(object):
|
||||
log.exception('Exception for function %s', function)
|
||||
else:
|
||||
trace_error_handler(log)
|
||||
log.error("Event %s not called by not registered" % event)
|
||||
log.error("Event %s called but not registered" % event)
|
||||
return results
|
||||
|
@ -4,8 +4,8 @@
|
||||
###############################################################################
|
||||
# OpenLP - Open Source Lyrics Projection #
|
||||
# --------------------------------------------------------------------------- #
|
||||
# Copyright (c) 2008-2013 Raoul Snyman #
|
||||
# Portions copyright (c) 2008-2013 Tim Bentley, Gerald Britton, Jonathan #
|
||||
# Copyright (c) 2008-2014 Raoul Snyman #
|
||||
# Portions copyright (c) 2008-2014 Tim Bentley, Gerald Britton, Jonathan #
|
||||
# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, #
|
||||
# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. #
|
||||
# Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru, #
|
||||
|
@ -386,7 +386,7 @@ class Settings(QtCore.QSettings):
|
||||
"""
|
||||
if self.group():
|
||||
key = self.group() + '/' + key
|
||||
return Settings.__default_settings__[key]
|
||||
return Settings.__default_settings__.get(key, '')
|
||||
|
||||
def remove_obsolete_settings(self):
|
||||
"""
|
||||
@ -424,9 +424,9 @@ class Settings(QtCore.QSettings):
|
||||
"""
|
||||
# if group() is not empty the group has not been specified together with the key.
|
||||
if self.group():
|
||||
default_value = Settings.__default_settings__[self.group() + '/' + key]
|
||||
default_value = Settings.__default_settings__.get(self.group() + '/' + key, '')
|
||||
else:
|
||||
default_value = Settings.__default_settings__[key]
|
||||
default_value = Settings.__default_settings__.get(key, '')
|
||||
setting = super(Settings, self).value(key, default_value)
|
||||
return self._convert_value(setting, default_value)
|
||||
|
||||
|
@ -89,8 +89,8 @@ def get_text_file_string(text_file):
|
||||
returns False. If there is an error loading the file or the content can't be decoded then the function will return
|
||||
None.
|
||||
|
||||
``textfile``
|
||||
The name of the file.
|
||||
:param text_file: The name of the file.
|
||||
:return The file as a single string
|
||||
"""
|
||||
if not os.path.isfile(text_file):
|
||||
return False
|
||||
@ -114,8 +114,8 @@ def str_to_bool(string_value):
|
||||
"""
|
||||
Convert a string version of a boolean into a real boolean.
|
||||
|
||||
``string_value``
|
||||
The string value to examine and convert to a boolean type.
|
||||
:param string_value: The string value to examine and convert to a boolean type.
|
||||
:return The correct boolean value
|
||||
"""
|
||||
if isinstance(string_value, bool):
|
||||
return string_value
|
||||
@ -127,9 +127,10 @@ def build_icon(icon):
|
||||
Build a QIcon instance from an existing QIcon, a resource location, or a physical file location. If the icon is a
|
||||
QIcon instance, that icon is simply returned. If not, it builds a QIcon instance from the resource or file name.
|
||||
|
||||
``icon``
|
||||
:param icon:
|
||||
The icon to build. This can be a QIcon, a resource string in the form ``:/resource/file.png``, or a file
|
||||
location like ``/path/to/file.png``. However, the **recommended** way is to specify a resource string.
|
||||
:return The build icon.
|
||||
"""
|
||||
button_icon = QtGui.QIcon()
|
||||
if isinstance(icon, QtGui.QIcon):
|
||||
@ -148,11 +149,8 @@ def image_to_byte(image, base_64=True):
|
||||
"""
|
||||
Resize an image to fit on the current screen for the web and returns it as a byte stream.
|
||||
|
||||
``image``
|
||||
The image to converted.
|
||||
|
||||
``base_64``
|
||||
If True returns the image as Base64 bytes, otherwise the image is returned as a byte array
|
||||
:param image: The image to converted.
|
||||
:param base_64: If True returns the image as Base64 bytes, otherwise the image is returned as a byte array.
|
||||
To preserve original intention, this defaults to True
|
||||
"""
|
||||
log.debug('image_to_byte - start')
|
||||
@ -172,18 +170,12 @@ def create_thumb(image_path, thumb_path, return_icon=True, size=None):
|
||||
"""
|
||||
Create a thumbnail from the given image path and depending on ``return_icon`` it returns an icon from this thumb.
|
||||
|
||||
``image_path``
|
||||
The image file to create the icon from.
|
||||
|
||||
``thumb_path``
|
||||
The filename to save the thumbnail to.
|
||||
|
||||
``return_icon``
|
||||
States if an icon should be build and returned from the thumb. Defaults to ``True``.
|
||||
|
||||
``size``
|
||||
Allows to state a own size (QtCore.QSize) to use. Defaults to ``None``, which means that a default height of 88
|
||||
is used.
|
||||
:param image_path: The image file to create the icon from.
|
||||
:param thumb_path: The filename to save the thumbnail to.
|
||||
:param return_icon: States if an icon should be build and returned from the thumb. Defaults to ``True``.
|
||||
:param size: Allows to state a own size (QtCore.QSize) to use. Defaults to ``None``, which means that a default
|
||||
height of 88 is used.
|
||||
:return The final icon.
|
||||
"""
|
||||
ext = os.path.splitext(thumb_path)[1].lower()
|
||||
reader = QtGui.QImageReader(image_path)
|
||||
@ -207,11 +199,9 @@ def validate_thumb(file_path, thumb_path):
|
||||
Validates whether an file's thumb still exists and if is up to date. **Note**, you must **not** call this function,
|
||||
before checking the existence of the file.
|
||||
|
||||
``file_path``
|
||||
The path to the file. The file **must** exist!
|
||||
|
||||
``thumb_path``
|
||||
The path to the thumb.
|
||||
:param file_path: The path to the file. The file **must** exist!
|
||||
:param thumb_path: The path to the thumb.
|
||||
:return True, False if the image has changed since the thumb was created.
|
||||
"""
|
||||
if not os.path.exists(thumb_path):
|
||||
return False
|
||||
@ -224,19 +214,12 @@ def resize_image(image_path, width, height, background='#000000'):
|
||||
"""
|
||||
Resize an image to fit on the current screen.
|
||||
|
||||
``image_path``
|
||||
The path to the image to resize.
|
||||
|
||||
``width``
|
||||
The new image width.
|
||||
|
||||
``height``
|
||||
The new image height.
|
||||
|
||||
``background``
|
||||
The background colour. Defaults to black.
|
||||
|
||||
DO NOT REMOVE THE DEFAULT BACKGROUND VALUE!
|
||||
|
||||
:param image_path: The path to the image to resize.
|
||||
:param width: The new image width.
|
||||
:param height: The new image height.
|
||||
:param background: The background colour. Defaults to black.
|
||||
"""
|
||||
log.debug('resize_image - start')
|
||||
reader = QtGui.QImageReader(image_path)
|
||||
@ -271,11 +254,8 @@ def check_item_selected(list_widget, message):
|
||||
"""
|
||||
Check if a list item is selected so an action may be performed on it
|
||||
|
||||
``list_widget``
|
||||
The list to check for selected items
|
||||
|
||||
``message``
|
||||
The message to give the user if no item is selected
|
||||
:param list_widget: The list to check for selected items
|
||||
:param message: The message to give the user if no item is selected
|
||||
"""
|
||||
if not list_widget.selectedIndexes():
|
||||
QtGui.QMessageBox.information(list_widget.parent(),
|
||||
@ -287,6 +267,8 @@ def check_item_selected(list_widget, message):
|
||||
def clean_tags(text):
|
||||
"""
|
||||
Remove Tags from text for display
|
||||
|
||||
:param text: Text to be cleaned
|
||||
"""
|
||||
text = text.replace('<br>', '\n')
|
||||
text = text.replace('{br}', '\n')
|
||||
@ -300,6 +282,8 @@ def clean_tags(text):
|
||||
def expand_tags(text):
|
||||
"""
|
||||
Expand tags HTML for display
|
||||
|
||||
:param text: The text to be expanded.
|
||||
"""
|
||||
for tag in FormattingTags.get_html_tags():
|
||||
text = text.replace(tag['start tag'], tag['start html'])
|
||||
@ -310,11 +294,11 @@ def expand_tags(text):
|
||||
def create_separated_list(string_list):
|
||||
"""
|
||||
Returns a string that represents a join of a list of strings with a localized separator. This function corresponds
|
||||
|
||||
to QLocale::createSeparatedList which was introduced in Qt 4.8 and implements the algorithm from
|
||||
http://www.unicode.org/reports/tr35/#ListPatterns
|
||||
|
||||
``string_list``
|
||||
List of unicode strings
|
||||
:param string_list: List of unicode strings
|
||||
"""
|
||||
if LooseVersion(Qt.PYQT_VERSION_STR) >= LooseVersion('4.9') and \
|
||||
LooseVersion(Qt.qVersion()) >= LooseVersion('4.8'):
|
||||
|
@ -133,7 +133,7 @@ class SpellTextEdit(QtGui.QPlainTextEdit):
|
||||
"""
|
||||
self.dictionary = enchant.Dict(action.text())
|
||||
self.highlighter.spelling_dictionary = self.dictionary
|
||||
self.highlighter.highlight_block(self.toPlainText())
|
||||
self.highlighter.highlightBlock(self.toPlainText())
|
||||
self.highlighter.rehighlight()
|
||||
|
||||
def correct_word(self, word):
|
||||
@ -180,9 +180,11 @@ class Highlighter(QtGui.QSyntaxHighlighter):
|
||||
super(Highlighter, self).__init__(*args)
|
||||
self.spelling_dictionary = None
|
||||
|
||||
def highlight_block(self, text):
|
||||
def highlightBlock(self, text):
|
||||
"""
|
||||
Highlight misspelt words in a block of text.
|
||||
|
||||
Note, this is a Qt hook.
|
||||
"""
|
||||
if not self.spelling_dictionary:
|
||||
return
|
||||
|
@ -342,18 +342,16 @@ def create_valign_selection_widgets(parent):
|
||||
return label, combo_box
|
||||
|
||||
|
||||
def find_and_set_in_combo_box(combo_box, value_to_find):
|
||||
def find_and_set_in_combo_box(combo_box, value_to_find, set_missing=True):
|
||||
"""
|
||||
Find a string in a combo box and set it as the selected item if present
|
||||
|
||||
``combo_box``
|
||||
The combo box to check for selected items
|
||||
|
||||
``value_to_find``
|
||||
The value to find
|
||||
:param combo_box: The combo box to check for selected items
|
||||
:param value_to_find: The value to find
|
||||
:param set_missing: if not found leave value as current
|
||||
"""
|
||||
index = combo_box.findText(value_to_find, QtCore.Qt.MatchExactly)
|
||||
if index == -1:
|
||||
# Not Found.
|
||||
index = 0
|
||||
index = 0 if set_missing else combo_box.currentIndex()
|
||||
combo_box.setCurrentIndex(index)
|
||||
|
@ -99,11 +99,10 @@ from .formattingtagcontroller import FormattingTagController
|
||||
from .shortcutlistform import ShortcutListForm
|
||||
from .mediadockmanager import MediaDockManager
|
||||
from .servicemanager import ServiceManager
|
||||
from .thememanagerhelper import ThemeManagerHelper
|
||||
from .thememanager import ThemeManager
|
||||
|
||||
__all__ = ['SplashScreen', 'AboutForm', 'SettingsForm', 'MainDisplay', 'SlideController', 'ServiceManager',
|
||||
'ThemeManager', 'MediaDockManager', 'ServiceItemEditForm', 'FirstTimeForm', 'FirstTimeLanguageForm', 'ThemeForm',
|
||||
'ThemeLayoutForm', 'FileRenameForm', 'StartTimeForm', 'MainDisplay', 'Display', 'ServiceNoteForm',
|
||||
'SlideController', 'DisplayController', 'GeneralTab', 'ThemesTab', 'AdvancedTab', 'PluginForm',
|
||||
'FormattingTagForm', 'ShortcutListForm', 'FormattingTagController', 'ThemeManagerHelper']
|
||||
'FormattingTagForm', 'ShortcutListForm', 'FormattingTagController']
|
||||
|
@ -94,8 +94,8 @@ class ListPreviewWidget(QtGui.QTableWidget):
|
||||
Displays the given slide.
|
||||
"""
|
||||
self.service_item = service_item
|
||||
self.clear()
|
||||
self.setRowCount(0)
|
||||
self.clear()
|
||||
self.setColumnWidth(0, width)
|
||||
row = 0
|
||||
text = []
|
||||
|
@ -35,7 +35,7 @@ import os
|
||||
import datetime
|
||||
from PyQt4 import QtCore, QtGui
|
||||
|
||||
from openlp.core.common import Registry, Settings, UiStrings, translate
|
||||
from openlp.core.common import Registry, RegistryMixin, Settings, UiStrings, translate
|
||||
from openlp.core.lib import OpenLPToolbar
|
||||
from openlp.core.lib.ui import critical_error_message_box
|
||||
from openlp.core.ui.media import MediaState, MediaInfo, MediaType, get_media_players, set_media_players
|
||||
@ -99,7 +99,7 @@ class MediaController(object):
|
||||
Constructor
|
||||
"""
|
||||
Registry().register('media_controller', self)
|
||||
Registry().register_function('bootstrap_initialise', self.check_available_media_players)
|
||||
Registry().register_function('bootstrap_initialise', self.bootstrap_initialise)
|
||||
self.media_players = {}
|
||||
self.display_controllers = {}
|
||||
self.current_media_players = {}
|
||||
@ -134,20 +134,22 @@ class MediaController(object):
|
||||
"""
|
||||
Set the active players and available media files
|
||||
"""
|
||||
suffix_list = []
|
||||
self.audio_extensions_list = []
|
||||
for player in list(self.media_players.values()):
|
||||
if player.is_active:
|
||||
for item in player.audio_extensions_list:
|
||||
if not item in self.audio_extensions_list:
|
||||
self.audio_extensions_list.append(item)
|
||||
self.service_manager.supported_suffixes(item[2:])
|
||||
suffix_list.append(item[2:])
|
||||
self.video_extensions_list = []
|
||||
for player in list(self.media_players.values()):
|
||||
if player.is_active:
|
||||
for item in player.video_extensions_list:
|
||||
if item not in self.video_extensions_list:
|
||||
self.video_extensions_list.extend(item)
|
||||
self.service_manager.supported_suffixes(item[2:])
|
||||
suffix_list.append(item[2:])
|
||||
self.service_manager.supported_suffixes(suffix_list)
|
||||
|
||||
def register_players(self, player):
|
||||
"""
|
||||
@ -159,7 +161,7 @@ class MediaController(object):
|
||||
"""
|
||||
self.media_players[player.name] = player
|
||||
|
||||
def check_available_media_players(self):
|
||||
def bootstrap_initialise(self):
|
||||
"""
|
||||
Check to see if we have any media Player's available.
|
||||
"""
|
||||
@ -169,27 +171,28 @@ class MediaController(object):
|
||||
if filename.endswith('player.py') and not filename == 'mediaplayer.py':
|
||||
path = os.path.join(controller_dir, filename)
|
||||
if os.path.isfile(path):
|
||||
modulename = 'openlp.core.ui.media.' + os.path.splitext(filename)[0]
|
||||
log.debug('Importing controller %s', modulename)
|
||||
module_name = 'openlp.core.ui.media.' + os.path.splitext(filename)[0]
|
||||
log.debug('Importing controller %s', module_name)
|
||||
try:
|
||||
__import__(modulename, globals(), locals(), [])
|
||||
__import__(module_name, globals(), locals(), [])
|
||||
# On some platforms importing vlc.py might cause
|
||||
# also OSError exceptions. (e.g. Mac OS X)
|
||||
except (ImportError, OSError):
|
||||
log.warn('Failed to import %s on path %s', modulename, path)
|
||||
log.warn('Failed to import %s on path %s', module_name, path)
|
||||
player_classes = MediaPlayer.__subclasses__()
|
||||
for player_class in player_classes:
|
||||
player = player_class(self)
|
||||
self.register_players(player)
|
||||
if not self.media_players:
|
||||
return False
|
||||
savedPlayers, overriddenPlayer = get_media_players()
|
||||
invalid_media_players = [mediaPlayer for mediaPlayer in savedPlayers
|
||||
if not mediaPlayer in self.media_players or not self.media_players[mediaPlayer].check_available()]
|
||||
saved_players, overridden_player = get_media_players()
|
||||
invalid_media_players = \
|
||||
[mediaPlayer for mediaPlayer in saved_players if not mediaPlayer in self.media_players or
|
||||
not self.media_players[mediaPlayer].check_available()]
|
||||
if invalid_media_players:
|
||||
for invalidPlayer in invalid_media_players:
|
||||
savedPlayers.remove(invalidPlayer)
|
||||
set_media_players(savedPlayers, overriddenPlayer)
|
||||
saved_players.remove(invalidPlayer)
|
||||
set_media_players(saved_players, overridden_player)
|
||||
self._set_active_players()
|
||||
self._generate_extensions_lists()
|
||||
return True
|
||||
@ -270,14 +273,17 @@ class MediaController(object):
|
||||
# Build a Media ToolBar
|
||||
controller.mediabar = OpenLPToolbar(controller)
|
||||
controller.mediabar.add_toolbar_action('playbackPlay', text='media_playback_play',
|
||||
icon=':/slides/media_playback_start.png',
|
||||
tooltip=translate('OpenLP.SlideController', 'Start playing media.'), triggers=controller.send_to_plugins)
|
||||
icon=':/slides/media_playback_start.png',
|
||||
tooltip=translate('OpenLP.SlideController', 'Start playing media.'),
|
||||
triggers=controller.send_to_plugins)
|
||||
controller.mediabar.add_toolbar_action('playbackPause', text='media_playback_pause',
|
||||
icon=':/slides/media_playback_pause.png',
|
||||
tooltip=translate('OpenLP.SlideController', 'Pause playing media.'), triggers=controller.send_to_plugins)
|
||||
icon=':/slides/media_playback_pause.png',
|
||||
tooltip=translate('OpenLP.SlideController', 'Pause playing media.'),
|
||||
triggers=controller.send_to_plugins)
|
||||
controller.mediabar.add_toolbar_action('playbackStop', text='media_playback_stop',
|
||||
icon=':/slides/media_playback_stop.png',
|
||||
tooltip=translate('OpenLP.SlideController', 'Stop playing media.'), triggers=controller.send_to_plugins)
|
||||
icon=':/slides/media_playback_stop.png',
|
||||
tooltip=translate('OpenLP.SlideController', 'Stop playing media.'),
|
||||
triggers=controller.send_to_plugins)
|
||||
# Build the seek_slider.
|
||||
controller.seek_slider = MediaSlider(QtCore.Qt.Horizontal, self, controller)
|
||||
controller.seek_slider.setMaximum(1000)
|
||||
@ -445,11 +451,11 @@ class MediaController(object):
|
||||
if not self._check_file_type(controller, display, service_item):
|
||||
# Media could not be loaded correctly
|
||||
critical_error_message_box(translate('MediaPlugin.MediaItem', 'Unsupported File'),
|
||||
translate('MediaPlugin.MediaItem', 'Unsupported File'))
|
||||
translate('MediaPlugin.MediaItem', 'Unsupported File'))
|
||||
return False
|
||||
if not self.media_play(controller):
|
||||
critical_error_message_box(translate('MediaPlugin.MediaItem', 'Unsupported File'),
|
||||
translate('MediaPlugin.MediaItem', 'Unsupported File'))
|
||||
translate('MediaPlugin.MediaItem', 'Unsupported File'))
|
||||
return False
|
||||
service_item.set_media_length(controller.media_info.length)
|
||||
self.media_stop(controller)
|
||||
|
@ -37,8 +37,7 @@ from openlp.core.ui.media import MediaState
|
||||
|
||||
class MediaPlayer(object):
|
||||
"""
|
||||
This is the base class media Player class to provide OpenLP with a
|
||||
pluggable media display framework.
|
||||
This is the base class media Player class to provide OpenLP with a pluggable media display framework.
|
||||
"""
|
||||
|
||||
def __init__(self, parent, name='media_player'):
|
||||
|
@ -80,12 +80,12 @@ class PhononPlayer(MediaPlayer):
|
||||
self.parent = parent
|
||||
self.additional_extensions = ADDITIONAL_EXT
|
||||
mimetypes.init()
|
||||
for mimetype in Phonon.BackendCapabilities.availableMimeTypes():
|
||||
mimetype = str(mimetype)
|
||||
if mimetype.startswith('audio/'):
|
||||
self._addToList(self.audio_extensions_list, mimetype)
|
||||
elif mimetype.startswith('video/'):
|
||||
self._addToList(self.video_extensions_list, mimetype)
|
||||
for mime_type in Phonon.BackendCapabilities.availableMimeTypes():
|
||||
mime_type = str(mime_type)
|
||||
if mime_type.startswith('audio/'):
|
||||
self._addToList(self.audio_extensions_list, mime_type)
|
||||
elif mime_type.startswith('video/'):
|
||||
self._addToList(self.video_extensions_list, mime_type)
|
||||
|
||||
def _addToList(self, mimetype_list, mimetype):
|
||||
"""
|
||||
@ -144,14 +144,14 @@ class PhononPlayer(MediaPlayer):
|
||||
self.volume(display, volume)
|
||||
return True
|
||||
|
||||
def media_state_wait(self, display, mediaState):
|
||||
def media_state_wait(self, display, media_state):
|
||||
"""
|
||||
Wait for the video to change its state
|
||||
Wait no longer than 5 seconds.
|
||||
"""
|
||||
start = datetime.now()
|
||||
current_state = display.media_object.state()
|
||||
while current_state != mediaState:
|
||||
while current_state != media_state:
|
||||
current_state = display.media_object.state()
|
||||
if current_state == Phonon.ErrorState:
|
||||
return False
|
||||
@ -172,8 +172,7 @@ class PhononPlayer(MediaPlayer):
|
||||
"""
|
||||
controller = display.controller
|
||||
start_time = 0
|
||||
if display.media_object.state() != Phonon.PausedState and \
|
||||
controller.media_info.start_time > 0:
|
||||
if display.media_object.state() != Phonon.PausedState and controller.media_info.start_time > 0:
|
||||
start_time = controller.media_info.start_time
|
||||
display.media_object.play()
|
||||
if not self.media_state_wait(display, Phonon.PlayingState):
|
||||
@ -262,8 +261,8 @@ class PhononPlayer(MediaPlayer):
|
||||
Return some info about this player
|
||||
"""
|
||||
return(translate('Media.player', 'Phonon is a media player which '
|
||||
'interacts with the operating system to provide media capabilities.') +
|
||||
'<br/> <strong>' + translate('Media.player', 'Audio') +
|
||||
'</strong><br/>' + str(self.audio_extensions_list) +
|
||||
'<br/><strong>' + translate('Media.player', 'Video') +
|
||||
'</strong><br/>' + str(self.video_extensions_list) + '<br/>')
|
||||
'interacts with the operating system to provide media capabilities.') +
|
||||
'<br/> <strong>' + translate('Media.player', 'Audio') +
|
||||
'</strong><br/>' + str(self.audio_extensions_list) +
|
||||
'<br/><strong>' + translate('Media.player', 'Video') +
|
||||
'</strong><br/>' + str(self.video_extensions_list) + '<br/>')
|
||||
|
@ -39,13 +39,13 @@ from openlp.core.ui.media import get_media_players, set_media_players
|
||||
|
||||
class MediaQCheckBox(QtGui.QCheckBox):
|
||||
"""
|
||||
MediaQCheckBox adds an extra property, playerName to the QCheckBox class.
|
||||
MediaQCheckBox adds an extra property, player_name to the QCheckBox class.
|
||||
"""
|
||||
def set_player_name(self, name):
|
||||
"""
|
||||
Set the player name
|
||||
"""
|
||||
self.playerName = name
|
||||
self.player_name = name
|
||||
|
||||
|
||||
class PlayerTab(SettingsTab):
|
||||
@ -113,9 +113,9 @@ class PlayerTab(SettingsTab):
|
||||
self.ordering_button_layout.setObjectName('ordering_button_layout')
|
||||
self.ordering_button_layout.addStretch(1)
|
||||
self.ordering_up_button = create_button(self, 'ordering_up_button', role='up',
|
||||
click=self.on_up_button_clicked)
|
||||
click=self.on_up_button_clicked)
|
||||
self.ordering_down_button = create_button(self, 'ordering_down_button', role='down',
|
||||
click=self.on_down_button_clicked)
|
||||
click=self.on_down_button_clicked)
|
||||
self.ordering_button_layout.addWidget(self.ordering_up_button)
|
||||
self.ordering_button_layout.addWidget(self.ordering_down_button)
|
||||
self.ordering_button_layout.addStretch(1)
|
||||
@ -135,8 +135,8 @@ class PlayerTab(SettingsTab):
|
||||
self.background_color_group_box.setTitle(UiStrings().BackgroundColor)
|
||||
self.background_color_label.setText(UiStrings().DefaultColor)
|
||||
self.information_label.setText(translate('OpenLP.PlayerTab',
|
||||
'Visible background for videos with aspect ratio different to screen.'))
|
||||
self.retranslatePlayers()
|
||||
'Visible background for videos with aspect ratio different to screen.'))
|
||||
self.retranslate_players()
|
||||
|
||||
def on_background_color_button_clicked(self):
|
||||
"""
|
||||
@ -151,7 +151,7 @@ class PlayerTab(SettingsTab):
|
||||
"""
|
||||
Add or remove players depending on their status
|
||||
"""
|
||||
player = self.sender().playerName
|
||||
player = self.sender().player_name
|
||||
if check_state == QtCore.Qt.Checked:
|
||||
if player not in self.used_players:
|
||||
self.used_players.append(player)
|
||||
@ -249,9 +249,9 @@ class PlayerTab(SettingsTab):
|
||||
else:
|
||||
checkbox.setChecked(False)
|
||||
self.update_player_list()
|
||||
self.retranslatePlayers()
|
||||
self.retranslate_players()
|
||||
|
||||
def retranslatePlayers(self):
|
||||
def retranslate_players(self):
|
||||
"""
|
||||
Translations for players is dependent on their setup as well
|
||||
"""
|
||||
|
@ -118,35 +118,35 @@ class VlcPlayer(MediaPlayer):
|
||||
"""
|
||||
Set up the media player
|
||||
"""
|
||||
display.vlcWidget = QtGui.QFrame(display)
|
||||
display.vlcWidget.setFrameStyle(QtGui.QFrame.NoFrame)
|
||||
display.vlc_widget = QtGui.QFrame(display)
|
||||
display.vlc_widget.setFrameStyle(QtGui.QFrame.NoFrame)
|
||||
# creating a basic vlc instance
|
||||
command_line_options = '--no-video-title-show'
|
||||
if not display.has_audio:
|
||||
command_line_options += ' --no-audio --no-video-title-show'
|
||||
if Settings().value('advanced/hide mouse') and display.controller.is_live:
|
||||
command_line_options += ' --mouse-hide-timeout=0'
|
||||
display.vlcInstance = vlc.Instance(command_line_options)
|
||||
display.vlc_instance = vlc.Instance(command_line_options)
|
||||
# creating an empty vlc media player
|
||||
display.vlcMediaPlayer = display.vlcInstance.media_player_new()
|
||||
display.vlcWidget.resize(display.size())
|
||||
display.vlcWidget.raise_()
|
||||
display.vlcWidget.hide()
|
||||
display.vlc_media_player = display.vlc_instance.media_player_new()
|
||||
display.vlc_widget.resize(display.size())
|
||||
display.vlc_widget.raise_()
|
||||
display.vlc_widget.hide()
|
||||
# The media player has to be 'connected' to the QFrame.
|
||||
# (otherwise a video would be displayed in it's own window)
|
||||
# This is platform specific!
|
||||
# You have to give the id of the QFrame (or similar object)
|
||||
# to vlc, different platforms have different functions for this.
|
||||
win_id = int(display.vlcWidget.winId())
|
||||
win_id = int(display.vlc_widget.winId())
|
||||
if sys.platform == "win32":
|
||||
display.vlcMediaPlayer.set_hwnd(win_id)
|
||||
display.vlc_media_player.set_hwnd(win_id)
|
||||
elif sys.platform == "darwin":
|
||||
# We have to use 'set_nsobject' since Qt4 on OSX uses Cocoa
|
||||
# framework and not the old Carbon.
|
||||
display.vlcMediaPlayer.set_nsobject(win_id)
|
||||
display.vlc_media_player.set_nsobject(win_id)
|
||||
else:
|
||||
# for Linux using the X Server
|
||||
display.vlcMediaPlayer.set_xwindow(win_id)
|
||||
display.vlc_media_player.set_xwindow(win_id)
|
||||
self.has_own_widget = True
|
||||
|
||||
def check_available(self):
|
||||
@ -165,18 +165,18 @@ class VlcPlayer(MediaPlayer):
|
||||
file_path = str(controller.media_info.file_info.absoluteFilePath())
|
||||
path = os.path.normcase(file_path)
|
||||
# create the media
|
||||
display.vlcMedia = display.vlcInstance.media_new_path(path)
|
||||
display.vlc_media = display.vlc_instance.media_new_path(path)
|
||||
# put the media in the media player
|
||||
display.vlcMediaPlayer.set_media(display.vlcMedia)
|
||||
display.vlc_media_player.set_media(display.vlc_media)
|
||||
# parse the metadata of the file
|
||||
display.vlcMedia.parse()
|
||||
display.vlc_media.parse()
|
||||
self.volume(display, volume)
|
||||
# We need to set media_info.length during load because we want
|
||||
# to avoid start and stop the video twice. Once for real playback
|
||||
# and once to just get media length.
|
||||
#
|
||||
# Media plugin depends on knowing media length before playback.
|
||||
controller.media_info.length = int(display.vlcMediaPlayer.get_media().get_duration() / 1000)
|
||||
controller.media_info.length = int(display.vlc_media_player.get_media().get_duration() / 1000)
|
||||
return True
|
||||
|
||||
def media_state_wait(self, display, media_state):
|
||||
@ -185,8 +185,8 @@ class VlcPlayer(MediaPlayer):
|
||||
Wait no longer than 60 seconds. (loading an iso file needs a long time)
|
||||
"""
|
||||
start = datetime.now()
|
||||
while not media_state == display.vlcMedia.get_state():
|
||||
if display.vlcMedia.get_state() == vlc.State.Error:
|
||||
while not media_state == display.vlc_media.get_state():
|
||||
if display.vlc_media.get_state() == vlc.State.Error:
|
||||
return False
|
||||
self.application.process_events()
|
||||
if (datetime.now() - start).seconds > 60:
|
||||
@ -197,7 +197,7 @@ class VlcPlayer(MediaPlayer):
|
||||
"""
|
||||
Resize the player
|
||||
"""
|
||||
display.vlcWidget.resize(display.size())
|
||||
display.vlc_widget.resize(display.size())
|
||||
|
||||
def play(self, display):
|
||||
"""
|
||||
@ -207,25 +207,25 @@ class VlcPlayer(MediaPlayer):
|
||||
start_time = 0
|
||||
if self.state != MediaState.Paused and controller.media_info.start_time > 0:
|
||||
start_time = controller.media_info.start_time
|
||||
display.vlcMediaPlayer.play()
|
||||
display.vlc_media_player.play()
|
||||
if not self.media_state_wait(display, vlc.State.Playing):
|
||||
return False
|
||||
self.volume(display, controller.media_info.volume)
|
||||
if start_time > 0:
|
||||
self.seek(display, controller.media_info.start_time * 1000)
|
||||
controller.media_info.length = int(display.vlcMediaPlayer.get_media().get_duration() / 1000)
|
||||
controller.media_info.length = int(display.vlc_media_player.get_media().get_duration() / 1000)
|
||||
controller.seek_slider.setMaximum(controller.media_info.length * 1000)
|
||||
self.state = MediaState.Playing
|
||||
display.vlcWidget.raise_()
|
||||
display.vlc_widget.raise_()
|
||||
return True
|
||||
|
||||
def pause(self, display):
|
||||
"""
|
||||
Pause the current item
|
||||
"""
|
||||
if display.vlcMedia.get_state() != vlc.State.Playing:
|
||||
if display.vlc_media.get_state() != vlc.State.Playing:
|
||||
return
|
||||
display.vlcMediaPlayer.pause()
|
||||
display.vlc_media_player.pause()
|
||||
if self.media_state_wait(display, vlc.State.Paused):
|
||||
self.state = MediaState.Paused
|
||||
|
||||
@ -233,7 +233,7 @@ class VlcPlayer(MediaPlayer):
|
||||
"""
|
||||
Stop the current item
|
||||
"""
|
||||
display.vlcMediaPlayer.stop()
|
||||
display.vlc_media_player.stop()
|
||||
self.state = MediaState.Stopped
|
||||
|
||||
def volume(self, display, vol):
|
||||
@ -241,21 +241,21 @@ class VlcPlayer(MediaPlayer):
|
||||
Set the volume
|
||||
"""
|
||||
if display.has_audio:
|
||||
display.vlcMediaPlayer.audio_set_volume(vol)
|
||||
display.vlc_media_player.audio_set_volume(vol)
|
||||
|
||||
def seek(self, display, seek_value):
|
||||
"""
|
||||
Go to a particular position
|
||||
"""
|
||||
if display.vlcMediaPlayer.is_seekable():
|
||||
display.vlcMediaPlayer.set_time(seek_value)
|
||||
if display.vlc_media_player.is_seekable():
|
||||
display.vlc_media_player.set_time(seek_value)
|
||||
|
||||
def reset(self, display):
|
||||
"""
|
||||
Reset the player
|
||||
"""
|
||||
display.vlcMediaPlayer.stop()
|
||||
display.vlcWidget.setVisible(False)
|
||||
display.vlc_media_player.stop()
|
||||
display.vlc_widget.setVisible(False)
|
||||
self.state = MediaState.Off
|
||||
|
||||
def set_visible(self, display, status):
|
||||
@ -263,23 +263,23 @@ class VlcPlayer(MediaPlayer):
|
||||
Set the visibility
|
||||
"""
|
||||
if self.has_own_widget:
|
||||
display.vlcWidget.setVisible(status)
|
||||
display.vlc_widget.setVisible(status)
|
||||
|
||||
def update_ui(self, display):
|
||||
"""
|
||||
Update the UI
|
||||
"""
|
||||
# Stop video if playback is finished.
|
||||
if display.vlcMedia.get_state() == vlc.State.Ended:
|
||||
if display.vlc_media.get_state() == vlc.State.Ended:
|
||||
self.stop(display)
|
||||
controller = display.controller
|
||||
if controller.media_info.end_time > 0:
|
||||
if display.vlcMediaPlayer.get_time() > controller.media_info.end_time * 1000:
|
||||
if display.vlc_media_player.get_time() > controller.media_info.end_time * 1000:
|
||||
self.stop(display)
|
||||
self.set_visible(display, False)
|
||||
if not controller.seek_slider.isSliderDown():
|
||||
controller.seek_slider.blockSignals(True)
|
||||
controller.seek_slider.setSliderPosition(display.vlcMediaPlayer.get_time())
|
||||
controller.seek_slider.setSliderPosition(display.vlc_media_player.get_time())
|
||||
controller.seek_slider.blockSignals(False)
|
||||
|
||||
def get_info(self):
|
||||
@ -287,8 +287,8 @@ class VlcPlayer(MediaPlayer):
|
||||
Return some information about this player
|
||||
"""
|
||||
return(translate('Media.player', 'VLC is an external player which '
|
||||
'supports a number of different formats.') +
|
||||
'<br/> <strong>' + translate('Media.player', 'Audio') +
|
||||
'</strong><br/>' + str(AUDIO_EXT) + '<br/><strong>' +
|
||||
translate('Media.player', 'Video') + '</strong><br/>' +
|
||||
str(VIDEO_EXT) + '<br/>')
|
||||
'supports a number of different formats.') +
|
||||
'<br/> <strong>' + translate('Media.player', 'Audio') +
|
||||
'</strong><br/>' + str(AUDIO_EXT) + '<br/><strong>' +
|
||||
translate('Media.player', 'Video') + '</strong><br/>' +
|
||||
str(VIDEO_EXT) + '<br/>')
|
@ -376,9 +376,9 @@ class WebkitPlayer(MediaPlayer):
|
||||
else:
|
||||
is_visible = "hidden"
|
||||
if controller.media_info.is_flash:
|
||||
display.frame.evaluateJavaScript('show_flash("setVisible", null, null, "%s");' % (is_visible))
|
||||
display.frame.evaluateJavaScript('show_flash("setVisible", null, null, "%s");' % is_visible)
|
||||
else:
|
||||
display.frame.evaluateJavaScript('show_video("setVisible", null, null, null, "%s");' % (is_visible))
|
||||
display.frame.evaluateJavaScript('show_video("setVisible", null, null, null, "%s");' % is_visible)
|
||||
|
||||
def update_ui(self, display):
|
||||
"""
|
||||
@ -412,9 +412,9 @@ class WebkitPlayer(MediaPlayer):
|
||||
Return some information about this player
|
||||
"""
|
||||
return(translate('Media.player', 'Webkit is a media player which runs '
|
||||
'inside a web browser. This player allows text over video to be '
|
||||
'rendered.') +
|
||||
'<br/> <strong>' + translate('Media.player', 'Audio') +
|
||||
'</strong><br/>' + str(AUDIO_EXT) + '<br/><strong>' +
|
||||
translate('Media.player', 'Video') + '</strong><br/>' +
|
||||
str(VIDEO_EXT) + '<br/>')
|
||||
'inside a web browser. This player allows text over video to be '
|
||||
'rendered.') +
|
||||
'<br/> <strong>' + translate('Media.player', 'Audio') +
|
||||
'</strong><br/>' + str(AUDIO_EXT) + '<br/><strong>' +
|
||||
translate('Media.player', 'Video') + '</strong><br/>' +
|
||||
str(VIDEO_EXT) + '<br/>')
|
||||
|
@ -4,8 +4,8 @@
|
||||
###############################################################################
|
||||
# OpenLP - Open Source Lyrics Projection #
|
||||
# --------------------------------------------------------------------------- #
|
||||
# Copyright (c) 2008-2013 Raoul Snyman #
|
||||
# Portions copyright (c) 2008-2013 Tim Bentley, Gerald Britton, Jonathan #
|
||||
# Copyright (c) 2008-2014 Raoul Snyman #
|
||||
# Portions copyright (c) 2008-2014 Tim Bentley, Gerald Britton, Jonathan #
|
||||
# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, #
|
||||
# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. #
|
||||
# Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru, #
|
||||
@ -30,7 +30,6 @@
|
||||
The service manager sets up, loads, saves and manages services.
|
||||
"""
|
||||
import html
|
||||
import logging
|
||||
import os
|
||||
import shutil
|
||||
import zipfile
|
||||
@ -38,11 +37,10 @@ import json
|
||||
from tempfile import mkstemp
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
from PyQt4 import QtCore, QtGui
|
||||
|
||||
from openlp.core.common import Registry, AppLocation, Settings, ThemeLevel, check_directory_exists, UiStrings, translate
|
||||
from openlp.core.common import Registry, AppLocation, Settings, ThemeLevel, OpenLPMixin, RegistryMixin, \
|
||||
check_directory_exists, UiStrings, translate
|
||||
from openlp.core.lib import OpenLPToolbar, ServiceItem, ItemCapabilities, PluginStatus, build_icon
|
||||
from openlp.core.lib.ui import critical_error_message_box, create_widget_action, find_and_set_in_combo_box
|
||||
from openlp.core.ui import ServiceNoteForm, ServiceItemEditForm, StartTimeForm
|
||||
@ -65,6 +63,7 @@ class ServiceManagerList(QtGui.QTreeWidget):
|
||||
def keyPressEvent(self, event):
|
||||
"""
|
||||
Capture Key press and respond accordingly.
|
||||
:param event:
|
||||
"""
|
||||
if isinstance(event, QtGui.QKeyEvent):
|
||||
# here accept the event and do something
|
||||
@ -75,7 +74,7 @@ class ServiceManagerList(QtGui.QTreeWidget):
|
||||
self.service_manager.on_move_selection_down()
|
||||
event.accept()
|
||||
elif event.key() == QtCore.Qt.Key_Delete:
|
||||
self.service_manager.onDeleteFromService()
|
||||
self.service_manager.on_delete_from_service()
|
||||
event.accept()
|
||||
event.ignore()
|
||||
else:
|
||||
@ -85,6 +84,7 @@ class ServiceManagerList(QtGui.QTreeWidget):
|
||||
"""
|
||||
Drag and drop event does not care what data is selected as the recipient will use events to request the data
|
||||
move just tell it what plugin to call
|
||||
:param event:
|
||||
"""
|
||||
if event.buttons() != QtCore.Qt.LeftButton:
|
||||
event.ignore()
|
||||
@ -99,16 +99,21 @@ class ServiceManagerList(QtGui.QTreeWidget):
|
||||
drag.start(QtCore.Qt.CopyAction)
|
||||
|
||||
|
||||
class ServiceManagerDialog(object):
|
||||
class Ui_ServiceManager(object):
|
||||
"""
|
||||
UI part of the Service Manager
|
||||
"""
|
||||
def setup_ui(self, widget):
|
||||
"""
|
||||
Define the UI
|
||||
:param widget:
|
||||
"""
|
||||
# start with the layout
|
||||
self.layout = QtGui.QVBoxLayout(widget)
|
||||
self.layout.setSpacing(0)
|
||||
self.layout.setMargin(0)
|
||||
# Create the top toolbar
|
||||
self.toolbar = OpenLPToolbar(self)
|
||||
self.toolbar = OpenLPToolbar(widget)
|
||||
self.toolbar.add_toolbar_action('newService', text=UiStrings().NewService, icon=':/general/general_new.png',
|
||||
tooltip=UiStrings().CreateService, triggers=self.on_new_service_clicked)
|
||||
self.toolbar.add_toolbar_action('openService', text=UiStrings().OpenService,
|
||||
@ -120,7 +125,7 @@ class ServiceManagerDialog(object):
|
||||
tooltip=translate('OpenLP.ServiceManager', 'Save this service.'),
|
||||
triggers=self.decide_save_method)
|
||||
self.toolbar.addSeparator()
|
||||
self.theme_label = QtGui.QLabel('%s:' % UiStrings().Theme, self)
|
||||
self.theme_label = QtGui.QLabel('%s:' % UiStrings().Theme, widget)
|
||||
self.theme_label.setMargin(3)
|
||||
self.theme_label.setObjectName('theme_label')
|
||||
self.toolbar.add_toolbar_widget(self.theme_label)
|
||||
@ -133,7 +138,7 @@ class ServiceManagerDialog(object):
|
||||
self.toolbar.setObjectName('toolbar')
|
||||
self.layout.addWidget(self.toolbar)
|
||||
# Create the service manager list
|
||||
self.service_manager_list = ServiceManagerList(self)
|
||||
self.service_manager_list = ServiceManagerList(widget)
|
||||
self.service_manager_list.setEditTriggers(
|
||||
QtGui.QAbstractItemView.CurrentChanged |
|
||||
QtGui.QAbstractItemView.DoubleClicked |
|
||||
@ -151,51 +156,61 @@ class ServiceManagerDialog(object):
|
||||
self.service_manager_list.__class__.dropEvent = self.drop_event
|
||||
self.layout.addWidget(self.service_manager_list)
|
||||
# Add the bottom toolbar
|
||||
self.order_toolbar = OpenLPToolbar(self)
|
||||
self.order_toolbar = OpenLPToolbar(widget)
|
||||
action_list = ActionList.get_instance()
|
||||
action_list.add_category(UiStrings().Service, CategoryOrder.standard_toolbar)
|
||||
self.service_manager_list.move_top = self.order_toolbar.add_toolbar_action('moveTop',
|
||||
self.service_manager_list.move_top = self.order_toolbar.add_toolbar_action(
|
||||
'moveTop',
|
||||
text=translate('OpenLP.ServiceManager', 'Move to &top'), icon=':/services/service_top.png',
|
||||
tooltip=translate('OpenLP.ServiceManager', 'Move item to the top of the service.'),
|
||||
can_shortcuts=True, category=UiStrings().Service, triggers=self.onServiceTop)
|
||||
self.service_manager_list.move_up = self.order_toolbar.add_toolbar_action('moveUp',
|
||||
can_shortcuts=True, category=UiStrings().Service, triggers=self.on_service_top)
|
||||
self.service_manager_list.move_up = self.order_toolbar.add_toolbar_action(
|
||||
'moveUp',
|
||||
text=translate('OpenLP.ServiceManager', 'Move &up'), icon=':/services/service_up.png',
|
||||
tooltip=translate('OpenLP.ServiceManager', 'Move item up one position in the service.'),
|
||||
can_shortcuts=True, category=UiStrings().Service, triggers=self.onServiceUp)
|
||||
self.service_manager_list.move_down = self.order_toolbar.add_toolbar_action('moveDown',
|
||||
can_shortcuts=True, category=UiStrings().Service, triggers=self.on_service_up)
|
||||
self.service_manager_list.move_down = self.order_toolbar.add_toolbar_action(
|
||||
'moveDown',
|
||||
text=translate('OpenLP.ServiceManager', 'Move &down'), icon=':/services/service_down.png',
|
||||
tooltip=translate('OpenLP.ServiceManager', 'Move item down one position in the service.'),
|
||||
can_shortcuts=True, category=UiStrings().Service, triggers=self.onServiceDown)
|
||||
self.service_manager_list.move_bottom = self.order_toolbar.add_toolbar_action('moveBottom',
|
||||
can_shortcuts=True, category=UiStrings().Service, triggers=self.on_service_down)
|
||||
self.service_manager_list.move_bottom = self.order_toolbar.add_toolbar_action(
|
||||
'moveBottom',
|
||||
text=translate('OpenLP.ServiceManager', 'Move to &bottom'), icon=':/services/service_bottom.png',
|
||||
tooltip=translate('OpenLP.ServiceManager', 'Move item to the end of the service.'),
|
||||
can_shortcuts=True, category=UiStrings().Service, triggers=self.onServiceEnd)
|
||||
self.service_manager_list.down = self.order_toolbar.add_toolbar_action('down',
|
||||
can_shortcuts=True, category=UiStrings().Service, triggers=self.on_service_end)
|
||||
self.service_manager_list.down = self.order_toolbar.add_toolbar_action(
|
||||
'down',
|
||||
text=translate('OpenLP.ServiceManager', 'Move &down'), can_shortcuts=True,
|
||||
tooltip=translate('OpenLP.ServiceManager', 'Moves the selection down the window.'), visible=False,
|
||||
triggers=self.on_move_selection_down)
|
||||
action_list.add_action(self.service_manager_list.down)
|
||||
self.service_manager_list.up = self.order_toolbar.add_toolbar_action('up',
|
||||
self.service_manager_list.up = self.order_toolbar.add_toolbar_action(
|
||||
'up',
|
||||
text=translate('OpenLP.ServiceManager', 'Move up'), can_shortcuts=True,
|
||||
tooltip=translate('OpenLP.ServiceManager', 'Moves the selection up the window.'), visible=False,
|
||||
triggers=self.on_move_selection_up)
|
||||
action_list.add_action(self.service_manager_list.up)
|
||||
self.order_toolbar.addSeparator()
|
||||
self.service_manager_list.delete = self.order_toolbar.add_toolbar_action('delete', can_shortcuts=True,
|
||||
self.service_manager_list.delete = self.order_toolbar.add_toolbar_action(
|
||||
'delete', can_shortcuts=True,
|
||||
text=translate('OpenLP.ServiceManager', '&Delete From Service'), icon=':/general/general_delete.png',
|
||||
tooltip=translate('OpenLP.ServiceManager', 'Delete the selected item from the service.'),
|
||||
triggers=self.onDeleteFromService)
|
||||
triggers=self.on_delete_from_service)
|
||||
self.order_toolbar.addSeparator()
|
||||
self.service_manager_list.expand = self.order_toolbar.add_toolbar_action('expand', can_shortcuts=True,
|
||||
self.service_manager_list.expand = self.order_toolbar.add_toolbar_action(
|
||||
'expand', can_shortcuts=True,
|
||||
text=translate('OpenLP.ServiceManager', '&Expand all'), icon=':/services/service_expand_all.png',
|
||||
tooltip=translate('OpenLP.ServiceManager', 'Expand all the service items.'),
|
||||
category=UiStrings().Service, triggers=self.on_expand_all)
|
||||
self.service_manager_list.collapse = self.order_toolbar.add_toolbar_action('collapse', can_shortcuts=True,
|
||||
self.service_manager_list.collapse = self.order_toolbar.add_toolbar_action(
|
||||
'collapse', can_shortcuts=True,
|
||||
text=translate('OpenLP.ServiceManager', '&Collapse all'), icon=':/services/service_collapse_all.png',
|
||||
tooltip=translate('OpenLP.ServiceManager', 'Collapse all the service items.'),
|
||||
category=UiStrings().Service, triggers=self.on_collapse_all)
|
||||
self.order_toolbar.addSeparator()
|
||||
self.service_manager_list.make_live = self.order_toolbar.add_toolbar_action('make_live', can_shortcuts=True,
|
||||
self.service_manager_list.make_live = self.order_toolbar.add_toolbar_action(
|
||||
'make_live', can_shortcuts=True,
|
||||
text=translate('OpenLP.ServiceManager', 'Go Live'), icon=':/general/general_live.png',
|
||||
tooltip=translate('OpenLP.ServiceManager', 'Send the selected item to Live.'),
|
||||
category=UiStrings().Service,
|
||||
@ -276,7 +291,7 @@ class ServiceManagerDialog(object):
|
||||
self.service_manager_list.down,
|
||||
self.service_manager_list.expand,
|
||||
self.service_manager_list.collapse
|
||||
])
|
||||
])
|
||||
Registry().register_function('theme_update_list', self.update_theme_list)
|
||||
Registry().register_function('config_screen_changed', self.regenerate_service_items)
|
||||
Registry().register_function('theme_update_global', self.theme_change)
|
||||
@ -286,13 +301,12 @@ class ServiceManagerDialog(object):
|
||||
"""
|
||||
Accept Drag events
|
||||
|
||||
``event``
|
||||
Handle of the event pint passed
|
||||
:param event: Handle of the event passed
|
||||
"""
|
||||
event.accept()
|
||||
|
||||
|
||||
class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
||||
class ServiceManager(OpenLPMixin, RegistryMixin, QtGui.QWidget, Ui_ServiceManager):
|
||||
"""
|
||||
Manages the services. This involves taking text strings from plugins and adding them to the service. This service
|
||||
can then be zipped up with all the resources used into one OSZ or oszl file for use on any OpenLP v2 installation.
|
||||
@ -305,7 +319,6 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
||||
super(ServiceManager, self).__init__(parent)
|
||||
self.active = build_icon(':/media/auto-start_active.png')
|
||||
self.inactive = build_icon(':/media/auto-start_inactive.png')
|
||||
Registry().register('service_manager', self)
|
||||
self.service_items = []
|
||||
self.suffixes = []
|
||||
self.drop_position = 0
|
||||
@ -314,20 +327,28 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
||||
self._modified = False
|
||||
self._file_name = ''
|
||||
self.service_has_all_original_files = True
|
||||
self.service_note_form = ServiceNoteForm()
|
||||
self.service_item_edit_form = ServiceItemEditForm()
|
||||
self.start_time_form = StartTimeForm()
|
||||
# start with the layout
|
||||
self.layout = QtGui.QVBoxLayout(self)
|
||||
self.layout.setSpacing(0)
|
||||
self.layout.setMargin(0)
|
||||
|
||||
def bootstrap_initialise(self):
|
||||
"""
|
||||
To be called as part of initialisation
|
||||
"""
|
||||
self.setup_ui(self)
|
||||
# Need to use event as called across threads and UI is updated
|
||||
QtCore.QObject.connect(self, QtCore.SIGNAL('servicemanager_set_item'), self.on_set_item)
|
||||
|
||||
def bootstrap_post_set_up(self):
|
||||
"""
|
||||
Can be set up as a late setup
|
||||
"""
|
||||
self.service_note_form = ServiceNoteForm()
|
||||
self.service_item_edit_form = ServiceItemEditForm()
|
||||
self.start_time_form = StartTimeForm()
|
||||
|
||||
def set_modified(self, modified=True):
|
||||
"""
|
||||
Setter for property "modified". Sets whether or not the current service has been modified.
|
||||
|
||||
:param modified: Indicates if the service has new or removed items. Used to trigger a remote update.
|
||||
"""
|
||||
if modified:
|
||||
self.service_id += 1
|
||||
@ -344,6 +365,8 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
||||
def set_file_name(self, file_name):
|
||||
"""
|
||||
Setter for service file.
|
||||
|
||||
:param file_name: The service file name
|
||||
"""
|
||||
self._file_name = str(file_name)
|
||||
self.main_window.set_service_modified(self.is_modified(), self.short_file_name())
|
||||
@ -369,19 +392,20 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
||||
"""
|
||||
self.suffixes = []
|
||||
|
||||
def supported_suffixes(self, suffix):
|
||||
def supported_suffixes(self, suffix_list):
|
||||
"""
|
||||
Adds Suffixes supported to the master list. Called from Plugins.
|
||||
|
||||
``suffix``
|
||||
New Suffix to be supported
|
||||
:param suffix_list: New Suffix's to be supported
|
||||
"""
|
||||
if not suffix in self.suffixes:
|
||||
self.suffixes.append(suffix)
|
||||
for suffix in suffix_list:
|
||||
if not suffix in self.suffixes:
|
||||
self.suffixes.append(suffix)
|
||||
|
||||
def on_new_service_clicked(self):
|
||||
def on_new_service_clicked(self, field=None):
|
||||
"""
|
||||
Create a new service.
|
||||
:param field:
|
||||
"""
|
||||
if self.is_modified():
|
||||
result = self.save_modified_service()
|
||||
@ -396,8 +420,7 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
||||
"""
|
||||
Loads the service file and saves the existing one it there is one unchanged.
|
||||
|
||||
``load_file``
|
||||
The service file to the loaded. Will be None is from menu so selection will be required.
|
||||
:param load_file: The service file to the loaded. Will be None is from menu so selection will be required.
|
||||
"""
|
||||
if self.is_modified():
|
||||
result = self.save_modified_service()
|
||||
@ -432,9 +455,10 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
||||
QtGui.QMessageBox.Save | QtGui.QMessageBox.Discard |
|
||||
QtGui.QMessageBox.Cancel, QtGui.QMessageBox.Save)
|
||||
|
||||
def on_recent_service_clicked(self):
|
||||
def on_recent_service_clicked(self, field=None):
|
||||
"""
|
||||
Load a recent file as the service triggered by mainwindow recent service list.
|
||||
:param field:
|
||||
"""
|
||||
sender = self.sender()
|
||||
self.load_file(sender.data())
|
||||
@ -451,6 +475,19 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
||||
Settings().setValue('servicemanager/last file', '')
|
||||
self.plugin_manager.new_service_created()
|
||||
|
||||
def create_basic_service(self):
|
||||
"""
|
||||
Create the initial service array with the base items to be saved.
|
||||
|
||||
:return service array
|
||||
"""
|
||||
service = []
|
||||
core = {'lite-service': self._save_lite,
|
||||
'service-theme': self.service_theme
|
||||
}
|
||||
service.append({'openlp_core': core})
|
||||
return service
|
||||
|
||||
def save_file(self):
|
||||
"""
|
||||
Save the current service file.
|
||||
@ -464,14 +501,14 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
||||
temp_file, temp_file_name = mkstemp('.osz', 'openlp_')
|
||||
# We don't need the file handle.
|
||||
os.close(temp_file)
|
||||
log.debug(temp_file_name)
|
||||
self.log_debug(temp_file_name)
|
||||
path_file_name = str(self.file_name())
|
||||
path, file_name = os.path.split(path_file_name)
|
||||
base_name = os.path.splitext(file_name)[0]
|
||||
service_file_name = '%s.osj' % base_name
|
||||
log.debug('ServiceManager.save_file - %s', path_file_name)
|
||||
self.log_debug('ServiceManager.save_file - %s' % path_file_name)
|
||||
Settings().setValue(self.main_window.service_manager_settings_section + '/last directory', path)
|
||||
service = []
|
||||
service = self.create_basic_service()
|
||||
write_list = []
|
||||
missing_list = []
|
||||
audio_files = []
|
||||
@ -522,11 +559,11 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
||||
for file_item in write_list:
|
||||
file_size = os.path.getsize(file_item)
|
||||
total_size += file_size
|
||||
log.debug('ServiceManager.save_file - ZIP contents size is %i bytes' % total_size)
|
||||
self.log_debug('ServiceManager.save_file - ZIP contents size is %i bytes' % total_size)
|
||||
service_content = json.dumps(service)
|
||||
# Usual Zip file cannot exceed 2GiB, file with Zip64 cannot be extracted using unzip in UNIX.
|
||||
allow_zip_64 = (total_size > 2147483648 + len(service_content))
|
||||
log.debug('ServiceManager.save_file - allowZip64 is %s' % allow_zip_64)
|
||||
self.log_debug('ServiceManager.save_file - allowZip64 is %s' % allow_zip_64)
|
||||
zip_file = None
|
||||
success = True
|
||||
self.main_window.increment_progress_bar()
|
||||
@ -549,7 +586,7 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
||||
shutil.copy(audio_from, save_file)
|
||||
zip_file.write(audio_from, audio_to)
|
||||
except IOError:
|
||||
log.exception('Failed to save service to disk: %s', temp_file_name)
|
||||
self.log_exception('Failed to save service to disk: %s' % temp_file_name)
|
||||
self.main_window.error_message(translate('OpenLP.ServiceManager', 'Error Saving File'),
|
||||
translate('OpenLP.ServiceManager', 'There was an error saving your file.'))
|
||||
success = False
|
||||
@ -578,14 +615,14 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
||||
temp_file, temp_file_name = mkstemp('.oszl', 'openlp_')
|
||||
# We don't need the file handle.
|
||||
os.close(temp_file)
|
||||
log.debug(temp_file_name)
|
||||
self.log_debug(temp_file_name)
|
||||
path_file_name = str(self.file_name())
|
||||
path, file_name = os.path.split(path_file_name)
|
||||
base_name = os.path.splitext(file_name)[0]
|
||||
service_file_name = '%s.osj' % base_name
|
||||
log.debug('ServiceManager.save_file - %s', path_file_name)
|
||||
self.log_debug('ServiceManager.save_file - %s' % path_file_name)
|
||||
Settings().setValue(self.main_window.service_manager_settings_section + '/last directory', path)
|
||||
service = []
|
||||
service = self.create_basic_service()
|
||||
self.application.set_busy_cursor()
|
||||
# Number of items + 1 to zip it
|
||||
self.main_window.display_progress_bar(len(self.service_items) + 1)
|
||||
@ -604,7 +641,7 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
||||
# First we add service contents.
|
||||
zip_file.writestr(service_file_name, service_content)
|
||||
except IOError:
|
||||
log.exception('Failed to save service to disk: %s', temp_file_name)
|
||||
self.log_exception('Failed to save service to disk: %s', temp_file_name)
|
||||
self.main_window.error_message(translate('OpenLP.ServiceManager', 'Error Saving File'),
|
||||
translate('OpenLP.ServiceManager', 'There was an error saving your file.'))
|
||||
success = False
|
||||
@ -623,7 +660,7 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
||||
delete_file(temp_file_name)
|
||||
return success
|
||||
|
||||
def save_file_as(self):
|
||||
def save_file_as(self, field=None):
|
||||
"""
|
||||
Get a file name and then call :func:`ServiceManager.save_file` to save the file.
|
||||
"""
|
||||
@ -656,8 +693,8 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
||||
'(*.oszl)'))
|
||||
else:
|
||||
file_name = QtGui.QFileDialog.getSaveFileName(self.main_window, UiStrings().SaveService, path,
|
||||
translate('OpenLP.ServiceManager', 'OpenLP Service Files (*'
|
||||
'.osz);;'))
|
||||
translate('OpenLP.ServiceManager',
|
||||
'OpenLP Service Files (*.osz);;'))
|
||||
if not file_name:
|
||||
return False
|
||||
if os.path.splitext(file_name)[1] == '':
|
||||
@ -668,9 +705,10 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
||||
self.set_file_name(file_name)
|
||||
self.decide_save_method()
|
||||
|
||||
def decide_save_method(self):
|
||||
def decide_save_method(self, field=None):
|
||||
"""
|
||||
Determine which type of save method to use.
|
||||
:param field:
|
||||
"""
|
||||
if not self.file_name():
|
||||
return self.save_file_as()
|
||||
@ -682,6 +720,7 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
||||
def load_file(self, file_name):
|
||||
"""
|
||||
Load an existing service file
|
||||
:param file_name:
|
||||
"""
|
||||
if not file_name:
|
||||
return False
|
||||
@ -697,14 +736,14 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
||||
try:
|
||||
ucs_file = zip_info.filename
|
||||
except UnicodeDecodeError:
|
||||
log.exception('file_name "%s" is not valid UTF-8' % zip_info.file_name)
|
||||
self.log_exception('file_name "%s" is not valid UTF-8' % zip_info.file_name)
|
||||
critical_error_message_box(message=translate('OpenLP.ServiceManager',
|
||||
'File is not a valid service.\n The content encoding is not UTF-8.'))
|
||||
continue
|
||||
os_file = ucs_file.replace('/', os.path.sep)
|
||||
if not os_file.startswith('audio'):
|
||||
os_file = os.path.split(os_file)[1]
|
||||
log.debug('Extract file: %s', os_file)
|
||||
self.log_debug('Extract file: %s' % os_file)
|
||||
zip_info.filename = os_file
|
||||
zip_file.extract(zip_info, self.service_path)
|
||||
if os_file.endswith('osj') or os_file.endswith('osd'):
|
||||
@ -722,38 +761,26 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
||||
self.new_file()
|
||||
self.set_file_name(file_name)
|
||||
self.main_window.display_progress_bar(len(items))
|
||||
for item in items:
|
||||
self.main_window.increment_progress_bar()
|
||||
service_item = ServiceItem()
|
||||
if self._save_lite:
|
||||
service_item.set_from_service(item)
|
||||
else:
|
||||
service_item.set_from_service(item, self.service_path)
|
||||
service_item.validate_item(self.suffixes)
|
||||
if service_item.is_capable(ItemCapabilities.OnLoadUpdate):
|
||||
new_item = Registry().get(service_item.name).service_load(service_item)
|
||||
if new_item:
|
||||
service_item = new_item
|
||||
self.add_service_item(service_item, repaint=False)
|
||||
self.process_service_items(items)
|
||||
delete_file(p_file)
|
||||
self.main_window.add_recent_file(file_name)
|
||||
self.set_modified(False)
|
||||
Settings().setValue('servicemanager/last file', file_name)
|
||||
else:
|
||||
critical_error_message_box(message=translate('OpenLP.ServiceManager', 'File is not a valid service.'))
|
||||
log.error('File contains no service data')
|
||||
self.log_error('File contains no service data')
|
||||
except (IOError, NameError, zipfile.BadZipfile):
|
||||
log.exception('Problem loading service file %s' % file_name)
|
||||
self.log_exception('Problem loading service file %s' % file_name)
|
||||
critical_error_message_box(message=translate('OpenLP.ServiceManager',
|
||||
'File could not be opened because it is corrupt.'))
|
||||
except zipfile.BadZipfile:
|
||||
if os.path.getsize(file_name) == 0:
|
||||
log.exception('Service file is zero sized: %s' % file_name)
|
||||
self.log_exception('Service file is zero sized: %s' % file_name)
|
||||
QtGui.QMessageBox.information(self, translate('OpenLP.ServiceManager', 'Empty File'),
|
||||
translate('OpenLP.ServiceManager', 'This service file does not contain '
|
||||
'any data.'))
|
||||
else:
|
||||
log.exception('Service file is cannot be extracted as zip: %s' % file_name)
|
||||
self.log_exception('Service file is cannot be extracted as zip: %s' % file_name)
|
||||
QtGui.QMessageBox.information(self, translate('OpenLP.ServiceManager', 'Corrupt File'),
|
||||
translate('OpenLP.ServiceManager',
|
||||
'This file is either corrupt or it is not an OpenLP 2 service file.'))
|
||||
@ -768,6 +795,34 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
||||
self.application.set_normal_cursor()
|
||||
self.repaint_service_list(-1, -1)
|
||||
|
||||
def process_service_items(self, service_items):
|
||||
"""
|
||||
Process all the array of service items loaded from the saved service
|
||||
|
||||
:param service_items: list of service_items
|
||||
"""
|
||||
for item in service_items:
|
||||
self.main_window.increment_progress_bar()
|
||||
service_item = ServiceItem()
|
||||
if 'openlp_core' in item:
|
||||
item = item.get('openlp_core')
|
||||
theme = item.get('service-theme', None)
|
||||
if theme:
|
||||
find_and_set_in_combo_box(self.theme_combo_box, theme, set_missing=False)
|
||||
if theme == self.theme_combo_box.currentText():
|
||||
self.renderer.set_service_theme(theme)
|
||||
else:
|
||||
if self._save_lite:
|
||||
service_item.set_from_service(item)
|
||||
else:
|
||||
service_item.set_from_service(item, self.service_path)
|
||||
service_item.validate_item(self.suffixes)
|
||||
if service_item.is_capable(ItemCapabilities.OnLoadUpdate):
|
||||
new_item = Registry().get(service_item.name).service_load(service_item)
|
||||
if new_item:
|
||||
service_item = new_item
|
||||
self.add_service_item(service_item, repaint=False)
|
||||
|
||||
def load_last_file(self):
|
||||
"""
|
||||
Load the last service item from the service manager when the service was last closed. Can be blank if there was
|
||||
@ -780,6 +835,8 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
||||
def context_menu(self, point):
|
||||
"""
|
||||
The Right click context menu from the Serviceitem list
|
||||
|
||||
:param point: The location of the cursor.
|
||||
"""
|
||||
item = self.service_manager_list.itemAt(point)
|
||||
if item is None:
|
||||
@ -843,9 +900,10 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
||||
theme_action.setChecked(True)
|
||||
self.menu.exec_(self.service_manager_list.mapToGlobal(point))
|
||||
|
||||
def on_service_item_note_form(self):
|
||||
def on_service_item_note_form(self, field=None):
|
||||
"""
|
||||
Allow the service note to be edited
|
||||
:param field:
|
||||
"""
|
||||
item = self.find_service_item()[0]
|
||||
self.service_note_form.text_edit.setPlainText(self.service_items[item]['service_item'].notes)
|
||||
@ -854,18 +912,21 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
||||
self.repaint_service_list(item, -1)
|
||||
self.set_modified()
|
||||
|
||||
def on_start_time_form(self):
|
||||
def on_start_time_form(self, field=None):
|
||||
"""
|
||||
Opens a dialog to type in service item notes.
|
||||
:param field:
|
||||
"""
|
||||
item = self.find_service_item()[0]
|
||||
self.start_time_form.item = self.service_items[item]
|
||||
if self.start_time_form.exec_():
|
||||
self.repaint_service_list(item, -1)
|
||||
|
||||
def toggle_auto_play_slides_once(self):
|
||||
def toggle_auto_play_slides_once(self, field=None):
|
||||
"""
|
||||
Toggle Auto play slide once. Inverts auto play once option for the item
|
||||
|
||||
:param field:
|
||||
"""
|
||||
item = self.find_service_item()[0]
|
||||
service_item = self.service_items[item]['service_item']
|
||||
@ -878,9 +939,11 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
||||
self.main_window.general_settings_section + '/loop delay')
|
||||
self.set_modified()
|
||||
|
||||
def toggle_auto_play_slides_loop(self):
|
||||
def toggle_auto_play_slides_loop(self, field=None):
|
||||
"""
|
||||
Toggle Auto play slide loop.
|
||||
|
||||
:param field:
|
||||
"""
|
||||
item = self.find_service_item()[0]
|
||||
service_item = self.service_items[item]['service_item']
|
||||
@ -893,9 +956,10 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
||||
self.main_window.general_settings_section + '/loop delay')
|
||||
self.set_modified()
|
||||
|
||||
def on_timed_slide_interval(self):
|
||||
def on_timed_slide_interval(self, field=None):
|
||||
"""
|
||||
Shows input dialog for enter interval in seconds for delay
|
||||
:param field:
|
||||
"""
|
||||
item = self.find_service_item()[0]
|
||||
service_item = self.service_items[item]['service_item']
|
||||
@ -926,9 +990,10 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
||||
self.service_items[item]['service_item'].will_auto_start = \
|
||||
not self.service_items[item]['service_item'].will_auto_start
|
||||
|
||||
def on_service_item_edit_form(self):
|
||||
def on_service_item_edit_form(self, field=None):
|
||||
"""
|
||||
Opens a dialog to edit the service item and update the service display if changes are saved.
|
||||
:param field:
|
||||
"""
|
||||
item = self.find_service_item()[0]
|
||||
self.service_item_edit_form.set_service_item(self.service_items[item]['service_item'])
|
||||
@ -941,11 +1006,8 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
||||
Called by the SlideController to request a preview item be made live and allows the next preview to be updated
|
||||
if relevant.
|
||||
|
||||
``unique_identifier``
|
||||
Reference to the service_item
|
||||
|
||||
``row``
|
||||
individual row number
|
||||
:param unique_identifier: Reference to the service_item
|
||||
:param row: individual row number
|
||||
"""
|
||||
for sitem in self.service_items:
|
||||
if sitem['service_item'].unique_identifier == unique_identifier:
|
||||
@ -958,9 +1020,9 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
||||
"""
|
||||
Called by the SlideController to select the next service item.
|
||||
"""
|
||||
if not self.service_manager_list.selected_items():
|
||||
if not self.service_manager_list.selectedItems():
|
||||
return
|
||||
selected = self.service_manager_list.selected_items()[0]
|
||||
selected = self.service_manager_list.selectedItems()[0]
|
||||
look_for = 0
|
||||
service_iterator = QtGui.QTreeWidgetItemIterator(self.service_manager_list)
|
||||
while service_iterator.value():
|
||||
@ -976,13 +1038,11 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
||||
"""
|
||||
Called by the SlideController to select the previous service item.
|
||||
|
||||
``last_slide``
|
||||
Is this the last slide in the service_item
|
||||
|
||||
:param last_slide: Is this the last slide in the service_item.
|
||||
"""
|
||||
if not self.service_manager_list.selected_items():
|
||||
if not self.service_manager_list.selectedItems():
|
||||
return
|
||||
selected = self.service_manager_list.selected_items()[0]
|
||||
selected = self.service_manager_list.selectedItems()[0]
|
||||
prev_item = None
|
||||
prev_item_last_slide = None
|
||||
service_iterator = QtGui.QTreeWidgetItemIterator(self.service_manager_list)
|
||||
@ -1006,17 +1066,22 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
||||
prev_item_last_slide = service_iterator.value()
|
||||
service_iterator += 1
|
||||
|
||||
def on_set_item(self, message):
|
||||
def on_set_item(self, message, field=None):
|
||||
"""
|
||||
Called by a signal to select a specific item and make it live usually from remote.
|
||||
|
||||
:param field:
|
||||
:param message: The data passed in from a remove message
|
||||
"""
|
||||
self.set_item(int(message))
|
||||
|
||||
def set_item(self, index):
|
||||
"""
|
||||
Makes a specific item in the service live.
|
||||
|
||||
:param index: The index of the service item list to be actioned.
|
||||
"""
|
||||
if index >= 0 and index < self.service_manager_list.topLevelItemCount():
|
||||
if 0 >= index < self.service_manager_list.topLevelItemCount():
|
||||
item = self.service_manager_list.topLevelItem(index)
|
||||
self.service_manager_list.setCurrentItem(item)
|
||||
self.make_live()
|
||||
@ -1041,9 +1106,10 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
||||
return
|
||||
self.service_manager_list.setCurrentItem(item_after)
|
||||
|
||||
def on_collapse_all(self):
|
||||
def on_collapse_all(self, field=None):
|
||||
"""
|
||||
Collapse all the service items.
|
||||
:param field:
|
||||
"""
|
||||
for item in self.service_items:
|
||||
item['expanded'] = False
|
||||
@ -1052,13 +1118,16 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
||||
def collapsed(self, item):
|
||||
"""
|
||||
Record if an item is collapsed. Used when repainting the list to get the correct state.
|
||||
|
||||
:param item: The service item to be checked
|
||||
"""
|
||||
pos = item.data(0, QtCore.Qt.UserRole)
|
||||
self.service_items[pos - 1]['expanded'] = False
|
||||
|
||||
def on_expand_all(self):
|
||||
def on_expand_all(self, field=None):
|
||||
"""
|
||||
Collapse all the service items.
|
||||
:param field:
|
||||
"""
|
||||
for item in self.service_items:
|
||||
item['expanded'] = True
|
||||
@ -1067,13 +1136,16 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
||||
def expanded(self, item):
|
||||
"""
|
||||
Record if an item is collapsed. Used when repainting the list to get the correct state.
|
||||
|
||||
:param item: The service item to be checked
|
||||
"""
|
||||
pos = item.data(0, QtCore.Qt.UserRole)
|
||||
self.service_items[pos - 1]['expanded'] = True
|
||||
|
||||
def onServiceTop(self):
|
||||
def on_service_top(self, field=None):
|
||||
"""
|
||||
Move the current ServiceItem to the top of the list.
|
||||
:param field:
|
||||
"""
|
||||
item, child = self.find_service_item()
|
||||
if item < len(self.service_items) and item != -1:
|
||||
@ -1083,9 +1155,10 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
||||
self.repaint_service_list(0, child)
|
||||
self.set_modified()
|
||||
|
||||
def onServiceUp(self):
|
||||
def on_service_up(self, field=None):
|
||||
"""
|
||||
Move the current ServiceItem one position up in the list.
|
||||
:param field:
|
||||
"""
|
||||
item, child = self.find_service_item()
|
||||
if item > 0:
|
||||
@ -1095,9 +1168,10 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
||||
self.repaint_service_list(item - 1, child)
|
||||
self.set_modified()
|
||||
|
||||
def onServiceDown(self):
|
||||
def on_service_down(self, field=None):
|
||||
"""
|
||||
Move the current ServiceItem one position down in the list.
|
||||
:param field:
|
||||
"""
|
||||
item, child = self.find_service_item()
|
||||
if item < len(self.service_items) and item != -1:
|
||||
@ -1107,9 +1181,10 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
||||
self.repaint_service_list(item + 1, child)
|
||||
self.set_modified()
|
||||
|
||||
def onServiceEnd(self):
|
||||
def on_service_end(self, field=None):
|
||||
"""
|
||||
Move the current ServiceItem to the bottom of the list.
|
||||
:param field:
|
||||
"""
|
||||
item, child = self.find_service_item()
|
||||
if item < len(self.service_items) and item != -1:
|
||||
@ -1119,9 +1194,10 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
||||
self.repaint_service_list(len(self.service_items) - 1, child)
|
||||
self.set_modified()
|
||||
|
||||
def onDeleteFromService(self):
|
||||
def on_delete_from_service(self, field=None):
|
||||
"""
|
||||
Remove the current ServiceItem from the list.
|
||||
:param field:
|
||||
"""
|
||||
item = self.find_service_item()[0]
|
||||
if item != -1:
|
||||
@ -1134,11 +1210,8 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
||||
Clear the existing service list and prepaint all the items. This is used when moving items as the move takes
|
||||
place in a supporting list, and when regenerating all the items due to theme changes.
|
||||
|
||||
``service_item``
|
||||
The item which changed. (int)
|
||||
|
||||
``service_item_child``
|
||||
The child of the ``service_item``, which will be selected. (int)
|
||||
:param service_item: The item which changed. (int)
|
||||
:param service_item_child: The child of the ``service_item``, which will be selected. (int)
|
||||
"""
|
||||
# Correct order of items in array
|
||||
count = 1
|
||||
@ -1218,18 +1291,18 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
||||
"""
|
||||
Empties the service_path of temporary files on system exit.
|
||||
"""
|
||||
log.debug('Cleaning up service_path')
|
||||
for file_name in os.listdir(self.service_path):
|
||||
file_path = os.path.join(self.service_path, file_name)
|
||||
delete_file(file_path)
|
||||
if os.path.exists(os.path.join(self.service_path, 'audio')):
|
||||
shutil.rmtree(os.path.join(self.service_path, 'audio'), True)
|
||||
|
||||
def on_theme_combo_box_selected(self, currentIndex):
|
||||
def on_theme_combo_box_selected(self, current_index):
|
||||
"""
|
||||
Set the theme for the current service.
|
||||
|
||||
:param current_index: The combo box index for the selected item
|
||||
"""
|
||||
log.debug('on_theme_combo_box_selected')
|
||||
self.service_theme = self.theme_combo_box.currentText()
|
||||
self.renderer.set_service_theme(self.service_theme)
|
||||
Settings().setValue(self.main_window.service_manager_settings_section + '/service theme', self.service_theme)
|
||||
@ -1239,7 +1312,6 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
||||
"""
|
||||
The theme may have changed in the settings dialog so make sure the theme combo box is in the correct state.
|
||||
"""
|
||||
log.debug('theme_change')
|
||||
visible = self.renderer.theme_level == ThemeLevel.Global
|
||||
self.theme_label.setVisible(visible)
|
||||
self.theme_combo_box.setVisible(visible)
|
||||
@ -1247,9 +1319,10 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
||||
def regenerate_service_items(self, changed=False):
|
||||
"""
|
||||
Rebuild the service list as things have changed and a repaint is the easiest way to do this.
|
||||
|
||||
:param changed: True if the list has changed for new / removed items. False for a theme change.
|
||||
"""
|
||||
self.application.set_busy_cursor()
|
||||
log.debug('regenerate_service_items')
|
||||
# force reset of renderer as theme data has changed
|
||||
self.service_has_all_original_files = True
|
||||
if self.service_items:
|
||||
@ -1281,46 +1354,49 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
||||
self.repaint_service_list(-1, -1)
|
||||
self.application.set_normal_cursor()
|
||||
|
||||
def replace_service_item(self, newItem):
|
||||
def replace_service_item(self, new_item):
|
||||
"""
|
||||
Using the service item passed replace the one with the same edit id if found.
|
||||
|
||||
:param new_item: a new service item to up date an existing one.
|
||||
"""
|
||||
for item_count, item in enumerate(self.service_items):
|
||||
if item['service_item'].edit_id == newItem.edit_id and item['service_item'].name == newItem.name:
|
||||
newItem.render()
|
||||
newItem.merge(item['service_item'])
|
||||
item['service_item'] = newItem
|
||||
if item['service_item'].edit_id == new_item.edit_id and item['service_item'].name == new_item.name:
|
||||
new_item.render()
|
||||
new_item.merge(item['service_item'])
|
||||
item['service_item'] = new_item
|
||||
self.repaint_service_list(item_count + 1, 0)
|
||||
self.live_controller.replace_service_manager_item(newItem)
|
||||
self.live_controller.replace_service_manager_item(new_item)
|
||||
self.set_modified()
|
||||
|
||||
def add_service_item(self, item, rebuild=False, expand=None, replace=False, repaint=True, selected=False):
|
||||
"""
|
||||
Add a Service item to the list
|
||||
|
||||
``item``
|
||||
Service Item to be added
|
||||
|
||||
``expand``
|
||||
Override the default expand settings. (Tristate)
|
||||
:param item: Service Item to be added
|
||||
:param rebuild: Do we need to rebuild the live display (Default False)
|
||||
:param expand: Override the default expand settings. (Tristate)
|
||||
:param replace: Is the service item a replacement (Default False)
|
||||
:param repaint: Do we need to repaint the service item list (Default True)
|
||||
:param selected: Has the item been selected (Default False)
|
||||
"""
|
||||
# if not passed set to config value
|
||||
if expand is None:
|
||||
expand = Settings().value('advanced/expand service item')
|
||||
item.from_service = True
|
||||
if replace:
|
||||
sitem, child = self.find_service_item()
|
||||
item.merge(self.service_items[sitem]['service_item'])
|
||||
self.service_items[sitem]['service_item'] = item
|
||||
self.repaint_service_list(sitem, child)
|
||||
s_item, child = self.find_service_item()
|
||||
item.merge(self.service_items[s_item]['service_item'])
|
||||
self.service_items[s_item]['service_item'] = item
|
||||
self.repaint_service_list(s_item, child)
|
||||
self.live_controller.replace_service_manager_item(item)
|
||||
else:
|
||||
item.render()
|
||||
# nothing selected for dnd
|
||||
if self.drop_position == 0:
|
||||
if isinstance(item, list):
|
||||
for inditem in item:
|
||||
self.service_items.append({'service_item': inditem,
|
||||
for ind_item in item:
|
||||
self.service_items.append({'service_item': ind_item,
|
||||
'order': len(self.service_items) + 1,
|
||||
'expanded': expand, 'selected': selected})
|
||||
else:
|
||||
@ -1331,8 +1407,8 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
||||
self.repaint_service_list(len(self.service_items) - 1, -1)
|
||||
else:
|
||||
self.service_items.insert(self.drop_position,
|
||||
{'service_item': item, 'order': self.drop_position,
|
||||
'expanded': expand, 'selected': selected})
|
||||
{'service_item': item, 'order': self.drop_position,
|
||||
'expanded': expand, 'selected': selected})
|
||||
self.repaint_service_list(self.drop_position, -1)
|
||||
# if rebuilding list make sure live is fixed.
|
||||
if rebuild:
|
||||
@ -1364,9 +1440,10 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
||||
else:
|
||||
return self.service_items[item]['service_item']
|
||||
|
||||
def on_make_live(self):
|
||||
def on_make_live(self, field=None):
|
||||
"""
|
||||
Send the current item to the Live slide controller but triggered by a tablewidget click event.
|
||||
:param field:
|
||||
"""
|
||||
self.make_live()
|
||||
|
||||
@ -1374,8 +1451,7 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
||||
"""
|
||||
Send the current item to the Live slide controller
|
||||
|
||||
``row``
|
||||
Row number to be displayed if from preview. -1 is passed if the value is not set
|
||||
:param row: Row number to be displayed if from preview. -1 is passed if the value is not set
|
||||
"""
|
||||
item, child = self.find_service_item()
|
||||
# No items in service
|
||||
@ -1401,9 +1477,10 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
||||
'is missing or inactive'))
|
||||
self.application.set_normal_cursor()
|
||||
|
||||
def remote_edit(self):
|
||||
def remote_edit(self, field=None):
|
||||
"""
|
||||
Triggers a remote edit to a plugin to allow item to be edited.
|
||||
:param field:
|
||||
"""
|
||||
item = self.find_service_item()[0]
|
||||
if self.service_items[item]['service_item'].is_capable(ItemCapabilities.CanEdit):
|
||||
@ -1412,9 +1489,10 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
||||
if new_item:
|
||||
self.add_service_item(new_item, replace=True)
|
||||
|
||||
def create_custom(self):
|
||||
def create_custom(self, field=None):
|
||||
"""
|
||||
Saves the current text item as a custom slide
|
||||
:param field:
|
||||
"""
|
||||
item = self.find_service_item()[0]
|
||||
Registry().execute('custom_create_from_service', self.service_items[item]['service_item'])
|
||||
@ -1448,8 +1526,7 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
||||
Receive drop event and trigger an internal event to get the plugins to build and push the correct service item.
|
||||
The drag event payload carries the plugin name
|
||||
|
||||
``event``
|
||||
Handle of the event pint passed
|
||||
:param event: Handle of the event passed
|
||||
"""
|
||||
link = event.mimeData()
|
||||
if link.hasUrls():
|
||||
@ -1508,8 +1585,7 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
||||
"""
|
||||
Called from ThemeManager when the Themes have changed
|
||||
|
||||
``theme_list``
|
||||
A list of current themes to be displayed
|
||||
:param theme_list: A list of current themes to be displayed
|
||||
"""
|
||||
self.theme_combo_box.clear()
|
||||
self.theme_menu.clear()
|
||||
@ -1532,9 +1608,11 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
||||
self.renderer.set_service_theme(self.service_theme)
|
||||
self.regenerate_service_items()
|
||||
|
||||
def on_theme_change_action(self):
|
||||
def on_theme_change_action(self, field=None):
|
||||
"""
|
||||
Handles theme change events
|
||||
|
||||
:param field:
|
||||
"""
|
||||
theme = self.sender().objectName()
|
||||
# No object name means that the "Default" theme is supposed to be used.
|
||||
@ -1547,6 +1625,8 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
||||
def _get_parent_item_data(self, item):
|
||||
"""
|
||||
Finds and returns the parent item for any item
|
||||
|
||||
:param item: The service item list item to be checked.
|
||||
"""
|
||||
parent_item = item.parent()
|
||||
if parent_item is None:
|
||||
|
@ -51,7 +51,7 @@ class SettingsForm(QtGui.QDialog, Ui_SettingsDialog):
|
||||
Initialise the settings form
|
||||
"""
|
||||
Registry().register('settings_form', self)
|
||||
Registry().register_function('bootstrap_post_set_up', self.post_set_up)
|
||||
Registry().register_function('bootstrap_post_set_up', self.bootstrap_post_set_up)
|
||||
super(SettingsForm, self).__init__(parent)
|
||||
self.processes = []
|
||||
self.setupUi(self)
|
||||
@ -117,7 +117,7 @@ class SettingsForm(QtGui.QDialog, Ui_SettingsDialog):
|
||||
self.stacked_layout.widget(tabIndex).cancel()
|
||||
return QtGui.QDialog.reject(self)
|
||||
|
||||
def post_set_up(self):
|
||||
def bootstrap_post_set_up(self):
|
||||
"""
|
||||
Run any post-setup code for the tabs on the form
|
||||
"""
|
||||
|
@ -72,6 +72,11 @@ WIDE_MENU = [
|
||||
'desktop_screen_button'
|
||||
]
|
||||
|
||||
NON_TEXT_MENU = [
|
||||
'blank_screen_button',
|
||||
'desktop_screen_button'
|
||||
]
|
||||
|
||||
|
||||
class DisplayController(QtGui.QWidget):
|
||||
"""
|
||||
@ -116,6 +121,9 @@ class SlideController(DisplayController):
|
||||
self.screen_size_changed()
|
||||
|
||||
def initialise(self):
|
||||
"""
|
||||
Initialise the UI elements of the controller
|
||||
"""
|
||||
self.screens = ScreenList()
|
||||
try:
|
||||
self.ratio = self.screens.current['size'].width() / self.screens.current['size'].height()
|
||||
@ -442,6 +450,8 @@ class SlideController(DisplayController):
|
||||
def set_live_hot_keys(self, parent=None):
|
||||
"""
|
||||
Set the live hotkeys
|
||||
|
||||
:param parent: The parent UI object for actions to be added to.
|
||||
"""
|
||||
self.previous_service = create_action(parent, 'previousService',
|
||||
text=translate('OpenLP.SlideController', 'Previous Service'),
|
||||
@ -469,6 +479,8 @@ class SlideController(DisplayController):
|
||||
def toggle_display(self, action):
|
||||
"""
|
||||
Toggle the display settings triggered from remote messages.
|
||||
|
||||
:param action: The blank action to be processed.
|
||||
"""
|
||||
if action == 'blank' or action == 'hide':
|
||||
self.on_blank_display(True)
|
||||
@ -544,6 +556,8 @@ class SlideController(DisplayController):
|
||||
def __add_actions_to_widget(self, widget):
|
||||
"""
|
||||
Add actions to the widget specified by `widget`
|
||||
|
||||
:param widget: The UI widget for the actions
|
||||
"""
|
||||
widget.addActions([
|
||||
self.previous_item, self.next_item,
|
||||
@ -574,6 +588,8 @@ class SlideController(DisplayController):
|
||||
def on_controller_size_changed(self, width):
|
||||
"""
|
||||
Change layout of display control buttons on controller size change
|
||||
|
||||
:param width: the new width of the display
|
||||
"""
|
||||
if self.is_live:
|
||||
# Space used by the toolbar.
|
||||
@ -581,12 +597,24 @@ class SlideController(DisplayController):
|
||||
# Add the threshold to prevent flickering.
|
||||
if width > used_space + HIDE_MENU_THRESHOLD and self.hide_menu.isVisible():
|
||||
self.toolbar.set_widget_visible(NARROW_MENU, False)
|
||||
self.toolbar.set_widget_visible(WIDE_MENU)
|
||||
self.set_blank_menu()
|
||||
# Take away a threshold to prevent flickering.
|
||||
elif width < used_space - HIDE_MENU_THRESHOLD and not self.hide_menu.isVisible():
|
||||
self.toolbar.set_widget_visible(WIDE_MENU, False)
|
||||
self.set_blank_menu(False)
|
||||
self.toolbar.set_widget_visible(NARROW_MENU)
|
||||
|
||||
def set_blank_menu(self, visible=True):
|
||||
"""
|
||||
Set the correct menu type dependent on the service item type
|
||||
|
||||
:param visible: Do I need to hide the menu?
|
||||
"""
|
||||
self.toolbar.set_widget_visible(WIDE_MENU, False)
|
||||
if self.service_item and self.service_item.is_text():
|
||||
self.toolbar.set_widget_visible(WIDE_MENU, visible)
|
||||
else:
|
||||
self.toolbar.set_widget_visible(NON_TEXT_MENU, visible)
|
||||
|
||||
def on_song_bar_handler(self):
|
||||
"""
|
||||
Some song handler
|
||||
@ -612,6 +640,8 @@ class SlideController(DisplayController):
|
||||
def enable_tool_bar(self, item):
|
||||
"""
|
||||
Allows the toolbars to be reconfigured based on Controller Type and ServiceItem Type
|
||||
|
||||
:param item: current service item being processed
|
||||
"""
|
||||
if self.is_live:
|
||||
self.enable_live_tool_bar(item)
|
||||
@ -621,6 +651,8 @@ class SlideController(DisplayController):
|
||||
def enable_live_tool_bar(self, item):
|
||||
"""
|
||||
Allows the live toolbar to be customised
|
||||
|
||||
:param item: The current service item
|
||||
"""
|
||||
# Work-around for OS X, hide and then show the toolbar
|
||||
# See bug #791050
|
||||
@ -643,6 +675,7 @@ class SlideController(DisplayController):
|
||||
self.mediabar.show()
|
||||
self.previous_item.setVisible(not item.is_media())
|
||||
self.next_item.setVisible(not item.is_media())
|
||||
self.set_blank_menu()
|
||||
# Work-around for OS X, hide and then show the toolbar
|
||||
# See bug #791050
|
||||
self.toolbar.show()
|
||||
@ -977,11 +1010,13 @@ class SlideController(DisplayController):
|
||||
self.display.image(to_display)
|
||||
# reset the store used to display first image
|
||||
self.service_item.bg_image_bytes = None
|
||||
self.update_preview()
|
||||
self.selected_row = row
|
||||
self.update_preview()
|
||||
self.preview_widget.change_slide(row)
|
||||
Registry().execute('slidecontroller_%s_changed' % self.type_prefix, row)
|
||||
self.display.setFocus()
|
||||
if self.type_prefix == 'live':
|
||||
Registry().execute('websock_send', '')
|
||||
|
||||
def on_slide_change(self, row):
|
||||
"""
|
||||
@ -990,7 +1025,6 @@ class SlideController(DisplayController):
|
||||
self.preview_widget.change_slide(row)
|
||||
self.update_preview()
|
||||
self.selected_row = row
|
||||
Registry().execute('slidecontroller_%s_changed' % self.type_prefix, row)
|
||||
|
||||
def update_preview(self):
|
||||
"""
|
||||
|
@ -42,28 +42,25 @@ from openlp.core.lib import FileDialog, ImageSource, OpenLPToolbar, get_text_fil
|
||||
check_item_selected, create_thumb, validate_thumb
|
||||
from openlp.core.lib.theme import ThemeXML, BackgroundType
|
||||
from openlp.core.lib.ui import critical_error_message_box, create_widget_action
|
||||
from openlp.core.ui import FileRenameForm, ThemeForm, ThemeManagerHelper
|
||||
from openlp.core.ui import FileRenameForm, ThemeForm
|
||||
from openlp.core.utils import delete_file, get_locale_key, get_filesystem_encoding
|
||||
|
||||
|
||||
class ThemeManager(RegistryMixin, OpenLPMixin, QtGui.QWidget, ThemeManagerHelper):
|
||||
class Ui_ThemeManager(object):
|
||||
"""
|
||||
Manages the orders of Theme.
|
||||
UI part of the Theme Manager
|
||||
"""
|
||||
def __init__(self, parent=None):
|
||||
def setup_ui(self, widget):
|
||||
"""
|
||||
Constructor
|
||||
Define the UI
|
||||
:param widget: The screen object the the dialog is to be attached to.
|
||||
"""
|
||||
super(ThemeManager, self).__init__(parent)
|
||||
self.settings_section = 'themes'
|
||||
self.theme_form = ThemeForm(self)
|
||||
self.file_rename_form = FileRenameForm()
|
||||
# start with the layout
|
||||
self.layout = QtGui.QVBoxLayout(self)
|
||||
self.layout = QtGui.QVBoxLayout(widget)
|
||||
self.layout.setSpacing(0)
|
||||
self.layout.setMargin(0)
|
||||
self.layout.setObjectName('layout')
|
||||
self.toolbar = OpenLPToolbar(self)
|
||||
self.toolbar = OpenLPToolbar(widget)
|
||||
self.toolbar.setObjectName('toolbar')
|
||||
self.toolbar.add_toolbar_action('newTheme',
|
||||
text=UiStrings().NewTheme, icon=':/themes/theme_new.png',
|
||||
@ -96,7 +93,7 @@ class ThemeManager(RegistryMixin, OpenLPMixin, QtGui.QWidget, ThemeManagerHelper
|
||||
self.theme_widget = QtGui.QWidgetAction(self.toolbar)
|
||||
self.theme_widget.setObjectName('theme_widget')
|
||||
# create theme manager list
|
||||
self.theme_list_widget = QtGui.QListWidget(self)
|
||||
self.theme_list_widget = QtGui.QListWidget(widget)
|
||||
self.theme_list_widget.setAlternatingRowColors(True)
|
||||
self.theme_list_widget.setIconSize(QtCore.QSize(88, 50))
|
||||
self.theme_list_widget.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
|
||||
@ -128,7 +125,18 @@ class ThemeManager(RegistryMixin, OpenLPMixin, QtGui.QWidget, ThemeManagerHelper
|
||||
# Signals
|
||||
self.theme_list_widget.doubleClicked.connect(self.change_global_from_screen)
|
||||
self.theme_list_widget.currentItemChanged.connect(self.check_list_state)
|
||||
Registry().register_function('theme_update_global', self.change_global_from_tab)
|
||||
|
||||
|
||||
class ThemeManager(OpenLPMixin, RegistryMixin, QtGui.QWidget, Ui_ThemeManager):
|
||||
"""
|
||||
Manages the orders of Theme.
|
||||
"""
|
||||
def __init__(self, parent=None):
|
||||
"""
|
||||
Constructor
|
||||
"""
|
||||
super(ThemeManager, self).__init__(parent)
|
||||
self.settings_section = 'themes'
|
||||
# Variables
|
||||
self.theme_list = []
|
||||
self.old_background_image = None
|
||||
@ -137,7 +145,7 @@ class ThemeManager(RegistryMixin, OpenLPMixin, QtGui.QWidget, ThemeManagerHelper
|
||||
"""
|
||||
process the bootstrap initialise setup request
|
||||
"""
|
||||
self.log_debug('initialise called')
|
||||
self.setup_ui(self)
|
||||
self.global_theme = Settings().value(self.settings_section + '/global theme')
|
||||
self.build_theme_path()
|
||||
self.load_first_time_themes()
|
||||
@ -146,12 +154,28 @@ class ThemeManager(RegistryMixin, OpenLPMixin, QtGui.QWidget, ThemeManagerHelper
|
||||
"""
|
||||
process the bootstrap post setup request
|
||||
"""
|
||||
self.theme_form = ThemeForm(self)
|
||||
self.theme_form.path = self.path
|
||||
self.file_rename_form = FileRenameForm()
|
||||
Registry().register_function('theme_update_global', self.change_global_from_tab)
|
||||
self._push_themes()
|
||||
|
||||
def build_theme_path(self):
|
||||
"""
|
||||
Set up the theme path variables
|
||||
"""
|
||||
self.path = AppLocation.get_section_data_path(self.settings_section)
|
||||
check_directory_exists(self.path)
|
||||
self.thumb_path = os.path.join(self.path, 'thumbnails')
|
||||
check_directory_exists(self.thumb_path)
|
||||
|
||||
def check_list_state(self, item, field=None):
|
||||
"""
|
||||
If Default theme selected remove delete button.
|
||||
Note for some reason a dummy field is required. Nothing is passed!
|
||||
|
||||
:param field:
|
||||
:param item: Service Item to process
|
||||
"""
|
||||
if item is None:
|
||||
return
|
||||
@ -165,8 +189,9 @@ class ThemeManager(RegistryMixin, OpenLPMixin, QtGui.QWidget, ThemeManagerHelper
|
||||
|
||||
def context_menu(self, point):
|
||||
"""
|
||||
Build the Right Click Context menu and set state depending on
|
||||
the type of theme.
|
||||
Build the Right Click Context menu and set state depending on the type of theme.
|
||||
|
||||
:param point: The position of the mouse so the correct item can be found.
|
||||
"""
|
||||
item = self.theme_list_widget.itemAt(point)
|
||||
if item is None:
|
||||
@ -200,8 +225,9 @@ class ThemeManager(RegistryMixin, OpenLPMixin, QtGui.QWidget, ThemeManagerHelper
|
||||
|
||||
def change_global_from_screen(self, index=-1):
|
||||
"""
|
||||
Change the global theme when a theme is double clicked upon in the
|
||||
Theme Manager list
|
||||
Change the global theme when a theme is double clicked upon in the Theme Manager list.
|
||||
|
||||
:param index:
|
||||
"""
|
||||
selected_row = self.theme_list_widget.currentRow()
|
||||
for count in range(0, self.theme_list_widget.count()):
|
||||
@ -221,8 +247,9 @@ class ThemeManager(RegistryMixin, OpenLPMixin, QtGui.QWidget, ThemeManagerHelper
|
||||
|
||||
def on_add_theme(self, field=None):
|
||||
"""
|
||||
Loads a new theme with the default settings and then launches the theme
|
||||
editing form for the user to make their customisations.
|
||||
Loads a new theme with the default settings and then launches the theme editing form for the user to make
|
||||
their customisations.
|
||||
:param field:
|
||||
"""
|
||||
theme = ThemeXML()
|
||||
theme.set_default_header_footer()
|
||||
@ -233,6 +260,7 @@ class ThemeManager(RegistryMixin, OpenLPMixin, QtGui.QWidget, ThemeManagerHelper
|
||||
def on_rename_theme(self, field=None):
|
||||
"""
|
||||
Renames an existing theme to a new name
|
||||
:param field:
|
||||
"""
|
||||
if self._validate_theme_action(translate('OpenLP.ThemeManager', 'You must select a theme to rename.'),
|
||||
translate('OpenLP.ThemeManager', 'Rename Confirmation'),
|
||||
@ -257,6 +285,7 @@ class ThemeManager(RegistryMixin, OpenLPMixin, QtGui.QWidget, ThemeManagerHelper
|
||||
def on_copy_theme(self, field=None):
|
||||
"""
|
||||
Copies an existing theme to a new name
|
||||
:param field:
|
||||
"""
|
||||
item = self.theme_list_widget.currentItem()
|
||||
old_theme_name = item.data(QtCore.Qt.UserRole)
|
||||
@ -271,6 +300,9 @@ class ThemeManager(RegistryMixin, OpenLPMixin, QtGui.QWidget, ThemeManagerHelper
|
||||
def clone_theme_data(self, theme_data, new_theme_name):
|
||||
"""
|
||||
Takes a theme and makes a new copy of it as well as saving it.
|
||||
|
||||
:param theme_data: The theme to be used
|
||||
:param new_theme_name: The new theme name to save the data to
|
||||
"""
|
||||
save_to = None
|
||||
save_from = None
|
||||
@ -286,6 +318,7 @@ class ThemeManager(RegistryMixin, OpenLPMixin, QtGui.QWidget, ThemeManagerHelper
|
||||
"""
|
||||
Loads the settings for the theme that is to be edited and launches the
|
||||
theme editing form so the user can make their changes.
|
||||
:param field:
|
||||
"""
|
||||
if check_item_selected(self.theme_list_widget,
|
||||
translate('OpenLP.ThemeManager', 'You must select a theme to edit.')):
|
||||
@ -301,11 +334,12 @@ class ThemeManager(RegistryMixin, OpenLPMixin, QtGui.QWidget, ThemeManagerHelper
|
||||
|
||||
def on_delete_theme(self, field=None):
|
||||
"""
|
||||
Delete a theme
|
||||
Delete a theme triggered by the UI.
|
||||
:param field:
|
||||
"""
|
||||
if self._validate_theme_action(translate('OpenLP.ThemeManager', 'You must select a theme to delete.'),
|
||||
translate('OpenLP.ThemeManager', 'Delete Confirmation'),
|
||||
translate('OpenLP.ThemeManager', 'Delete %s theme?')):
|
||||
translate('OpenLP.ThemeManager', 'Delete Confirmation'),
|
||||
translate('OpenLP.ThemeManager', 'Delete %s theme?')):
|
||||
item = self.theme_list_widget.currentItem()
|
||||
theme = item.text()
|
||||
row = self.theme_list_widget.row(item)
|
||||
@ -320,8 +354,7 @@ class ThemeManager(RegistryMixin, OpenLPMixin, QtGui.QWidget, ThemeManagerHelper
|
||||
"""
|
||||
Delete a theme.
|
||||
|
||||
``theme``
|
||||
The theme to delete.
|
||||
:param theme: The theme to delete.
|
||||
"""
|
||||
self.theme_list.remove(theme)
|
||||
thumb = '%s.png' % theme
|
||||
@ -337,6 +370,7 @@ class ThemeManager(RegistryMixin, OpenLPMixin, QtGui.QWidget, ThemeManagerHelper
|
||||
def on_export_theme(self, field=None):
|
||||
"""
|
||||
Export the theme in a zip file
|
||||
:param field:
|
||||
"""
|
||||
item = self.theme_list_widget.currentItem()
|
||||
if item is None:
|
||||
@ -378,6 +412,7 @@ class ThemeManager(RegistryMixin, OpenLPMixin, QtGui.QWidget, ThemeManagerHelper
|
||||
"""
|
||||
Opens a file dialog to select the theme file(s) to import before attempting to extract OpenLP themes from
|
||||
those files. This process will load both OpenLP version 1 and version 2 themes.
|
||||
:param field:
|
||||
"""
|
||||
files = FileDialog.getOpenFileNames(self,
|
||||
translate('OpenLP.ThemeManager', 'Select Theme Import File'),
|
||||
@ -462,8 +497,8 @@ class ThemeManager(RegistryMixin, OpenLPMixin, QtGui.QWidget, ThemeManagerHelper
|
||||
"""
|
||||
Returns a theme object from an XML file
|
||||
|
||||
``theme_name``
|
||||
Name of the theme to load from file
|
||||
:param theme_name: Name of the theme to load from file
|
||||
:return The theme object.
|
||||
"""
|
||||
self.log_debug('get theme data for theme %s' % theme_name)
|
||||
xml_file = os.path.join(self.path, str(theme_name), str(theme_name) + '.xml')
|
||||
@ -472,11 +507,13 @@ class ThemeManager(RegistryMixin, OpenLPMixin, QtGui.QWidget, ThemeManagerHelper
|
||||
self.log_debug('No theme data - using default theme')
|
||||
return ThemeXML()
|
||||
else:
|
||||
return self._create_theme_from_Xml(xml, self.path)
|
||||
return self._create_theme_from_xml(xml, self.path)
|
||||
|
||||
def over_write_message_box(self, theme_name):
|
||||
"""
|
||||
Display a warning box to the user that a theme already exists
|
||||
:param theme_name: Name of the theme.
|
||||
:return Confirm if the theme is to be overeritten.
|
||||
"""
|
||||
ret = QtGui.QMessageBox.question(self, translate('OpenLP.ThemeManager', 'Theme Already Exists'),
|
||||
translate('OpenLP.ThemeManager',
|
||||
@ -489,11 +526,12 @@ class ThemeManager(RegistryMixin, OpenLPMixin, QtGui.QWidget, ThemeManagerHelper
|
||||
|
||||
def unzip_theme(self, file_name, directory):
|
||||
"""
|
||||
Unzip the theme, remove the preview file if stored
|
||||
Generate a new preview file. Check the XML theme version and upgrade if
|
||||
necessary.
|
||||
Unzip the theme, remove the preview file if stored. Generate a new preview file. Check the XML theme version
|
||||
and upgrade if necessary.
|
||||
:param file_name:
|
||||
:param directory:
|
||||
"""
|
||||
self.log_debug('Unzipping theme %s', file_name)
|
||||
self.log_debug('Unzipping theme %s' % file_name)
|
||||
file_name = str(file_name)
|
||||
theme_zip = None
|
||||
out_file = None
|
||||
@ -548,8 +586,8 @@ class ThemeManager(RegistryMixin, OpenLPMixin, QtGui.QWidget, ThemeManagerHelper
|
||||
if not abort_import:
|
||||
# As all files are closed, we can create the Theme.
|
||||
if file_xml:
|
||||
theme = self._create_theme_from_Xml(file_xml, self.path)
|
||||
self.generate_and_save_image(directory, theme_name, theme)
|
||||
theme = self._create_theme_from_xml(file_xml, self.path)
|
||||
self.generate_and_save_image(theme_name, theme)
|
||||
# Only show the error message, when IOError was not raised (in
|
||||
# this case the error message has already been shown).
|
||||
elif theme_zip is not None:
|
||||
@ -562,8 +600,8 @@ class ThemeManager(RegistryMixin, OpenLPMixin, QtGui.QWidget, ThemeManagerHelper
|
||||
"""
|
||||
Check if theme already exists and displays error message
|
||||
|
||||
``theme_name``
|
||||
Name of the Theme to test
|
||||
:param theme_name: Name of the Theme to test
|
||||
:return True or False if theme exists
|
||||
"""
|
||||
theme_dir = os.path.join(self.path, theme_name)
|
||||
if os.path.exists(theme_dir):
|
||||
@ -575,7 +613,11 @@ class ThemeManager(RegistryMixin, OpenLPMixin, QtGui.QWidget, ThemeManagerHelper
|
||||
|
||||
def save_theme(self, theme, image_from, image_to):
|
||||
"""
|
||||
Called by thememaintenance Dialog to save the theme and to trigger the reload of the theme list
|
||||
Called by theme maintenance Dialog to save the theme and to trigger the reload of the theme list
|
||||
|
||||
:param theme: The theme data object.
|
||||
:param image_from: Where the theme image is currently located.
|
||||
:param image_to: Where the Theme Image is to be saved to
|
||||
"""
|
||||
self._write_theme(theme, image_from, image_to)
|
||||
if theme.background_type == BackgroundType.to_string(BackgroundType.Image):
|
||||
@ -587,6 +629,10 @@ class ThemeManager(RegistryMixin, OpenLPMixin, QtGui.QWidget, ThemeManagerHelper
|
||||
def _write_theme(self, theme, image_from, image_to):
|
||||
"""
|
||||
Writes the theme to the disk and handles the background image if necessary
|
||||
|
||||
:param theme: The theme data object.
|
||||
:param image_from: Where the theme image is currently located.
|
||||
:param image_to: Where the Theme Image is to be saved to
|
||||
"""
|
||||
name = theme.theme_name
|
||||
theme_pretty_xml = theme.extract_formatted_xml()
|
||||
@ -611,11 +657,14 @@ class ThemeManager(RegistryMixin, OpenLPMixin, QtGui.QWidget, ThemeManagerHelper
|
||||
except IOError as xxx_todo_changeme:
|
||||
shutil.Error = xxx_todo_changeme
|
||||
self.log_exception('Failed to save theme image')
|
||||
self.generate_and_save_image(self.path, name, theme)
|
||||
self.generate_and_save_image(name, theme)
|
||||
|
||||
def generate_and_save_image(self, directory, name, theme):
|
||||
def generate_and_save_image(self, name, theme):
|
||||
"""
|
||||
Generate and save a preview image
|
||||
|
||||
:param name: The name of the theme.
|
||||
:param theme: The theme data object.
|
||||
"""
|
||||
frame = self.generate_image(theme)
|
||||
sample_path_name = os.path.join(self.path, name + '.png')
|
||||
@ -632,7 +681,7 @@ class ThemeManager(RegistryMixin, OpenLPMixin, QtGui.QWidget, ThemeManagerHelper
|
||||
self.main_window.display_progress_bar(len(self.theme_list))
|
||||
for theme in self.theme_list:
|
||||
self.main_window.increment_progress_bar()
|
||||
self.generate_and_save_image(self.path, theme, self.get_theme_data(theme))
|
||||
self.generate_and_save_image(theme, self.get_theme_data(theme))
|
||||
self.main_window.finished_progress_bar()
|
||||
self.load_themes()
|
||||
|
||||
@ -640,11 +689,8 @@ class ThemeManager(RegistryMixin, OpenLPMixin, QtGui.QWidget, ThemeManagerHelper
|
||||
"""
|
||||
Call the renderer to build a Sample Image
|
||||
|
||||
``theme_data``
|
||||
The theme to generated a preview for.
|
||||
|
||||
``force_page``
|
||||
Flag to tell message lines per page need to be generated.
|
||||
:param theme_data: The theme to generated a preview for.
|
||||
:param force_page: Flag to tell message lines per page need to be generated.
|
||||
"""
|
||||
return self.renderer.generate_preview(theme_data, force_page)
|
||||
|
||||
@ -652,26 +698,33 @@ class ThemeManager(RegistryMixin, OpenLPMixin, QtGui.QWidget, ThemeManagerHelper
|
||||
"""
|
||||
Return an image representing the look of the theme
|
||||
|
||||
``theme``
|
||||
The theme to return the image for
|
||||
:param theme: The theme to return the image for.
|
||||
"""
|
||||
return os.path.join(self.path, theme + '.png')
|
||||
|
||||
def _create_theme_from_Xml(self, theme_xml, path):
|
||||
def _create_theme_from_xml(self, theme_xml, image_path):
|
||||
"""
|
||||
Return a theme object using information parsed from XML
|
||||
|
||||
``theme_xml``
|
||||
The XML data to load into the theme
|
||||
:param theme_xml: The Theme data object.
|
||||
:param image_path: Where the theme image is stored
|
||||
:return Theme data.
|
||||
"""
|
||||
theme = ThemeXML()
|
||||
theme.parse(theme_xml)
|
||||
theme.extend_image_filename(path)
|
||||
theme.extend_image_filename(image_path)
|
||||
return theme
|
||||
|
||||
def _validate_theme_action(self, select_text, confirm_title, confirm_text, test_plugin=True, confirm=True):
|
||||
"""
|
||||
Check to see if theme has been selected and the destructive action is allowed.
|
||||
|
||||
:param select_text: Text for message box if no item selected.
|
||||
:param confirm_title: Confirm message title to be displayed.
|
||||
:param confirm_text: Confirm message text to be displayed.
|
||||
:param test_plugin: Do we check the plugins for theme usage.
|
||||
:param confirm: Do we display a confirm box before run checks.
|
||||
:return True or False depending on the validity.
|
||||
"""
|
||||
self.global_theme = Settings().value(self.settings_section + '/global theme')
|
||||
if check_item_selected(self.theme_list_widget, select_text):
|
||||
|
@ -127,9 +127,15 @@ __default_settings__ = {
|
||||
|
||||
|
||||
class AlertsPlugin(Plugin):
|
||||
"""
|
||||
The Alerts Plugin Class
|
||||
"""
|
||||
log.info('Alerts Plugin loaded')
|
||||
|
||||
def __init__(self):
|
||||
"""
|
||||
Class __init__ method
|
||||
"""
|
||||
super(AlertsPlugin, self).__init__('alerts', __default_settings__, settings_tab_class=AlertsTab)
|
||||
self.weight = -3
|
||||
self.icon_path = ':/plugins/plugin_alerts.png'
|
||||
@ -142,17 +148,20 @@ class AlertsPlugin(Plugin):
|
||||
"""
|
||||
Give the alerts plugin the opportunity to add items to the **Tools** menu.
|
||||
|
||||
``tools_menu``
|
||||
The actual **Tools** menu item, so that your actions can use it as their parent.
|
||||
:param tools_menu: The actual **Tools** menu item, so that your actions can use it as their parent.
|
||||
"""
|
||||
log.info('add tools menu')
|
||||
self.tools_alert_item = create_action(tools_menu, 'toolsAlertItem',
|
||||
text=translate('AlertsPlugin', '&Alert'), icon=':/plugins/plugin_alerts.png',
|
||||
statustip=translate('AlertsPlugin', 'Show an alert message.'),
|
||||
visible=False, can_shortcuts=True, triggers=self.on_alerts_trigger)
|
||||
text=translate('AlertsPlugin', '&Alert'),
|
||||
icon=':/plugins/plugin_alerts.png',
|
||||
statustip=translate('AlertsPlugin', 'Show an alert message.'),
|
||||
visible=False, can_shortcuts=True, triggers=self.on_alerts_trigger)
|
||||
self.main_window.tools_menu.addAction(self.tools_alert_item)
|
||||
|
||||
def initialise(self):
|
||||
"""
|
||||
Initialise plugin
|
||||
"""
|
||||
log.info('Alerts Initialising')
|
||||
super(AlertsPlugin, self).initialise()
|
||||
self.tools_alert_item.setVisible(True)
|
||||
@ -171,16 +180,27 @@ class AlertsPlugin(Plugin):
|
||||
action_list.remove_action(self.tools_alert_item, 'Tools')
|
||||
|
||||
def toggle_alerts_state(self):
|
||||
"""
|
||||
Switch the alerts state
|
||||
"""
|
||||
self.alerts_active = not self.alerts_active
|
||||
Settings().setValue(self.settings_section + '/active', self.alerts_active)
|
||||
|
||||
def on_alerts_trigger(self):
|
||||
"""
|
||||
Start of the Alerts dialog triggered from the main menu.
|
||||
"""
|
||||
self.alert_form.load_list()
|
||||
self.alert_form.exec_()
|
||||
|
||||
def about(self):
|
||||
"""
|
||||
Plugin Alerts about method
|
||||
|
||||
:return: text
|
||||
"""
|
||||
about_text = translate('AlertsPlugin', '<strong>Alerts Plugin</strong>'
|
||||
'<br />The alert plugin controls the displaying of nursery alerts on the display screen.')
|
||||
'<br />The alert plugin controls the displaying of alerts on the display screen.')
|
||||
return about_text
|
||||
|
||||
def set_plugin_text_strings(self):
|
||||
@ -209,7 +229,7 @@ class AlertsPlugin(Plugin):
|
||||
"""
|
||||
align = VerticalType.Names[self.settings_tab.location]
|
||||
return CSS % (align, self.settings_tab.font_face, self.settings_tab.font_size, self.settings_tab.font_color,
|
||||
self.settings_tab.background_color)
|
||||
self.settings_tab.background_color)
|
||||
|
||||
def get_display_html(self):
|
||||
"""
|
||||
@ -219,12 +239,11 @@ class AlertsPlugin(Plugin):
|
||||
|
||||
def refresh_css(self, frame):
|
||||
"""
|
||||
Trigger an update of the CSS in the maindisplay.
|
||||
Trigger an update of the CSS in the main display.
|
||||
|
||||
``frame``
|
||||
The Web frame holding the page.
|
||||
:param frame: The Web frame holding the page.
|
||||
"""
|
||||
align = VerticalType.Names[self.settings_tab.location]
|
||||
frame.evaluateJavaScript('update_css("%s", "%s", "%s", "%s", "%s")' %
|
||||
(align, self.settings_tab.font_face, self.settings_tab.font_size,
|
||||
self.settings_tab.font_color, self.settings_tab.background_color))
|
||||
(align, self.settings_tab.font_face, self.settings_tab.font_size,
|
||||
self.settings_tab.font_color, self.settings_tab.background_color))
|
||||
|
@ -36,6 +36,11 @@ from openlp.core.lib.ui import create_button, create_button_box
|
||||
|
||||
class Ui_AlertDialog(object):
|
||||
def setupUi(self, alert_dialog):
|
||||
"""
|
||||
Setup the Alert UI dialog
|
||||
|
||||
:param alert_dialog: The dialog
|
||||
"""
|
||||
alert_dialog.setObjectName('alert_dialog')
|
||||
alert_dialog.resize(400, 300)
|
||||
alert_dialog.setWindowIcon(build_icon(':/icon/openlp-logo-16x16.png'))
|
||||
@ -72,20 +77,25 @@ class Ui_AlertDialog(object):
|
||||
self.save_button.setObjectName('save_button')
|
||||
self.manage_button_layout.addWidget(self.save_button)
|
||||
self.delete_button = create_button(alert_dialog, 'delete_button', role='delete', enabled=False,
|
||||
click=alert_dialog.on_delete_button_clicked)
|
||||
click=alert_dialog.on_delete_button_clicked)
|
||||
self.manage_button_layout.addWidget(self.delete_button)
|
||||
self.manage_button_layout.addStretch()
|
||||
self.alert_dialog_layout.addLayout(self.manage_button_layout, 1, 1)
|
||||
display_icon = build_icon(':/general/general_live.png')
|
||||
self.display_button = create_button(alert_dialog, 'display_button', icon=display_icon, enabled=False)
|
||||
self.display_close_button = create_button(alert_dialog, 'display_close_button', icon=display_icon,
|
||||
enabled=False)
|
||||
enabled=False)
|
||||
self.button_box = create_button_box(alert_dialog, 'button_box', ['close'],
|
||||
[self.display_button, self.display_close_button])
|
||||
[self.display_button, self.display_close_button])
|
||||
self.alert_dialog_layout.addWidget(self.button_box, 2, 0, 1, 2)
|
||||
self.retranslateUi(alert_dialog)
|
||||
|
||||
def retranslateUi(self, alert_dialog):
|
||||
"""
|
||||
Retranslate the UI strings
|
||||
|
||||
:param alert_dialog: The dialog
|
||||
"""
|
||||
alert_dialog.setWindowTitle(translate('AlertsPlugin.AlertForm', 'Alert Message'))
|
||||
self.alert_entry_label.setText(translate('AlertsPlugin.AlertForm', 'Alert &text:'))
|
||||
self.alert_parameter.setText(translate('AlertsPlugin.AlertForm', '&Parameter:'))
|
||||
|
@ -113,9 +113,10 @@ class AlertForm(QtGui.QDialog, Ui_AlertDialog):
|
||||
"""
|
||||
if not self.alert_text_edit.text():
|
||||
QtGui.QMessageBox.information(self,
|
||||
translate('AlertsPlugin.AlertForm', 'New Alert'),
|
||||
translate('AlertsPlugin.AlertForm', 'You haven\'t specified any text for your alert. \n'
|
||||
'Please type in some text before clicking New.'))
|
||||
translate('AlertsPlugin.AlertForm', 'New Alert'),
|
||||
translate('AlertsPlugin.AlertForm',
|
||||
'You haven\'t specified any text for your alert. \n'
|
||||
'Please type in some text before clicking New.'))
|
||||
else:
|
||||
alert = AlertItem()
|
||||
alert.text = self.alert_text_edit.text()
|
||||
@ -153,10 +154,10 @@ class AlertForm(QtGui.QDialog, Ui_AlertDialog):
|
||||
List item has been double clicked to display it.
|
||||
"""
|
||||
item = self.alert_list_widget.selectedIndexes()[0]
|
||||
bitem = self.alert_list_widget.item(item.row())
|
||||
self.trigger_alert(bitem.text())
|
||||
self.alert_text_edit.setText(bitem.text())
|
||||
self.item_id = bitem.data(QtCore.Qt.UserRole)
|
||||
list_item = self.alert_list_widget.item(item.row())
|
||||
self.trigger_alert(list_item.text())
|
||||
self.alert_text_edit.setText(list_item.text())
|
||||
self.item_id = list_item.data(QtCore.Qt.UserRole)
|
||||
self.save_button.setEnabled(False)
|
||||
|
||||
def on_single_click(self):
|
||||
@ -164,9 +165,9 @@ class AlertForm(QtGui.QDialog, Ui_AlertDialog):
|
||||
List item has been single clicked to add it to the edit field so it can be changed.
|
||||
"""
|
||||
item = self.alert_list_widget.selectedIndexes()[0]
|
||||
bitem = self.alert_list_widget.item(item.row())
|
||||
self.alert_text_edit.setText(bitem.text())
|
||||
self.item_id = bitem.data(QtCore.Qt.UserRole)
|
||||
list_item = self.alert_list_widget.item(item.row())
|
||||
self.alert_text_edit.setText(list_item.text())
|
||||
self.item_id = list_item.data(QtCore.Qt.UserRole)
|
||||
# If the alert does not contain '<>' we clear the ParameterEdit field.
|
||||
if self.alert_text_edit.text().find('<>') == -1:
|
||||
self.parameter_edit.setText('')
|
||||
@ -176,26 +177,30 @@ class AlertForm(QtGui.QDialog, Ui_AlertDialog):
|
||||
"""
|
||||
Prepares the alert text for displaying.
|
||||
|
||||
``text``
|
||||
The alert text (unicode).
|
||||
:param text: The alert text.
|
||||
"""
|
||||
if not text:
|
||||
return False
|
||||
# We found '<>' in the alert text, but the ParameterEdit field is empty.
|
||||
if text.find('<>') != -1 and not self.parameter_edit.text() and QtGui.QMessageBox.question(self,
|
||||
translate('AlertsPlugin.AlertForm', 'No Parameter Found'),
|
||||
translate('AlertsPlugin.AlertForm', 'You have not entered a parameter to be replaced.\n'
|
||||
'Do you want to continue anyway?'),
|
||||
QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.No | QtGui.QMessageBox.Yes)) == QtGui.QMessageBox.No:
|
||||
if text.find('<>') != -1 and not self.parameter_edit.text() and \
|
||||
QtGui.QMessageBox.question(self,
|
||||
translate('AlertsPlugin.AlertForm', 'No Parameter Found'),
|
||||
translate('AlertsPlugin.AlertForm',
|
||||
'You have not entered a parameter to be replaced.\n'
|
||||
'Do you want to continue anyway?'),
|
||||
QtGui.QMessageBox.StandardButtons(
|
||||
QtGui.QMessageBox.No | QtGui.QMessageBox.Yes)) == QtGui.QMessageBox.No:
|
||||
self.parameter_edit.setFocus()
|
||||
return False
|
||||
# The ParameterEdit field is not empty, but we have not found '<>'
|
||||
# in the alert text.
|
||||
elif text.find('<>') == -1 and self.parameter_edit.text() and QtGui.QMessageBox.question(self,
|
||||
translate('AlertsPlugin.AlertForm', 'No Placeholder Found'),
|
||||
translate('AlertsPlugin.AlertForm', 'The alert text does not contain \'<>\'.\n'
|
||||
'Do you want to continue anyway?'),
|
||||
QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.No | QtGui.QMessageBox.Yes)) == QtGui.QMessageBox.No:
|
||||
elif text.find('<>') == -1 and self.parameter_edit.text() and \
|
||||
QtGui.QMessageBox.question(self,
|
||||
translate('AlertsPlugin.AlertForm', 'No Placeholder Found'),
|
||||
translate('AlertsPlugin.AlertForm', 'The alert text does not contain \'<>\'.\n'
|
||||
'Do you want to continue anyway?'),
|
||||
QtGui.QMessageBox.StandardButtons(
|
||||
QtGui.QMessageBox.No | QtGui.QMessageBox.Yes)) == QtGui.QMessageBox.No:
|
||||
self.parameter_edit.setFocus()
|
||||
return False
|
||||
text = text.replace('<>', self.parameter_edit.text())
|
||||
@ -207,8 +212,7 @@ class AlertForm(QtGui.QDialog, Ui_AlertDialog):
|
||||
Called when the *alert_list_widget*'s current row has been changed. This enables or disables buttons which
|
||||
require an item to act on.
|
||||
|
||||
``row``
|
||||
The row (int). If there is no current row, the value is -1.
|
||||
:param row: The row (int). If there is no current row, the value is -1.
|
||||
"""
|
||||
if row == -1:
|
||||
self.display_button.setEnabled(False)
|
||||
|
@ -59,6 +59,8 @@ class AlertsManager(QtCore.QObject):
|
||||
def alert_text(self, message):
|
||||
"""
|
||||
Called via a alerts_text event. Message is single element array containing text.
|
||||
|
||||
:param message: The message text to be displayed
|
||||
"""
|
||||
if message:
|
||||
self.display_alert(message[0])
|
||||
@ -67,8 +69,7 @@ class AlertsManager(QtCore.QObject):
|
||||
"""
|
||||
Called from the Alert Tab to display an alert.
|
||||
|
||||
``text``
|
||||
display text
|
||||
:param text: The text to display
|
||||
"""
|
||||
log.debug('display alert called %s' % text)
|
||||
if text:
|
||||
@ -98,8 +99,7 @@ class AlertsManager(QtCore.QObject):
|
||||
"""
|
||||
Time has finished so if our time then request the next Alert if there is one and reset the timer.
|
||||
|
||||
``event``
|
||||
the QT event that has been triggered.
|
||||
:param event: the QT event that has been triggered.
|
||||
"""
|
||||
log.debug('timer event')
|
||||
if event.timerId() == self.timer_id:
|
||||
|
@ -114,6 +114,9 @@ class AlertsTab(SettingsTab):
|
||||
self.font_preview.setText(UiStrings().OLPV2x)
|
||||
|
||||
def on_background_color_button_clicked(self):
|
||||
"""
|
||||
The background color has been changed.
|
||||
"""
|
||||
new_color = QtGui.QColorDialog.getColor(QtGui.QColor(self.background_color), self)
|
||||
if new_color.isValid():
|
||||
self.background_color = new_color.name()
|
||||
@ -121,9 +124,15 @@ class AlertsTab(SettingsTab):
|
||||
self.update_display()
|
||||
|
||||
def on_font_combo_box_clicked(self):
|
||||
"""
|
||||
The Font Combo was changed.
|
||||
"""
|
||||
self.update_display()
|
||||
|
||||
def on_font_color_button_clicked(self):
|
||||
"""
|
||||
The Font Color button has clicked.
|
||||
"""
|
||||
new_color = QtGui.QColorDialog.getColor(QtGui.QColor(self.font_color), self)
|
||||
if new_color.isValid():
|
||||
self.font_color = new_color.name()
|
||||
@ -131,14 +140,24 @@ class AlertsTab(SettingsTab):
|
||||
self.update_display()
|
||||
|
||||
def on_timeout_spin_box_changed(self):
|
||||
"""
|
||||
The Time out spin box has changed.
|
||||
|
||||
"""
|
||||
self.timeout = self.timeout_spin_box.value()
|
||||
self.changed = True
|
||||
|
||||
def on_font_size_spin_box_changed(self):
|
||||
"""
|
||||
The font size spin box has changed.
|
||||
"""
|
||||
self.font_size = self.font_size_spin_box.value()
|
||||
self.update_display()
|
||||
|
||||
def load(self):
|
||||
"""
|
||||
Load the settings into the UI.
|
||||
"""
|
||||
settings = Settings()
|
||||
settings.beginGroup(self.settings_section)
|
||||
self.timeout = settings.value('timeout')
|
||||
@ -160,6 +179,9 @@ class AlertsTab(SettingsTab):
|
||||
self.changed = False
|
||||
|
||||
def save(self):
|
||||
"""
|
||||
Save the changes on exit of the Settings dialog.
|
||||
"""
|
||||
settings = Settings()
|
||||
settings.beginGroup(self.settings_section)
|
||||
# Check value has changed as no event handles this field
|
||||
@ -179,6 +201,9 @@ class AlertsTab(SettingsTab):
|
||||
self.changed = False
|
||||
|
||||
def update_display(self):
|
||||
"""
|
||||
Update the preview display after changes have been made,
|
||||
"""
|
||||
font = QtGui.QFont()
|
||||
font.setFamily(self.font_combo_box.currentFont().family())
|
||||
font.setBold(True)
|
||||
|
@ -47,14 +47,14 @@ def init_schema(url):
|
||||
"""
|
||||
Setup the alerts database connection and initialise the database schema
|
||||
|
||||
``url``
|
||||
:param url:
|
||||
The database to setup
|
||||
"""
|
||||
session, metadata = init_db(url)
|
||||
|
||||
alerts_table = Table('alerts', metadata,
|
||||
Column('id', types.Integer(), primary_key=True),
|
||||
Column('text', types.UnicodeText, nullable=False))
|
||||
Column('id', types.Integer(), primary_key=True),
|
||||
Column('text', types.UnicodeText, nullable=False))
|
||||
|
||||
mapper(AlertItem, alerts_table)
|
||||
|
||||
|
@ -36,6 +36,10 @@ from openlp.core.lib.ui import create_button_box, create_button
|
||||
|
||||
class Ui_CustomEditDialog(object):
|
||||
def setupUi(self, custom_edit_dialog):
|
||||
"""
|
||||
Build the Edit Dialog UI
|
||||
:param custom_edit_dialog: The Dialog
|
||||
"""
|
||||
custom_edit_dialog.setObjectName('custom_edit_dialog')
|
||||
custom_edit_dialog.resize(450, 350)
|
||||
custom_edit_dialog.setWindowIcon(build_icon(':/icon/openlp-logo-16x16.png'))
|
||||
@ -100,7 +104,7 @@ class Ui_CustomEditDialog(object):
|
||||
self.dialog_layout.addLayout(self.bottom_form_layout)
|
||||
self.preview_button = QtGui.QPushButton()
|
||||
self.button_box = create_button_box(custom_edit_dialog, 'button_box', ['cancel', 'save'],
|
||||
[self.preview_button])
|
||||
[self.preview_button])
|
||||
self.dialog_layout.addWidget(self.button_box)
|
||||
self.retranslateUi(custom_edit_dialog)
|
||||
|
||||
|
@ -70,8 +70,7 @@ class EditCustomForm(QtGui.QDialog, Ui_CustomEditDialog):
|
||||
"""
|
||||
Load a list of themes into the themes combo box.
|
||||
|
||||
``theme_list``
|
||||
The list of themes to load.
|
||||
:param theme_list: The list of themes to load.
|
||||
"""
|
||||
self.theme_combo_box.clear()
|
||||
self.theme_combo_box.addItem('')
|
||||
@ -81,12 +80,8 @@ class EditCustomForm(QtGui.QDialog, Ui_CustomEditDialog):
|
||||
"""
|
||||
Called when editing or creating a new custom.
|
||||
|
||||
``id``
|
||||
The custom's id. If zero, then a new custom is created.
|
||||
|
||||
``preview``
|
||||
States whether the custom is edited while being previewed in the
|
||||
preview panel.
|
||||
:param id: The custom's id. If zero, then a new custom is created.
|
||||
:param preview: States whether the custom is edited while being previewed in the preview panel.
|
||||
"""
|
||||
self.slide_list_view.clear()
|
||||
if id == 0:
|
||||
@ -195,11 +190,8 @@ class EditCustomForm(QtGui.QDialog, Ui_CustomEditDialog):
|
||||
"""
|
||||
Updates the slide list after editing slides.
|
||||
|
||||
``slides``
|
||||
A list of all slides which have been edited.
|
||||
|
||||
``edit_all``
|
||||
Indicates if all slides or only one slide has been edited.
|
||||
:param slides: A list of all slides which have been edited.
|
||||
:param edit_all: Indicates if all slides or only one slide has been edited.
|
||||
"""
|
||||
if edit_all:
|
||||
self.slide_list_view.clear()
|
||||
@ -229,8 +221,7 @@ class EditCustomForm(QtGui.QDialog, Ui_CustomEditDialog):
|
||||
Called when the *slide_list_view*'s current row has been changed. This
|
||||
enables or disables buttons which require an slide to act on.
|
||||
|
||||
``row``
|
||||
The row (int). If there is no current row, the value is -1.
|
||||
:param row: The row (int). If there is no current row, the value is -1.
|
||||
"""
|
||||
if row == -1:
|
||||
self.delete_button.setEnabled(False)
|
||||
|
@ -57,8 +57,7 @@ class EditCustomSlideForm(QtGui.QDialog, Ui_CustomSlideEditDialog):
|
||||
"""
|
||||
Set the text for slide_text_edit.
|
||||
|
||||
``text``
|
||||
The text (unicode).
|
||||
:param text: The text (unicode).
|
||||
"""
|
||||
self.slide_text_edit.clear()
|
||||
if text:
|
||||
@ -87,7 +86,9 @@ class EditCustomSlideForm(QtGui.QDialog, Ui_CustomSlideEditDialog):
|
||||
|
||||
def insert_single_line_text_at_cursor(self, text):
|
||||
"""
|
||||
Adds ``text`` in a single line at the cursor position.
|
||||
Adds a single line at the cursor position.
|
||||
|
||||
:param text: The text to be inserted
|
||||
"""
|
||||
full_text = self.slide_text_edit.toPlainText()
|
||||
position = self.slide_text_edit.textCursor().position()
|
||||
|
@ -72,6 +72,8 @@ class CustomTab(SettingsTab):
|
||||
def on_display_footer_check_box_changed(self, check_state):
|
||||
"""
|
||||
Toggle the setting for displaying the footer.
|
||||
|
||||
:param check_state: The current check box state
|
||||
"""
|
||||
self.display_footer = False
|
||||
# we have a set value convert to True/False
|
||||
@ -79,9 +81,18 @@ class CustomTab(SettingsTab):
|
||||
self.display_footer = True
|
||||
|
||||
def on_add_from_service_check_box_changed(self, check_state):
|
||||
"""
|
||||
Allows service items to create Custom items.
|
||||
|
||||
:param check_state: The current check box state
|
||||
"""
|
||||
self.update_load = (check_state == QtCore.Qt.Checked)
|
||||
|
||||
def load(self):
|
||||
"""
|
||||
|
||||
Load the settings into the dialog
|
||||
"""
|
||||
settings = Settings()
|
||||
settings.beginGroup(self.settings_section)
|
||||
self.display_footer = settings.value('display footer')
|
||||
@ -91,6 +102,9 @@ class CustomTab(SettingsTab):
|
||||
settings.endGroup()
|
||||
|
||||
def save(self):
|
||||
"""
|
||||
Save the Dialog settings
|
||||
"""
|
||||
settings = Settings()
|
||||
settings.beginGroup(self.settings_section)
|
||||
settings.setValue('display footer', self.display_footer)
|
||||
|
@ -50,6 +50,7 @@ from lxml import etree, objectify
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
#TODO: These classes need to be refactored into a single class.
|
||||
class CustomXMLBuilder(object):
|
||||
"""
|
||||
@ -89,15 +90,11 @@ class CustomXMLBuilder(object):
|
||||
"""
|
||||
Add a verse to the ``<lyrics>`` tag.
|
||||
|
||||
``verse_type``
|
||||
A string denoting the type of verse. Possible values are "Chorus",
|
||||
"Verse", "Bridge", and "Custom".
|
||||
:param verse_type: A string denoting the type of verse. Possible values are "Chorus", "Verse", "Bridge",
|
||||
and "Custom".
|
||||
:param number: An integer denoting the number of the item, for example: verse 1.
|
||||
:param content: The actual text of the verse to be stored.
|
||||
|
||||
``number``
|
||||
An integer denoting the number of the item, for example: verse 1.
|
||||
|
||||
``content``
|
||||
The actual text of the verse to be stored.
|
||||
"""
|
||||
verse = self.custom_xml.createElement('verse')
|
||||
verse.setAttribute('type', verse_type)
|
||||
|
@ -71,7 +71,7 @@ def init_schema(url):
|
||||
Column('text', types.UnicodeText, nullable=False),
|
||||
Column('credits', types.UnicodeText),
|
||||
Column('theme_name', types.Unicode(128))
|
||||
)
|
||||
)
|
||||
|
||||
mapper(CustomSlide, custom_slide_table)
|
||||
|
||||
|
@ -73,6 +73,9 @@ class CustomMediaItem(MediaManagerItem):
|
||||
self.remote_custom = -1
|
||||
|
||||
def add_end_header_bar(self):
|
||||
"""
|
||||
Add the Custom End Head bar and register events and functions
|
||||
"""
|
||||
self.toolbar.addSeparator()
|
||||
self.add_search_to_toolbar()
|
||||
# Signals and slots
|
||||
@ -91,10 +94,17 @@ class CustomMediaItem(MediaManagerItem):
|
||||
self.add_custom_from_service = Settings().value(self.settings_section + '/add custom from service')
|
||||
|
||||
def retranslateUi(self):
|
||||
"""
|
||||
|
||||
|
||||
"""
|
||||
self.search_text_label.setText('%s:' % UiStrings().Search)
|
||||
self.search_text_button.setText(UiStrings().Search)
|
||||
|
||||
def initialise(self):
|
||||
"""
|
||||
Initialise the UI so it can provide Searches
|
||||
"""
|
||||
self.search_text_edit.set_search_types([(CustomSearch.Titles, ':/songs/song_search_title.png',
|
||||
translate('SongsPlugin.MediaItem', 'Titles'),
|
||||
translate('SongsPlugin.MediaItem', 'Search Titles...')),
|
||||
@ -107,6 +117,11 @@ class CustomMediaItem(MediaManagerItem):
|
||||
|
||||
def load_list(self, custom_slides, target_group=None):
|
||||
# Sort out what custom we want to select after loading the list.
|
||||
"""
|
||||
|
||||
:param custom_slides:
|
||||
:param target_group:
|
||||
"""
|
||||
self.save_auto_select_id()
|
||||
self.list_view.clear()
|
||||
custom_slides.sort()
|
||||
@ -123,6 +138,9 @@ class CustomMediaItem(MediaManagerItem):
|
||||
# active trigger it and clean up so it will not update again.
|
||||
|
||||
def on_new_click(self):
|
||||
"""
|
||||
Handle the New item event
|
||||
"""
|
||||
self.edit_custom_form.load_custom(0)
|
||||
self.edit_custom_form.exec_()
|
||||
self.on_clear_text_button_click()
|
||||
@ -132,6 +150,9 @@ class CustomMediaItem(MediaManagerItem):
|
||||
"""
|
||||
Called by ServiceManager or SlideController by event passing the custom Id in the payload along with an
|
||||
indicator to say which type of display is required.
|
||||
|
||||
:param custom_id: The id of the item to be edited
|
||||
:param preview: Do we need to update the Preview after the edit. (Default False)
|
||||
"""
|
||||
custom_id = int(custom_id)
|
||||
valid = self.plugin.db_manager.get_object(CustomSlide, custom_id)
|
||||
@ -184,12 +205,20 @@ class CustomMediaItem(MediaManagerItem):
|
||||
self.on_search_text_button_clicked()
|
||||
|
||||
def on_focus(self):
|
||||
"""
|
||||
Set the focus
|
||||
"""
|
||||
self.search_text_edit.setFocus()
|
||||
|
||||
def generate_slide_data(self, service_item, item=None, xml_version=False,
|
||||
remote=False, context=ServiceItemContext.Service):
|
||||
"""
|
||||
Generate the slide data. Needs to be implemented by the plugin.
|
||||
:param service_item: To be updated
|
||||
:param item: The custom database item to be used
|
||||
:param xml_version: No used
|
||||
:param remote: Is this triggered by the Preview Controller or Service Manager.
|
||||
:param context: Why is this item required to be build (Default Service).
|
||||
"""
|
||||
item_id = self._get_id_of_item_to_generate(item, self.remote_custom)
|
||||
service_item.add_capability(ItemCapabilities.CanEdit)
|
||||
@ -243,6 +272,8 @@ class CustomMediaItem(MediaManagerItem):
|
||||
"""
|
||||
If search as type enabled invoke the search on each key press. If the Title is being searched do not start until
|
||||
2 characters have been entered.
|
||||
|
||||
:param text: The search text
|
||||
"""
|
||||
search_length = 2
|
||||
if len(text) > search_length:
|
||||
@ -253,6 +284,8 @@ class CustomMediaItem(MediaManagerItem):
|
||||
def service_load(self, item):
|
||||
"""
|
||||
Triggered by a custom item being loaded by the service manager.
|
||||
|
||||
:param item: The service Item from the service to load found in the database.
|
||||
"""
|
||||
log.debug('service_load')
|
||||
if self.plugin.status != PluginStatus.Active:
|
||||
@ -271,6 +304,8 @@ class CustomMediaItem(MediaManagerItem):
|
||||
def create_from_service_item(self, item):
|
||||
"""
|
||||
Create a custom slide from a text service item
|
||||
|
||||
:param item: the service item to be converted to a Custom item
|
||||
"""
|
||||
custom = CustomSlide()
|
||||
custom.title = item.title
|
||||
@ -303,6 +338,9 @@ class CustomMediaItem(MediaManagerItem):
|
||||
def search(self, string, show_error):
|
||||
"""
|
||||
Search the database for a given item.
|
||||
|
||||
:param string: The search string
|
||||
:param show_error: The error string to be show.
|
||||
"""
|
||||
search = '%' + string.lower() + '%'
|
||||
search_results = self.plugin.db_manager.get_all_objects(CustomSlide,
|
||||
|
@ -94,7 +94,7 @@ class MediaMediaItem(MediaManagerItem):
|
||||
self.reset_action.setToolTip(UiStrings().ResetLiveBG)
|
||||
self.automatic = UiStrings().Automatic
|
||||
self.display_type_label.setText(translate('MediaPlugin.MediaItem', 'Use Player:'))
|
||||
self.rebuild_players()
|
||||
#self.rebuild_players()
|
||||
|
||||
def required_icons(self):
|
||||
"""
|
||||
@ -112,9 +112,9 @@ class MediaMediaItem(MediaManagerItem):
|
||||
def add_end_header_bar(self):
|
||||
# Replace backgrounds do not work at present so remove functionality.
|
||||
self.replace_action = self.toolbar.add_toolbar_action('replace_action', icon=':/slides/slide_blank.png',
|
||||
triggers=self.onReplaceClick)
|
||||
triggers=self.on_replace_click)
|
||||
self.reset_action = self.toolbar.add_toolbar_action('reset_action', icon=':/system/system_close.png',
|
||||
visible=False, triggers=self.onResetClick)
|
||||
visible=False, triggers=self.on_reset_click)
|
||||
self.media_widget = QtGui.QWidget(self)
|
||||
self.media_widget.setObjectName('media_widget')
|
||||
self.display_layout = QtGui.QFormLayout(self.media_widget)
|
||||
@ -128,16 +128,16 @@ class MediaMediaItem(MediaManagerItem):
|
||||
self.display_layout.addRow(self.display_type_label, self.display_type_combo_box)
|
||||
# Add the Media widget to the page layout.
|
||||
self.page_layout.addWidget(self.media_widget)
|
||||
self.display_type_combo_box.currentIndexChanged.connect(self.overridePlayerChanged)
|
||||
self.display_type_combo_box.currentIndexChanged.connect(self.override_player_changed)
|
||||
|
||||
def overridePlayerChanged(self, index):
|
||||
def override_player_changed(self, index):
|
||||
player = get_media_players()[0]
|
||||
if index == 0:
|
||||
set_media_players(player)
|
||||
else:
|
||||
set_media_players(player, player[index-1])
|
||||
|
||||
def onResetClick(self):
|
||||
def on_reset_click(self):
|
||||
"""
|
||||
Called to reset the Live background with the media selected,
|
||||
"""
|
||||
@ -150,12 +150,13 @@ class MediaMediaItem(MediaManagerItem):
|
||||
"""
|
||||
self.reset_action.setVisible(False)
|
||||
|
||||
def onReplaceClick(self):
|
||||
def on_replace_click(self):
|
||||
"""
|
||||
Called to replace Live background with the media selected.
|
||||
"""
|
||||
if check_item_selected(self.list_view,
|
||||
translate('MediaPlugin.MediaItem', 'You must select a media file to replace the background with.')):
|
||||
translate('MediaPlugin.MediaItem',
|
||||
'You must select a media file to replace the background with.')):
|
||||
item = self.list_view.currentItem()
|
||||
filename = item.data(QtCore.Qt.UserRole)
|
||||
if os.path.exists(filename):
|
||||
@ -168,14 +169,16 @@ class MediaMediaItem(MediaManagerItem):
|
||||
self.reset_action.setVisible(True)
|
||||
else:
|
||||
critical_error_message_box(UiStrings().LiveBGError,
|
||||
translate('MediaPlugin.MediaItem', 'There was no display item to amend.'))
|
||||
translate('MediaPlugin.MediaItem',
|
||||
'There was no display item to amend.'))
|
||||
else:
|
||||
critical_error_message_box(UiStrings().LiveBGError,
|
||||
translate('MediaPlugin.MediaItem',
|
||||
'There was a problem replacing your background, the media file "%s" no longer exists.') % filename)
|
||||
translate('MediaPlugin.MediaItem',
|
||||
'There was a problem replacing your background, '
|
||||
'the media file "%s" no longer exists.') % filename)
|
||||
|
||||
def generate_slide_data(self, service_item, item=None, xml_version=False, remote=False,
|
||||
context=ServiceItemContext.Live):
|
||||
context=ServiceItemContext.Live):
|
||||
"""
|
||||
Generate the slide data. Needs to be implemented by the plugin.
|
||||
"""
|
||||
@ -211,16 +214,16 @@ class MediaMediaItem(MediaManagerItem):
|
||||
def initialise(self):
|
||||
self.list_view.clear()
|
||||
self.list_view.setIconSize(QtCore.QSize(88, 50))
|
||||
self.servicePath = os.path.join(AppLocation.get_section_data_path(self.settings_section), 'thumbnails')
|
||||
check_directory_exists(self.servicePath)
|
||||
self.service_path = os.path.join(AppLocation.get_section_data_path(self.settings_section), 'thumbnails')
|
||||
check_directory_exists(self.service_path)
|
||||
self.load_list(Settings().value(self.settings_section + '/media files'))
|
||||
self.populateDisplayTypes()
|
||||
self.populate_display_types()
|
||||
|
||||
def rebuild_players(self):
|
||||
"""
|
||||
Rebuild the tab in the media manager when changes are made in the settings.
|
||||
"""
|
||||
self.populateDisplayTypes()
|
||||
self.populate_display_types()
|
||||
self.on_new_file_masks = translate('MediaPlugin.MediaItem', 'Videos (%s);;Audio (%s);;%s (*)') % (
|
||||
' '.join(self.media_controller.video_extensions_list),
|
||||
' '.join(self.media_controller.audio_extensions_list), UiStrings().AllFiles)
|
||||
@ -228,25 +231,25 @@ class MediaMediaItem(MediaManagerItem):
|
||||
def display_setup(self):
|
||||
self.media_controller.setup_display(self.display_controller.preview_display, False)
|
||||
|
||||
def populateDisplayTypes(self):
|
||||
def populate_display_types(self):
|
||||
"""
|
||||
Load the combobox with the enabled media players, allowing user to select a specific player if settings allow.
|
||||
"""
|
||||
# block signals to avoid unnecessary overridePlayerChanged Signals while combo box creation
|
||||
# block signals to avoid unnecessary override_player_changed Signals while combo box creation
|
||||
self.display_type_combo_box.blockSignals(True)
|
||||
self.display_type_combo_box.clear()
|
||||
usedPlayers, overridePlayer = get_media_players()
|
||||
used_players, override_player = get_media_players()
|
||||
media_players = self.media_controller.media_players
|
||||
currentIndex = 0
|
||||
for player in usedPlayers:
|
||||
current_index = 0
|
||||
for player in used_players:
|
||||
# load the drop down selection
|
||||
self.display_type_combo_box.addItem(media_players[player].original_name)
|
||||
if overridePlayer == player:
|
||||
currentIndex = len(self.display_type_combo_box)
|
||||
if override_player == player:
|
||||
current_index = len(self.display_type_combo_box)
|
||||
if self.display_type_combo_box.count() > 1:
|
||||
self.display_type_combo_box.insertItem(0, self.automatic)
|
||||
self.display_type_combo_box.setCurrentIndex(currentIndex)
|
||||
if overridePlayer:
|
||||
self.display_type_combo_box.setCurrentIndex(current_index)
|
||||
if override_player:
|
||||
self.media_widget.show()
|
||||
else:
|
||||
self.media_widget.hide()
|
||||
@ -257,7 +260,7 @@ class MediaMediaItem(MediaManagerItem):
|
||||
Remove a media item from the list.
|
||||
"""
|
||||
if check_item_selected(self.list_view,
|
||||
translate('MediaPlugin.MediaItem', 'You must select a media file to delete.')):
|
||||
translate('MediaPlugin.MediaItem', 'You must select a media file to delete.')):
|
||||
row_list = [item.row() for item in self.list_view.selectedIndexes()]
|
||||
row_list.sort(reverse=True)
|
||||
for row in row_list:
|
||||
@ -266,25 +269,25 @@ class MediaMediaItem(MediaManagerItem):
|
||||
|
||||
def load_list(self, media, target_group=None):
|
||||
# Sort the media by its filename considering language specific characters.
|
||||
media.sort(key=lambda filename: get_locale_key(os.path.split(str(filename))[1]))
|
||||
media.sort(key=lambda file_name: get_locale_key(os.path.split(str(file_name))[1]))
|
||||
for track in media:
|
||||
track_info = QtCore.QFileInfo(track)
|
||||
if not os.path.exists(track):
|
||||
filename = os.path.split(str(track))[1]
|
||||
item_name = QtGui.QListWidgetItem(filename)
|
||||
file_name = os.path.split(str(track))[1]
|
||||
item_name = QtGui.QListWidgetItem(file_name)
|
||||
item_name.setIcon(ERROR_ICON)
|
||||
item_name.setData(QtCore.Qt.UserRole, track)
|
||||
elif track_info.isFile():
|
||||
filename = os.path.split(str(track))[1]
|
||||
item_name = QtGui.QListWidgetItem(filename)
|
||||
if '*.%s' % (filename.split('.')[-1].lower()) in self.media_controller.audio_extensions_list:
|
||||
file_name = os.path.split(str(track))[1]
|
||||
item_name = QtGui.QListWidgetItem(file_name)
|
||||
if '*.%s' % (file_name.split('.')[-1].lower()) in self.media_controller.audio_extensions_list:
|
||||
item_name.setIcon(AUDIO_ICON)
|
||||
else:
|
||||
item_name.setIcon(VIDEO_ICON)
|
||||
item_name.setData(QtCore.Qt.UserRole, track)
|
||||
else:
|
||||
filename = os.path.split(str(track))[1]
|
||||
item_name = QtGui.QListWidgetItem(filename)
|
||||
file_name = os.path.split(str(track))[1]
|
||||
item_name = QtGui.QListWidgetItem(file_name)
|
||||
item_name.setIcon(build_icon(DVD_ICON))
|
||||
item_name.setData(QtCore.Qt.UserRole, track)
|
||||
item_name.setToolTip(track)
|
||||
@ -302,7 +305,7 @@ class MediaMediaItem(MediaManagerItem):
|
||||
media = [x for x in media if os.path.splitext(x)[1] in extension]
|
||||
return media
|
||||
|
||||
def search(self, string, showError):
|
||||
def search(self, string, show_error):
|
||||
files = Settings().value(self.settings_section + '/media files')
|
||||
results = []
|
||||
string = string.lower()
|
||||
|
@ -41,7 +41,8 @@ if os.name == 'nt':
|
||||
import win32ui
|
||||
import pywintypes
|
||||
|
||||
from openlp.core.lib import ScreenList, Registry
|
||||
from openlp.core.lib import ScreenList
|
||||
from openlp.core.common import Registry
|
||||
from .presentationcontroller import PresentationController, PresentationDocument
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
@ -35,6 +35,7 @@
|
||||
<link rel="stylesheet" href="/files/openlp.css" />
|
||||
<link rel="shortcut icon" type="image/x-icon" href="/files/images/favicon.ico">
|
||||
<script type="text/javascript" src="/files/jquery.js"></script>
|
||||
<script type="text/javascript" src="/files/WebSocketEvents.js"></script>
|
||||
<script type="text/javascript" src="/files/openlp.js"></script>
|
||||
<script type="text/javascript" src="/files/jquery.mobile.js"></script>
|
||||
<script type="text/javascript">
|
||||
|
@ -400,4 +400,4 @@ $("#search").live("pageinit", function (event) {
|
||||
});
|
||||
//setInterval("OpenLP.pollServer();", 30000);
|
||||
//OpenLP.pollServer();
|
||||
var ws = new wsEventEngine("ws://" + window.location.hostname + ":9999/Test", OpenLP.pollServer, 500);
|
||||
var ws = new wsEventEngine("ws://" + window.location.hostname + ":8888/Test", OpenLP.pollServer, 500);
|
||||
|
@ -30,5 +30,6 @@
|
||||
from .remotetab import RemoteTab
|
||||
from .httprouter import HttpRouter
|
||||
from .httpserver import OpenLPServer
|
||||
from .websocket import WebSocketManager
|
||||
|
||||
__all__ = ['RemoteTab', 'OpenLPServer', 'HttpRouter']
|
||||
__all__ = ['RemoteTab', 'OpenLPServer', 'HttpRouter','WebSocketManager']
|
||||
|
@ -31,7 +31,8 @@ import logging
|
||||
import time
|
||||
|
||||
from openlp.core.lib import Plugin, StringContent, translate, build_icon
|
||||
from openlp.plugins.remotes.lib import RemoteTab, OpenLPServer
|
||||
from openlp.core.common import Registry
|
||||
from openlp.plugins.remotes.lib import RemoteTab, OpenLPServer, WebSocketManager
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
@ -59,6 +60,7 @@ class RemotesPlugin(Plugin):
|
||||
self.icon = build_icon(self.icon_path)
|
||||
self.weight = -1
|
||||
self.server = None
|
||||
self.websocketserver = None
|
||||
|
||||
def initialise(self):
|
||||
"""
|
||||
@ -67,6 +69,9 @@ class RemotesPlugin(Plugin):
|
||||
log.debug('initialise')
|
||||
super(RemotesPlugin, self).initialise()
|
||||
self.server = OpenLPServer()
|
||||
self.websocketserver = WebSocketManager()
|
||||
self.websocketserver.start()
|
||||
Registry().register_function('websock_send', self.websocketserver.send)
|
||||
|
||||
def finalise(self):
|
||||
"""
|
||||
@ -77,6 +82,9 @@ class RemotesPlugin(Plugin):
|
||||
if self.server:
|
||||
self.server.stop_server()
|
||||
self.server = None
|
||||
if self.websocketserver:
|
||||
self.websocketserver.stop()
|
||||
self.websocketserver = None
|
||||
|
||||
def about(self):
|
||||
"""
|
||||
|
@ -4,8 +4,8 @@
|
||||
###############################################################################
|
||||
# OpenLP - Open Source Lyrics Projection #
|
||||
# --------------------------------------------------------------------------- #
|
||||
# Copyright (c) 2008-2013 Raoul Snyman #
|
||||
# Portions copyright (c) 2008-2013 Tim Bentley, Gerald Britton, Jonathan #
|
||||
# Copyright (c) 2008-2014 Raoul Snyman #
|
||||
# Portions copyright (c) 2008-2014 Tim Bentley, Gerald Britton, Jonathan #
|
||||
# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, #
|
||||
# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. #
|
||||
# Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru, #
|
||||
|
@ -59,22 +59,18 @@ class TestRegistry(TestCase):
|
||||
with self.assertRaises(KeyError) as context:
|
||||
Registry().register('test1', mock_1)
|
||||
self.assertEqual(context.exception.args[0], 'Duplicate service exception test1',
|
||||
'KeyError exception should have been thrown for duplicate service')
|
||||
'KeyError exception should have been thrown for duplicate service')
|
||||
|
||||
# WHEN I try to get back a non existent component
|
||||
# THEN I will get an exception
|
||||
with self.assertRaises(KeyError) as context:
|
||||
temp = Registry().get('test2')
|
||||
self.assertEqual(context.exception.args[0], 'Service test2 not found in list',
|
||||
'KeyError exception should have been thrown for missing service')
|
||||
temp = Registry().get('test2')
|
||||
self.assertEqual(temp, None, 'None should have been returned for missing service')
|
||||
|
||||
# WHEN I try to replace a component I should be allowed (testing only)
|
||||
Registry().remove('test1')
|
||||
# THEN I will get an exception
|
||||
with self.assertRaises(KeyError) as context:
|
||||
temp = Registry().get('test1')
|
||||
self.assertEqual(context.exception.args[0], 'Service test1 not found in list',
|
||||
'KeyError exception should have been thrown for deleted service')
|
||||
temp = Registry().get('test1')
|
||||
self.assertEqual(temp, None, 'None should have been returned for deleted service')
|
||||
|
||||
def registry_function_test(self):
|
||||
"""
|
||||
|
@ -71,7 +71,7 @@ class TestPluginManager(TestCase):
|
||||
|
||||
# THEN: The create_media_manager_item() method should have been called
|
||||
self.assertEqual(0, mocked_plugin.create_media_manager_item.call_count,
|
||||
'The create_media_manager_item() method should not have been called.')
|
||||
'The create_media_manager_item() method should not have been called.')
|
||||
|
||||
def hook_media_manager_with_active_plugin_test(self):
|
||||
"""
|
||||
@ -104,7 +104,7 @@ class TestPluginManager(TestCase):
|
||||
|
||||
# THEN: The hook_settings_tabs() method should have been called
|
||||
self.assertEqual(0, mocked_plugin.create_media_manager_item.call_count,
|
||||
'The create_media_manager_item() method should not have been called.')
|
||||
'The create_media_manager_item() method should not have been called.')
|
||||
|
||||
def hook_settings_tabs_with_disabled_plugin_and_mocked_form_test(self):
|
||||
"""
|
||||
@ -124,9 +124,9 @@ class TestPluginManager(TestCase):
|
||||
|
||||
# THEN: The create_settings_tab() method should not have been called, but the plugins lists should be the same
|
||||
self.assertEqual(0, mocked_plugin.create_settings_tab.call_count,
|
||||
'The create_media_manager_item() method should not have been called.')
|
||||
'The create_media_manager_item() method should not have been called.')
|
||||
self.assertEqual(mocked_settings_form.plugin_manager.plugins, plugin_manager.plugins,
|
||||
'The plugins on the settings form should be the same as the plugins in the plugin manager')
|
||||
'The plugins on the settings form should be the same as the plugins in the plugin manager')
|
||||
|
||||
def hook_settings_tabs_with_active_plugin_and_mocked_form_test(self):
|
||||
"""
|
||||
@ -146,9 +146,9 @@ class TestPluginManager(TestCase):
|
||||
|
||||
# THEN: The create_media_manager_item() method should have been called with the mocked settings form
|
||||
self.assertEqual(1, mocked_plugin.create_settings_tab.call_count,
|
||||
'The create_media_manager_item() method should have been called once.')
|
||||
'The create_media_manager_item() method should have been called once.')
|
||||
self.assertEqual(plugin_manager.plugins, mocked_settings_form.plugin_manager.plugins,
|
||||
'The plugins on the settings form should be the same as the plugins in the plugin manager')
|
||||
'The plugins on the settings form should be the same as the plugins in the plugin manager')
|
||||
|
||||
def hook_settings_tabs_with_active_plugin_and_no_form_test(self):
|
||||
"""
|
||||
@ -181,7 +181,7 @@ class TestPluginManager(TestCase):
|
||||
|
||||
# THEN: The create_media_manager_item() method should have been called
|
||||
self.assertEqual(0, mocked_plugin.add_import_menu_item.call_count,
|
||||
'The add_import_menu_item() method should not have been called.')
|
||||
'The add_import_menu_item() method should not have been called.')
|
||||
|
||||
def hook_import_menu_with_active_plugin_test(self):
|
||||
"""
|
||||
@ -214,7 +214,7 @@ class TestPluginManager(TestCase):
|
||||
|
||||
# THEN: The add_export_menu_Item() method should not have been called
|
||||
self.assertEqual(0, mocked_plugin.add_export_menu_Item.call_count,
|
||||
'The add_export_menu_Item() method should not have been called.')
|
||||
'The add_export_menu_Item() method should not have been called.')
|
||||
|
||||
def hook_export_menu_with_active_plugin_test(self):
|
||||
"""
|
||||
@ -248,7 +248,7 @@ class TestPluginManager(TestCase):
|
||||
|
||||
# THEN: The upgrade_settings() method should not have been called
|
||||
self.assertEqual(0, mocked_plugin.upgrade_settings.call_count,
|
||||
'The upgrade_settings() method should not have been called.')
|
||||
'The upgrade_settings() method should not have been called.')
|
||||
|
||||
def hook_upgrade_plugin_settings_with_active_plugin_test(self):
|
||||
"""
|
||||
@ -282,7 +282,7 @@ class TestPluginManager(TestCase):
|
||||
|
||||
# THEN: The add_tools_menu_item() method should have been called
|
||||
self.assertEqual(0, mocked_plugin.add_tools_menu_item.call_count,
|
||||
'The add_tools_menu_item() method should not have been called.')
|
||||
'The add_tools_menu_item() method should not have been called.')
|
||||
|
||||
def hook_tools_menu_with_active_plugin_test(self):
|
||||
"""
|
||||
|
@ -84,4 +84,4 @@ class TestScreenList(TestCase):
|
||||
new_screen_count = len(self.screens.screen_list)
|
||||
self.assertEqual(old_screen_count + 1, new_screen_count, 'The new_screens list should be bigger')
|
||||
self.assertEqual(SCREEN, self.screens.screen_list.pop(),
|
||||
'The 2nd screen should be identical to the first screen')
|
||||
'The 2nd screen should be identical to the first screen')
|
||||
|
@ -97,7 +97,7 @@ class TestServiceItem(TestCase):
|
||||
# THEN: The frames should also be valid
|
||||
self.assertEqual('Test Custom', service_item.get_display_title(), 'The title should be "Test Custom"')
|
||||
self.assertEqual(VERSE[:-1], service_item.get_frames()[0]['text'],
|
||||
'The returned text matches the input, except the last line feed')
|
||||
'The returned text matches the input, except the last line feed')
|
||||
self.assertEqual(VERSE.split('\n', 1)[0], service_item.get_rendered_frame(1),
|
||||
'The first line has been returned')
|
||||
self.assertEqual('Slide 1', service_item.get_frame_title(0), '"Slide 1" has been returned as the title')
|
||||
@ -125,24 +125,24 @@ class TestServiceItem(TestCase):
|
||||
# THEN: We should get back a valid service item
|
||||
self.assertTrue(service_item.is_valid, 'The new service item should be valid')
|
||||
self.assertEqual(os.path.normpath(test_file), os.path.normpath(service_item.get_rendered_frame(0)),
|
||||
'The first frame should match the path to the image')
|
||||
'The first frame should match the path to the image')
|
||||
self.assertEqual(frame_array, service_item.get_frames()[0],
|
||||
'The return should match frame array1')
|
||||
'The return should match frame array1')
|
||||
self.assertEqual(test_file, service_item.get_frame_path(0),
|
||||
'The frame path should match the full path to the image')
|
||||
'The frame path should match the full path to the image')
|
||||
self.assertEqual(image_name, service_item.get_frame_title(0),
|
||||
'The frame title should match the image name')
|
||||
'The frame title should match the image name')
|
||||
self.assertEqual(image_name, service_item.get_display_title(),
|
||||
'The display title should match the first image name')
|
||||
'The display title should match the first image name')
|
||||
self.assertTrue(service_item.is_image(), 'This service item should be of an "image" type')
|
||||
self.assertTrue(service_item.is_capable(ItemCapabilities.CanMaintain),
|
||||
'This service item should be able to be Maintained')
|
||||
'This service item should be able to be Maintained')
|
||||
self.assertTrue(service_item.is_capable(ItemCapabilities.CanPreview),
|
||||
'This service item should be able to be be Previewed')
|
||||
'This service item should be able to be be Previewed')
|
||||
self.assertTrue(service_item.is_capable(ItemCapabilities.CanLoop),
|
||||
'This service item should be able to be run in a can be made to Loop')
|
||||
'This service item should be able to be run in a can be made to Loop')
|
||||
self.assertTrue(service_item.is_capable(ItemCapabilities.CanAppend),
|
||||
'This service item should be able to have new items added to it')
|
||||
'This service item should be able to have new items added to it')
|
||||
|
||||
def service_item_load_image_from_local_service_test(self):
|
||||
"""
|
||||
@ -178,33 +178,33 @@ class TestServiceItem(TestCase):
|
||||
self.assertTrue(service_item.is_valid, 'The first service item should be valid')
|
||||
self.assertTrue(service_item2.is_valid, 'The second service item should be valid')
|
||||
self.assertEqual(test_file1, os.path.normpath(service_item.get_rendered_frame(0)),
|
||||
'The first frame should match the path to the image')
|
||||
'The first frame should match the path to the image')
|
||||
self.assertEqual(test_file2, os.path.normpath(service_item2.get_rendered_frame(0)),
|
||||
'The Second frame should match the path to the image')
|
||||
'The Second frame should match the path to the image')
|
||||
# There is a problem with the following two asserts in Windows
|
||||
# and it is not easily fixable (although it looks simple)
|
||||
if os.name != 'nt':
|
||||
self.assertEqual(frame_array1, service_item.get_frames()[0], 'The return should match the frame array1')
|
||||
self.assertEqual(frame_array2, service_item2.get_frames()[0], 'The return should match the frame array2')
|
||||
self.assertEqual(test_file1, os.path.normpath(service_item.get_frame_path(0)),
|
||||
'The frame path should match the full path to the image')
|
||||
'The frame path should match the full path to the image')
|
||||
self.assertEqual(test_file2, os.path.normpath(service_item2.get_frame_path(0)),
|
||||
'The frame path should match the full path to the image')
|
||||
'The frame path should match the full path to the image')
|
||||
self.assertEqual(image_name1, service_item.get_frame_title(0),
|
||||
'The 1st frame title should match the image name')
|
||||
'The 1st frame title should match the image name')
|
||||
self.assertEqual(image_name2, service_item2.get_frame_title(0),
|
||||
'The 2nd frame title should match the image name')
|
||||
'The 2nd frame title should match the image name')
|
||||
self.assertEqual(service_item.name, service_item.title.lower(),
|
||||
'The plugin name should match the display title, as there are > 1 Images')
|
||||
'The plugin name should match the display title, as there are > 1 Images')
|
||||
self.assertTrue(service_item.is_image(), 'This service item should be of an "image" type')
|
||||
self.assertTrue(service_item.is_capable(ItemCapabilities.CanMaintain),
|
||||
'This service item should be able to be Maintained')
|
||||
'This service item should be able to be Maintained')
|
||||
self.assertTrue(service_item.is_capable(ItemCapabilities.CanPreview),
|
||||
'This service item should be able to be be Previewed')
|
||||
'This service item should be able to be be Previewed')
|
||||
self.assertTrue(service_item.is_capable(ItemCapabilities.CanLoop),
|
||||
'This service item should be able to be run in a can be made to Loop')
|
||||
'This service item should be able to be run in a can be made to Loop')
|
||||
self.assertTrue(service_item.is_capable(ItemCapabilities.CanAppend),
|
||||
'This service item should be able to have new items added to it')
|
||||
'This service item should be able to have new items added to it')
|
||||
|
||||
def add_from_command_for_a_presentation_test(self):
|
||||
"""
|
||||
|
85
tests/functional/openlp_core_ui/test_servicemanager.py
Normal file
85
tests/functional/openlp_core_ui/test_servicemanager.py
Normal file
@ -0,0 +1,85 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
|
||||
|
||||
###############################################################################
|
||||
# OpenLP - Open Source Lyrics Projection #
|
||||
# --------------------------------------------------------------------------- #
|
||||
# Copyright (c) 2008-2014 Raoul Snyman #
|
||||
# Portions copyright (c) 2008-2014 Tim Bentley, Gerald Britton, Jonathan #
|
||||
# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, #
|
||||
# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. #
|
||||
# Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru, #
|
||||
# Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, #
|
||||
# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Dave Warnock, #
|
||||
# Frode Woldsund, Martin Zibricky, Patrick Zimmermann #
|
||||
# --------------------------------------------------------------------------- #
|
||||
# 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.slidecontroller package.
|
||||
"""
|
||||
from unittest import TestCase
|
||||
|
||||
from openlp.core.common import Registry
|
||||
from openlp.core.ui import ServiceManager
|
||||
|
||||
from tests.interfaces import MagicMock, patch
|
||||
|
||||
|
||||
class TestServiceManager(TestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""
|
||||
Create the UI
|
||||
"""
|
||||
Registry.create()
|
||||
#self.app = QtGui.QApplication([])
|
||||
#ScreenList.create(self.app.desktop())
|
||||
#Registry().register('application', MagicMock())
|
||||
#with patch('openlp.core.lib.PluginManager'):
|
||||
# self.main_window = MainWindow()
|
||||
#self.service_manager = Registry().get('service_manager')
|
||||
|
||||
def tearDown(self):
|
||||
"""
|
||||
Delete all the C++ objects at the end so that we don't have a segfault
|
||||
"""
|
||||
#del self.main_window
|
||||
#del self.app
|
||||
pass
|
||||
|
||||
def initial_service_manager_test(self):
|
||||
"""
|
||||
Test the initial of service manager.
|
||||
"""
|
||||
# GIVEN: A new service manager instance.
|
||||
ServiceManager(None)
|
||||
# WHEN: the default service manager is built.
|
||||
# THEN: The the controller should be registered in the registry.
|
||||
self.assertNotEqual(Registry().get('service_manager'), None, 'The base service manager should be registered')
|
||||
|
||||
def create_basic_service_test(self):
|
||||
"""
|
||||
Test the create basic service array
|
||||
"""
|
||||
# GIVEN: A new service manager instance.
|
||||
service_manager = ServiceManager(None)
|
||||
# WHEN: when the basic service array is created.
|
||||
service_manager._save_lite = False
|
||||
service_manager.service_theme = 'test_theme'
|
||||
service = service_manager.create_basic_service()[0]
|
||||
# THEN: The the controller should be registered in the registry.
|
||||
self.assertNotEqual(service, None, 'The base service should be created')
|
||||
self.assertEqual(service['openlp_core']['service-theme'], 'test_theme', 'The test theme should be saved')
|
||||
self.assertEqual(service['openlp_core']['lite-service'], False, 'The lite service should be saved')
|
@ -27,24 +27,52 @@
|
||||
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||
###############################################################################
|
||||
"""
|
||||
The Theme Controller helps manages adding, deleteing and modifying of themes.
|
||||
Package to test the openlp.core.ui.slidecontroller package.
|
||||
"""
|
||||
import os
|
||||
from unittest import TestCase
|
||||
|
||||
from openlp.core.common import AppLocation, check_directory_exists
|
||||
from openlp.core.ui import SlideController
|
||||
|
||||
from tests.interfaces import MagicMock, patch
|
||||
|
||||
|
||||
class ThemeManagerHelper(object):
|
||||
"""
|
||||
Manages the non ui theme functions.
|
||||
"""
|
||||
def build_theme_path(self):
|
||||
class TestSlideController(TestCase):
|
||||
|
||||
def initial_slide_controller_test(self):
|
||||
"""
|
||||
Set up the theme path variables
|
||||
Test the initial slide controller state .
|
||||
"""
|
||||
self.log_debug('build theme path called')
|
||||
self.path = AppLocation.get_section_data_path(self.settings_section)
|
||||
check_directory_exists(self.path)
|
||||
self.thumb_path = os.path.join(self.path, 'thumbnails')
|
||||
check_directory_exists(self.thumb_path)
|
||||
self.theme_form.path = self.path
|
||||
# GIVEN: A new slideController instance.
|
||||
slide_controller = SlideController(None)
|
||||
# WHEN: the default controller is built.
|
||||
# THEN: The controller should not be a live controller.
|
||||
self.assertEqual(slide_controller.is_live, False, 'The base slide controller should not be a live controller')
|
||||
|
||||
def toggle_blank_test(self):
|
||||
"""
|
||||
Test the setting of the display blank icons by display type.
|
||||
"""
|
||||
# GIVEN: A new slideController instance.
|
||||
slide_controller = SlideController(None)
|
||||
service_item = MagicMock()
|
||||
toolbar = MagicMock()
|
||||
toolbar.set_widget_visible = self.dummy_widget_visible
|
||||
slide_controller.toolbar = toolbar
|
||||
slide_controller.service_item = service_item
|
||||
|
||||
# WHEN a text based service item is used
|
||||
slide_controller.service_item.is_text = MagicMock(return_value=True)
|
||||
slide_controller.set_blank_menu()
|
||||
|
||||
# THEN: then call set up the toolbar to blank the display screen.
|
||||
self.assertEqual(len(self.test_widget), 3, 'There should be three icons to display on the screen')
|
||||
|
||||
# WHEN a non text based service item is used
|
||||
slide_controller.service_item.is_text = MagicMock(return_value=False)
|
||||
slide_controller.set_blank_menu()
|
||||
|
||||
# THEN: then call set up the toolbar to blank the display screen.
|
||||
self.assertEqual(len(self.test_widget), 2, 'There should be only two icons to display on the screen')
|
||||
|
||||
def dummy_widget_visible(self, widget, visible=True):
|
||||
self.test_widget = widget
|
@ -38,7 +38,7 @@ class TestListPreviewWidget(TestCase):
|
||||
|
||||
def initial_slide_count_test(self):
|
||||
"""
|
||||
Test the inital slide count.
|
||||
Test the initial slide count .
|
||||
"""
|
||||
# GIVEN: A new ListPreviewWidget instance.
|
||||
# WHEN: No SlideItem has been added yet.
|
||||
@ -47,7 +47,7 @@ class TestListPreviewWidget(TestCase):
|
||||
|
||||
def initial_slide_number_test(self):
|
||||
"""
|
||||
Test the inital slide number.
|
||||
Test the initial current slide number.
|
||||
"""
|
||||
# GIVEN: A new ListPreviewWidget instance.
|
||||
# WHEN: No SlideItem has been added yet.
|
||||
|
@ -22,12 +22,13 @@ class TestMainWindow(TestCase):
|
||||
# Mock cursor busy/normal methods.
|
||||
self.app.set_busy_cursor = MagicMock()
|
||||
self.app.set_normal_cursor = MagicMock()
|
||||
self.app.args =[]
|
||||
self.app.args = []
|
||||
Registry().register('application', self.app)
|
||||
# Mock classes and methods used by mainwindow.
|
||||
with patch('openlp.core.ui.mainwindow.SettingsForm') as mocked_settings_form, \
|
||||
patch('openlp.core.ui.mainwindow.ImageManager') as mocked_image_manager, \
|
||||
patch('openlp.core.ui.mainwindow.SlideController') as mocked_slide_controller, \
|
||||
patch('openlp.core.ui.mainwindow.LiveController') as mocked_live_controller, \
|
||||
patch('openlp.core.ui.mainwindow.PreviewController') as mocked_preview_controller, \
|
||||
patch('openlp.core.ui.mainwindow.OpenLPDockWidget') as mocked_dock_widget, \
|
||||
patch('openlp.core.ui.mainwindow.QtGui.QToolBox') as mocked_q_tool_box_class, \
|
||||
patch('openlp.core.ui.mainwindow.QtGui.QMainWindow.addDockWidget') as mocked_add_dock_method, \
|
||||
@ -53,7 +54,7 @@ class TestMainWindow(TestCase):
|
||||
mocked_value.side_effect = [True, 2]
|
||||
|
||||
# WHEN: Call the restore method.
|
||||
Registry().execute('bootstrap_post_set_up')
|
||||
self.main_window.restore_current_media_manager_item()
|
||||
|
||||
# THEN: The current widget should have been set.
|
||||
self.main_window.media_tool_box.setCurrentIndex.assert_called_with(2)
|
||||
|
@ -4,10 +4,10 @@
|
||||
|
||||
from unittest import TestCase
|
||||
|
||||
from PyQt4 import QtGui
|
||||
from PyQt4 import QtGui, QtTest, QtCore
|
||||
|
||||
from openlp.core.common import Registry
|
||||
from openlp.core.lib import ScreenList, ServiceItem
|
||||
from openlp.core.lib import ScreenList, ServiceItem, ItemCapabilities
|
||||
from openlp.core.ui.mainwindow import MainWindow
|
||||
from tests.interfaces import MagicMock, patch
|
||||
|
||||
@ -35,20 +35,22 @@ class TestServiceManager(TestCase):
|
||||
|
||||
def basic_service_manager_test(self):
|
||||
"""
|
||||
Test the Service Manager display functionality
|
||||
Test the Service Manager UI Functionality
|
||||
"""
|
||||
# GIVEN: A New Service Manager instance
|
||||
|
||||
# WHEN I have an empty display
|
||||
# WHEN I have set up the display
|
||||
self.service_manager.setup_ui(self.service_manager)
|
||||
# THEN the count of items should be zero
|
||||
self.assertEqual(self.service_manager.service_manager_list.topLevelItemCount(), 0,
|
||||
'The service manager list should be empty ')
|
||||
|
||||
def context_menu_test(self):
|
||||
def default_context_menu_test(self):
|
||||
"""
|
||||
Test the context_menu() method.
|
||||
Test the context_menu() method with a default service item
|
||||
"""
|
||||
# GIVEN: A service item added
|
||||
self.service_manager.setup_ui(self.service_manager)
|
||||
with patch('PyQt4.QtGui.QTreeWidget.itemAt') as mocked_item_at_method, \
|
||||
patch('PyQt4.QtGui.QWidget.mapToGlobal'), \
|
||||
patch('PyQt4.QtGui.QMenu.exec_'):
|
||||
@ -84,3 +86,236 @@ class TestServiceManager(TestCase):
|
||||
'The action should be set invisible.'
|
||||
self.service_manager.auto_start_action.setVisible.assert_called_once_with(False), \
|
||||
'The action should be set invisible.'
|
||||
|
||||
def edit_context_menu_test(self):
|
||||
"""
|
||||
Test the context_menu() method with a edit service item
|
||||
"""
|
||||
# GIVEN: A service item added
|
||||
self.service_manager.setup_ui(self.service_manager)
|
||||
with patch('PyQt4.QtGui.QTreeWidget.itemAt') as mocked_item_at_method, \
|
||||
patch('PyQt4.QtGui.QWidget.mapToGlobal'), \
|
||||
patch('PyQt4.QtGui.QMenu.exec_'):
|
||||
mocked_item = MagicMock()
|
||||
mocked_item.parent.return_value = None
|
||||
mocked_item_at_method.return_value = mocked_item
|
||||
# We want 1 to be returned for the position
|
||||
mocked_item.data.return_value = 1
|
||||
# A service item without capabilities.
|
||||
service_item = ServiceItem()
|
||||
service_item.add_capability(ItemCapabilities.CanEdit)
|
||||
service_item.edit_id = 1
|
||||
self.service_manager.service_items = [{'service_item': service_item}]
|
||||
q_point = None
|
||||
# Mocked actions.
|
||||
self.service_manager.edit_action.setVisible = MagicMock()
|
||||
self.service_manager.create_custom_action.setVisible = MagicMock()
|
||||
self.service_manager.maintain_action.setVisible = MagicMock()
|
||||
self.service_manager.notes_action.setVisible = MagicMock()
|
||||
self.service_manager.time_action.setVisible = MagicMock()
|
||||
self.service_manager.auto_start_action.setVisible = MagicMock()
|
||||
|
||||
# WHEN: Show the context menu.
|
||||
self.service_manager.context_menu(q_point)
|
||||
|
||||
# THEN: The following actions should be not visible.
|
||||
self.service_manager.edit_action.setVisible.assert_called_with(True), \
|
||||
'The action should be set visible.'
|
||||
self.service_manager.create_custom_action.setVisible.assert_called_once_with(False), \
|
||||
'The action should be set invisible.'
|
||||
self.service_manager.maintain_action.setVisible.assert_called_once_with(False), \
|
||||
'The action should be set invisible.'
|
||||
self.service_manager.notes_action.setVisible.assert_called_with(True), 'The action should be set visible.'
|
||||
self.service_manager.time_action.setVisible.assert_called_once_with(False), \
|
||||
'The action should be set invisible.'
|
||||
self.service_manager.auto_start_action.setVisible.assert_called_once_with(False), \
|
||||
'The action should be set invisible.'
|
||||
|
||||
def maintain_context_menu_test(self):
|
||||
"""
|
||||
Test the context_menu() method with a maintain
|
||||
"""
|
||||
# GIVEN: A service item added
|
||||
self.service_manager.setup_ui(self.service_manager)
|
||||
with patch('PyQt4.QtGui.QTreeWidget.itemAt') as mocked_item_at_method, \
|
||||
patch('PyQt4.QtGui.QWidget.mapToGlobal'), \
|
||||
patch('PyQt4.QtGui.QMenu.exec_'):
|
||||
mocked_item = MagicMock()
|
||||
mocked_item.parent.return_value = None
|
||||
mocked_item_at_method.return_value = mocked_item
|
||||
# We want 1 to be returned for the position
|
||||
mocked_item.data.return_value = 1
|
||||
# A service item without capabilities.
|
||||
service_item = ServiceItem()
|
||||
service_item.add_capability(ItemCapabilities.CanMaintain)
|
||||
self.service_manager.service_items = [{'service_item': service_item}]
|
||||
q_point = None
|
||||
# Mocked actions.
|
||||
self.service_manager.edit_action.setVisible = MagicMock()
|
||||
self.service_manager.create_custom_action.setVisible = MagicMock()
|
||||
self.service_manager.maintain_action.setVisible = MagicMock()
|
||||
self.service_manager.notes_action.setVisible = MagicMock()
|
||||
self.service_manager.time_action.setVisible = MagicMock()
|
||||
self.service_manager.auto_start_action.setVisible = MagicMock()
|
||||
|
||||
# WHEN: Show the context menu.
|
||||
self.service_manager.context_menu(q_point)
|
||||
|
||||
# THEN: The following actions should be not visible.
|
||||
self.service_manager.edit_action.setVisible.assert_called_once_with(False), \
|
||||
'The action should be set invisible.'
|
||||
self.service_manager.create_custom_action.setVisible.assert_called_once_with(False), \
|
||||
'The action should be set invisible.'
|
||||
self.service_manager.maintain_action.setVisible.assert_called_with(True), \
|
||||
'The action should be set visible.'
|
||||
self.service_manager.notes_action.setVisible.assert_called_with(True), 'The action should be set visible.'
|
||||
self.service_manager.time_action.setVisible.assert_called_once_with(False), \
|
||||
'The action should be set invisible.'
|
||||
self.service_manager.auto_start_action.setVisible.assert_called_once_with(False), \
|
||||
'The action should be set invisible.'
|
||||
|
||||
def loopy_context_menu_test(self):
|
||||
"""
|
||||
Test the context_menu() method with a loop
|
||||
"""
|
||||
# GIVEN: A service item added
|
||||
self.service_manager.setup_ui(self.service_manager)
|
||||
with patch('PyQt4.QtGui.QTreeWidget.itemAt') as mocked_item_at_method, \
|
||||
patch('PyQt4.QtGui.QWidget.mapToGlobal'), \
|
||||
patch('PyQt4.QtGui.QMenu.exec_'):
|
||||
mocked_item = MagicMock()
|
||||
mocked_item.parent.return_value = None
|
||||
mocked_item_at_method.return_value = mocked_item
|
||||
# We want 1 to be returned for the position
|
||||
mocked_item.data.return_value = 1
|
||||
# A service item without capabilities.
|
||||
service_item = ServiceItem()
|
||||
service_item.add_capability(ItemCapabilities.CanLoop)
|
||||
service_item._raw_frames.append("One")
|
||||
service_item._raw_frames.append("Two")
|
||||
self.service_manager.service_items = [{'service_item': service_item}]
|
||||
q_point = None
|
||||
# Mocked actions.
|
||||
self.service_manager.edit_action.setVisible = MagicMock()
|
||||
self.service_manager.create_custom_action.setVisible = MagicMock()
|
||||
self.service_manager.maintain_action.setVisible = MagicMock()
|
||||
self.service_manager.notes_action.setVisible = MagicMock()
|
||||
self.service_manager.time_action.setVisible = MagicMock()
|
||||
self.service_manager.auto_start_action.setVisible = MagicMock()
|
||||
|
||||
# WHEN: Show the context menu.
|
||||
self.service_manager.context_menu(q_point)
|
||||
|
||||
# THEN: The following actions should be not visible.
|
||||
self.service_manager.edit_action.setVisible.assert_called_once_with(False), \
|
||||
'The action should be set invisible.'
|
||||
self.service_manager.create_custom_action.setVisible.assert_called_once_with(False), \
|
||||
'The action should be set invisible.'
|
||||
self.service_manager.maintain_action.setVisible.assert_called_once_with(False), \
|
||||
'The action should be set invisible.'
|
||||
self.service_manager.notes_action.setVisible.assert_called_with(True), 'The action should be set visible.'
|
||||
self.service_manager.time_action.setVisible.assert_called_once_with(False), \
|
||||
'The action should be set invisible.'
|
||||
self.service_manager.auto_start_action.setVisible.assert_called_once_with(False), \
|
||||
'The action should be set invisible.'
|
||||
|
||||
def start_time_context_menu_test(self):
|
||||
"""
|
||||
Test the context_menu() method with a start time
|
||||
"""
|
||||
# GIVEN: A service item added
|
||||
self.service_manager.setup_ui(self.service_manager)
|
||||
with patch('PyQt4.QtGui.QTreeWidget.itemAt') as mocked_item_at_method, \
|
||||
patch('PyQt4.QtGui.QWidget.mapToGlobal'), \
|
||||
patch('PyQt4.QtGui.QMenu.exec_'):
|
||||
mocked_item = MagicMock()
|
||||
mocked_item.parent.return_value = None
|
||||
mocked_item_at_method.return_value = mocked_item
|
||||
# We want 1 to be returned for the position
|
||||
mocked_item.data.return_value = 1
|
||||
# A service item without capabilities.
|
||||
service_item = ServiceItem()
|
||||
service_item.add_capability(ItemCapabilities.HasVariableStartTime)
|
||||
self.service_manager.service_items = [{'service_item': service_item}]
|
||||
q_point = None
|
||||
# Mocked actions.
|
||||
self.service_manager.edit_action.setVisible = MagicMock()
|
||||
self.service_manager.create_custom_action.setVisible = MagicMock()
|
||||
self.service_manager.maintain_action.setVisible = MagicMock()
|
||||
self.service_manager.notes_action.setVisible = MagicMock()
|
||||
self.service_manager.time_action.setVisible = MagicMock()
|
||||
self.service_manager.auto_start_action.setVisible = MagicMock()
|
||||
|
||||
# WHEN: Show the context menu.
|
||||
self.service_manager.context_menu(q_point)
|
||||
|
||||
# THEN: The following actions should be not visible.
|
||||
self.service_manager.edit_action.setVisible.assert_called_once_with(False), \
|
||||
'The action should be set invisible.'
|
||||
self.service_manager.create_custom_action.setVisible.assert_called_once_with(False), \
|
||||
'The action should be set invisible.'
|
||||
self.service_manager.maintain_action.setVisible.assert_called_once_with(False), \
|
||||
'The action should be set invisible.'
|
||||
self.service_manager.notes_action.setVisible.assert_called_with(True), 'The action should be set visible.'
|
||||
self.service_manager.time_action.setVisible.assert_called_with(True), \
|
||||
'The action should be set visible.'
|
||||
self.service_manager.auto_start_action.setVisible.assert_called_once_with(False), \
|
||||
'The action should be set invisible.'
|
||||
|
||||
def auto_start_context_menu_test(self):
|
||||
"""
|
||||
Test the context_menu() method with can auto start
|
||||
"""
|
||||
# GIVEN: A service item added
|
||||
self.service_manager.setup_ui(self.service_manager)
|
||||
with patch('PyQt4.QtGui.QTreeWidget.itemAt') as mocked_item_at_method, \
|
||||
patch('PyQt4.QtGui.QWidget.mapToGlobal'), \
|
||||
patch('PyQt4.QtGui.QMenu.exec_'):
|
||||
mocked_item = MagicMock()
|
||||
mocked_item.parent.return_value = None
|
||||
mocked_item_at_method.return_value = mocked_item
|
||||
# We want 1 to be returned for the position
|
||||
mocked_item.data.return_value = 1
|
||||
# A service item without capabilities.
|
||||
service_item = ServiceItem()
|
||||
service_item.add_capability(ItemCapabilities.CanAutoStartForLive)
|
||||
self.service_manager.service_items = [{'service_item': service_item}]
|
||||
q_point = None
|
||||
# Mocked actions.
|
||||
self.service_manager.edit_action.setVisible = MagicMock()
|
||||
self.service_manager.create_custom_action.setVisible = MagicMock()
|
||||
self.service_manager.maintain_action.setVisible = MagicMock()
|
||||
self.service_manager.notes_action.setVisible = MagicMock()
|
||||
self.service_manager.time_action.setVisible = MagicMock()
|
||||
self.service_manager.auto_start_action.setVisible = MagicMock()
|
||||
|
||||
# WHEN: Show the context menu.
|
||||
self.service_manager.context_menu(q_point)
|
||||
|
||||
# THEN: The following actions should be not visible.
|
||||
self.service_manager.edit_action.setVisible.assert_called_once_with(False), \
|
||||
'The action should be set invisible.'
|
||||
self.service_manager.create_custom_action.setVisible.assert_called_once_with(False), \
|
||||
'The action should be set invisible.'
|
||||
self.service_manager.maintain_action.setVisible.assert_called_once_with(False), \
|
||||
'The action should be set invisible.'
|
||||
self.service_manager.notes_action.setVisible.assert_called_with(True), 'The action should be set visible.'
|
||||
self.service_manager.time_action.setVisible.assert_called_once_with(False), \
|
||||
'The action should be set invisible.'
|
||||
self.service_manager.auto_start_action.setVisible.assert_called_with(True), \
|
||||
'The action should be set visible.'
|
||||
|
||||
def click_on_new_service_test(self):
|
||||
"""
|
||||
Test the on_new_service event handler is called by the UI
|
||||
"""
|
||||
# GIVEN: An initial form
|
||||
mocked_event = MagicMock()
|
||||
self.service_manager.on_new_service_clicked = mocked_event
|
||||
self.service_manager.setup_ui(self.service_manager)
|
||||
|
||||
# WHEN displaying the UI and pressing cancel
|
||||
new_service = self.service_manager.toolbar.actions['newService']
|
||||
new_service.trigger()
|
||||
|
||||
assert mocked_event.call_count == 1, 'The on_new_service_clicked method should have been called once'
|
||||
|
@ -34,28 +34,28 @@ class TestStartTimeDialog(TestCase):
|
||||
"""
|
||||
Test StartTimeDialog are defaults correct
|
||||
"""
|
||||
self.assertEqual(self.form.hourSpinBox.minimum(), 0, 'The minimum hour should stay the same as the dialog')
|
||||
self.assertEqual(self.form.hourSpinBox.maximum(), 4, 'The maximum hour should stay the same as the dialog')
|
||||
self.assertEqual(self.form.minuteSpinBox.minimum(), 0,
|
||||
'The minimum minute should stay the same as the dialog')
|
||||
self.assertEqual(self.form.minuteSpinBox.maximum(), 59,
|
||||
'The maximum minute should stay the same as the dialog')
|
||||
self.assertEqual(self.form.secondSpinBox.minimum(), 0,
|
||||
'The minimum second should stay the same as the dialog')
|
||||
self.assertEqual(self.form.secondSpinBox.maximum(), 59,
|
||||
'The maximum second should stay the same as the dialog')
|
||||
self.assertEqual(self.form.hourFinishSpinBox.minimum(), 0,
|
||||
'The minimum finish hour should stay the same as the dialog')
|
||||
self.assertEqual(self.form.hourFinishSpinBox.maximum(), 4,
|
||||
'The maximum finish hour should stay the same as the dialog')
|
||||
self.assertEqual(self.form.minuteFinishSpinBox.minimum(), 0,
|
||||
'The minimum finish minute should stay the same as the dialog')
|
||||
self.assertEqual(self.form.minuteFinishSpinBox.maximum(), 59,
|
||||
'The maximum finish minute should stay the same as the dialog')
|
||||
self.assertEqual(self.form.secondFinishSpinBox.minimum(), 0,
|
||||
'The minimum finish second should stay the same as the dialog')
|
||||
self.assertEqual(self.form.secondFinishSpinBox.maximum(), 59,
|
||||
'The maximum finish second should stay the same as the dialog')
|
||||
self.assertEqual(self.form.hour_spin_box.minimum(), 0, 'The minimum hour should stay the same as the dialog')
|
||||
self.assertEqual(self.form.hour_spin_box.maximum(), 4, 'The maximum hour should stay the same as the dialog')
|
||||
self.assertEqual(self.form.minute_spin_box.minimum(), 0,
|
||||
'The minimum minute should stay the same as the dialog')
|
||||
self.assertEqual(self.form.minute_spin_box.maximum(), 59,
|
||||
'The maximum minute should stay the same as the dialog')
|
||||
self.assertEqual(self.form.second_spin_box.minimum(), 0,
|
||||
'The minimum second should stay the same as the dialog')
|
||||
self.assertEqual(self.form.second_spin_box.maximum(), 59,
|
||||
'The maximum second should stay the same as the dialog')
|
||||
self.assertEqual(self.form.hour_finish_spin_box.minimum(), 0,
|
||||
'The minimum finish hour should stay the same as the dialog')
|
||||
self.assertEqual(self.form.hour_finish_spin_box.maximum(), 4,
|
||||
'The maximum finish hour should stay the same as the dialog')
|
||||
self.assertEqual(self.form.minute_finish_spin_box.minimum(), 0,
|
||||
'The minimum finish minute should stay the same as the dialog')
|
||||
self.assertEqual(self.form.minute_finish_spin_box.maximum(), 59,
|
||||
'The maximum finish minute should stay the same as the dialog')
|
||||
self.assertEqual(self.form.second_finish_spin_box.minimum(), 0,
|
||||
'The minimum finish second should stay the same as the dialog')
|
||||
self.assertEqual(self.form.second_finish_spin_box.maximum(), 59,
|
||||
'The maximum finish second should stay the same as the dialog')
|
||||
|
||||
def time_display_test(self):
|
||||
"""
|
||||
@ -75,22 +75,22 @@ class TestStartTimeDialog(TestCase):
|
||||
QtTest.QTest.mouseClick(ok_widget, QtCore.Qt.LeftButton)
|
||||
|
||||
# THEN the following input values are returned
|
||||
self.assertEqual(self.form.hourSpinBox.value(), 0)
|
||||
self.assertEqual(self.form.minuteSpinBox.value(), 1)
|
||||
self.assertEqual(self.form.secondSpinBox.value(), 1)
|
||||
self.assertEqual(self.form.hour_spin_box.value(), 0)
|
||||
self.assertEqual(self.form.minute_spin_box.value(), 1)
|
||||
self.assertEqual(self.form.second_spin_box.value(), 1)
|
||||
self.assertEqual(self.form.item['service_item'].start_time, 61, 'The start time should stay the same')
|
||||
|
||||
# WHEN displaying the UI, changing the time to 2min 3secs and pressing enter
|
||||
self.form.item = {'service_item': mocked_serviceitem}
|
||||
with patch('PyQt4.QtGui.QDialog.exec_'):
|
||||
self.form.exec_()
|
||||
self.form.minuteSpinBox.setValue(2)
|
||||
self.form.secondSpinBox.setValue(3)
|
||||
self.form.minute_spin_box.setValue(2)
|
||||
self.form.second_spin_box.setValue(3)
|
||||
ok_widget = self.form.button_box.button(self.form.button_box.Ok)
|
||||
QtTest.QTest.mouseClick(ok_widget, QtCore.Qt.LeftButton)
|
||||
|
||||
# THEN the following values are returned
|
||||
self.assertEqual(self.form.hourSpinBox.value(), 0)
|
||||
self.assertEqual(self.form.minuteSpinBox.value(), 2)
|
||||
self.assertEqual(self.form.secondSpinBox.value(), 3)
|
||||
self.assertEqual(self.form.hour_spin_box.value(), 0)
|
||||
self.assertEqual(self.form.minute_spin_box.value(), 2)
|
||||
self.assertEqual(self.form.second_spin_box.value(), 3)
|
||||
self.assertEqual(self.form.item['service_item'].start_time, 123, 'The start time should have changed')
|
||||
|
@ -27,20 +27,22 @@
|
||||
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||
###############################################################################
|
||||
"""
|
||||
Interface tests to test the thememanagerhelper class and related methods.
|
||||
Interface tests to test the themeManager class and related methods.
|
||||
"""
|
||||
import os
|
||||
from unittest import TestCase
|
||||
from tempfile import mkstemp
|
||||
|
||||
from openlp.core.common import Settings
|
||||
from openlp.core.ui import ThemeManagerHelper
|
||||
from PyQt4 import QtGui
|
||||
|
||||
from openlp.core.common import Registry, Settings
|
||||
from openlp.core.ui import ThemeManager
|
||||
from tests.functional import patch, MagicMock
|
||||
|
||||
|
||||
class TestThemeManagerHelper(TestCase):
|
||||
class TestThemeManager(TestCase):
|
||||
"""
|
||||
Test the functions in the ThemeManagerHelp[er module
|
||||
Test the functions in the ThemeManager module
|
||||
"""
|
||||
def setUp(self):
|
||||
"""
|
||||
@ -48,8 +50,9 @@ class TestThemeManagerHelper(TestCase):
|
||||
"""
|
||||
fd, self.ini_file = mkstemp('.ini')
|
||||
Settings().set_filename(self.ini_file)
|
||||
self.helper = ThemeManagerHelper()
|
||||
self.helper.settings_section = "themes"
|
||||
self.app = QtGui.QApplication([])
|
||||
Registry.create()
|
||||
self.theme_manager = ThemeManager()
|
||||
|
||||
def tearDown(self):
|
||||
"""
|
||||
@ -57,30 +60,31 @@ class TestThemeManagerHelper(TestCase):
|
||||
"""
|
||||
os.unlink(self.ini_file)
|
||||
os.unlink(Settings().fileName())
|
||||
del self.app
|
||||
|
||||
def test_initialise(self):
|
||||
def initialise_test(self):
|
||||
"""
|
||||
Test the thememanagerhelper initialise - basic test
|
||||
Test the thememanager initialise - basic test
|
||||
"""
|
||||
# GIVEN: A new a call to initialise
|
||||
self.theme_manager.build_theme_path = MagicMock()
|
||||
self.theme_manager.load_first_time_themes = MagicMock()
|
||||
Settings().setValue('themes/global theme', 'my_theme')
|
||||
self.helper.build_theme_path = MagicMock()
|
||||
self.helper.load_first_time_themes = MagicMock()
|
||||
|
||||
# WHEN: the initialistion is run
|
||||
self.helper.initialise()
|
||||
self.theme_manager.bootstrap_initialise()
|
||||
|
||||
# THEN:
|
||||
self.assertEqual(1, self.helper.build_theme_path.call_count,
|
||||
self.assertEqual(1, self.theme_manager.build_theme_path.call_count,
|
||||
'The function build_theme_path should have been called')
|
||||
self.assertEqual(1, self.helper.load_first_time_themes.call_count,
|
||||
self.assertEqual(1, self.theme_manager.load_first_time_themes.call_count,
|
||||
'The function load_first_time_themes should have been called only once')
|
||||
self.assertEqual(self.helper.global_theme, 'my_theme',
|
||||
self.assertEqual(self.theme_manager.global_theme, 'my_theme',
|
||||
'The global theme should have been set to my_theme')
|
||||
|
||||
def test_build_theme_path(self):
|
||||
def build_theme_path_test(self):
|
||||
"""
|
||||
Test the thememanagerhelper build_theme_path - basic test
|
||||
Test the thememanager build_theme_path - basic test
|
||||
"""
|
||||
# GIVEN: A new a call to initialise
|
||||
with patch('openlp.core.common.applocation.check_directory_exists') as mocked_check_directory_exists:
|
||||
@ -88,12 +92,28 @@ class TestThemeManagerHelper(TestCase):
|
||||
mocked_check_directory_exists.return_value = True
|
||||
Settings().setValue('themes/global theme', 'my_theme')
|
||||
|
||||
self.helper.theme_form = MagicMock()
|
||||
#self.helper.load_first_time_themes = MagicMock()
|
||||
self.theme_manager.theme_form = MagicMock()
|
||||
self.theme_manager.load_first_time_themes = MagicMock()
|
||||
|
||||
# WHEN: the build_theme_path is run
|
||||
self.helper.build_theme_path()
|
||||
self.theme_manager.build_theme_path()
|
||||
|
||||
# THEN:
|
||||
self.assertEqual(self.helper.path, self.helper.theme_form.path,
|
||||
'The theme path and the main path should be the same value')
|
||||
# THEN:
|
||||
assert self.theme_manager.thumb_path.startswith(self.theme_manager.path) is True, \
|
||||
'The thumb path and the main path should start with the same value'
|
||||
|
||||
def click_on_new_theme_test(self):
|
||||
"""
|
||||
Test the on_add_theme event handler is called by the UI
|
||||
"""
|
||||
# GIVEN: An initial form
|
||||
Settings().setValue('themes/global theme', 'my_theme')
|
||||
mocked_event = MagicMock()
|
||||
self.theme_manager.on_add_theme = mocked_event
|
||||
self.theme_manager.setup_ui(self.theme_manager)
|
||||
|
||||
# WHEN displaying the UI and pressing cancel
|
||||
new_theme = self.theme_manager.toolbar.actions['newTheme']
|
||||
new_theme.trigger()
|
||||
|
||||
assert mocked_event.call_count == 1, 'The on_add_theme method should have been called once'
|
Loading…
Reference in New Issue
Block a user