forked from openlp/openlp
HEAD
This commit is contained in:
commit
4e2ea75962
@ -23,3 +23,5 @@ resources/windows/warnOpenLP.txt
|
||||
openlp.cfg
|
||||
.idea
|
||||
openlp.pro
|
||||
.kdev4
|
||||
tests.kdev4
|
||||
|
@ -1 +1 @@
|
||||
2.0
|
||||
2.1.0-bzr2141
|
||||
|
@ -34,7 +34,6 @@ import logging
|
||||
import os
|
||||
from urllib import quote_plus as urlquote
|
||||
|
||||
from PyQt4 import QtCore
|
||||
from sqlalchemy import Table, MetaData, Column, types, create_engine
|
||||
from sqlalchemy.exc import SQLAlchemyError, InvalidRequestError, DBAPIError, OperationalError
|
||||
from sqlalchemy.orm import scoped_session, sessionmaker, mapper
|
||||
|
@ -31,8 +31,6 @@ Provide HTML Tag management and Formatting Tag access class
|
||||
"""
|
||||
import cPickle
|
||||
|
||||
from PyQt4 import QtCore
|
||||
|
||||
from openlp.core.lib import translate, Settings
|
||||
|
||||
class FormattingTags(object):
|
||||
|
@ -37,7 +37,9 @@ import logging
|
||||
import os
|
||||
import uuid
|
||||
|
||||
from openlp.core.lib import build_icon, clean_tags, expand_tags, translate, ImageSource
|
||||
from PyQt4 import QtGui
|
||||
|
||||
from openlp.core.lib import build_icon, clean_tags, expand_tags, translate, ImageSource, Settings
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
@ -181,7 +183,11 @@ class ServiceItem(object):
|
||||
self.background_audio = []
|
||||
self.theme_overwritten = False
|
||||
self.temporary_edit = False
|
||||
self.auto_play_slides_once = False
|
||||
self.auto_play_slides_loop = False
|
||||
self.timed_slide_interval = 0
|
||||
self.will_auto_start = False
|
||||
self.has_original_files = True
|
||||
self._new_item()
|
||||
|
||||
def _new_item(self):
|
||||
@ -190,6 +196,7 @@ class ServiceItem(object):
|
||||
service items to see if they are the same.
|
||||
"""
|
||||
self._uuid = unicode(uuid.uuid1())
|
||||
self.validate_item()
|
||||
|
||||
def add_capability(self, capability):
|
||||
"""
|
||||
@ -340,6 +347,9 @@ class ServiceItem(object):
|
||||
u'search': self.search_string,
|
||||
u'data': self.data_string,
|
||||
u'xml_version': self.xml_version,
|
||||
u'auto_play_slides_once': self.auto_play_slides_once,
|
||||
u'auto_play_slides_loop': self.auto_play_slides_loop,
|
||||
u'timed_slide_interval': self.timed_slide_interval,
|
||||
u'start_time': self.start_time,
|
||||
u'end_time': self.end_time,
|
||||
u'media_length': self.media_length,
|
||||
@ -394,7 +404,11 @@ class ServiceItem(object):
|
||||
self.start_time = header.get(u'start_time', 0)
|
||||
self.end_time = header.get(u'end_time', 0)
|
||||
self.media_length = header.get(u'media_length', 0)
|
||||
self.auto_play_slides_once = header.get(u'auto_play_slides_once', False)
|
||||
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.has_original_files = True
|
||||
if u'background_audio' in header:
|
||||
self.background_audio = []
|
||||
for filename in header[u'background_audio']:
|
||||
@ -405,20 +419,23 @@ class ServiceItem(object):
|
||||
for slide in serviceitem[u'serviceitem'][u'data']:
|
||||
self._raw_frames.append(slide)
|
||||
elif self.service_item_type == ServiceItemType.Image:
|
||||
settingsSection = serviceitem[u'serviceitem'][u'header'][u'name']
|
||||
background = QtGui.QColor(Settings().value(settingsSection + u'/background color', u'#000000'))
|
||||
if path:
|
||||
self.has_original_files = False
|
||||
for text_image in serviceitem[u'serviceitem'][u'data']:
|
||||
filename = os.path.join(path, text_image)
|
||||
self.add_from_image(filename, text_image)
|
||||
self.add_from_image(filename, text_image, background)
|
||||
else:
|
||||
for text_image in serviceitem[u'serviceitem'][u'data']:
|
||||
self.add_from_image(text_image[u'path'], text_image[u'title'])
|
||||
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 path:
|
||||
self.has_original_files = False
|
||||
self.add_from_command(path, text_image[u'title'], text_image[u'image'])
|
||||
else:
|
||||
self.add_from_command(text_image[u'path'], text_image[u'title'], text_image[u'image'])
|
||||
|
||||
self._new_item()
|
||||
|
||||
def get_display_title(self):
|
||||
@ -605,8 +622,25 @@ class ServiceItem(object):
|
||||
if self.get_frame_path(frame=frame) in invalid_paths:
|
||||
self.remove_frame(frame)
|
||||
|
||||
def validate(self):
|
||||
def missing_frames(self):
|
||||
"""
|
||||
Validates this service item
|
||||
Returns if there are any frames in the service item
|
||||
"""
|
||||
return bool(self._raw_frames)
|
||||
return not bool(self._raw_frames)
|
||||
|
||||
def validate_item(self, suffix_list=None):
|
||||
"""
|
||||
Validates a service item to make sure it is valid
|
||||
"""
|
||||
self.is_valid = True
|
||||
for frame in self._raw_frames:
|
||||
if self.is_image() and not os.path.exists((frame[u'path'])):
|
||||
self.is_valid = False
|
||||
elif self.is_command():
|
||||
file = os.path.join(frame[u'path'],frame[u'title'])
|
||||
if not os.path.exists(file):
|
||||
self.is_valid = False
|
||||
if suffix_list and not self.is_text():
|
||||
type = frame[u'title'].split(u'.')[-1]
|
||||
if type.lower() not in suffix_list:
|
||||
self.is_valid = False
|
||||
|
@ -26,5 +26,9 @@
|
||||
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
|
||||
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||
###############################################################################
|
||||
"""
|
||||
The :mod:`~openlp.core.theme` module contains all the themeing functions used by
|
||||
OpenLP when displaying a song or a scripture.
|
||||
"""
|
||||
|
||||
from openlp.core.theme.theme import Theme
|
||||
|
@ -29,9 +29,7 @@
|
||||
"""
|
||||
The :mod:`ui` module provides the core user interface for OpenLP
|
||||
"""
|
||||
from PyQt4 import QtGui
|
||||
|
||||
from openlp.core.lib import translate
|
||||
|
||||
class HideMode(object):
|
||||
"""
|
||||
|
@ -29,7 +29,7 @@
|
||||
|
||||
from PyQt4 import QtCore, QtGui
|
||||
|
||||
from openlp.core.lib import translate, build_icon
|
||||
from openlp.core.lib import translate
|
||||
from openlp.core.lib.ui import create_button, create_button_box
|
||||
|
||||
class Ui_ExceptionDialog(object):
|
||||
|
@ -29,19 +29,49 @@
|
||||
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
import datetime
|
||||
from PyQt4 import QtCore, QtGui
|
||||
|
||||
from openlp.core.lib import OpenLPToolbar, Receiver, translate, Settings
|
||||
from openlp.core.lib.ui import UiStrings, critical_error_message_box
|
||||
from openlp.core.ui.media import MediaState, MediaInfo, MediaType, \
|
||||
get_media_players, set_media_players
|
||||
from openlp.core.ui.media import MediaState, MediaInfo, MediaType, get_media_players, set_media_players
|
||||
from openlp.core.ui.media.mediaplayer import MediaPlayer
|
||||
from openlp.core.utils import AppLocation
|
||||
from openlp.core.ui import DisplayControllerType
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
class MediaSlider(QtGui.QSlider):
|
||||
"""
|
||||
Allows the mouse events of a slider to be overridden and extra functionality added
|
||||
"""
|
||||
def __init__(self, direction, manager, controller, parent=None):
|
||||
QtGui.QSlider.__init__(self, direction)
|
||||
self.manager = manager
|
||||
self.controller = controller
|
||||
|
||||
def mouseMoveEvent(self, event):
|
||||
"""
|
||||
Override event to allow hover time to be displayed.
|
||||
"""
|
||||
timevalue = QtGui.QStyle.sliderValueFromPosition(self.minimum(), self.maximum(), event.x(), self.width())
|
||||
self.setToolTip(u'%s' % datetime.timedelta(seconds=int(timevalue/1000)))
|
||||
QtGui.QSlider.mouseMoveEvent(self, event)
|
||||
|
||||
def mousePressEvent(self, event):
|
||||
"""
|
||||
Mouse Press event no new functionality
|
||||
"""
|
||||
QtGui.QSlider.mousePressEvent(self, event)
|
||||
|
||||
def mouseReleaseEvent(self, event):
|
||||
"""
|
||||
Set the slider position when the mouse is clicked and released on the slider.
|
||||
"""
|
||||
self.setValue(QtGui.QStyle.sliderValueFromPosition(self.minimum(), self.maximum(), event.x(), self.width()))
|
||||
QtGui.QSlider.mouseReleaseEvent(self, event)
|
||||
|
||||
|
||||
class MediaController(object):
|
||||
"""
|
||||
The implementation of the Media Controller. The Media Controller adds an own
|
||||
@ -69,8 +99,8 @@ class MediaController(object):
|
||||
QtCore.QObject.connect(Receiver.get_receiver(), QtCore.SIGNAL(u'playbackPlay'), self.media_play_msg)
|
||||
QtCore.QObject.connect(Receiver.get_receiver(), QtCore.SIGNAL(u'playbackPause'), self.media_pause_msg)
|
||||
QtCore.QObject.connect(Receiver.get_receiver(), QtCore.SIGNAL(u'playbackStop'), self.media_stop_msg)
|
||||
QtCore.QObject.connect(Receiver.get_receiver(), QtCore.SIGNAL(u'seekSlider'), self.media_seek)
|
||||
QtCore.QObject.connect(Receiver.get_receiver(), QtCore.SIGNAL(u'volumeSlider'), self.media_volume)
|
||||
QtCore.QObject.connect(Receiver.get_receiver(), QtCore.SIGNAL(u'seekSlider'), self.media_seek_msg)
|
||||
QtCore.QObject.connect(Receiver.get_receiver(), QtCore.SIGNAL(u'volumeSlider'), self.media_volume_msg)
|
||||
QtCore.QObject.connect(Receiver.get_receiver(), QtCore.SIGNAL(u'media_hide'), self.media_hide)
|
||||
QtCore.QObject.connect(Receiver.get_receiver(), QtCore.SIGNAL(u'media_blank'), self.media_blank)
|
||||
QtCore.QObject.connect(Receiver.get_receiver(), QtCore.SIGNAL(u'media_unblank'), self.media_unblank)
|
||||
@ -241,9 +271,10 @@ class MediaController(object):
|
||||
icon=u':/slides/media_playback_stop.png',
|
||||
tooltip=translate('OpenLP.SlideController', 'Stop playing media.'), triggers=controller.sendToPlugins)
|
||||
# Build the seekSlider.
|
||||
controller.seekSlider = QtGui.QSlider(QtCore.Qt.Horizontal)
|
||||
controller.seekSlider = MediaSlider(QtCore.Qt.Horizontal, self, controller)
|
||||
controller.seekSlider.setMaximum(1000)
|
||||
controller.seekSlider.setTracking(False)
|
||||
controller.seekSlider.setTracking(True)
|
||||
controller.seekSlider.setMouseTracking(True)
|
||||
controller.seekSlider.setToolTip(translate('OpenLP.SlideController', 'Video position.'))
|
||||
controller.seekSlider.setGeometry(QtCore.QRect(90, 260, 221, 24))
|
||||
controller.seekSlider.setObjectName(u'seekSlider')
|
||||
@ -344,12 +375,8 @@ class MediaController(object):
|
||||
# stop running videos
|
||||
self.media_reset(controller)
|
||||
controller.media_info = MediaInfo()
|
||||
if videoBehindText:
|
||||
controller.media_info.volume = 0
|
||||
controller.media_info.is_background = True
|
||||
else:
|
||||
controller.media_info.volume = controller.volumeSlider.value()
|
||||
controller.media_info.is_background = False
|
||||
controller.media_info.volume = controller.volumeSlider.value()
|
||||
controller.media_info.is_background = videoBehindText
|
||||
controller.media_info.file_info = QtCore.QFileInfo(serviceItem.get_frame_path())
|
||||
display = self._define_display(controller)
|
||||
if controller.isLive:
|
||||
@ -361,7 +388,7 @@ class MediaController(object):
|
||||
controller.media_info.start_time = 0
|
||||
controller.media_info.end_time = 0
|
||||
else:
|
||||
controller.media_info.start_time = display.serviceItem.start_time
|
||||
controller.media_info.start_time = serviceItem.start_time
|
||||
controller.media_info.end_time = serviceItem.end_time
|
||||
elif controller.previewDisplay:
|
||||
isValid = self._check_file_type(controller, display, serviceItem)
|
||||
@ -483,9 +510,17 @@ class MediaController(object):
|
||||
The controller to be played
|
||||
"""
|
||||
log.debug(u'media_play')
|
||||
controller.seekSlider.blockSignals(True)
|
||||
controller.volumeSlider.blockSignals(True)
|
||||
display = self._define_display(controller)
|
||||
if not self.currentMediaPlayer[controller.controllerType].play(display):
|
||||
controller.seekSlider.blockSignals(False)
|
||||
controller.volumeSlider.blockSignals(False)
|
||||
return False
|
||||
if controller.media_info.is_background:
|
||||
self.media_volume(controller, 0)
|
||||
else:
|
||||
self.media_volume(controller, controller.media_info.volume)
|
||||
if status:
|
||||
display.frame.evaluateJavaScript(u'show_blank("desktop");')
|
||||
self.currentMediaPlayer[controller.controllerType].set_visible(display, True)
|
||||
@ -503,6 +538,8 @@ class MediaController(object):
|
||||
# Start Timer for ui updates
|
||||
if not self.timer.isActive():
|
||||
self.timer.start()
|
||||
controller.seekSlider.blockSignals(False)
|
||||
controller.volumeSlider.blockSignals(False)
|
||||
return True
|
||||
|
||||
def media_pause_msg(self, msg):
|
||||
@ -557,7 +594,7 @@ class MediaController(object):
|
||||
controller.mediabar.actions[u'playbackStop'].setVisible(False)
|
||||
controller.mediabar.actions[u'playbackPause'].setVisible(False)
|
||||
|
||||
def media_volume(self, msg):
|
||||
def media_volume_msg(self, msg):
|
||||
"""
|
||||
Changes the volume of a running video
|
||||
|
||||
@ -566,11 +603,21 @@ class MediaController(object):
|
||||
"""
|
||||
controller = msg[0]
|
||||
vol = msg[1][0]
|
||||
log.debug(u'media_volume %d' % vol)
|
||||
display = self._define_display(controller)
|
||||
self.currentMediaPlayer[controller.controllerType].volume(display, vol)
|
||||
self.media_volume(controller, vol)
|
||||
|
||||
def media_seek(self, msg):
|
||||
def media_volume(self, controller, volume):
|
||||
"""
|
||||
Changes the volume of a running video
|
||||
|
||||
``msg``
|
||||
First element is the controller which should be used
|
||||
"""
|
||||
log.debug(u'media_volume %d' % volume)
|
||||
display = self._define_display(controller)
|
||||
self.currentMediaPlayer[controller.controllerType].volume(display, volume)
|
||||
controller.volumeSlider.setValue(volume)
|
||||
|
||||
def media_seek_msg(self, msg):
|
||||
"""
|
||||
Responds to the request to change the seek Slider of a loaded video
|
||||
|
||||
@ -581,6 +628,17 @@ class MediaController(object):
|
||||
log.debug(u'media_seek')
|
||||
controller = msg[0]
|
||||
seekVal = msg[1][0]
|
||||
self.media_seek(controller, seekVal)
|
||||
|
||||
def media_seek(self, controller, seekVal):
|
||||
"""
|
||||
Responds to the request to change the seek Slider of a loaded video
|
||||
|
||||
``msg``
|
||||
First element is the controller which should be used
|
||||
Second element is a list with the seek Value as first element
|
||||
"""
|
||||
log.debug(u'media_seek')
|
||||
display = self._define_display(controller)
|
||||
self.currentMediaPlayer[controller.controllerType].seek(display, seekVal)
|
||||
|
||||
@ -610,7 +668,8 @@ class MediaController(object):
|
||||
return
|
||||
controller = self.mainWindow.liveController
|
||||
display = self._define_display(controller)
|
||||
if self.currentMediaPlayer[controller.controllerType].state == MediaState.Playing:
|
||||
if controller.controllerType in self.currentMediaPlayer and \
|
||||
self.currentMediaPlayer[controller.controllerType].state == MediaState.Playing:
|
||||
self.currentMediaPlayer[controller.controllerType].pause(display)
|
||||
self.currentMediaPlayer[controller.controllerType].set_visible(display, False)
|
||||
|
||||
|
@ -31,7 +31,7 @@ import logging
|
||||
import mimetypes
|
||||
from datetime import datetime
|
||||
|
||||
from PyQt4 import QtCore, QtGui
|
||||
from PyQt4 import QtGui
|
||||
from PyQt4.phonon import Phonon
|
||||
|
||||
from openlp.core.lib import Receiver, translate, Settings
|
||||
@ -215,8 +215,9 @@ class PhononPlayer(MediaPlayer):
|
||||
self.stop(display)
|
||||
self.set_visible(display, False)
|
||||
if not controller.seekSlider.isSliderDown():
|
||||
controller.seekSlider.setSliderPosition(
|
||||
display.mediaObject.currentTime())
|
||||
controller.seekSlider.blockSignals(True)
|
||||
controller.seekSlider.setSliderPosition(display.mediaObject.currentTime())
|
||||
controller.seekSlider.blockSignals(False)
|
||||
|
||||
def get_media_display_css(self):
|
||||
"""
|
||||
|
4
openlp/core/ui/media/vendor/__init__.py
vendored
4
openlp/core/ui/media/vendor/__init__.py
vendored
@ -26,3 +26,7 @@
|
||||
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
|
||||
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||
###############################################################################
|
||||
"""
|
||||
The :mod:`~openlp.core.ui.media.vendor` module contains any scripts or libraries
|
||||
from 3rd party vendors which are required to make certain media modules work.
|
||||
"""
|
||||
|
@ -33,7 +33,7 @@ import logging
|
||||
import os
|
||||
import sys
|
||||
|
||||
from PyQt4 import QtCore, QtGui
|
||||
from PyQt4 import QtGui
|
||||
|
||||
from openlp.core.lib import Receiver, translate, Settings
|
||||
from openlp.core.ui.media import MediaState
|
||||
@ -109,6 +109,7 @@ class VlcPlayer(MediaPlayer):
|
||||
|
||||
def setup(self, display):
|
||||
display.vlcWidget = QtGui.QFrame(display)
|
||||
display.vlcWidget.setFrameStyle(QtGui.QFrame.NoFrame)
|
||||
# creating a basic vlc instance
|
||||
command_line_options = u'--no-video-title-show'
|
||||
if not display.hasAudio:
|
||||
@ -188,6 +189,7 @@ class VlcPlayer(MediaPlayer):
|
||||
display.vlcMediaPlayer.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)
|
||||
@ -234,7 +236,9 @@ class VlcPlayer(MediaPlayer):
|
||||
self.stop(display)
|
||||
self.set_visible(display, False)
|
||||
if not controller.seekSlider.isSliderDown():
|
||||
controller.seekSlider.blockSignals(True)
|
||||
controller.seekSlider.setSliderPosition(display.vlcMediaPlayer.get_time())
|
||||
controller.seekSlider.blockSignals(False)
|
||||
|
||||
def get_info(self):
|
||||
return(translate('Media.player', 'VLC is an external player which '
|
||||
|
@ -423,7 +423,9 @@ class WebkitPlayer(MediaPlayer):
|
||||
controller.media_info.length = length
|
||||
controller.seekSlider.setMaximum(length)
|
||||
if not controller.seekSlider.isSliderDown():
|
||||
controller.seekSlider.blockSignals(True)
|
||||
controller.seekSlider.setSliderPosition(currentTime)
|
||||
controller.seekSlider.blockSignals(False)
|
||||
|
||||
def get_info(self):
|
||||
return(translate('Media.player', 'Webkit is a media player which runs '
|
||||
|
@ -69,7 +69,7 @@ class ScreenList(object):
|
||||
screen_list.screen_list = []
|
||||
screen_list.display_count = 0
|
||||
screen_list.screen_count_changed()
|
||||
screen_list._load_screen_settings()
|
||||
screen_list.load_screen_settings()
|
||||
QtCore.QObject.connect(desktop, QtCore.SIGNAL(u'resized(int)'), screen_list.screen_resolution_changed)
|
||||
QtCore.QObject.connect(desktop, QtCore.SIGNAL(u'screenCountChanged(int)'), screen_list.screen_count_changed)
|
||||
return screen_list
|
||||
@ -237,7 +237,7 @@ class ScreenList(object):
|
||||
y >= size.y() and y <= (size.y() + size.height()):
|
||||
return screen[u'number']
|
||||
|
||||
def _load_screen_settings(self):
|
||||
def load_screen_settings(self):
|
||||
"""
|
||||
Loads the screen size and the monitor number from the settings.
|
||||
"""
|
||||
|
@ -114,6 +114,7 @@ class ServiceManager(QtGui.QWidget):
|
||||
# is a new service and has not been saved
|
||||
self._modified = False
|
||||
self._fileName = u''
|
||||
self.service_has_all_original_files = True
|
||||
self.serviceNoteForm = ServiceNoteForm(self.mainwindow)
|
||||
self.serviceItemEditForm = ServiceItemEditForm(self.mainwindow)
|
||||
self.startTimeForm = StartTimeForm(self.mainwindow)
|
||||
@ -256,6 +257,20 @@ class ServiceManager(QtGui.QWidget):
|
||||
text=translate('OpenLP.ServiceManager', 'Create New &Custom Slide'),
|
||||
icon=u':/general/general_edit.png', triggers=self.create_custom)
|
||||
self.menu.addSeparator()
|
||||
# Add AutoPlay menu actions
|
||||
self.autoPlaySlidesGroup = QtGui.QMenu(translate('OpenLP.ServiceManager', '&Auto play slides'))
|
||||
self.menu.addMenu(self.autoPlaySlidesGroup)
|
||||
self.autoPlaySlidesLoop = create_widget_action(self.autoPlaySlidesGroup,
|
||||
text=translate('OpenLP.ServiceManager', 'Auto play slides &Loop'),
|
||||
checked=False, triggers=self.toggleAutoPlaySlidesLoop)
|
||||
self.autoPlaySlidesOnce = create_widget_action(self.autoPlaySlidesGroup,
|
||||
text=translate('OpenLP.ServiceManager', 'Auto play slides &Once'),
|
||||
checked=False, triggers=self.toggleAutoPlaySlidesOnce)
|
||||
self.autoPlaySlidesGroup.addSeparator()
|
||||
self.timedSlideInterval = create_widget_action(self.autoPlaySlidesGroup,
|
||||
text=translate('OpenLP.ServiceManager', '&Delay between slides'),
|
||||
checked=False, triggers=self.onTimedSlideInterval)
|
||||
self.menu.addSeparator()
|
||||
self.previewAction = create_widget_action(self.menu, text=translate('OpenLP.ServiceManager', 'Show &Preview'),
|
||||
icon=u':/general/general_preview.png', triggers=self.makePreview)
|
||||
# Add already existing make live action to the menu.
|
||||
@ -458,7 +473,7 @@ class ServiceManager(QtGui.QWidget):
|
||||
for item in list(self.serviceItems):
|
||||
self.mainwindow.incrementProgressBar()
|
||||
item[u'service_item'].remove_invalid_frames(missing_list)
|
||||
if not item[u'service_item'].validate():
|
||||
if item[u'service_item'].missing_frames():
|
||||
self.serviceItems.remove(item)
|
||||
else:
|
||||
service_item = item[u'service_item'].get_service_repr(self._saveLite)
|
||||
@ -620,7 +635,7 @@ class ServiceManager(QtGui.QWidget):
|
||||
path = os.path.join(directory, default_filename)
|
||||
# SaveAs from osz to oszl is not valid as the files will be deleted
|
||||
# on exit which is not sensible or usable in the long term.
|
||||
if self._fileName.endswith(u'oszl') or not self._fileName:
|
||||
if self._fileName.endswith(u'oszl') or self.service_has_all_original_files:
|
||||
fileName = QtGui.QFileDialog.getSaveFileName(self.mainwindow, UiStrings().SaveService, path,
|
||||
translate('OpenLP.ServiceManager',
|
||||
'OpenLP Service Files (*.osz);; OpenLP Service Files - lite (*.oszl)'))
|
||||
@ -693,7 +708,7 @@ class ServiceManager(QtGui.QWidget):
|
||||
serviceItem.set_from_service(item)
|
||||
else:
|
||||
serviceItem.set_from_service(item, self.servicePath)
|
||||
self.validateItem(serviceItem)
|
||||
serviceItem.validate_item(self.suffixes)
|
||||
self.load_item_uuid = 0
|
||||
if serviceItem.is_capable(ItemCapabilities.OnLoadUpdate):
|
||||
Receiver.send_message(u'%s_service_load' % serviceItem.name.lower(), serviceItem)
|
||||
@ -765,6 +780,22 @@ class ServiceManager(QtGui.QWidget):
|
||||
self.maintainAction.setVisible(True)
|
||||
if item.parent() is None:
|
||||
self.notesAction.setVisible(True)
|
||||
if serviceItem[u'service_item'].is_capable(ItemCapabilities.CanLoop) and \
|
||||
len(serviceItem[u'service_item'].get_frames()) > 1:
|
||||
self.autoPlaySlidesGroup.menuAction().setVisible(True)
|
||||
self.autoPlaySlidesOnce.setChecked(serviceItem[u'service_item'].auto_play_slides_once)
|
||||
self.autoPlaySlidesLoop.setChecked(serviceItem[u'service_item'].auto_play_slides_loop)
|
||||
self.timedSlideInterval.setChecked(serviceItem[u'service_item'].timed_slide_interval > 0)
|
||||
if serviceItem[u'service_item'].timed_slide_interval > 0:
|
||||
delay_suffix = u' '
|
||||
delay_suffix += unicode(serviceItem[u'service_item'].timed_slide_interval)
|
||||
delay_suffix += u' s'
|
||||
else:
|
||||
delay_suffix = u' ...'
|
||||
self.timedSlideInterval.setText(translate('OpenLP.ServiceManager', '&Delay between slides') + delay_suffix)
|
||||
# TODO for future: make group explains itself more visually
|
||||
else:
|
||||
self.autoPlaySlidesGroup.menuAction().setVisible(False)
|
||||
if serviceItem[u'service_item'].is_capable(ItemCapabilities.HasVariableStartTime):
|
||||
self.timeAction.setVisible(True)
|
||||
if serviceItem[u'service_item'].is_capable(ItemCapabilities.CanAutoStartForLive):
|
||||
@ -810,6 +841,59 @@ class ServiceManager(QtGui.QWidget):
|
||||
if self.startTimeForm.exec_():
|
||||
self.repaintServiceList(item, -1)
|
||||
|
||||
def toggleAutoPlaySlidesOnce(self):
|
||||
"""
|
||||
Toggle Auto play slide once.
|
||||
Inverts auto play once option for the item
|
||||
"""
|
||||
item = self.findServiceItem()[0]
|
||||
service_item = self.serviceItems[item][u'service_item']
|
||||
service_item.auto_play_slides_once = not service_item.auto_play_slides_once
|
||||
if service_item.auto_play_slides_once:
|
||||
service_item.auto_play_slides_loop = False
|
||||
self.autoPlaySlidesLoop.setChecked(False)
|
||||
if service_item.auto_play_slides_once and service_item.timed_slide_interval == 0:
|
||||
service_item.timed_slide_interval = Settings().value(u'loop delay', 5)
|
||||
self.setModified()
|
||||
|
||||
def toggleAutoPlaySlidesLoop(self):
|
||||
"""
|
||||
Toggle Auto play slide loop.
|
||||
"""
|
||||
item = self.findServiceItem()[0]
|
||||
service_item = self.serviceItems[item][u'service_item']
|
||||
service_item.auto_play_slides_loop = not service_item.auto_play_slides_loop
|
||||
if service_item.auto_play_slides_loop:
|
||||
service_item.auto_play_slides_once = False
|
||||
self.autoPlaySlidesOnce.setChecked(False)
|
||||
if service_item.auto_play_slides_loop and service_item.timed_slide_interval == 0:
|
||||
service_item.timed_slide_interval = Settings().value(u'loop delay', 5)
|
||||
self.setModified()
|
||||
|
||||
def onTimedSlideInterval(self):
|
||||
"""
|
||||
on set times slide interval.
|
||||
Shows input dialog for enter interval in seconds for delay
|
||||
"""
|
||||
item = self.findServiceItem()[0]
|
||||
service_item = self.serviceItems[item][u'service_item']
|
||||
if service_item.timed_slide_interval == 0:
|
||||
timed_slide_interval = Settings().value(u'loop delay', 5)
|
||||
else:
|
||||
timed_slide_interval = service_item.timed_slide_interval
|
||||
timed_slide_interval, ok = QtGui.QInputDialog.getInteger(self, translate('OpenLP.ServiceManager',
|
||||
'Input delay'), translate('OpenLP.ServiceManager', 'Delay between slides in seconds.'),
|
||||
timed_slide_interval, 0, 180, 1)
|
||||
if ok:
|
||||
service_item.timed_slide_interval = timed_slide_interval
|
||||
if service_item.timed_slide_interval <> 0 and not service_item.auto_play_slides_loop\
|
||||
and not service_item.auto_play_slides_once:
|
||||
service_item.auto_play_slides_loop = True
|
||||
elif service_item.timed_slide_interval == 0:
|
||||
service_item.auto_play_slides_loop = False
|
||||
service_item.auto_play_slides_once = False
|
||||
self.setModified()
|
||||
|
||||
def onAutoStart(self):
|
||||
"""
|
||||
Toggles to Auto Start Setting.
|
||||
@ -1032,9 +1116,12 @@ class ServiceManager(QtGui.QWidget):
|
||||
"""
|
||||
# Correct order of items in array
|
||||
count = 1
|
||||
self.service_has_all_original_files = True
|
||||
for item in self.serviceItems:
|
||||
item[u'order'] = count
|
||||
count += 1
|
||||
if not item[u'service_item'].has_original_files:
|
||||
self.service_has_all_original_files = False
|
||||
# Repaint the screen
|
||||
self.serviceManagerList.clear()
|
||||
for itemcount, item in enumerate(self.serviceItems):
|
||||
@ -1093,18 +1180,6 @@ class ServiceManager(QtGui.QWidget):
|
||||
self.serviceManagerList.setCurrentItem(treewidgetitem)
|
||||
treewidgetitem.setExpanded(item[u'expanded'])
|
||||
|
||||
def validateItem(self, serviceItem):
|
||||
"""
|
||||
Validates the service item and if the suffix matches an accepted
|
||||
one it allows the item to be displayed.
|
||||
"""
|
||||
#@todo check file items exist
|
||||
if serviceItem.is_command():
|
||||
type = serviceItem._raw_frames[0][u'title'].split(u'.')[-1]
|
||||
if type.lower() not in self.suffixes:
|
||||
serviceItem.is_valid = False
|
||||
#@todo check file items exist
|
||||
|
||||
def cleanUp(self):
|
||||
"""
|
||||
Empties the servicePath of temporary files on system exit.
|
||||
@ -1144,7 +1219,7 @@ class ServiceManager(QtGui.QWidget):
|
||||
Receiver.send_message(u'cursor_busy')
|
||||
log.debug(u'regenerateServiceItems')
|
||||
# force reset of renderer as theme data has changed
|
||||
self.mainwindow.renderer.themedata = None
|
||||
self.service_has_all_original_files = True
|
||||
if self.serviceItems:
|
||||
for item in self.serviceItems:
|
||||
item[u'selected'] = False
|
||||
@ -1299,6 +1374,8 @@ class ServiceManager(QtGui.QWidget):
|
||||
if self.serviceItems and item < len(self.serviceItems) and \
|
||||
self.serviceItems[item][u'service_item'].is_capable(ItemCapabilities.CanPreview):
|
||||
self.mainwindow.previewController.addServiceManagerItem(self.serviceItems[item][u'service_item'], 0)
|
||||
next_item = self.serviceManagerList.topLevelItem(item)
|
||||
self.serviceManagerList.setCurrentItem(next_item)
|
||||
self.mainwindow.liveController.previewListWidget.setFocus()
|
||||
else:
|
||||
critical_error_message_box(translate('OpenLP.ServiceManager', 'Missing Display Handler'),
|
||||
|
@ -31,7 +31,7 @@ The :mod:`settingsform` provides a user interface for the OpenLP settings
|
||||
"""
|
||||
import logging
|
||||
|
||||
from PyQt4 import QtCore, QtGui
|
||||
from PyQt4 import QtGui
|
||||
|
||||
from openlp.core.lib import Receiver, build_icon, PluginStatus
|
||||
from openlp.core.ui import AdvancedTab, GeneralTab, ThemesTab
|
||||
@ -141,4 +141,4 @@ class SettingsForm(QtGui.QDialog, Ui_SettingsDialog):
|
||||
"""
|
||||
if self.resetSuffixes:
|
||||
self.mainWindow.serviceManagerContents.resetSupportedSuffixes()
|
||||
self.resetSuffixes = False
|
||||
self.resetSuffixes = False
|
||||
|
@ -692,6 +692,14 @@ class SlideController(DisplayController):
|
||||
self.slideSelected()
|
||||
else:
|
||||
self._processItem(item, slidenum)
|
||||
if self.isLive and item.auto_play_slides_loop and item.timed_slide_interval > 0:
|
||||
self.playSlidesLoop.setChecked(item.auto_play_slides_loop)
|
||||
self.delaySpinBox.setValue(int(item.timed_slide_interval))
|
||||
self.onPlaySlidesLoop()
|
||||
elif self.isLive and item.auto_play_slides_once and item.timed_slide_interval > 0:
|
||||
self.playSlidesOnce.setChecked(item.auto_play_slides_once)
|
||||
self.delaySpinBox.setValue(int(item.timed_slide_interval))
|
||||
self.onPlaySlidesOnce()
|
||||
|
||||
def _processItem(self, serviceItem, slideno):
|
||||
"""
|
||||
@ -881,6 +889,7 @@ class SlideController(DisplayController):
|
||||
Settings().remove(self.parent().generalSettingsSection + u'/screen blank')
|
||||
self.blankPlugin()
|
||||
self.updatePreview()
|
||||
self.onToggleLoop()
|
||||
|
||||
def onThemeDisplay(self, checked=None):
|
||||
"""
|
||||
@ -899,6 +908,7 @@ class SlideController(DisplayController):
|
||||
Settings().remove(self.parent().generalSettingsSection + u'/screen blank')
|
||||
self.blankPlugin()
|
||||
self.updatePreview()
|
||||
self.onToggleLoop()
|
||||
|
||||
def onHideDisplay(self, checked=None):
|
||||
"""
|
||||
@ -917,6 +927,7 @@ class SlideController(DisplayController):
|
||||
Settings().remove(self.parent().generalSettingsSection + u'/screen blank')
|
||||
self.hidePlugin(checked)
|
||||
self.updatePreview()
|
||||
self.onToggleLoop()
|
||||
|
||||
def blankPlugin(self):
|
||||
"""
|
||||
@ -933,8 +944,7 @@ class SlideController(DisplayController):
|
||||
else:
|
||||
if not self.serviceItem.is_command():
|
||||
Receiver.send_message(u'live_display_show')
|
||||
Receiver.send_message(u'%s_unblank' % self.serviceItem.name.lower(),
|
||||
[self.serviceItem, self.isLive])
|
||||
Receiver.send_message(u'%s_unblank' % self.serviceItem.name.lower(), [self.serviceItem, self.isLive])
|
||||
else:
|
||||
if hide_mode:
|
||||
Receiver.send_message(u'live_display_hide', hide_mode)
|
||||
@ -949,13 +959,11 @@ class SlideController(DisplayController):
|
||||
if self.serviceItem is not None:
|
||||
if hide:
|
||||
Receiver.send_message(u'live_display_hide', HideMode.Screen)
|
||||
Receiver.send_message(u'%s_hide' % self.serviceItem.name.lower(),
|
||||
[self.serviceItem, self.isLive])
|
||||
Receiver.send_message(u'%s_hide' % self.serviceItem.name.lower(), [self.serviceItem, self.isLive])
|
||||
else:
|
||||
if not self.serviceItem.is_command():
|
||||
Receiver.send_message(u'live_display_show')
|
||||
Receiver.send_message(u'%s_unblank' % self.serviceItem.name.lower(),
|
||||
[self.serviceItem, self.isLive])
|
||||
Receiver.send_message(u'%s_unblank' % self.serviceItem.name.lower(), [self.serviceItem, self.isLive])
|
||||
else:
|
||||
if hide:
|
||||
Receiver.send_message(u'live_display_hide', HideMode.Screen)
|
||||
@ -995,6 +1003,7 @@ class SlideController(DisplayController):
|
||||
self.selectedRow = row
|
||||
self.__checkUpdateSelectedSlide(row)
|
||||
Receiver.send_message(u'slidecontroller_%s_changed' % self.typePrefix, row)
|
||||
self.display.setFocus()
|
||||
|
||||
def onSlideChange(self, row):
|
||||
"""
|
||||
@ -1094,7 +1103,8 @@ class SlideController(DisplayController):
|
||||
"""
|
||||
Toggles the loop state.
|
||||
"""
|
||||
if self.playSlidesLoop.isChecked() or self.playSlidesOnce.isChecked():
|
||||
hide_mode = self.hideMode()
|
||||
if hide_mode is None and (self.playSlidesLoop.isChecked() or self.playSlidesOnce.isChecked()):
|
||||
self.onStartLoop()
|
||||
else:
|
||||
self.onStopLoop()
|
||||
@ -1128,11 +1138,11 @@ class SlideController(DisplayController):
|
||||
self.playSlidesLoop.setText(UiStrings().StopPlaySlidesInLoop)
|
||||
self.playSlidesOnce.setIcon(build_icon(u':/media/media_time.png'))
|
||||
self.playSlidesOnce.setText(UiStrings().PlaySlidesToEnd)
|
||||
self.playSlidesMenu.setDefaultAction(self.playSlidesLoop)
|
||||
self.playSlidesOnce.setChecked(False)
|
||||
else:
|
||||
self.playSlidesLoop.setIcon(build_icon(u':/media/media_time.png'))
|
||||
self.playSlidesLoop.setText(UiStrings().PlaySlidesInLoop)
|
||||
self.playSlidesMenu.setDefaultAction(self.playSlidesLoop)
|
||||
self.playSlidesOnce.setChecked(False)
|
||||
self.onToggleLoop()
|
||||
|
||||
def onPlaySlidesOnce(self, checked=None):
|
||||
@ -1149,11 +1159,11 @@ class SlideController(DisplayController):
|
||||
self.playSlidesOnce.setText(UiStrings().StopPlaySlidesToEnd)
|
||||
self.playSlidesLoop.setIcon(build_icon(u':/media/media_time.png'))
|
||||
self.playSlidesLoop.setText(UiStrings().PlaySlidesInLoop)
|
||||
self.playSlidesMenu.setDefaultAction(self.playSlidesOnce)
|
||||
self.playSlidesLoop.setChecked(False)
|
||||
else:
|
||||
self.playSlidesOnce.setIcon(build_icon(u':/media/media_time'))
|
||||
self.playSlidesOnce.setText(UiStrings().PlaySlidesToEnd)
|
||||
self.playSlidesMenu.setDefaultAction(self.playSlidesOnce)
|
||||
self.playSlidesLoop.setChecked(False)
|
||||
self.onToggleLoop()
|
||||
|
||||
def setAudioItemsVisibility(self, visible):
|
||||
|
@ -29,7 +29,7 @@
|
||||
"""
|
||||
The :mod:`openlp.core.utils` module provides the utility libraries for OpenLP.
|
||||
"""
|
||||
from datetime import datetime
|
||||
from datetime import datetime, timedelta
|
||||
from distutils.version import LooseVersion
|
||||
import logging
|
||||
import locale
|
||||
@ -277,20 +277,31 @@ def check_latest_version(current_version):
|
||||
|
||||
``current_version``
|
||||
The current version of OpenLP.
|
||||
|
||||
**Rules around versions and version files:**
|
||||
|
||||
* If a version number has a build (i.e. -bzr1234), then it is a nightly.
|
||||
* If a version number's minor version is an odd number, it is a development release.
|
||||
* If a version number's minor version is an even number, it is a stable release.
|
||||
"""
|
||||
version_string = current_version[u'full']
|
||||
# set to prod in the distribution config file.
|
||||
settings = Settings()
|
||||
settings.beginGroup(u'general')
|
||||
last_test = settings.value(u'last version test', datetime.now().date())
|
||||
# This defaults to yesterday in order to force the update check to run when you've never run it before.
|
||||
last_test = settings.value(u'last version test', datetime.now().date() - timedelta(days=1))
|
||||
this_test = datetime.now().date()
|
||||
settings.setValue(u'last version test', this_test)
|
||||
settings.endGroup()
|
||||
if last_test != this_test:
|
||||
if current_version[u'build']:
|
||||
req = urllib2.Request(u'http://www.openlp.org/files/dev_version.txt')
|
||||
req = urllib2.Request(u'http://www.openlp.org/files/nightly_version.txt')
|
||||
else:
|
||||
req = urllib2.Request(u'http://www.openlp.org/files/version.txt')
|
||||
version_parts = current_version[u'version'].split(u'.')
|
||||
if int(version_parts[1]) % 2 != 0:
|
||||
req = urllib2.Request(u'http://www.openlp.org/files/dev_version.txt')
|
||||
else:
|
||||
req = urllib2.Request(u'http://www.openlp.org/files/version.txt')
|
||||
req.add_header(u'User-Agent', u'OpenLP/%s' % current_version[u'full'])
|
||||
remote_version = None
|
||||
try:
|
||||
|
@ -26,5 +26,31 @@
|
||||
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
|
||||
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||
###############################################################################
|
||||
"""
|
||||
Forms in OpenLP are made up of two classes. One class holds all the graphical
|
||||
elements, like buttons and lists, and the other class holds all the functional
|
||||
code, like slots and loading and saving.
|
||||
|
||||
The first class, commonly known as the **Dialog** class, is typically named
|
||||
``Ui_<name>Dialog``. It is a slightly modified version of the class that the
|
||||
``pyuic4`` command produces from Qt4's .ui file. Typical modifications will be
|
||||
converting most strings from "" to u'' and using OpenLP's ``translate()``
|
||||
function for translating strings.
|
||||
|
||||
The second class, commonly known as the **Form** class, is typically named
|
||||
``<name>Form``. This class is the one which is instantiated and used. It uses
|
||||
dual inheritance to inherit from (usually) QtGui.QDialog and the Ui class
|
||||
mentioned above, like so::
|
||||
|
||||
class AuthorsForm(QtGui.QDialog, Ui_AuthorsDialog):
|
||||
|
||||
def __init__(self, parent=None):
|
||||
QtGui.QDialog.__init__(self, parent)
|
||||
self.setupUi(self)
|
||||
|
||||
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 alertform import AlertForm
|
||||
|
@ -26,6 +26,10 @@
|
||||
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
|
||||
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||
###############################################################################
|
||||
"""
|
||||
The :mod:`~openlp.plugins.alerts.lib.alertsmanager` module contains the part of
|
||||
the plugin which manages storing and displaying of alerts.
|
||||
"""
|
||||
|
||||
import logging
|
||||
|
||||
|
@ -29,7 +29,7 @@
|
||||
|
||||
import logging
|
||||
|
||||
from PyQt4 import QtCore, QtGui
|
||||
from PyQt4 import QtGui
|
||||
|
||||
from openlp.core.lib import Plugin, StringContent, build_icon, translate, Settings
|
||||
from openlp.core.lib.ui import create_action, UiStrings
|
||||
|
@ -34,7 +34,7 @@ import logging
|
||||
import re
|
||||
|
||||
from openlp.core.lib import translate, Settings
|
||||
from openlp.plugins.bibles.lib.db import BiblesResourcesDB
|
||||
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
@ -30,8 +30,6 @@
|
||||
import logging
|
||||
import os
|
||||
|
||||
from PyQt4 import QtCore
|
||||
|
||||
from openlp.core.lib import Receiver, SettingsManager, translate, Settings
|
||||
from openlp.core.utils import AppLocation, delete_file
|
||||
from openlp.plugins.bibles.lib import parse_reference, get_reference_separator, LanguageSelection
|
||||
|
@ -26,6 +26,10 @@
|
||||
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
|
||||
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||
###############################################################################
|
||||
"""
|
||||
The :mod:`~openlp.plugins.custom.customplugin` module contains the Plugin class
|
||||
for the Custom Slides plugin.
|
||||
"""
|
||||
|
||||
import logging
|
||||
|
||||
|
@ -26,6 +26,10 @@
|
||||
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
|
||||
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||
###############################################################################
|
||||
"""
|
||||
The :mod:`~openlp.plugins.custom.lib.customtab` module contains the settings tab
|
||||
for the Custom Slides plugin, which is inserted into the configuration dialog.
|
||||
"""
|
||||
|
||||
from PyQt4 import QtCore, QtGui
|
||||
|
||||
@ -66,6 +70,9 @@ class CustomTab(SettingsTab):
|
||||
'Import missing custom slides from service files'))
|
||||
|
||||
def onDisplayFooterCheckBoxChanged(self, check_state):
|
||||
"""
|
||||
Toggle the setting for displaying the footer.
|
||||
"""
|
||||
self.displayFooter = False
|
||||
# we have a set value convert to True/False
|
||||
if check_state == QtCore.Qt.Checked:
|
||||
|
@ -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):
|
||||
"""
|
||||
This class builds the XML used to describe songs.
|
||||
@ -84,11 +85,11 @@ class CustomXMLBuilder(object):
|
||||
self.lyrics.setAttribute(u'language', u'en')
|
||||
self.song.appendChild(self.lyrics)
|
||||
|
||||
def add_verse_to_lyrics(self, type, number, content):
|
||||
def add_verse_to_lyrics(self, verse_type, number, content):
|
||||
"""
|
||||
Add a verse to the ``<lyrics>`` tag.
|
||||
|
||||
``type``
|
||||
``verse_type``
|
||||
A string denoting the type of verse. Possible values are "Chorus",
|
||||
"Verse", "Bridge", and "Custom".
|
||||
|
||||
@ -99,7 +100,7 @@ class CustomXMLBuilder(object):
|
||||
The actual text of the verse to be stored.
|
||||
"""
|
||||
verse = self.custom_xml.createElement(u'verse')
|
||||
verse.setAttribute(u'type', type)
|
||||
verse.setAttribute(u'type', verse_type)
|
||||
verse.setAttribute(u'label', number)
|
||||
self.lyrics.appendChild(verse)
|
||||
# add data as a CDATA section to protect the XML from special chars
|
||||
|
@ -33,11 +33,11 @@ import os
|
||||
from PyQt4 import QtCore, QtGui
|
||||
|
||||
from openlp.core.lib import MediaManagerItem, build_icon, ItemCapabilities, SettingsManager, translate, \
|
||||
check_item_selected, Receiver, MediaType, ServiceItem, build_html, ServiceItemContext, Settings
|
||||
check_item_selected, Receiver, MediaType, ServiceItem, ServiceItemContext, Settings, check_directory_exists
|
||||
from openlp.core.lib.ui import UiStrings, critical_error_message_box, create_horizontal_adjusting_combo_box
|
||||
from openlp.core.ui import DisplayController, Display, DisplayControllerType
|
||||
from openlp.core.ui.media import get_media_players, set_media_players
|
||||
from openlp.core.utils import locale_compare
|
||||
from openlp.core.utils import AppLocation, locale_compare
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
@ -130,8 +130,7 @@ class MediaMediaItem(MediaManagerItem):
|
||||
"""
|
||||
Called to reset the Live background with the media selected,
|
||||
"""
|
||||
self.plugin.liveController.mediaController.media_reset(
|
||||
self.plugin.liveController)
|
||||
self.plugin.liveController.mediaController.media_reset(self.plugin.liveController)
|
||||
self.resetAction.setVisible(False)
|
||||
|
||||
def videobackgroundReplaced(self):
|
||||
@ -145,8 +144,7 @@ class MediaMediaItem(MediaManagerItem):
|
||||
Called to replace Live background with the media selected.
|
||||
"""
|
||||
if check_item_selected(self.listView,
|
||||
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.listView.currentItem()
|
||||
filename = item.data(QtCore.Qt.UserRole)
|
||||
if os.path.exists(filename):
|
||||
@ -166,8 +164,8 @@ class MediaMediaItem(MediaManagerItem):
|
||||
translate('MediaPlugin.MediaItem',
|
||||
'There was a problem replacing your background, the media file "%s" no longer exists.') % filename)
|
||||
|
||||
def generateSlideData(self, service_item, item=None, xmlVersion=False,
|
||||
remote=False, context=ServiceItemContext.Live):
|
||||
def generateSlideData(self, service_item, item=None, xmlVersion=False, remote=False,
|
||||
context=ServiceItemContext.Live):
|
||||
if item is None:
|
||||
item = self.listView.currentItem()
|
||||
if item is None:
|
||||
@ -201,6 +199,8 @@ class MediaMediaItem(MediaManagerItem):
|
||||
def initialise(self):
|
||||
self.listView.clear()
|
||||
self.listView.setIconSize(QtCore.QSize(88, 50))
|
||||
self.servicePath = os.path.join(AppLocation.get_section_data_path(self.settingsSection), u'thumbnails')
|
||||
check_directory_exists(self.servicePath)
|
||||
self.loadList(SettingsManager.load_list(self.settingsSection, u'media'))
|
||||
self.populateDisplayTypes()
|
||||
|
||||
@ -247,14 +247,13 @@ class MediaMediaItem(MediaManagerItem):
|
||||
"""
|
||||
Remove a media item from the list.
|
||||
"""
|
||||
if check_item_selected(self.listView, translate('MediaPlugin.MediaItem',
|
||||
'You must select a media file to delete.')):
|
||||
if check_item_selected(self.listView,
|
||||
translate('MediaPlugin.MediaItem', 'You must select a media file to delete.')):
|
||||
row_list = [item.row() for item in self.listView.selectedIndexes()]
|
||||
row_list.sort(reverse=True)
|
||||
for row in row_list:
|
||||
self.listView.takeItem(row)
|
||||
SettingsManager.set_list(self.settingsSection,
|
||||
u'media', self.getFileList())
|
||||
SettingsManager.set_list(self.settingsSection, u'media', self.getFileList())
|
||||
|
||||
def loadList(self, media):
|
||||
# Sort the media by its filename considering language specific
|
||||
|
@ -28,7 +28,6 @@
|
||||
###############################################################################
|
||||
|
||||
import logging
|
||||
import os
|
||||
|
||||
from PyQt4 import QtCore
|
||||
|
||||
|
@ -77,7 +77,7 @@ class PresentationPlugin(Plugin):
|
||||
if self.controllers[controller].enabled():
|
||||
try:
|
||||
self.controllers[controller].start_process()
|
||||
except:
|
||||
except Exception:
|
||||
log.warn(u'Failed to start controller process')
|
||||
self.controllers[controller].available = False
|
||||
self.mediaItem.buildFileMaskString()
|
||||
|
@ -26,7 +26,6 @@
|
||||
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
|
||||
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||
###############################################################################
|
||||
|
||||
"""
|
||||
Forms in OpenLP are made up of two classes. One class holds all the graphical
|
||||
elements, like buttons and lists, and the other class holds all the functional
|
||||
|
@ -26,6 +26,10 @@
|
||||
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
|
||||
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||
###############################################################################
|
||||
"""
|
||||
The :mod:`~openlp.plugins.songs.forms.editsongform` module contains the form
|
||||
used to edit songs.
|
||||
"""
|
||||
|
||||
import logging
|
||||
import re
|
||||
@ -42,7 +46,7 @@ from openlp.plugins.songs.forms import EditVerseForm, MediaFilesForm
|
||||
from openlp.plugins.songs.lib import SongXML, VerseType, clean_song
|
||||
from openlp.plugins.songs.lib.db import Book, Song, Author, Topic, MediaFile
|
||||
from openlp.plugins.songs.lib.ui import SongStrings
|
||||
from editsongdialog import Ui_EditSongDialog
|
||||
from openlp.plugins.songs.forms.editsongdialog import Ui_EditSongDialog
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
@ -56,7 +60,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
|
||||
"""
|
||||
Constructor
|
||||
"""
|
||||
QtGui.QDialog.__init__(self, parent)
|
||||
super(EditSongForm, self).__init__(parent)
|
||||
self.mediaitem = mediaitem
|
||||
self.song = None
|
||||
# can this be automated?
|
||||
@ -113,12 +117,18 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
|
||||
self.whitespace = re.compile(r'\W+', re.UNICODE)
|
||||
|
||||
def initialise(self):
|
||||
"""
|
||||
Set up the form for when it is displayed.
|
||||
"""
|
||||
self.verseEditButton.setEnabled(False)
|
||||
self.verseDeleteButton.setEnabled(False)
|
||||
self.authorRemoveButton.setEnabled(False)
|
||||
self.topicRemoveButton.setEnabled(False)
|
||||
|
||||
def loadAuthors(self):
|
||||
"""
|
||||
Load the authors from the database into the combobox.
|
||||
"""
|
||||
authors = self.manager.get_all_objects(Author,
|
||||
order_by_ref=Author.display_name)
|
||||
self.authorsComboBox.clear()
|
||||
@ -132,14 +142,23 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
|
||||
set_case_insensitive_completer(self.authors, self.authorsComboBox)
|
||||
|
||||
def loadTopics(self):
|
||||
"""
|
||||
Load the topics into the combobox.
|
||||
"""
|
||||
self.topics = []
|
||||
self.__loadObjects(Topic, self.topicsComboBox, self.topics)
|
||||
|
||||
def loadBooks(self):
|
||||
"""
|
||||
Load the song books into the combobox
|
||||
"""
|
||||
self.books = []
|
||||
self.__loadObjects(Book, self.songBookComboBox, self.books)
|
||||
|
||||
def __loadObjects(self, cls, combo, cache):
|
||||
"""
|
||||
Generically load a set of objects into a cache and a combobox.
|
||||
"""
|
||||
objects = self.manager.get_all_objects(cls, order_by_ref=cls.name)
|
||||
combo.clear()
|
||||
combo.addItem(u'')
|
||||
@ -151,6 +170,9 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
|
||||
set_case_insensitive_completer(cache, combo)
|
||||
|
||||
def loadThemes(self, theme_list):
|
||||
"""
|
||||
Load the themes into a combobox.
|
||||
"""
|
||||
self.themeComboBox.clear()
|
||||
self.themeComboBox.addItem(u'')
|
||||
self.themes = theme_list
|
||||
@ -158,6 +180,9 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
|
||||
set_case_insensitive_completer(self.themes, self.themeComboBox)
|
||||
|
||||
def loadMediaFiles(self):
|
||||
"""
|
||||
Load the media files into a combobox.
|
||||
"""
|
||||
self.audioAddFromMediaButton.setVisible(False)
|
||||
for plugin in self.parent().pluginManager.plugins:
|
||||
if plugin.name == u'media' and plugin.status == PluginStatus.Active:
|
||||
@ -166,6 +191,9 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
|
||||
break
|
||||
|
||||
def newSong(self):
|
||||
"""
|
||||
Blank the edit form out in preparation for a new song.
|
||||
"""
|
||||
log.debug(u'New Song')
|
||||
self.song = None
|
||||
self.initialise()
|
||||
@ -313,6 +341,9 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
|
||||
self.verseListWidget.repaint()
|
||||
|
||||
def onAuthorAddButtonClicked(self):
|
||||
"""
|
||||
Add the author to the list of authors associated with this song when the button is clicked.
|
||||
"""
|
||||
item = int(self.authorsComboBox.currentIndex())
|
||||
text = self.authorsComboBox.currentText().strip(u' \r\n\t')
|
||||
# This if statement is for OS X, which doesn't seem to work well with
|
||||
@ -361,10 +392,16 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
|
||||
self.authorsListView.addItem(author_item)
|
||||
|
||||
def onAuthorsListViewClicked(self):
|
||||
"""
|
||||
Run a set of actions when an author in the list is selected (mainly enable the delete button).
|
||||
"""
|
||||
if self.authorsListView.count() > 1:
|
||||
self.authorRemoveButton.setEnabled(True)
|
||||
|
||||
def onAuthorRemoveButtonClicked(self):
|
||||
"""
|
||||
Remove the author from the list when the delete button is clicked.
|
||||
"""
|
||||
self.authorRemoveButton.setEnabled(False)
|
||||
item = self.authorsListView.currentItem()
|
||||
row = self.authorsListView.row(item)
|
||||
|
@ -28,7 +28,7 @@
|
||||
###############################################################################
|
||||
import re
|
||||
|
||||
from PyQt4 import QtGui, QtCore
|
||||
from PyQt4 import QtGui
|
||||
|
||||
from openlp.core.lib import translate
|
||||
from openlp.core.utils import CONTROL_CHARS, locale_direct_compare
|
||||
|
@ -36,7 +36,6 @@ import re
|
||||
from sqlalchemy import Column, ForeignKey, Table, types
|
||||
from sqlalchemy.orm import mapper, relation, reconstructor
|
||||
from sqlalchemy.sql.expression import func
|
||||
from PyQt4 import QtCore
|
||||
|
||||
from openlp.core.lib.db import BaseModel, init_db
|
||||
|
||||
|
@ -30,8 +30,6 @@
|
||||
The :mod:`dreambeamimport` module provides the functionality for importing
|
||||
DreamBeam songs into the OpenLP database.
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
import logging
|
||||
|
||||
from lxml import etree, objectify
|
||||
@ -46,11 +44,11 @@ class DreamBeamImport(SongImport):
|
||||
"""
|
||||
The :class:`DreamBeamImport` class provides the ability to import song files from
|
||||
DreamBeam.
|
||||
|
||||
|
||||
An example of DreamBeam xml mark-up::
|
||||
|
||||
|
||||
<?xml version="1.0"?>
|
||||
<DreamSong xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
<DreamSong xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
|
||||
<WordWrap>false</WordWrap>
|
||||
<Version>0.80</Version>
|
||||
@ -84,7 +82,7 @@ class DreamBeamImport(SongImport):
|
||||
|
||||
* \*.xml
|
||||
"""
|
||||
|
||||
|
||||
def doImport(self):
|
||||
"""
|
||||
Receive a single file or a list of files to import.
|
||||
|
@ -30,8 +30,6 @@
|
||||
The :mod:`mediashoutimport` module provides the functionality for importing
|
||||
a MediaShout database into the OpenLP database.
|
||||
"""
|
||||
import re
|
||||
import os
|
||||
import pyodbc
|
||||
|
||||
from openlp.core.lib import translate
|
||||
|
@ -31,9 +31,7 @@ The :mod:`songproimport` module provides the functionality for importing SongPro
|
||||
songs into the OpenLP database.
|
||||
"""
|
||||
import re
|
||||
import os
|
||||
|
||||
from openlp.core.lib import translate
|
||||
from openlp.plugins.songs.lib import strip_rtf
|
||||
from openlp.plugins.songs.lib.songimport import SongImport
|
||||
|
||||
|
@ -107,13 +107,13 @@ class WowImport(SongImport):
|
||||
"""
|
||||
if isinstance(self.importSource, list):
|
||||
self.importWizard.progressBar.setMaximum(len(self.importSource))
|
||||
for file in self.importSource:
|
||||
for source in self.importSource:
|
||||
if self.stopImportFlag:
|
||||
return
|
||||
self.setDefaults()
|
||||
song_data = open(file, 'rb')
|
||||
song_data = open(source, 'rb')
|
||||
if song_data.read(19) != u'WoW File\nSong Words':
|
||||
self.logError(file, unicode(translate('SongsPlugin.WordsofWorshipSongImport',
|
||||
self.logError(source, unicode(translate('SongsPlugin.WordsofWorshipSongImport',
|
||||
('Invalid Words of Worship song file. Missing "Wow File\\nSong Words" header.'))))
|
||||
continue
|
||||
# Seek to byte which stores number of blocks in the song
|
||||
@ -121,7 +121,7 @@ class WowImport(SongImport):
|
||||
no_of_blocks = ord(song_data.read(1))
|
||||
song_data.seek(66)
|
||||
if song_data.read(16) != u'CSongDoc::CBlock':
|
||||
self.logError(file, unicode(translate('SongsPlugin.WordsofWorshipSongImport',
|
||||
self.logError(source, unicode(translate('SongsPlugin.WordsofWorshipSongImport',
|
||||
('Invalid Words of Worship song file. Missing "CSongDoc::CBlock" string.'))))
|
||||
continue
|
||||
# Seek to the beginning of the first block
|
||||
@ -150,9 +150,9 @@ class WowImport(SongImport):
|
||||
copyright_length = ord(song_data.read(1))
|
||||
if copyright_length:
|
||||
self.addCopyright(unicode(song_data.read(copyright_length), u'cp1252'))
|
||||
file_name = os.path.split(file)[1]
|
||||
file_name = os.path.split(source)[1]
|
||||
# Get the song title
|
||||
self.title = file_name.rpartition(u'.')[0]
|
||||
song_data.close()
|
||||
if not self.finish():
|
||||
self.logError(file)
|
||||
self.logError(source)
|
||||
|
@ -26,6 +26,10 @@
|
||||
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
|
||||
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||
###############################################################################
|
||||
"""
|
||||
The :mod:`~openlp.plugins.songs.songsplugin` module contains the Plugin class
|
||||
for the Songs plugin.
|
||||
"""
|
||||
|
||||
import logging
|
||||
import os
|
||||
|
@ -38,6 +38,7 @@ modules, simply run this script::
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
from distutils.version import LooseVersion
|
||||
|
||||
is_win = sys.platform.startswith('win')
|
||||
|
||||
@ -89,15 +90,13 @@ OPTIONAL_MODULES = [
|
||||
w = sys.stdout.write
|
||||
|
||||
def check_vers(version, required, text):
|
||||
if type(version) is str:
|
||||
version = version.split('.')
|
||||
version = map(int, version)
|
||||
if type(required) is str:
|
||||
required = required.split('.')
|
||||
required = map(int, required)
|
||||
w(' %s >= %s ... ' % (text, '.'.join(map(str, required))))
|
||||
if version >= required:
|
||||
w('.'.join(map(str, version)) + os.linesep)
|
||||
if type(version) is not str:
|
||||
version = '.'.join(map(str, version))
|
||||
if type(required) is not str:
|
||||
required = '.'.join(map(str, required))
|
||||
w(' %s >= %s ... ' % (text, required))
|
||||
if LooseVersion(version) >= LooseVersion(required):
|
||||
w(version + os.linesep)
|
||||
return True
|
||||
else:
|
||||
w('FAIL' + os.linesep)
|
||||
|
@ -5,7 +5,8 @@ from unittest import TestCase
|
||||
|
||||
from mock import MagicMock, patch
|
||||
|
||||
from openlp.core.lib import str_to_bool, translate, check_directory_exists, get_text_file_string
|
||||
from openlp.core.lib import str_to_bool, translate, check_directory_exists, get_text_file_string, build_icon, \
|
||||
image_to_byte, check_item_selected
|
||||
|
||||
class TestLib(TestCase):
|
||||
|
||||
@ -197,3 +198,104 @@ class TestLib(TestCase):
|
||||
"""
|
||||
assert True, u'Impossible to test due to conflicts when mocking out the "open" function'
|
||||
|
||||
def build_icon_with_qicon_test(self):
|
||||
"""
|
||||
Test the build_icon() function with a QIcon instance
|
||||
"""
|
||||
with patch(u'openlp.core.lib.QtGui') as MockedQtGui:
|
||||
# GIVEN: A mocked QIcon
|
||||
MockedQtGui.QIcon = MagicMock
|
||||
mocked_icon = MockedQtGui.QIcon()
|
||||
|
||||
# WHEN: We pass a QIcon instance in
|
||||
result = build_icon(mocked_icon)
|
||||
|
||||
# THEN: The result should be our mocked QIcon
|
||||
assert result is mocked_icon, u'The result should be the mocked QIcon'
|
||||
|
||||
def build_icon_with_resource_test(self):
|
||||
"""
|
||||
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:
|
||||
# GIVEN: A mocked QIcon and a mocked QPixmap
|
||||
MockedQtGui.QIcon = MagicMock
|
||||
MockedQtGui.QIcon.Normal = 1
|
||||
MockedQtGui.QIcon.Off = 2
|
||||
MockedQPixmap.return_value = u'mocked_pixmap'
|
||||
resource_uri = u':/resource/uri'
|
||||
|
||||
# WHEN: We pass a QIcon instance in
|
||||
result = build_icon(resource_uri)
|
||||
|
||||
# THEN: The result should be our mocked QIcon
|
||||
MockedQPixmap.assert_called_with(resource_uri)
|
||||
# There really should be more assert statements here but due to type checking and things they all break. The
|
||||
# best we can do is to assert that we get back a MagicMock object.
|
||||
assert isinstance(result, MagicMock), u'The result should be a MagicMock, because we mocked it out'
|
||||
|
||||
def image_to_byte_test(self):
|
||||
"""
|
||||
Test the image_to_byte() function
|
||||
"""
|
||||
with patch(u'openlp.core.lib.QtCore') as MockedQtCore:
|
||||
# GIVEN: A set of mocked-out Qt classes
|
||||
mocked_byte_array = MagicMock()
|
||||
MockedQtCore.QByteArray.return_value = mocked_byte_array
|
||||
mocked_byte_array.toBase64.return_value = u'base64mock'
|
||||
mocked_buffer = MagicMock()
|
||||
MockedQtCore.QBuffer.return_value = mocked_buffer
|
||||
MockedQtCore.QIODevice.WriteOnly = u'writeonly'
|
||||
mocked_image = MagicMock()
|
||||
|
||||
# WHEN: We convert an image to a byte array
|
||||
result = image_to_byte(mocked_image)
|
||||
|
||||
# THEN: We should receive a value of u'base64mock'
|
||||
MockedQtCore.QByteArray.assert_called_with()
|
||||
MockedQtCore.QBuffer.assert_called_with(mocked_byte_array)
|
||||
mocked_buffer.open.assert_called_with(u'writeonly')
|
||||
mocked_image.save.assert_called_with(mocked_buffer, "PNG")
|
||||
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 check_item_selected_true_test(self):
|
||||
"""
|
||||
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')
|
||||
mocked_list_widget = MagicMock()
|
||||
mocked_list_widget.selectedIndexes.return_value = True
|
||||
message = u'message'
|
||||
|
||||
# WHEN: We check if there are selected items
|
||||
result = check_item_selected(mocked_list_widget, message)
|
||||
|
||||
# THEN: The selectedIndexes function should have been called and the result should be true
|
||||
mocked_list_widget.selectedIndexes.assert_called_with()
|
||||
assert result, u'The result should be True'
|
||||
|
||||
def check_item_selected_false_test(self):
|
||||
"""
|
||||
Test that the check_item_selected() function returns False when there are no selected indexes.
|
||||
"""
|
||||
# GIVEN: A mocked out QtGui module and a list widget with selected indexes
|
||||
with patch(u'openlp.core.lib.QtGui') as MockedQtGui, \
|
||||
patch(u'openlp.core.lib.translate') as mocked_translate:
|
||||
mocked_translate.return_value = u'mocked translate'
|
||||
mocked_list_widget = MagicMock()
|
||||
mocked_list_widget.selectedIndexes.return_value = False
|
||||
mocked_list_widget.parent.return_value = u'parent'
|
||||
message = u'message'
|
||||
|
||||
# WHEN: We check if there are selected items
|
||||
result = check_item_selected(mocked_list_widget, message)
|
||||
|
||||
# THEN: The selectedIndexes function should have been called and the result should be true
|
||||
mocked_list_widget.selectedIndexes.assert_called_with()
|
||||
MockedQtGui.QMessageBox.information.assert_called_with(u'parent', u'mocked translate', 'message')
|
||||
assert not result, u'The result should be False'
|
||||
|
||||
|
||||
|
163
tests/functional/openlp_core_lib/test_serviceitem.py
Normal file
163
tests/functional/openlp_core_lib/test_serviceitem.py
Normal file
@ -0,0 +1,163 @@
|
||||
"""
|
||||
Package to test the openlp.core.lib package.
|
||||
"""
|
||||
import os
|
||||
|
||||
from unittest import TestCase
|
||||
from mock import MagicMock
|
||||
from openlp.core.lib import ServiceItem
|
||||
|
||||
VERSE = u'The Lord said to {r}Noah{/r}: \n'\
|
||||
'There\'s gonna be a {su}floody{/su}, {sb}floody{/sb}\n'\
|
||||
'The Lord said to {g}Noah{/g}:\n'\
|
||||
'There\'s gonna be a {st}floody{/st}, {it}floody{/it}\n'\
|
||||
'Get those children out of the muddy, muddy \n'\
|
||||
'{r}C{/r}{b}h{/b}{bl}i{/bl}{y}l{/y}{g}d{/g}{pk}'\
|
||||
'r{/pk}{o}e{/o}{pp}n{/pp} of the Lord\n'
|
||||
FOOTER = [u'Arky Arky (Unknown)', u'Public Domain', u'CCLI 123456']
|
||||
|
||||
TESTPATH = os.path.abspath(os.path.join(os.path.dirname(__file__), u'..', u'..', u'resources'))
|
||||
|
||||
class TestServiceItem(TestCase):
|
||||
|
||||
def serviceitem_basic_test(self):
|
||||
"""
|
||||
Test the Service Item basic test
|
||||
"""
|
||||
# GIVEN: A new service item
|
||||
|
||||
# WHEN:A service item is created (without a plugin)
|
||||
service_item = ServiceItem(None)
|
||||
|
||||
# THEN: We should get back a valid service item
|
||||
assert service_item.is_valid is True, u'The new service item should be valid'
|
||||
assert service_item.missing_frames() is True, u'There should not be any frames in the service item'
|
||||
|
||||
def serviceitem_add_text_test(self):
|
||||
"""
|
||||
Test the Service Item add text test
|
||||
"""
|
||||
# GIVEN: A new service item
|
||||
service_item = ServiceItem(None)
|
||||
|
||||
# WHEN: adding text to a service item
|
||||
service_item.add_from_text(VERSE)
|
||||
service_item.raw_footer = FOOTER
|
||||
|
||||
# THEN: We should get back a valid service item
|
||||
assert service_item.is_valid is True, u'The new service item should be valid'
|
||||
assert service_item.missing_frames() is False, u'check frames loaded '
|
||||
|
||||
# GIVEN: A service item with text
|
||||
mocked_renderer = MagicMock()
|
||||
mocked_renderer.format_slide.return_value = [VERSE]
|
||||
service_item.renderer = mocked_renderer
|
||||
|
||||
# WHEN: Render called
|
||||
assert len(service_item._display_frames) == 0, u'A blank Service Item with no display frames'
|
||||
service_item.render(True)
|
||||
|
||||
# THEN: We should have a page of output.
|
||||
assert len(service_item._display_frames) == 1, u'A valid rendered Service Item has 1 display frame'
|
||||
assert service_item.get_rendered_frame(0) == VERSE.split(u'\n')[0], u'A output has rendered correctly.'
|
||||
|
||||
def serviceitem_add_image_test(self):
|
||||
"""
|
||||
Test the Service Item add image test
|
||||
"""
|
||||
# GIVEN: A new service item and a mocked renderer
|
||||
service_item = ServiceItem(None)
|
||||
service_item.name = u'test'
|
||||
mocked_renderer = MagicMock()
|
||||
service_item.renderer = mocked_renderer
|
||||
|
||||
# WHEN: adding image to a service item
|
||||
test_image = os.path.join(TESTPATH, u'church.jpg')
|
||||
service_item.add_from_image(test_image, u'Image Title')
|
||||
|
||||
# THEN: We should get back a valid service item
|
||||
assert service_item.is_valid is True, u'The new service item should be valid'
|
||||
assert len(service_item._display_frames) == 0, u'The service item has no display frames'
|
||||
|
||||
# THEN: We should have a page of output.
|
||||
assert len(service_item._raw_frames) == 1, u'A valid rendered Service Item has display frames'
|
||||
assert service_item.get_rendered_frame(0) == test_image
|
||||
|
||||
# WHEN: adding a second image to a service item
|
||||
service_item.add_from_image(test_image, u'Image1 Title')
|
||||
|
||||
# THEN: We should have an increased page of output.
|
||||
assert len(service_item._raw_frames) == 2, u'A valid rendered Service Item has display frames'
|
||||
assert service_item.get_rendered_frame(0) == test_image
|
||||
assert service_item.get_rendered_frame(0) == service_item.get_rendered_frame(1)
|
||||
|
||||
# WHEN requesting a saved service item
|
||||
service = service_item.get_service_repr(True)
|
||||
|
||||
# THEN: We should have two parts of the service.
|
||||
assert len(service) == 2, u'A saved service has two parts'
|
||||
assert service[u'header'][u'name'] == u'test' , u'A test plugin was returned'
|
||||
assert service[u'data'][0][u'title'] == u'Image Title' , u'The first title name matches the request'
|
||||
assert service[u'data'][0][u'path'] == test_image , u'The first image name matches'
|
||||
assert service[u'data'][0][u'title'] != service[u'data'][1][u'title'], \
|
||||
u'The individual titles should not match'
|
||||
assert service[u'data'][0][u'path'] == service[u'data'][1][u'path'], u'The file paths should match'
|
||||
|
||||
# WHEN validating a service item
|
||||
service_item.validate_item([u'jpg'])
|
||||
|
||||
# THEN the service item should be valid
|
||||
assert service_item.is_valid is True, u'The new service item should be valid'
|
||||
|
||||
# WHEN: adding a second image to a service item
|
||||
service_item.add_from_image(u'resources/church1.jpg', u'Image1 Title')
|
||||
|
||||
# WHEN validating a service item
|
||||
service_item.validate_item([u'jpg'])
|
||||
|
||||
# THEN the service item should be valid
|
||||
assert service_item.is_valid is False, u'The service item is not valid due to validation changes'
|
||||
|
||||
def serviceitem_add_command_test(self):
|
||||
"""
|
||||
Test the Service Item add command test
|
||||
"""
|
||||
# GIVEN: A new service item and a mocked renderer
|
||||
service_item = ServiceItem(None)
|
||||
service_item.name = u'test'
|
||||
mocked_renderer = MagicMock()
|
||||
service_item.renderer = mocked_renderer
|
||||
|
||||
# WHEN: adding image to a service item
|
||||
test_file = os.path.join(TESTPATH, u'church.jpg')
|
||||
service_item.add_from_command(TESTPATH, u'church.jpg', test_file)
|
||||
|
||||
# THEN: We should get back a valid service item
|
||||
assert service_item.is_valid is True, u'The new service item should be valid'
|
||||
assert len(service_item._display_frames) == 0, u'The service item has no display frames '
|
||||
|
||||
# THEN: We should have a page of output.
|
||||
assert len(service_item._raw_frames) == 1, u'A valid rendered Service Item has one raw frame'
|
||||
assert service_item.get_rendered_frame(0) == test_file, u'The image matches the input'
|
||||
|
||||
# WHEN requesting a saved service item
|
||||
service = service_item.get_service_repr(True)
|
||||
|
||||
# THEN: We should have two parts of the service.
|
||||
assert len(service) == 2, u'A saved service has two parts'
|
||||
assert service[u'header'][u'name'] == u'test' , u'A test plugin'
|
||||
assert service[u'data'][0][u'title'] == u'church.jpg' , u'The first title name '
|
||||
assert service[u'data'][0][u'path'] == TESTPATH , u'The first image name'
|
||||
assert service[u'data'][0][u'image'] == test_file , u'The first image name'
|
||||
|
||||
# WHEN validating a service item
|
||||
service_item.validate_item([u'jpg'])
|
||||
|
||||
# THEN the service item should be valid
|
||||
assert service_item.is_valid is True, u'The service item is valid'
|
||||
|
||||
# WHEN validating a service item with a different suffix
|
||||
service_item.validate_item([u'png'])
|
||||
|
||||
# THEN the service item should not be valid
|
||||
assert service_item.is_valid is False, u'The service item is not valid'
|
48
tests/functional/openlp_core_ui/test_starttimedialog.py
Normal file
48
tests/functional/openlp_core_ui/test_starttimedialog.py
Normal file
@ -0,0 +1,48 @@
|
||||
"""
|
||||
Package to test the openlp.core.ui package.
|
||||
"""
|
||||
import sys
|
||||
|
||||
from unittest import TestCase
|
||||
from mock import MagicMock
|
||||
from openlp.core.ui import starttimeform
|
||||
from PyQt4 import QtCore, QtGui, QtTest
|
||||
|
||||
class TestStartTimeDialog(TestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""
|
||||
Create the UI
|
||||
"""
|
||||
self.app = QtGui.QApplication(sys.argv)
|
||||
self.window = QtGui.QMainWindow()
|
||||
self.form = starttimeform.StartTimeForm(self.window)
|
||||
|
||||
def ui_defaults_test(self):
|
||||
"""
|
||||
Test StartTimeDialog defaults
|
||||
"""
|
||||
self.assertEqual(self.form.hourSpinBox.minimum(), 0)
|
||||
self.assertEqual(self.form.hourSpinBox.maximum(), 4)
|
||||
self.assertEqual(self.form.minuteSpinBox.minimum(), 0)
|
||||
self.assertEqual(self.form.minuteSpinBox.maximum(), 59)
|
||||
self.assertEqual(self.form.secondSpinBox.minimum(), 0)
|
||||
self.assertEqual(self.form.secondSpinBox.maximum(), 59)
|
||||
self.assertEqual(self.form.hourFinishSpinBox.minimum(), 0)
|
||||
self.assertEqual(self.form.hourFinishSpinBox.maximum(), 4)
|
||||
self.assertEqual(self.form.minuteFinishSpinBox.minimum(), 0)
|
||||
self.assertEqual(self.form.minuteFinishSpinBox.maximum(), 59)
|
||||
self.assertEqual(self.form.secondFinishSpinBox.minimum(), 0)
|
||||
self.assertEqual(self.form.secondFinishSpinBox.maximum(), 59)
|
||||
|
||||
def time_display_test(self):
|
||||
"""
|
||||
Test StartTimeDialog display initialisation
|
||||
"""
|
||||
#GIVEN: A service item with with time
|
||||
mocked_serviceitem = MagicMock()
|
||||
mocked_serviceitem.start_time = 61
|
||||
mocked_serviceitem.end_time = 3701
|
||||
|
||||
self.form.item = mocked_serviceitem
|
||||
#self.form.exec_()
|
BIN
tests/resources/church.jpg
Normal file
BIN
tests/resources/church.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 213 KiB |
@ -34,4 +34,4 @@ from openlp.core.ui.mainwindow import MainWindow
|
||||
def test_start_app(openlpapp):
|
||||
assert type(openlpapp) == OpenLP
|
||||
assert type(openlpapp.mainWindow) == MainWindow
|
||||
assert unicode(openlpapp.mainWindow.windowTitle()) == u'OpenLP 2.0'
|
||||
assert unicode(openlpapp.mainWindow.windowTitle()) == u'OpenLP 2.1'
|
||||
|
Loading…
Reference in New Issue
Block a user