forked from openlp/openlp
head
This commit is contained in:
commit
67d12883c1
@ -30,6 +30,7 @@
|
||||
The :mod:`lib` module contains most of the components and libraries that make
|
||||
OpenLP work.
|
||||
"""
|
||||
from __future__ import division
|
||||
from distutils.version import LooseVersion
|
||||
import logging
|
||||
import os
|
||||
@ -154,7 +155,7 @@ def build_icon(icon):
|
||||
|
||||
``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``.
|
||||
location like ``/path/to/file.png``. However, the **recommended** way is to specify a resource string.
|
||||
"""
|
||||
button_icon = QtGui.QIcon()
|
||||
if isinstance(icon, QtGui.QIcon):
|
||||
@ -201,12 +202,13 @@ def create_thumb(image_path, thumb_path, return_icon=True, size=None):
|
||||
States if an icon should be build and returned from the thumb. Defaults to ``True``.
|
||||
|
||||
``size``
|
||||
Allows to state a own size to use. Defaults to ``None``, which means that a default height of 88 is used.
|
||||
Allows to state a own size (QtCore.QSize) to use. Defaults to ``None``, which means that a default height of 88
|
||||
is used.
|
||||
"""
|
||||
ext = os.path.splitext(thumb_path)[1].lower()
|
||||
reader = QtGui.QImageReader(image_path)
|
||||
if size is None:
|
||||
ratio = float(reader.size().width()) / float(reader.size().height())
|
||||
ratio = reader.size().width() / reader.size().height()
|
||||
reader.setScaledSize(QtCore.QSize(int(ratio * 88), 88))
|
||||
else:
|
||||
reader.setScaledSize(size)
|
||||
@ -259,8 +261,8 @@ def resize_image(image_path, width, height, background=u'#000000'):
|
||||
log.debug(u'resize_image - start')
|
||||
reader = QtGui.QImageReader(image_path)
|
||||
# The image's ratio.
|
||||
image_ratio = float(reader.size().width()) / float(reader.size().height())
|
||||
resize_ratio = float(width) / float(height)
|
||||
image_ratio = reader.size().width() / reader.size().height()
|
||||
resize_ratio = width / height
|
||||
# Figure out the size we want to resize the image to (keep aspect ratio).
|
||||
if image_ratio == resize_ratio:
|
||||
size = QtCore.QSize(width, height)
|
||||
@ -281,7 +283,7 @@ def resize_image(image_path, width, height, background=u'#000000'):
|
||||
new_image = QtGui.QImage(width, height, QtGui.QImage.Format_ARGB32_Premultiplied)
|
||||
painter = QtGui.QPainter(new_image)
|
||||
painter.fillRect(new_image.rect(), QtGui.QColor(background))
|
||||
painter.drawImage((width - real_width) / 2, (height - real_height) / 2, preview)
|
||||
painter.drawImage((width - real_width) // 2, (height - real_height) // 2, preview)
|
||||
return new_image
|
||||
|
||||
|
||||
|
@ -30,6 +30,7 @@
|
||||
"""
|
||||
Provide additional functionality required by OpenLP from the inherited QDockWidget.
|
||||
"""
|
||||
from __future__ import division
|
||||
import logging
|
||||
|
||||
from PyQt4 import QtGui
|
||||
@ -55,7 +56,7 @@ class OpenLPDockWidget(QtGui.QDockWidget):
|
||||
self.setWindowIcon(build_icon(icon))
|
||||
# Sort out the minimum width.
|
||||
screens = ScreenList()
|
||||
main_window_docbars = screens.current[u'size'].width() / 5
|
||||
main_window_docbars = screens.current[u'size'].width() // 5
|
||||
if main_window_docbars > 300:
|
||||
self.setMinimumWidth(300)
|
||||
else:
|
||||
|
@ -26,7 +26,7 @@
|
||||
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
|
||||
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||
###############################################################################
|
||||
|
||||
from __future__ import division
|
||||
import logging
|
||||
|
||||
from PyQt4 import QtWebKit
|
||||
@ -276,7 +276,7 @@ def build_background_css(item, width):
|
||||
``item``
|
||||
Service Item containing theme and location information
|
||||
"""
|
||||
width = int(width) / 2
|
||||
width = int(width) // 2
|
||||
theme = item.themedata
|
||||
background = u'background-color: black'
|
||||
if theme:
|
||||
|
@ -102,7 +102,6 @@ class MediaManagerItem(QtGui.QWidget):
|
||||
self.setupUi()
|
||||
self.retranslateUi()
|
||||
self.auto_select_id = -1
|
||||
Registry().register_function(u'%s_service_load' % self.plugin.name, self.service_load)
|
||||
# Need to use event as called across threads and UI is updated
|
||||
QtCore.QObject.connect(self, QtCore.SIGNAL(u'%s_go_live' % self.plugin.name), self.go_live_remote)
|
||||
QtCore.QObject.connect(self, QtCore.SIGNAL(u'%s_add_to_service' % self.plugin.name), self.add_to_service_remote)
|
||||
@ -585,12 +584,15 @@ class MediaManagerItem(QtGui.QWidget):
|
||||
else:
|
||||
return None
|
||||
|
||||
def service_load(self, message):
|
||||
def service_load(self, item):
|
||||
"""
|
||||
Method to add processing when a service has been loaded and individual service items need to be processed by the
|
||||
plugins.
|
||||
|
||||
``item``
|
||||
The item to be processed and returned.
|
||||
"""
|
||||
pass
|
||||
return item
|
||||
|
||||
def check_search_result(self):
|
||||
"""
|
||||
|
@ -26,7 +26,7 @@
|
||||
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
|
||||
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||
###############################################################################
|
||||
|
||||
from __future__ import division
|
||||
import logging
|
||||
|
||||
from PyQt4 import QtGui, QtCore, QtWebKit
|
||||
@ -327,7 +327,7 @@ class Renderer(object):
|
||||
screen_size = self.screens.current[u'size']
|
||||
self.width = screen_size.width()
|
||||
self.height = screen_size.height()
|
||||
self.screen_ratio = float(self.height) / float(self.width)
|
||||
self.screen_ratio = self.height / self.width
|
||||
log.debug(u'_calculate default %s, %f' % (screen_size, self.screen_ratio))
|
||||
# 90% is start of footer
|
||||
self.footer_start = int(self.height * 0.90)
|
||||
@ -546,15 +546,15 @@ class Renderer(object):
|
||||
"""
|
||||
smallest_index = 0
|
||||
highest_index = len(html_list) - 1
|
||||
index = int(highest_index / 2)
|
||||
index = highest_index // 2
|
||||
while True:
|
||||
if not self._text_fits_on_slide(previous_html + separator.join(html_list[:index + 1]).strip()):
|
||||
# We know that it does not fit, so change/calculate the new index and highest_index accordingly.
|
||||
highest_index = index
|
||||
index = int(index - (index - smallest_index) / 2)
|
||||
index = index - (index - smallest_index) // 2
|
||||
else:
|
||||
smallest_index = index
|
||||
index = int(index + (highest_index - index) / 2)
|
||||
index = index + (highest_index - index) // 2
|
||||
# We found the number of words which will fit.
|
||||
if smallest_index == index or highest_index == index:
|
||||
index = smallest_index
|
||||
@ -582,7 +582,7 @@ class Renderer(object):
|
||||
html_list[0] = html_tags + html_list[0]
|
||||
smallest_index = 0
|
||||
highest_index = len(html_list) - 1
|
||||
index = int(highest_index / 2)
|
||||
index = highest_index // 2
|
||||
return previous_html, previous_raw
|
||||
|
||||
def _text_fits_on_slide(self, text):
|
||||
|
@ -30,6 +30,7 @@
|
||||
The :mod:`screen` module provides management functionality for a machines'
|
||||
displays.
|
||||
"""
|
||||
from __future__ import division
|
||||
import logging
|
||||
import copy
|
||||
|
||||
@ -232,8 +233,8 @@ class ScreenList(object):
|
||||
``window``
|
||||
A QWidget we are finding the location of.
|
||||
"""
|
||||
x = window.x() + (window.width() / 2)
|
||||
y = window.y() + (window.height() / 2)
|
||||
x = window.x() + (window.width() // 2)
|
||||
y = window.y() + (window.height() // 2)
|
||||
for screen in self.screen_list:
|
||||
size = screen[u'size']
|
||||
if x >= size.x() and x <= (size.x() + size.width()) and y >= size.y() and y <= (size.y() + size.height()):
|
||||
|
@ -26,7 +26,7 @@
|
||||
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
|
||||
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||
###############################################################################
|
||||
|
||||
from __future__ import division
|
||||
import logging
|
||||
|
||||
from PyQt4 import QtCore, QtGui
|
||||
@ -85,10 +85,10 @@ class SearchEdit(QtGui.QLineEdit):
|
||||
size = self.clear_button.size()
|
||||
frame_width = self.style().pixelMetric(QtGui.QStyle.PM_DefaultFrameWidth)
|
||||
self.clear_button.move(self.rect().right() - frame_width - size.width(),
|
||||
(self.rect().bottom() + 1 - size.height()) / 2)
|
||||
(self.rect().bottom() + 1 - size.height()) // 2)
|
||||
if hasattr(self, u'menu_button'):
|
||||
size = self.menu_button.size()
|
||||
self.menu_button.move(self.rect().left() + frame_width + 2, (self.rect().bottom() + 1 - size.height()) / 2)
|
||||
self.menu_button.move(self.rect().left() + frame_width + 2, (self.rect().bottom() + 1 - size.height()) // 2)
|
||||
|
||||
def current_search_type(self):
|
||||
"""
|
||||
|
@ -58,8 +58,7 @@ class ItemCapabilities(object):
|
||||
Provides an enumeration of a service item's capabilities
|
||||
|
||||
``CanPreview``
|
||||
The capability to allow the ServiceManager to add to the preview
|
||||
tab when making the previous item live.
|
||||
The capability to allow the ServiceManager to add to the preview tab when making the previous item live.
|
||||
|
||||
``CanEdit``
|
||||
The capability to allow the ServiceManager to allow the item to be edited
|
||||
@ -71,8 +70,7 @@ class ItemCapabilities(object):
|
||||
Determines is the service_item needs a Media Player
|
||||
|
||||
``CanLoop``
|
||||
The capability to allow the SlideController to allow the loop
|
||||
processing.
|
||||
The capability to allow the SlideController to allow the loop processing.
|
||||
|
||||
``CanAppend``
|
||||
The capability to allow the ServiceManager to add leaves to the
|
||||
@ -82,22 +80,19 @@ class ItemCapabilities(object):
|
||||
The capability to remove lines breaks in the renderer
|
||||
|
||||
``OnLoadUpdate``
|
||||
The capability to update MediaManager when a service Item is
|
||||
loaded.
|
||||
The capability to update MediaManager when a service Item is loaded.
|
||||
|
||||
``AddIfNewItem``
|
||||
Not Used
|
||||
|
||||
``ProvidesOwnDisplay``
|
||||
The capability to tell the SlideController the service Item has a
|
||||
different display.
|
||||
The capability to tell the SlideController the service Item has a different display.
|
||||
|
||||
``HasDetailedTitleDisplay``
|
||||
ServiceItem provides a title
|
||||
Being Removed and decommissioned.
|
||||
|
||||
``HasVariableStartTime``
|
||||
The capability to tell the ServiceManager that a change to start
|
||||
time is possible.
|
||||
The capability to tell the ServiceManager that a change to start time is possible.
|
||||
|
||||
``CanSoftBreak``
|
||||
The capability to tell the renderer that Soft Break is allowed
|
||||
@ -149,7 +144,7 @@ class ServiceItem(object):
|
||||
if plugin:
|
||||
self.name = plugin.name
|
||||
self.title = u''
|
||||
self.shortname = u''
|
||||
self.processor = None
|
||||
self.audit = u''
|
||||
self.items = []
|
||||
self.iconic_representation = None
|
||||
@ -353,7 +348,8 @@ class ServiceItem(object):
|
||||
u'media_length': self.media_length,
|
||||
u'background_audio': self.background_audio,
|
||||
u'theme_overwritten': self.theme_overwritten,
|
||||
u'will_auto_start': self.will_auto_start
|
||||
u'will_auto_start': self.will_auto_start,
|
||||
u'processor': self.processor
|
||||
}
|
||||
service_data = []
|
||||
if self.service_item_type == ServiceItemType.Text:
|
||||
@ -387,7 +383,6 @@ class ServiceItem(object):
|
||||
self.title = header[u'title']
|
||||
self.name = header[u'name']
|
||||
self.service_item_type = header[u'type']
|
||||
self.shortname = header[u'plugin']
|
||||
self.theme = header[u'theme']
|
||||
self.add_icon(header[u'icon'])
|
||||
self.raw_footer = header[u'footer']
|
||||
@ -406,7 +401,13 @@ class ServiceItem(object):
|
||||
self.auto_play_slides_loop = header.get(u'auto_play_slides_loop', False)
|
||||
self.timed_slide_interval = header.get(u'timed_slide_interval', 0)
|
||||
self.will_auto_start = header.get(u'will_auto_start', False)
|
||||
self.processor = header.get(u'processor', None)
|
||||
self.has_original_files = True
|
||||
#TODO Remove me in 2,3 build phase
|
||||
if self.is_capable(ItemCapabilities.HasDetailedTitleDisplay):
|
||||
self.capabilities.remove(ItemCapabilities.HasDetailedTitleDisplay)
|
||||
self.processor = self.title
|
||||
self.title = None
|
||||
if u'background_audio' in header:
|
||||
self.background_audio = []
|
||||
for filename in header[u'background_audio']:
|
||||
@ -429,6 +430,8 @@ class ServiceItem(object):
|
||||
self.add_from_image(text_image[u'path'], text_image[u'title'], background)
|
||||
elif self.service_item_type == ServiceItemType.Command:
|
||||
for text_image in serviceitem[u'serviceitem'][u'data']:
|
||||
if not self.title:
|
||||
self.title = text_image[u'title']
|
||||
if path:
|
||||
self.has_original_files = False
|
||||
self.add_from_command(path, text_image[u'title'], text_image[u'image'])
|
||||
@ -443,9 +446,7 @@ class ServiceItem(object):
|
||||
if self.is_text():
|
||||
return self.title
|
||||
else:
|
||||
if ItemCapabilities.HasDetailedTitleDisplay in self.capabilities:
|
||||
return self._raw_frames[0][u'title']
|
||||
elif len(self._raw_frames) > 1:
|
||||
if len(self._raw_frames) > 1:
|
||||
return self.title
|
||||
else:
|
||||
return self._raw_frames[0][u'title']
|
||||
|
@ -30,6 +30,7 @@
|
||||
The :mod:`~openlp.core.lib.settingstab` module contains the base SettingsTab class which plugins use for adding their
|
||||
own tab to the settings dialog.
|
||||
"""
|
||||
from __future__ import division
|
||||
|
||||
from PyQt4 import QtGui
|
||||
|
||||
@ -90,7 +91,7 @@ class SettingsTab(QtGui.QWidget):
|
||||
QtGui.QWidget.resizeEvent(self, event)
|
||||
width = self.width() - self.tab_layout.spacing() - \
|
||||
self.tab_layout.contentsMargins().left() - self.tab_layout.contentsMargins().right()
|
||||
left_width = min(width - self.right_column.minimumSizeHint().width(), width / 2)
|
||||
left_width = min(width - self.right_column.minimumSizeHint().width(), width // 2)
|
||||
left_width = max(left_width, self.left_column.minimumSizeHint().width())
|
||||
self.left_column.setFixedWidth(left_width)
|
||||
|
||||
|
@ -35,6 +35,7 @@ Some of the code for this form is based on the examples at:
|
||||
* `http://html5demos.com/two-videos`_
|
||||
|
||||
"""
|
||||
from __future__ import division
|
||||
import cgi
|
||||
import logging
|
||||
import sys
|
||||
@ -207,8 +208,8 @@ class MainDisplay(Display):
|
||||
painter_image.begin(self.initial_fame)
|
||||
painter_image.fillRect(self.initial_fame.rect(), background_color)
|
||||
painter_image.drawImage(
|
||||
(self.screen[u'size'].width() - splash_image.width()) / 2,
|
||||
(self.screen[u'size'].height() - splash_image.height()) / 2,
|
||||
(self.screen[u'size'].width() - splash_image.width()) // 2,
|
||||
(self.screen[u'size'].height() - splash_image.height()) // 2,
|
||||
splash_image)
|
||||
service_item = ServiceItem()
|
||||
service_item.bg_image_bytes = image_to_byte(self.initial_fame)
|
||||
@ -268,7 +269,7 @@ class MainDisplay(Display):
|
||||
self.resize(self.width(), alert_height)
|
||||
self.setVisible(True)
|
||||
if location == AlertLocation.Middle:
|
||||
self.move(self.screen[u'size'].left(), (self.screen[u'size'].height() - alert_height) / 2)
|
||||
self.move(self.screen[u'size'].left(), (self.screen[u'size'].height() - alert_height) // 2)
|
||||
elif location == AlertLocation.Bottom:
|
||||
self.move(self.screen[u'size'].left(), self.screen[u'size'].height() - alert_height)
|
||||
else:
|
||||
|
@ -466,8 +466,8 @@ class MediaController(object):
|
||||
The ServiceItem containing the details to be played.
|
||||
"""
|
||||
used_players = get_media_players()[0]
|
||||
if service_item.title != UiStrings().Automatic:
|
||||
used_players = [service_item.title.lower()]
|
||||
if service_item.processor != UiStrings().Automatic:
|
||||
used_players = [service_item.processor.lower()]
|
||||
if controller.media_info.file_info.isFile():
|
||||
suffix = u'*.%s' % controller.media_info.file_info.suffix().lower()
|
||||
for title in used_players:
|
||||
|
@ -295,8 +295,8 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
||||
Sets up the service manager, toolbars, list view, et al.
|
||||
"""
|
||||
QtGui.QWidget.__init__(self, parent)
|
||||
self.active = build_icon(QtGui.QImage(u':/media/auto-start_active.png'))
|
||||
self.inactive = build_icon(QtGui.QImage(u':/media/auto-start_inactive.png'))
|
||||
self.active = build_icon(u':/media/auto-start_active.png')
|
||||
self.inactive = build_icon(u':/media/auto-start_inactive.png')
|
||||
Registry().register(u'service_manager', self)
|
||||
self.service_items = []
|
||||
self.suffixes = []
|
||||
@ -715,13 +715,10 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
||||
else:
|
||||
service_item.set_from_service(item, self.servicePath)
|
||||
service_item.validate_item(self.suffixes)
|
||||
self.load_item_unique_identifier = 0
|
||||
if service_item.is_capable(ItemCapabilities.OnLoadUpdate):
|
||||
Registry().execute(u'%s_service_load' % service_item.name.lower(), service_item)
|
||||
# if the item has been processed
|
||||
if service_item.unique_identifier == self.load_item_unique_identifier:
|
||||
service_item.edit_id = int(self.load_item_edit_id)
|
||||
service_item.temporary_edit = self.load_item_temporary
|
||||
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)
|
||||
delete_file(p_file)
|
||||
self.main_window.add_recent_file(file_name)
|
||||
@ -1260,14 +1257,6 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
||||
self.repaint_service_list(-1, -1)
|
||||
self.application.set_normal_cursor()
|
||||
|
||||
def service_item_update(self, edit_id, unique_identifier, temporary=False):
|
||||
"""
|
||||
Triggered from plugins to update service items. Save the values as they will be used as part of the service load
|
||||
"""
|
||||
self.load_item_unique_identifier = unique_identifier
|
||||
self.load_item_edit_id = int(edit_id)
|
||||
self.load_item_temporary = str_to_bool(temporary)
|
||||
|
||||
def replace_service_item(self, newItem):
|
||||
"""
|
||||
Using the service item passed replace the one with the same edit id if found.
|
||||
|
@ -29,6 +29,8 @@
|
||||
"""
|
||||
The Themes configuration tab
|
||||
"""
|
||||
from __future__ import division
|
||||
|
||||
from PyQt4 import QtCore, QtGui
|
||||
|
||||
from openlp.core.lib import Registry, Settings, SettingsTab, UiStrings, translate
|
||||
@ -90,7 +92,7 @@ class ThemesTab(SettingsTab):
|
||||
self.global_level_label.setObjectName(u'global_level_label')
|
||||
self.level_layout.addRow(self.global_level_radio_button, self.global_level_label)
|
||||
label_top_margin = (self.song_level_radio_button.sizeHint().height() -
|
||||
self.song_level_label.sizeHint().height()) / 2
|
||||
self.song_level_label.sizeHint().height()) // 2
|
||||
for label in [self.song_level_label, self.service_level_label, self.global_level_label]:
|
||||
rect = label.rect()
|
||||
rect.setTop(rect.top() + label_top_margin)
|
||||
|
@ -75,9 +75,9 @@ class Ui_AlertDialog(object):
|
||||
self.manage_button_layout.addWidget(self.delete_button)
|
||||
self.manage_button_layout.addStretch()
|
||||
self.alert_dialog_layout.addLayout(self.manage_button_layout, 1, 1)
|
||||
displayIcon = build_icon(u':/general/general_live.png')
|
||||
self.display_button = create_button(alert_dialog, u'display_button', icon=displayIcon, enabled=False)
|
||||
self.display_close_button = create_button(alert_dialog, u'display_close_button', icon=displayIcon,
|
||||
display_icon = build_icon(u':/general/general_live.png')
|
||||
self.display_button = create_button(alert_dialog, u'display_button', icon=display_icon, enabled=False)
|
||||
self.display_close_button = create_button(alert_dialog, u'display_close_button', icon=display_icon,
|
||||
enabled=False)
|
||||
self.button_box = create_button_box(alert_dialog, u'button_box', [u'close'],
|
||||
[self.display_button, self.display_close_button])
|
||||
|
@ -511,7 +511,7 @@ class BibleImportForm(OpenLPWizard):
|
||||
name = bible[u'abbreviation']
|
||||
self.web_bible_list[download_type][version] = name.strip()
|
||||
|
||||
def preWizard(self):
|
||||
def pre_wizard(self):
|
||||
"""
|
||||
Prepare the UI for the import.
|
||||
"""
|
||||
|
@ -105,7 +105,7 @@ class BibleUpgradeForm(OpenLPWizard):
|
||||
Perform necessary functions depending on which wizard page is active.
|
||||
"""
|
||||
if self.page(pageId) == self.progress_page:
|
||||
self.preWizard()
|
||||
self.pre_wizard()
|
||||
self.performWizard()
|
||||
self.post_wizard()
|
||||
elif self.page(pageId) == self.selectPage and not self.files:
|
||||
@ -329,7 +329,7 @@ class BibleUpgradeForm(OpenLPWizard):
|
||||
self.cancel_button.setVisible(True)
|
||||
settings.endGroup()
|
||||
|
||||
def preWizard(self):
|
||||
def pre_wizard(self):
|
||||
"""
|
||||
Prepare the UI for the upgrade.
|
||||
"""
|
||||
|
@ -40,6 +40,7 @@ from openlp.plugins.custom.lib.db import CustomSlide
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class CustomSearch(object):
|
||||
"""
|
||||
An enumeration for custom search methods.
|
||||
@ -214,7 +215,6 @@ class CustomMediaItem(MediaManagerItem):
|
||||
Settings().setValue(u'%s/last search type' % self.settings_section, self.search_text_edit.current_search_type())
|
||||
# Reload the list considering the new search type.
|
||||
search_keywords = self.search_text_edit.displayText()
|
||||
search_results = []
|
||||
search_type = self.search_text_edit.current_search_type()
|
||||
if search_type == CustomSearch.Titles:
|
||||
log.debug(u'Titles Search')
|
||||
@ -252,7 +252,8 @@ class CustomMediaItem(MediaManagerItem):
|
||||
and_(CustomSlide.title == item.title, CustomSlide.theme_name == item.theme,
|
||||
CustomSlide.credits == item.raw_footer[0][len(item.title) + 1:]))
|
||||
if custom:
|
||||
self.service_manager.service_item_update(custom.id, item.unique_identifier)
|
||||
item.edit_id = custom.id
|
||||
return item
|
||||
else:
|
||||
if self.add_custom_from_service:
|
||||
self.create_from_service_item(item)
|
||||
@ -281,8 +282,6 @@ class CustomMediaItem(MediaManagerItem):
|
||||
custom.text = unicode(custom_xml.extract_xml(), u'utf-8')
|
||||
self.plugin.manager.save_object(custom)
|
||||
self.on_search_text_button_clicked()
|
||||
if item.name.lower() == u'custom':
|
||||
Registry().execute(u'service_item_update', u'%s:%s:%s' % (custom.id, item.unique_identifier, False))
|
||||
|
||||
def on_clear_text_button_click(self):
|
||||
"""
|
||||
|
@ -50,6 +50,7 @@ class ChooseGroupForm(QtGui.QDialog, Ui_ChooseGroupDialog):
|
||||
``selected_group``
|
||||
The ID of the group that should be selected by default when showing the dialog.
|
||||
"""
|
||||
self.new_group_edit.clear()
|
||||
if selected_group is not None:
|
||||
for index in range(self.group_combobox.count()):
|
||||
if self.group_combobox.itemData(index) == selected_group:
|
||||
|
@ -32,7 +32,6 @@ from PyQt4 import QtGui
|
||||
from openlp.core.lib import Registry, SettingsTab, Settings, UiStrings, translate
|
||||
|
||||
|
||||
|
||||
class ImageTab(SettingsTab):
|
||||
"""
|
||||
ImageTab is the images settings tab in the settings dialog.
|
||||
|
@ -391,6 +391,7 @@ class ImageMediaItem(MediaManagerItem):
|
||||
``initial_load``
|
||||
When set to False, the busy cursor and progressbar will be shown while loading images
|
||||
"""
|
||||
parent_group = None
|
||||
if target_group is None:
|
||||
# Find out if a group must be pre-selected
|
||||
preselect_group = None
|
||||
@ -436,6 +437,8 @@ class ImageMediaItem(MediaManagerItem):
|
||||
parent_group.parent_id = 0
|
||||
parent_group.group_name = self.choose_group_form.new_group_edit.text()
|
||||
self.manager.save_object(parent_group)
|
||||
self.fill_groups_combobox(self.choose_group_form.group_combobox)
|
||||
self.fill_groups_combobox(self.add_group_form.parent_group_combobox)
|
||||
else:
|
||||
parent_group = target_group.data(0, QtCore.Qt.UserRole)
|
||||
if isinstance(parent_group, ImageFilenames):
|
||||
@ -550,28 +553,25 @@ class ImageMediaItem(MediaManagerItem):
|
||||
service_item.add_capability(ItemCapabilities.CanAppend)
|
||||
# force a nonexistent theme
|
||||
service_item.theme = -1
|
||||
missing_items = []
|
||||
missing_items_filenames = []
|
||||
images_filenames = []
|
||||
# Expand groups to images
|
||||
for bitem in items:
|
||||
if isinstance(bitem.data(0, QtCore.Qt.UserRole), ImageGroups) or bitem.data(0, QtCore.Qt.UserRole) is None:
|
||||
for index in range(0, bitem.childCount()):
|
||||
if isinstance(bitem.child(index).data(0, QtCore.Qt.UserRole), ImageFilenames):
|
||||
items.append(bitem.child(index))
|
||||
items.remove(bitem)
|
||||
images_filenames.append(bitem.child(index).data(0, QtCore.Qt.UserRole).filename)
|
||||
elif isinstance(bitem.data(0, QtCore.Qt.UserRole), ImageFilenames):
|
||||
images_filenames.append(bitem.data(0, QtCore.Qt.UserRole).filename)
|
||||
# Don't try to display empty groups
|
||||
if not items:
|
||||
if not images_filenames:
|
||||
return False
|
||||
# Find missing files
|
||||
for bitem in items:
|
||||
filename = bitem.data(0, QtCore.Qt.UserRole).filename
|
||||
for filename in images_filenames:
|
||||
if not os.path.exists(filename):
|
||||
missing_items.append(bitem)
|
||||
missing_items_filenames.append(filename)
|
||||
for item in missing_items:
|
||||
items.remove(item)
|
||||
# We cannot continue, as all images do not exist.
|
||||
if not items:
|
||||
if not images_filenames:
|
||||
if not remote:
|
||||
critical_error_message_box(
|
||||
translate('ImagePlugin.MediaItem', 'Missing Image(s)'),
|
||||
@ -579,15 +579,14 @@ class ImageMediaItem(MediaManagerItem):
|
||||
u'\n'.join(missing_items_filenames))
|
||||
return False
|
||||
# We have missing as well as existing images. We ask what to do.
|
||||
elif missing_items and QtGui.QMessageBox.question(self,
|
||||
elif missing_items_filenames and QtGui.QMessageBox.question(self,
|
||||
translate('ImagePlugin.MediaItem', 'Missing Image(s)'),
|
||||
translate('ImagePlugin.MediaItem', 'The following image(s) no longer exist: %s\n'
|
||||
'Do you want to add the other images anyway?') % u'\n'.join(missing_items_filenames),
|
||||
QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.No | QtGui.QMessageBox.Yes)) == QtGui.QMessageBox.No:
|
||||
return False
|
||||
# Continue with the existing images.
|
||||
for bitem in items:
|
||||
filename = bitem.data(0, QtCore.Qt.UserRole).filename
|
||||
for filename in images_filenames:
|
||||
name = os.path.split(filename)[1]
|
||||
service_item.add_from_image(filename, name, background)
|
||||
return True
|
||||
|
@ -44,10 +44,10 @@ log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
CLAPPERBOARD = u':/media/slidecontroller_multimedia.png'
|
||||
VIDEO_ICON = build_icon(QtGui.QImage(u':/media/media_video.png'))
|
||||
AUDIO_ICON = build_icon(QtGui.QImage(u':/media/media_audio.png'))
|
||||
DVD_ICON = build_icon(QtGui.QImage(u':/media/media_video.png'))
|
||||
ERROR_ICON = build_icon(QtGui.QImage(u':/general/general_delete.png'))
|
||||
VIDEO_ICON = build_icon(u':/media/media_video.png')
|
||||
AUDIO_ICON = build_icon(u':/media/media_audio.png')
|
||||
DVD_ICON = build_icon(u':/media/media_video.png')
|
||||
ERROR_ICON = build_icon(u':/general/general_delete.png')
|
||||
|
||||
|
||||
class MediaMediaItem(MediaManagerItem):
|
||||
@ -155,7 +155,7 @@ class MediaMediaItem(MediaManagerItem):
|
||||
if os.path.exists(filename):
|
||||
service_item = ServiceItem()
|
||||
service_item.title = u'webkit'
|
||||
service_item.shortname = service_item.title
|
||||
service_item.processor = u'webkit'
|
||||
(path, name) = os.path.split(filename)
|
||||
service_item.add_from_command(path, name,CLAPPERBOARD)
|
||||
if self.media_controller.video(DisplayControllerType.Live, service_item, video_behind_text=True):
|
||||
@ -185,9 +185,9 @@ class MediaMediaItem(MediaManagerItem):
|
||||
translate('MediaPlugin.MediaItem', 'Missing Media File'),
|
||||
translate('MediaPlugin.MediaItem', 'The file %s no longer exists.') % filename)
|
||||
return False
|
||||
service_item.title = self.display_type_combo_box.currentText()
|
||||
service_item.shortname = service_item.title
|
||||
(path, name) = os.path.split(filename)
|
||||
service_item.title = name
|
||||
service_item.processor = self.display_type_combo_box.currentText()
|
||||
service_item.add_from_command(path, name, CLAPPERBOARD)
|
||||
# Only get start and end times if going to a service
|
||||
if context == ServiceItemContext.Service:
|
||||
@ -196,7 +196,6 @@ class MediaMediaItem(MediaManagerItem):
|
||||
return False
|
||||
service_item.add_capability(ItemCapabilities.CanAutoStartForLive)
|
||||
service_item.add_capability(ItemCapabilities.RequiresMedia)
|
||||
service_item.add_capability(ItemCapabilities.HasDetailedTitleDisplay)
|
||||
if Settings().value(self.settings_section + u'/media auto start') == QtCore.Qt.Checked:
|
||||
service_item.will_auto_start = True
|
||||
# force a non-existent theme
|
||||
@ -260,8 +259,7 @@ class MediaMediaItem(MediaManagerItem):
|
||||
Settings().setValue(self.settings_section + u'/media files', self.get_file_list())
|
||||
|
||||
def load_list(self, media, target_group=None):
|
||||
# Sort the media by its filename considering language specific
|
||||
# characters.
|
||||
# Sort the media by its filename considering language specific characters.
|
||||
media.sort(key=lambda filename: get_locale_key(os.path.split(unicode(filename))[1]))
|
||||
for track in media:
|
||||
track_info = QtCore.QFileInfo(track)
|
||||
|
@ -244,22 +244,20 @@ class PresentationMediaItem(MediaManagerItem):
|
||||
items = self.list_view.selectedItems()
|
||||
if len(items) > 1:
|
||||
return False
|
||||
service_item.title = self.display_type_combo_box.currentText()
|
||||
service_item.shortname = self.display_type_combo_box.currentText()
|
||||
service_item.processor = self.display_type_combo_box.currentText()
|
||||
service_item.add_capability(ItemCapabilities.ProvidesOwnDisplay)
|
||||
service_item.add_capability(ItemCapabilities.HasDetailedTitleDisplay)
|
||||
shortname = service_item.shortname
|
||||
if not shortname:
|
||||
if not self.display_type_combo_box.currentText():
|
||||
return False
|
||||
for bitem in items:
|
||||
filename = bitem.data(QtCore.Qt.UserRole)
|
||||
(path, name) = os.path.split(filename)
|
||||
service_item.title = name
|
||||
if os.path.exists(filename):
|
||||
if shortname == self.Automatic:
|
||||
service_item.shortname = self.findControllerByType(filename)
|
||||
if not service_item.shortname:
|
||||
if service_item.processor == self.Automatic:
|
||||
service_item.processor = self.findControllerByType(filename)
|
||||
if not service_item.processor:
|
||||
return False
|
||||
controller = self.controllers[service_item.shortname]
|
||||
(path, name) = os.path.split(filename)
|
||||
controller = self.controllers[service_item.processor]
|
||||
doc = controller.add_document(filename)
|
||||
if doc.get_thumbnail_path(1, True) is None:
|
||||
doc.load_presentation()
|
||||
|
@ -36,6 +36,7 @@ from openlp.core.ui import HideMode
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Controller(object):
|
||||
"""
|
||||
This is the Presentation listener who acts on events from the slide controller and passes the messages on the the
|
||||
@ -314,7 +315,7 @@ class MessageListener(object):
|
||||
item = message[0]
|
||||
hide_mode = message[2]
|
||||
file = item.get_frame_path()
|
||||
self.handler = item.title
|
||||
self.handler = item.processor
|
||||
if self.handler == self.media_item.Automatic:
|
||||
self.handler = self.media_item.findControllerByType(file)
|
||||
if not self.handler:
|
||||
|
@ -38,6 +38,7 @@ from openlp.core.utils import AppLocation
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class PresentationDocument(object):
|
||||
"""
|
||||
Base class for presentation documents to inherit from. Loads and closes the presentation as well as triggering the
|
||||
@ -322,7 +323,7 @@ class PresentationController(object):
|
||||
``supports``
|
||||
The primary native file types this application supports.
|
||||
|
||||
``alsosupports``
|
||||
``also_supports``
|
||||
Other file types the application can import, although not necessarily the first choice due to potential
|
||||
incompatibilities.
|
||||
|
||||
@ -358,7 +359,7 @@ class PresentationController(object):
|
||||
Name of the application, to appear in the application
|
||||
"""
|
||||
self.supports = []
|
||||
self.alsosupports = []
|
||||
self.also_supports = []
|
||||
self.docs = []
|
||||
self.plugin = plugin
|
||||
self.name = name
|
||||
|
@ -49,7 +49,7 @@ __default_settings__ = {
|
||||
u'presentations/Powerpoint': QtCore.Qt.Checked,
|
||||
u'presentations/Powerpoint Viewer': QtCore.Qt.Checked,
|
||||
u'presentations/presentations files': []
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class PresentationPlugin(Plugin):
|
||||
|
@ -147,7 +147,7 @@ window.OpenLP = {
|
||||
},
|
||||
pollServer: function () {
|
||||
$.getJSON(
|
||||
"/stage/api/poll",
|
||||
"/stage/poll",
|
||||
function (data, status) {
|
||||
var prevItem = OpenLP.currentItem;
|
||||
OpenLP.currentSlide = data.results.slide;
|
||||
|
@ -26,7 +26,7 @@
|
||||
window.OpenLP = {
|
||||
loadService: function (event) {
|
||||
$.getJSON(
|
||||
"/stage/api/service/list",
|
||||
"/stage/service/list",
|
||||
function (data, status) {
|
||||
OpenLP.nextSong = "";
|
||||
$("#notes").html("");
|
||||
@ -46,7 +46,7 @@ window.OpenLP = {
|
||||
},
|
||||
loadSlides: function (event) {
|
||||
$.getJSON(
|
||||
"/stage/api/controller/live/text",
|
||||
"/stage/controller/live/text",
|
||||
function (data, status) {
|
||||
OpenLP.currentSlides = data.results.slides;
|
||||
OpenLP.currentSlide = 0;
|
||||
@ -137,7 +137,7 @@ window.OpenLP = {
|
||||
},
|
||||
pollServer: function () {
|
||||
$.getJSON(
|
||||
"/stage/api/poll",
|
||||
"/stage/poll",
|
||||
function (data, status) {
|
||||
OpenLP.updateClock(data);
|
||||
if (OpenLP.currentItem != data.results.item ||
|
||||
|
@ -267,11 +267,11 @@ class HttpRouter(object):
|
||||
(u'^/(stage)$', self.serve_file),
|
||||
(r'^/files/(.*)$', self.serve_file),
|
||||
(r'^/api/poll$', self.poll),
|
||||
(r'^/stage/api/poll$', self.poll),
|
||||
(r'^/stage/poll$', self.poll),
|
||||
(r'^/api/controller/(live|preview)/(.*)$', self.controller),
|
||||
(r'^/stage/api/controller/(live|preview)/(.*)$', self.controller),
|
||||
(r'^/stage/controller/(live|preview)/(.*)$', self.controller),
|
||||
(r'^/api/service/(.*)$', self.service),
|
||||
(r'^/stage/api/service/(.*)$', self.service),
|
||||
(r'^/stage/service/(.*)$', self.service),
|
||||
(r'^/api/display/(hide|show|blank|theme|desktop)$', self.display),
|
||||
(r'^/api/alert$', self.alert),
|
||||
(r'^/api/plugin/(search)$', self.plugin_info),
|
||||
|
@ -52,3 +52,4 @@ This allows OpenLP to use ``self.object`` for all the GUI elements while keeping
|
||||
them separate from the functionality, so that it is easier to recreate the GUI
|
||||
from the .ui files later if necessary.
|
||||
"""
|
||||
from editsongform import EditSongForm
|
||||
|
@ -235,7 +235,7 @@ class SongExportForm(OpenLPWizard):
|
||||
self.availableListWidget.addItem(item)
|
||||
self.application.set_normal_cursor()
|
||||
|
||||
def preWizard(self):
|
||||
def pre_wizard(self):
|
||||
"""
|
||||
Perform pre export tasks.
|
||||
"""
|
||||
|
@ -325,7 +325,7 @@ class SongImportForm(OpenLPWizard):
|
||||
self.error_copy_to_button.setHidden(True)
|
||||
self.error_save_to_button.setHidden(True)
|
||||
|
||||
def preWizard(self):
|
||||
def pre_wizard(self):
|
||||
"""
|
||||
Perform pre import tasks
|
||||
"""
|
||||
|
@ -72,10 +72,7 @@ class SongMediaItem(MediaManagerItem):
|
||||
def __init__(self, parent, plugin):
|
||||
self.icon_path = u'songs/song'
|
||||
MediaManagerItem.__init__(self, parent, plugin)
|
||||
self.edit_song_form = EditSongForm(self, self.main_window, self.plugin.manager)
|
||||
self.openLyrics = OpenLyrics(self.plugin.manager)
|
||||
self.single_service_item = False
|
||||
self.song_maintenance_form = SongMaintenanceForm(self.plugin.manager, self)
|
||||
# Holds information about whether the edit is remotely triggered and which Song is required.
|
||||
self.remote_song = -1
|
||||
self.edit_item = None
|
||||
@ -132,6 +129,12 @@ class SongMediaItem(MediaManagerItem):
|
||||
'Maintain the lists of authors, topics and books.'))
|
||||
|
||||
def initialise(self):
|
||||
"""
|
||||
Initialise variables when they cannot be initialised in the constructor.
|
||||
"""
|
||||
self.song_maintenance_form = SongMaintenanceForm(self.plugin.manager, self)
|
||||
self.edit_song_form = EditSongForm(self, self.main_window, self.plugin.manager)
|
||||
self.openLyrics = OpenLyrics(self.plugin.manager)
|
||||
self.search_text_edit.set_search_types([
|
||||
(SongSearch.Entire, u':/songs/song_search_all.png',
|
||||
translate('SongsPlugin.MediaItem', 'Entire Song'),
|
||||
@ -157,7 +160,6 @@ class SongMediaItem(MediaManagerItem):
|
||||
Settings().setValue(u'%s/last search type' % self.settings_section, self.search_text_edit.current_search_type())
|
||||
# Reload the list considering the new search type.
|
||||
search_keywords = unicode(self.search_text_edit.displayText())
|
||||
search_results = []
|
||||
search_type = self.search_text_edit.current_search_type()
|
||||
if search_type == SongSearch.Entire:
|
||||
log.debug(u'Entire Song Search')
|
||||
@ -457,14 +459,7 @@ class SongMediaItem(MediaManagerItem):
|
||||
for slide in verses:
|
||||
service_item.add_from_text(unicode(slide))
|
||||
service_item.title = song.title
|
||||
author_list = [unicode(author.display_name) for author in song.authors]
|
||||
service_item.raw_footer.append(song.title)
|
||||
service_item.raw_footer.append(create_separated_list(author_list))
|
||||
service_item.raw_footer.append(song.copyright)
|
||||
if Settings().value(u'core/ccli number'):
|
||||
service_item.raw_footer.append(translate('SongsPlugin.MediaItem', 'CCLI License: ') +
|
||||
Settings().value(u'core/ccli number'))
|
||||
service_item.audit = [song.title, author_list, song.copyright, unicode(song.ccli_number)]
|
||||
author_list = self.generate_footer(service_item, song)
|
||||
service_item.data_string = {u'title': song.search_title, u'authors': u', '.join(author_list)}
|
||||
service_item.xml_version = self.openLyrics.song_to_xml(song)
|
||||
# Add the audio file to the service item.
|
||||
@ -473,6 +468,30 @@ class SongMediaItem(MediaManagerItem):
|
||||
service_item.background_audio = [m.file_name for m in song.media_files]
|
||||
return True
|
||||
|
||||
def generate_footer(self, item, song):
|
||||
"""
|
||||
Generates the song footer based on a song and adds details to a service item.
|
||||
author_list is only required for initial song generation.
|
||||
|
||||
``item``
|
||||
The service item to be amended
|
||||
|
||||
``song``
|
||||
The song to be used to generate the footer
|
||||
"""
|
||||
author_list = [unicode(author.display_name) for author in song.authors]
|
||||
item.audit = [
|
||||
song.title, author_list, song.copyright, unicode(song.ccli_number)
|
||||
]
|
||||
item.raw_footer = []
|
||||
item.raw_footer.append(song.title)
|
||||
item.raw_footer.append(create_separated_list(author_list))
|
||||
item.raw_footer.append(song.copyright)
|
||||
if Settings().value(u'core/ccli number'):
|
||||
item.raw_footer.append(translate('SongsPlugin.MediaItem', 'CCLI License: ') +
|
||||
Settings().value(u'core/ccli number'))
|
||||
return author_list
|
||||
|
||||
def service_load(self, item):
|
||||
"""
|
||||
Triggered by a song being loaded by the service manager.
|
||||
@ -490,9 +509,8 @@ class SongMediaItem(MediaManagerItem):
|
||||
else:
|
||||
search_results = self.plugin.manager.get_all_objects(Song,
|
||||
Song.search_title == item.data_string[u'title'], Song.search_title.asc())
|
||||
editId = 0
|
||||
edit_id = 0
|
||||
add_song = True
|
||||
temporary = False
|
||||
if search_results:
|
||||
for song in search_results:
|
||||
author_list = item.data_string[u'authors']
|
||||
@ -505,7 +523,7 @@ class SongMediaItem(MediaManagerItem):
|
||||
break
|
||||
if same_authors and author_list.strip(u', ') == u'':
|
||||
add_song = False
|
||||
editId = song.id
|
||||
edit_id = song.id
|
||||
break
|
||||
# If there's any backing tracks, copy them over.
|
||||
if item.background_audio:
|
||||
@ -523,11 +541,11 @@ class SongMediaItem(MediaManagerItem):
|
||||
# If there's any backing tracks, copy them over.
|
||||
if item.background_audio:
|
||||
self._update_background_audio(song, item)
|
||||
editId = song.id
|
||||
temporary = True
|
||||
# Update service with correct song id.
|
||||
if editId:
|
||||
self.service_manager.service_item_update(editId, item.unique_identifier, temporary)
|
||||
edit_id = song.id
|
||||
# Update service with correct song id and return it to caller.
|
||||
item.edit_id = edit_id
|
||||
self.generate_footer(item, song)
|
||||
return item
|
||||
|
||||
def search(self, string, showError):
|
||||
"""
|
||||
|
@ -235,8 +235,7 @@ class SongsPlugin(Plugin):
|
||||
u'delete': translate('SongsPlugin', 'Delete the selected song.'),
|
||||
u'preview': translate('SongsPlugin', 'Preview the selected song.'),
|
||||
u'live': translate('SongsPlugin', 'Send the selected song live.'),
|
||||
u'service': translate('SongsPlugin',
|
||||
'Add the selected song to the service.')
|
||||
u'service': translate('SongsPlugin', 'Add the selected song to the service.')
|
||||
}
|
||||
self.set_plugin_ui_text_strings(tooltips)
|
||||
|
||||
|
@ -1,13 +1,20 @@
|
||||
"""
|
||||
Package to test the openlp.core.lib package.
|
||||
"""
|
||||
import os
|
||||
|
||||
from unittest import TestCase
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
from mock import MagicMock, patch
|
||||
from PyQt4 import QtCore, QtGui
|
||||
|
||||
from openlp.core.lib import str_to_bool, create_thumb, translate, check_directory_exists, get_text_file_string, \
|
||||
build_icon, image_to_byte, check_item_selected, validate_thumb, create_separated_list, clean_tags, expand_tags
|
||||
|
||||
|
||||
TEST_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), u'..', u'..', u'resources'))
|
||||
|
||||
from openlp.core.lib import str_to_bool, translate, check_directory_exists, get_text_file_string, build_icon, \
|
||||
image_to_byte, check_item_selected, validate_thumb, create_separated_list, clean_tags, expand_tags
|
||||
|
||||
class TestLib(TestCase):
|
||||
|
||||
@ -125,7 +132,7 @@ class TestLib(TestCase):
|
||||
Test the check_directory_exists() function
|
||||
"""
|
||||
with patch(u'openlp.core.lib.os.path.exists') as mocked_exists, \
|
||||
patch(u'openlp.core.lib.os.makedirs') as mocked_makedirs:
|
||||
patch(u'openlp.core.lib.os.makedirs') as mocked_makedirs:
|
||||
# GIVEN: A directory to check and a mocked out os.makedirs and os.path.exists
|
||||
directory_to_check = u'existing/directory'
|
||||
|
||||
@ -219,7 +226,7 @@ class TestLib(TestCase):
|
||||
Test the build_icon() function with a resource URI
|
||||
"""
|
||||
with patch(u'openlp.core.lib.QtGui') as MockedQtGui, \
|
||||
patch(u'openlp.core.lib.QtGui.QPixmap') as MockedQPixmap:
|
||||
patch(u'openlp.core.lib.QtGui.QPixmap') as MockedQPixmap:
|
||||
# GIVEN: A mocked QIcon and a mocked QPixmap
|
||||
MockedQtGui.QIcon = MagicMock
|
||||
MockedQtGui.QIcon.Normal = 1
|
||||
@ -261,9 +268,43 @@ class TestLib(TestCase):
|
||||
mocked_byte_array.toBase64.assert_called_with()
|
||||
assert result == u'base64mock', u'The result should be the return value of the mocked out base64 method'
|
||||
|
||||
def create_thumb_with_size_test(self):
|
||||
"""
|
||||
Test the create_thumb() function
|
||||
"""
|
||||
# GIVEN: An image to create a thumb of.
|
||||
image_path = os.path.join(TEST_PATH, u'church.jpg')
|
||||
thumb_path = os.path.join(TEST_PATH, u'church_thumb.jpg')
|
||||
thumb_size = QtCore.QSize(10, 20)
|
||||
|
||||
# Remove the thumb so that the test actually tests if the thumb will be created. Maybe it was not deleted in the
|
||||
# last test.
|
||||
try:
|
||||
os.remove(thumb_path)
|
||||
except:
|
||||
pass
|
||||
|
||||
# Only continue when the thumb does not exist.
|
||||
assert not os.path.exists(thumb_path), u'Test was not ran, because the thumb already exists.'
|
||||
|
||||
# WHEN: Create the thumb.
|
||||
icon = create_thumb(image_path, thumb_path, size=thumb_size)
|
||||
|
||||
# THEN: Check if the thumb was created.
|
||||
assert os.path.exists(thumb_path), u'Test was not ran, because the thumb already exists.'
|
||||
assert isinstance(icon, QtGui.QIcon), u'The icon should be a QIcon.'
|
||||
assert not icon.isNull(), u'The icon should not be null.'
|
||||
assert QtGui.QImageReader(thumb_path).size() == thumb_size, u'The thumb should have the given size.'
|
||||
|
||||
# Remove the thumb so that the test actually tests if the thumb will be created.
|
||||
try:
|
||||
os.remove(thumb_path)
|
||||
except:
|
||||
pass
|
||||
|
||||
def check_item_selected_true_test(self):
|
||||
"""
|
||||
Test that the check_item_selected() function returns True when there are selected indexes.
|
||||
Test that the check_item_selected() function returns True when there are selected indexes
|
||||
"""
|
||||
# GIVEN: A mocked out QtGui module and a list widget with selected indexes
|
||||
MockedQtGui = patch(u'openlp.core.lib.QtGui')
|
||||
@ -423,7 +464,7 @@ class TestLib(TestCase):
|
||||
|
||||
def create_separated_list_qlocate_test(self):
|
||||
"""
|
||||
Test the create_separated_list function using the Qt provided method.
|
||||
Test the create_separated_list function using the Qt provided method
|
||||
"""
|
||||
with patch(u'openlp.core.lib.Qt') as mocked_qt, \
|
||||
patch(u'openlp.core.lib.QtCore.QLocale.createSeparatedList') as mocked_createSeparatedList:
|
||||
@ -442,7 +483,7 @@ class TestLib(TestCase):
|
||||
|
||||
def create_separated_list_empty_list_test(self):
|
||||
"""
|
||||
Test the create_separated_list function with an empty list.
|
||||
Test the create_separated_list function with an empty list
|
||||
"""
|
||||
with patch(u'openlp.core.lib.Qt') as mocked_qt:
|
||||
# GIVEN: An empty list and the mocked Qt module.
|
||||
@ -458,7 +499,7 @@ class TestLib(TestCase):
|
||||
|
||||
def create_separated_list_with_one_item_test(self):
|
||||
"""
|
||||
Test the create_separated_list function with a list consisting of only one entry.
|
||||
Test the create_separated_list function with a list consisting of only one entry
|
||||
"""
|
||||
with patch(u'openlp.core.lib.Qt') as mocked_qt:
|
||||
# GIVEN: A list with a string and the mocked Qt module.
|
||||
@ -474,7 +515,7 @@ class TestLib(TestCase):
|
||||
|
||||
def create_separated_list_with_two_items_test(self):
|
||||
"""
|
||||
Test the create_separated_list function with a list of two entries.
|
||||
Test the create_separated_list function with a list of two entries
|
||||
"""
|
||||
with patch(u'openlp.core.lib.Qt') as mocked_qt, patch(u'openlp.core.lib.translate') as mocked_translate:
|
||||
# GIVEN: A list of strings and the mocked Qt module.
|
||||
@ -491,7 +532,7 @@ class TestLib(TestCase):
|
||||
|
||||
def create_separated_list_with_three_items_test(self):
|
||||
"""
|
||||
Test the create_separated_list function with a list of three items.
|
||||
Test the create_separated_list function with a list of three items
|
||||
"""
|
||||
with patch(u'openlp.core.lib.Qt') as mocked_qt, patch(u'openlp.core.lib.translate') as mocked_translate:
|
||||
# GIVEN: A list with a string and the mocked Qt module.
|
||||
|
@ -1,7 +1,7 @@
|
||||
"""
|
||||
Package to test the openlp.core.lib.screenlist package.
|
||||
"""
|
||||
import copy
|
||||
|
||||
from unittest import TestCase
|
||||
|
||||
from mock import MagicMock
|
||||
|
@ -210,7 +210,6 @@ class TestServiceItem(TestCase):
|
||||
|
||||
# THEN: We should get back a valid service item
|
||||
assert service_item.is_valid is True, u'The new service item should be valid'
|
||||
print service_item.get_rendered_frame(0)
|
||||
assert service_item.get_rendered_frame(0) == test_file, u'The first frame should match the path to the image'
|
||||
assert service_item.get_frames()[0] == frame_array, u'The return should match frame array1'
|
||||
assert service_item.get_frame_path(0) == test_file, u'The frame path should match the full path to the image'
|
||||
@ -268,6 +267,26 @@ class TestServiceItem(TestCase):
|
||||
assert service_item.is_capable(ItemCapabilities.CanAppend) is True, \
|
||||
u'This service item should be able to have new items added to it'
|
||||
|
||||
def serviceitem_migrate_test_20_22(self):
|
||||
"""
|
||||
Test the Service Item - migrating a media only service item from 2.0 to 2.2 format
|
||||
"""
|
||||
# GIVEN: A new service item and a mocked add icon function
|
||||
service_item = ServiceItem(None)
|
||||
service_item.add_icon = MagicMock()
|
||||
|
||||
# WHEN: adding an media from a saved Service and mocked exists
|
||||
line = self.convert_file_service_item(u'migrate_video_20_22.osd')
|
||||
with patch('os.path.exists'):
|
||||
service_item.set_from_service(line, TEST_PATH)
|
||||
|
||||
# THEN: We should get back a converted service item
|
||||
assert service_item.is_valid is True, u'The new service item should be valid'
|
||||
assert service_item.processor is None, u'The Processor should have been set'
|
||||
assert service_item.title is None, u'The title should be set to a value'
|
||||
assert service_item.is_capable(ItemCapabilities.HasDetailedTitleDisplay) is False, \
|
||||
u'The Capability should have been removed'
|
||||
|
||||
def convert_file_service_item(self, name):
|
||||
service_file = os.path.join(TEST_PATH, name)
|
||||
try:
|
||||
|
@ -9,7 +9,7 @@ from unittest import TestCase
|
||||
from mock import MagicMock, patch
|
||||
|
||||
from openlp.core.lib import Registry
|
||||
from openlp.plugins.images.lib.db import ImageFilenames
|
||||
from openlp.plugins.images.lib.db import ImageFilenames, ImageGroups
|
||||
from openlp.plugins.images.lib.mediaitem import ImageMediaItem
|
||||
|
||||
|
||||
@ -23,6 +23,7 @@ class TestImageMediaItem(TestCase):
|
||||
Registry.create()
|
||||
Registry().register(u'service_list', MagicMock())
|
||||
Registry().register(u'main_window', self.mocked_main_window)
|
||||
Registry().register(u'live_controller', MagicMock())
|
||||
mocked_parent = MagicMock()
|
||||
mocked_plugin = MagicMock()
|
||||
with patch(u'openlp.plugins.images.lib.mediaitem.ImageMediaItem.__init__') as mocked_init:
|
||||
@ -35,7 +36,7 @@ class TestImageMediaItem(TestCase):
|
||||
"""
|
||||
# GIVEN: An empty image_list
|
||||
image_list = []
|
||||
with patch(u'openlp.plugins.images.lib.mediaitem.ImageMediaItem.load_full_list') as mocked_loadFullList:
|
||||
with patch(u'openlp.plugins.images.lib.mediaitem.ImageMediaItem.load_full_list') as mocked_load_full_list:
|
||||
self.media_item.manager = MagicMock()
|
||||
|
||||
# WHEN: We run save_new_images_list with the empty list
|
||||
@ -51,7 +52,7 @@ class TestImageMediaItem(TestCase):
|
||||
"""
|
||||
# GIVEN: A list with 1 image
|
||||
image_list = [ u'test_image.jpg' ]
|
||||
with patch(u'openlp.plugins.images.lib.mediaitem.ImageMediaItem.load_full_list') as mocked_loadFullList:
|
||||
with patch(u'openlp.plugins.images.lib.mediaitem.ImageMediaItem.load_full_list') as mocked_load_full_list:
|
||||
ImageFilenames.filename = ''
|
||||
self.media_item.manager = MagicMock()
|
||||
|
||||
@ -59,7 +60,7 @@ class TestImageMediaItem(TestCase):
|
||||
self.media_item.save_new_images_list(image_list, reload_list=True)
|
||||
|
||||
# THEN: load_full_list() should have been called
|
||||
assert mocked_loadFullList.call_count == 1, u'load_full_list() should have been called'
|
||||
assert mocked_load_full_list.call_count == 1, u'load_full_list() should have been called'
|
||||
|
||||
# CLEANUP: Remove added attribute from ImageFilenames
|
||||
delattr(ImageFilenames, 'filename')
|
||||
@ -70,14 +71,14 @@ class TestImageMediaItem(TestCase):
|
||||
"""
|
||||
# GIVEN: A list with 1 image
|
||||
image_list = [ u'test_image.jpg' ]
|
||||
with patch(u'openlp.plugins.images.lib.mediaitem.ImageMediaItem.load_full_list') as mocked_loadFullList:
|
||||
with patch(u'openlp.plugins.images.lib.mediaitem.ImageMediaItem.load_full_list') as mocked_load_full_list:
|
||||
self.media_item.manager = MagicMock()
|
||||
|
||||
# WHEN: We run save_new_images_list with reload_list=False
|
||||
self.media_item.save_new_images_list(image_list, reload_list=False)
|
||||
|
||||
# THEN: load_full_list() should not have been called
|
||||
assert mocked_loadFullList.call_count == 0, u'load_full_list() should not have been called'
|
||||
assert mocked_load_full_list.call_count == 0, u'load_full_list() should not have been called'
|
||||
|
||||
def save_new_images_list_multiple_images_test(self):
|
||||
"""
|
||||
@ -85,7 +86,7 @@ class TestImageMediaItem(TestCase):
|
||||
"""
|
||||
# GIVEN: A list with 3 images
|
||||
image_list = [ u'test_image_1.jpg', u'test_image_2.jpg', u'test_image_3.jpg' ]
|
||||
with patch(u'openlp.plugins.images.lib.mediaitem.ImageMediaItem.load_full_list') as mocked_loadFullList:
|
||||
with patch(u'openlp.plugins.images.lib.mediaitem.ImageMediaItem.load_full_list') as mocked_load_full_list:
|
||||
self.media_item.manager = MagicMock()
|
||||
|
||||
# WHEN: We run save_new_images_list with the list of 3 images
|
||||
@ -101,7 +102,7 @@ class TestImageMediaItem(TestCase):
|
||||
"""
|
||||
# GIVEN: A list with images and objects
|
||||
image_list = [ u'test_image_1.jpg', None, True, ImageFilenames(), 'test_image_2.jpg' ]
|
||||
with patch(u'openlp.plugins.images.lib.mediaitem.ImageMediaItem.load_full_list') as mocked_loadFullList:
|
||||
with patch(u'openlp.plugins.images.lib.mediaitem.ImageMediaItem.load_full_list') as mocked_load_full_list:
|
||||
self.media_item.manager = MagicMock()
|
||||
|
||||
# WHEN: We run save_new_images_list with the list of images and objects
|
||||
@ -110,3 +111,68 @@ class TestImageMediaItem(TestCase):
|
||||
# THEN: load_full_list() should not have been called
|
||||
assert self.media_item.manager.save_object.call_count == 2, \
|
||||
u'load_full_list() should have been called only once'
|
||||
|
||||
def on_reset_click_test(self):
|
||||
"""
|
||||
Test that on_reset_click() actually resets the background
|
||||
"""
|
||||
# GIVEN: A mocked version of reset_action
|
||||
self.media_item.reset_action = MagicMock()
|
||||
|
||||
# WHEN: on_reset_click is called
|
||||
self.media_item.on_reset_click()
|
||||
|
||||
# THEN: the reset_action should be set visible, and the image should be reset
|
||||
self.media_item.reset_action.setVisible.assert_called_with(False)
|
||||
self.media_item.live_controller.display.reset_image.assert_called_with()
|
||||
|
||||
def _recursively_delete_group_side_effect(*args, **kwargs):
|
||||
"""
|
||||
Side effect method that creates custom retun values for the recursively_delete_group method
|
||||
"""
|
||||
if args[1] == ImageFilenames and args[2]:
|
||||
# Create some fake objects that should be removed
|
||||
returned_object1 = ImageFilenames()
|
||||
returned_object1.id = 1
|
||||
returned_object1.filename = u'/tmp/test_file_1.jpg'
|
||||
returned_object2 = ImageFilenames()
|
||||
returned_object2.id = 2
|
||||
returned_object2.filename = u'/tmp/test_file_2.jpg'
|
||||
returned_object3 = ImageFilenames()
|
||||
returned_object3.id = 3
|
||||
returned_object3.filename = u'/tmp/test_file_3.jpg'
|
||||
return [returned_object1, returned_object2, returned_object3]
|
||||
if args[1] == ImageGroups and args[2]:
|
||||
# Change the parent_id that is matched so we don't get into an endless loop
|
||||
ImageGroups.parent_id = 0
|
||||
# Create a fake group that will be used in the next run
|
||||
returned_object1 = ImageGroups()
|
||||
returned_object1.id = 1
|
||||
return [returned_object1]
|
||||
return []
|
||||
|
||||
def recursively_delete_group_test(self):
|
||||
"""
|
||||
Test that recursively_delete_group() works
|
||||
"""
|
||||
# GIVEN: An ImageGroups object and mocked functions
|
||||
with patch(u'openlp.core.utils.delete_file') as mocked_delete_file:
|
||||
ImageFilenames.group_id = 1
|
||||
ImageGroups.parent_id = 1
|
||||
self.media_item.manager = MagicMock()
|
||||
self.media_item.manager.get_all_objects.side_effect = self._recursively_delete_group_side_effect
|
||||
self.media_item.servicePath = ""
|
||||
test_group = ImageGroups()
|
||||
test_group.id = 1
|
||||
|
||||
# WHEN: recursively_delete_group() is called
|
||||
self.media_item.recursively_delete_group(test_group)
|
||||
|
||||
# THEN:
|
||||
assert mocked_delete_file.call_count == 0, u'delete_file() should not be called'
|
||||
assert self.media_item.manager.delete_object.call_count == 7, \
|
||||
u'manager.delete_object() should be called exactly 7 times'
|
||||
|
||||
# CLEANUP: Remove added attribute from ImageFilenames and ImageGroups
|
||||
delattr(ImageFilenames, 'group_id')
|
||||
delattr(ImageGroups, 'parent_id')
|
||||
|
125
tests/functional/openlp_plugins/songs/test_mediaitem.py
Normal file
125
tests/functional/openlp_plugins/songs/test_mediaitem.py
Normal file
@ -0,0 +1,125 @@
|
||||
"""
|
||||
This module contains tests for the lib submodule of the Songs plugin.
|
||||
"""
|
||||
import os
|
||||
from tempfile import mkstemp
|
||||
from unittest import TestCase
|
||||
|
||||
from mock import patch, MagicMock
|
||||
|
||||
from PyQt4 import QtGui
|
||||
|
||||
from openlp.core.lib import Registry, ServiceItem, Settings
|
||||
|
||||
from openlp.plugins.songs.lib.mediaitem import SongMediaItem
|
||||
|
||||
|
||||
class TestMediaItem(TestCase):
|
||||
"""
|
||||
Test the functions in the :mod:`lib` module.
|
||||
"""
|
||||
def setUp(self):
|
||||
"""
|
||||
Set up the components need for all tests.
|
||||
"""
|
||||
Registry.create()
|
||||
Registry().register(u'service_list', MagicMock())
|
||||
Registry().register(u'main_window', MagicMock())
|
||||
with patch('openlp.core.lib.mediamanageritem.MediaManagerItem.__init__'), \
|
||||
patch('openlp.plugins.songs.forms.editsongform.EditSongForm.__init__'):
|
||||
self.media_item = SongMediaItem(MagicMock(), MagicMock())
|
||||
|
||||
fd, self.ini_file = mkstemp(u'.ini')
|
||||
Settings().set_filename(self.ini_file)
|
||||
self.application = QtGui.QApplication.instance()
|
||||
|
||||
def tearDown(self):
|
||||
"""
|
||||
Delete all the C++ objects at the end so that we don't have a segfault
|
||||
"""
|
||||
del self.application
|
||||
# Not all tests use settings!
|
||||
try:
|
||||
os.unlink(self.ini_file)
|
||||
os.unlink(Settings().fileName())
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def build_song_footer_one_author_test(self):
|
||||
"""
|
||||
Test build songs footer with basic song and one author
|
||||
"""
|
||||
# GIVEN: A Song and a Service Item
|
||||
mock_song = MagicMock()
|
||||
mock_song.title = u'My Song'
|
||||
mock_author = MagicMock()
|
||||
mock_author.display_name = u'my author'
|
||||
mock_song.authors = []
|
||||
mock_song.authors.append(mock_author)
|
||||
mock_song.copyright = u'My copyright'
|
||||
service_item = ServiceItem(None)
|
||||
|
||||
# WHEN: I generate the Footer with default settings
|
||||
author_list = self.media_item.generate_footer(service_item, mock_song)
|
||||
|
||||
# THEN: I get the following Array returned
|
||||
self.assertEqual(service_item.raw_footer, [u'My Song', u'my author', u'My copyright'],
|
||||
u'The array should be returned correctly with a song, one author and copyright')
|
||||
self.assertEqual(author_list, [u'my author'],
|
||||
u'The author list should be returned correctly with one author')
|
||||
|
||||
def build_song_footer_two_authors_test(self):
|
||||
"""
|
||||
Test build songs footer with basic song and two authors
|
||||
"""
|
||||
# GIVEN: A Song and a Service Item
|
||||
mock_song = MagicMock()
|
||||
mock_song.title = u'My Song'
|
||||
mock_author = MagicMock()
|
||||
mock_author.display_name = u'my author'
|
||||
mock_song.authors = []
|
||||
mock_song.authors.append(mock_author)
|
||||
mock_author = MagicMock()
|
||||
mock_author.display_name = u'another author'
|
||||
mock_song.authors.append(mock_author)
|
||||
mock_song.copyright = u'My copyright'
|
||||
service_item = ServiceItem(None)
|
||||
|
||||
# WHEN: I generate the Footer with default settings
|
||||
author_list = self.media_item.generate_footer(service_item, mock_song)
|
||||
|
||||
# THEN: I get the following Array returned
|
||||
self.assertEqual(service_item.raw_footer, [u'My Song', u'my author and another author', u'My copyright'],
|
||||
u'The array should be returned correctly with a song, two authors and copyright')
|
||||
self.assertEqual(author_list, [u'my author', u'another author'],
|
||||
u'The author list should be returned correctly with two authors')
|
||||
|
||||
def build_song_footer_base_ccli_test(self):
|
||||
"""
|
||||
Test build songs footer with basic song and two authors
|
||||
"""
|
||||
# GIVEN: A Song and a Service Item and a configured CCLI license
|
||||
mock_song = MagicMock()
|
||||
mock_song.title = u'My Song'
|
||||
mock_author = MagicMock()
|
||||
mock_author.display_name = u'my author'
|
||||
mock_song.authors = []
|
||||
mock_song.authors.append(mock_author)
|
||||
mock_song.copyright = u'My copyright'
|
||||
service_item = ServiceItem(None)
|
||||
Settings().setValue(u'core/ccli number', u'1234')
|
||||
|
||||
# WHEN: I generate the Footer with default settings
|
||||
self.media_item.generate_footer(service_item, mock_song)
|
||||
|
||||
# THEN: I get the following Array returned
|
||||
self.assertEqual(service_item.raw_footer, [u'My Song', u'my author', u'My copyright', u'CCLI License: 1234'],
|
||||
u'The array should be returned correctly with a song, an author, copyright and ccli')
|
||||
|
||||
# WHEN: I amend the CCLI value
|
||||
Settings().setValue(u'core/ccli number', u'4321')
|
||||
self.media_item.generate_footer(service_item, mock_song)
|
||||
|
||||
# THEN: I would get an amended footer string
|
||||
self.assertEqual(service_item.raw_footer, [u'My Song', u'my author', u'My copyright', u'CCLI License: 4321'],
|
||||
u'The array should be returned correctly with a song, an author, copyright and amended ccli')
|
98
tests/resources/migrate_video_20_22.osd
Normal file
98
tests/resources/migrate_video_20_22.osd
Normal file
@ -0,0 +1,98 @@
|
||||
(lp1
|
||||
(dp2
|
||||
Vserviceitem
|
||||
p3
|
||||
(dp4
|
||||
Vheader
|
||||
p5
|
||||
(dp6
|
||||
Vxml_version
|
||||
p7
|
||||
NsVauto_play_slides_loop
|
||||
p8
|
||||
I00
|
||||
sVauto_play_slides_once
|
||||
p9
|
||||
I00
|
||||
sVwill_auto_start
|
||||
p10
|
||||
I01
|
||||
sVtitle
|
||||
p11
|
||||
VVLC
|
||||
p12
|
||||
sVcapabilities
|
||||
p13
|
||||
(lp14
|
||||
I12
|
||||
aI16
|
||||
aI4
|
||||
aI11
|
||||
asVtheme
|
||||
p15
|
||||
I-1
|
||||
sVbackground_audio
|
||||
p16
|
||||
(lp17
|
||||
sVicon
|
||||
p18
|
||||
V:/plugins/plugin_media.png
|
||||
p19
|
||||
sVtype
|
||||
p20
|
||||
I3
|
||||
sVstart_time
|
||||
p21
|
||||
I0
|
||||
sVfrom_plugin
|
||||
p22
|
||||
I00
|
||||
sVmedia_length
|
||||
p23
|
||||
I144
|
||||
sVdata
|
||||
p24
|
||||
V
|
||||
sVtimed_slide_interval
|
||||
p25
|
||||
I0
|
||||
sVaudit
|
||||
p26
|
||||
V
|
||||
sVsearch
|
||||
p27
|
||||
V
|
||||
sVname
|
||||
p28
|
||||
Vmedia
|
||||
p29
|
||||
sVfooter
|
||||
p30
|
||||
(lp31
|
||||
sVnotes
|
||||
p32
|
||||
V
|
||||
sVplugin
|
||||
p33
|
||||
g29
|
||||
sVtheme_overwritten
|
||||
p34
|
||||
I00
|
||||
sVend_time
|
||||
p35
|
||||
I0
|
||||
ssg24
|
||||
(lp36
|
||||
(dp37
|
||||
Vpath
|
||||
p38
|
||||
V/home/tim/Videos/puppets
|
||||
p39
|
||||
sVimage
|
||||
p40
|
||||
V:/media/slidecontroller_multimedia.png
|
||||
p41
|
||||
sg11
|
||||
VMVI_3405.MOV
|
||||
p42
|
||||
sassa.
|
Loading…
Reference in New Issue
Block a user