This commit is contained in:
Raoul Snyman 2013-01-20 21:33:26 +02:00
commit 8c916254e4
44 changed files with 497 additions and 120 deletions

View File

@ -1 +1 @@
2.0 2.1.0-bzr2141

View File

@ -34,7 +34,6 @@ import logging
import os import os
from urllib import quote_plus as urlquote from urllib import quote_plus as urlquote
from PyQt4 import QtCore
from sqlalchemy import Table, MetaData, Column, types, create_engine from sqlalchemy import Table, MetaData, Column, types, create_engine
from sqlalchemy.exc import SQLAlchemyError, InvalidRequestError, DBAPIError, OperationalError from sqlalchemy.exc import SQLAlchemyError, InvalidRequestError, DBAPIError, OperationalError
from sqlalchemy.orm import scoped_session, sessionmaker, mapper from sqlalchemy.orm import scoped_session, sessionmaker, mapper

View File

@ -31,8 +31,6 @@ Provide HTML Tag management and Formatting Tag access class
""" """
import cPickle import cPickle
from PyQt4 import QtCore
from openlp.core.lib import translate, Settings from openlp.core.lib import translate, Settings
class FormattingTags(object): class FormattingTags(object):

View File

@ -37,7 +37,9 @@ import logging
import os import os
import uuid 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__) log = logging.getLogger(__name__)
@ -182,6 +184,7 @@ class ServiceItem(object):
self.theme_overwritten = False self.theme_overwritten = False
self.temporary_edit = False self.temporary_edit = False
self.will_auto_start = False self.will_auto_start = False
self.has_original_files = True
self._new_item() self._new_item()
def _new_item(self): def _new_item(self):
@ -190,6 +193,7 @@ class ServiceItem(object):
service items to see if they are the same. service items to see if they are the same.
""" """
self._uuid = unicode(uuid.uuid1()) self._uuid = unicode(uuid.uuid1())
self.validate_item()
def add_capability(self, capability): def add_capability(self, capability):
""" """
@ -395,6 +399,7 @@ class ServiceItem(object):
self.end_time = header.get(u'end_time', 0) self.end_time = header.get(u'end_time', 0)
self.media_length = header.get(u'media_length', 0) self.media_length = header.get(u'media_length', 0)
self.will_auto_start = header.get(u'will_auto_start', False) self.will_auto_start = header.get(u'will_auto_start', False)
self.has_original_files = True
if u'background_audio' in header: if u'background_audio' in header:
self.background_audio = [] self.background_audio = []
for filename in header[u'background_audio']: for filename in header[u'background_audio']:
@ -405,16 +410,20 @@ class ServiceItem(object):
for slide in serviceitem[u'serviceitem'][u'data']: for slide in serviceitem[u'serviceitem'][u'data']:
self._raw_frames.append(slide) self._raw_frames.append(slide)
elif self.service_item_type == ServiceItemType.Image: 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: if path:
self.has_original_files = False
for text_image in serviceitem[u'serviceitem'][u'data']: for text_image in serviceitem[u'serviceitem'][u'data']:
filename = os.path.join(path, text_image) filename = os.path.join(path, text_image)
self.add_from_image(filename, text_image) self.add_from_image(filename, text_image, background)
else: else:
for text_image in serviceitem[u'serviceitem'][u'data']: 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: elif self.service_item_type == ServiceItemType.Command:
for text_image in serviceitem[u'serviceitem'][u'data']: for text_image in serviceitem[u'serviceitem'][u'data']:
if path: if path:
self.has_original_files = False
self.add_from_command(path, text_image[u'title'], text_image[u'image']) self.add_from_command(path, text_image[u'title'], text_image[u'image'])
else: else:
self.add_from_command(text_image[u'path'], text_image[u'title'], text_image[u'image']) self.add_from_command(text_image[u'path'], text_image[u'title'], text_image[u'image'])
@ -605,8 +614,25 @@ class ServiceItem(object):
if self.get_frame_path(frame=frame) in invalid_paths: if self.get_frame_path(frame=frame) in invalid_paths:
self.remove_frame(frame) 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

View File

@ -26,5 +26,9 @@
# with this program; if not, write to the Free Software Foundation, Inc., 59 # # with this program; if not, write to the Free Software Foundation, Inc., 59 #
# Temple Place, Suite 330, Boston, MA 02111-1307 USA # # 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 from openlp.core.theme.theme import Theme

View File

@ -29,9 +29,7 @@
""" """
The :mod:`ui` module provides the core user interface for OpenLP The :mod:`ui` module provides the core user interface for OpenLP
""" """
from PyQt4 import QtGui
from openlp.core.lib import translate
class HideMode(object): class HideMode(object):
""" """

View File

@ -29,7 +29,7 @@
from PyQt4 import QtCore, QtGui 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 from openlp.core.lib.ui import create_button, create_button_box
class Ui_ExceptionDialog(object): class Ui_ExceptionDialog(object):

View File

@ -29,19 +29,49 @@
import logging import logging
import os import os
import sys import datetime
from PyQt4 import QtCore, QtGui from PyQt4 import QtCore, QtGui
from openlp.core.lib import OpenLPToolbar, Receiver, translate, Settings from openlp.core.lib import OpenLPToolbar, Receiver, translate, Settings
from openlp.core.lib.ui import UiStrings, critical_error_message_box from openlp.core.lib.ui import UiStrings, critical_error_message_box
from openlp.core.ui.media import MediaState, MediaInfo, MediaType, \ from openlp.core.ui.media import MediaState, MediaInfo, MediaType, get_media_players, set_media_players
get_media_players, set_media_players
from openlp.core.ui.media.mediaplayer import MediaPlayer from openlp.core.ui.media.mediaplayer import MediaPlayer
from openlp.core.utils import AppLocation from openlp.core.utils import AppLocation
from openlp.core.ui import DisplayControllerType from openlp.core.ui import DisplayControllerType
log = logging.getLogger(__name__) 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): class MediaController(object):
""" """
The implementation of the Media Controller. The Media Controller adds an own 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'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'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'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'seekSlider'), self.media_seek_msg)
QtCore.QObject.connect(Receiver.get_receiver(), QtCore.SIGNAL(u'volumeSlider'), self.media_volume) 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_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_blank'), self.media_blank)
QtCore.QObject.connect(Receiver.get_receiver(), QtCore.SIGNAL(u'media_unblank'), self.media_unblank) 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', icon=u':/slides/media_playback_stop.png',
tooltip=translate('OpenLP.SlideController', 'Stop playing media.'), triggers=controller.sendToPlugins) tooltip=translate('OpenLP.SlideController', 'Stop playing media.'), triggers=controller.sendToPlugins)
# Build the seekSlider. # Build the seekSlider.
controller.seekSlider = QtGui.QSlider(QtCore.Qt.Horizontal) controller.seekSlider = MediaSlider(QtCore.Qt.Horizontal, self, controller)
controller.seekSlider.setMaximum(1000) 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.setToolTip(translate('OpenLP.SlideController', 'Video position.'))
controller.seekSlider.setGeometry(QtCore.QRect(90, 260, 221, 24)) controller.seekSlider.setGeometry(QtCore.QRect(90, 260, 221, 24))
controller.seekSlider.setObjectName(u'seekSlider') controller.seekSlider.setObjectName(u'seekSlider')
@ -344,12 +375,8 @@ class MediaController(object):
# stop running videos # stop running videos
self.media_reset(controller) self.media_reset(controller)
controller.media_info = MediaInfo() 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.volume = controller.volumeSlider.value()
controller.media_info.is_background = False controller.media_info.is_background = videoBehindText
controller.media_info.file_info = QtCore.QFileInfo(serviceItem.get_frame_path()) controller.media_info.file_info = QtCore.QFileInfo(serviceItem.get_frame_path())
display = self._define_display(controller) display = self._define_display(controller)
if controller.isLive: if controller.isLive:
@ -361,7 +388,7 @@ class MediaController(object):
controller.media_info.start_time = 0 controller.media_info.start_time = 0
controller.media_info.end_time = 0 controller.media_info.end_time = 0
else: 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 controller.media_info.end_time = serviceItem.end_time
elif controller.previewDisplay: elif controller.previewDisplay:
isValid = self._check_file_type(controller, display, serviceItem) isValid = self._check_file_type(controller, display, serviceItem)
@ -483,9 +510,17 @@ class MediaController(object):
The controller to be played The controller to be played
""" """
log.debug(u'media_play') log.debug(u'media_play')
controller.seekSlider.blockSignals(True)
controller.volumeSlider.blockSignals(True)
display = self._define_display(controller) display = self._define_display(controller)
if not self.currentMediaPlayer[controller.controllerType].play(display): if not self.currentMediaPlayer[controller.controllerType].play(display):
controller.seekSlider.blockSignals(False)
controller.volumeSlider.blockSignals(False)
return 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: if status:
display.frame.evaluateJavaScript(u'show_blank("desktop");') display.frame.evaluateJavaScript(u'show_blank("desktop");')
self.currentMediaPlayer[controller.controllerType].set_visible(display, True) self.currentMediaPlayer[controller.controllerType].set_visible(display, True)
@ -503,6 +538,8 @@ class MediaController(object):
# Start Timer for ui updates # Start Timer for ui updates
if not self.timer.isActive(): if not self.timer.isActive():
self.timer.start() self.timer.start()
controller.seekSlider.blockSignals(False)
controller.volumeSlider.blockSignals(False)
return True return True
def media_pause_msg(self, msg): def media_pause_msg(self, msg):
@ -557,7 +594,7 @@ class MediaController(object):
controller.mediabar.actions[u'playbackStop'].setVisible(False) controller.mediabar.actions[u'playbackStop'].setVisible(False)
controller.mediabar.actions[u'playbackPause'].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 Changes the volume of a running video
@ -566,11 +603,21 @@ class MediaController(object):
""" """
controller = msg[0] controller = msg[0]
vol = msg[1][0] vol = msg[1][0]
log.debug(u'media_volume %d' % vol) self.media_volume(controller, vol)
display = self._define_display(controller)
self.currentMediaPlayer[controller.controllerType].volume(display, 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 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') log.debug(u'media_seek')
controller = msg[0] controller = msg[0]
seekVal = msg[1][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) display = self._define_display(controller)
self.currentMediaPlayer[controller.controllerType].seek(display, seekVal) self.currentMediaPlayer[controller.controllerType].seek(display, seekVal)
@ -610,7 +668,8 @@ class MediaController(object):
return return
controller = self.mainWindow.liveController controller = self.mainWindow.liveController
display = self._define_display(controller) 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].pause(display)
self.currentMediaPlayer[controller.controllerType].set_visible(display, False) self.currentMediaPlayer[controller.controllerType].set_visible(display, False)

View File

@ -31,7 +31,7 @@ import logging
import mimetypes import mimetypes
from datetime import datetime from datetime import datetime
from PyQt4 import QtCore, QtGui from PyQt4 import QtGui
from PyQt4.phonon import Phonon from PyQt4.phonon import Phonon
from openlp.core.lib import Receiver, translate, Settings from openlp.core.lib import Receiver, translate, Settings
@ -215,8 +215,9 @@ class PhononPlayer(MediaPlayer):
self.stop(display) self.stop(display)
self.set_visible(display, False) self.set_visible(display, False)
if not controller.seekSlider.isSliderDown(): if not controller.seekSlider.isSliderDown():
controller.seekSlider.setSliderPosition( controller.seekSlider.blockSignals(True)
display.mediaObject.currentTime()) controller.seekSlider.setSliderPosition(display.mediaObject.currentTime())
controller.seekSlider.blockSignals(False)
def get_media_display_css(self): def get_media_display_css(self):
""" """

View File

@ -26,3 +26,7 @@
# with this program; if not, write to the Free Software Foundation, Inc., 59 # # with this program; if not, write to the Free Software Foundation, Inc., 59 #
# Temple Place, Suite 330, Boston, MA 02111-1307 USA # # 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.
"""

View File

@ -33,7 +33,7 @@ import logging
import os import os
import sys import sys
from PyQt4 import QtCore, QtGui from PyQt4 import QtGui
from openlp.core.lib import Receiver, translate, Settings from openlp.core.lib import Receiver, translate, Settings
from openlp.core.ui.media import MediaState from openlp.core.ui.media import MediaState
@ -109,6 +109,7 @@ class VlcPlayer(MediaPlayer):
def setup(self, display): def setup(self, display):
display.vlcWidget = QtGui.QFrame(display) display.vlcWidget = QtGui.QFrame(display)
display.vlcWidget.setFrameStyle(QtGui.QFrame.NoFrame)
# creating a basic vlc instance # creating a basic vlc instance
command_line_options = u'--no-video-title-show' command_line_options = u'--no-video-title-show'
if not display.hasAudio: if not display.hasAudio:
@ -188,6 +189,7 @@ class VlcPlayer(MediaPlayer):
display.vlcMediaPlayer.play() display.vlcMediaPlayer.play()
if not self.media_state_wait(display, vlc.State.Playing): if not self.media_state_wait(display, vlc.State.Playing):
return False return False
self.volume(display, controller.media_info.volume)
if start_time > 0: if start_time > 0:
self.seek(display, controller.media_info.start_time * 1000) self.seek(display, controller.media_info.start_time * 1000)
controller.media_info.length = int(display.vlcMediaPlayer.get_media().get_duration() / 1000) controller.media_info.length = int(display.vlcMediaPlayer.get_media().get_duration() / 1000)
@ -234,7 +236,9 @@ class VlcPlayer(MediaPlayer):
self.stop(display) self.stop(display)
self.set_visible(display, False) self.set_visible(display, False)
if not controller.seekSlider.isSliderDown(): if not controller.seekSlider.isSliderDown():
controller.seekSlider.blockSignals(True)
controller.seekSlider.setSliderPosition(display.vlcMediaPlayer.get_time()) controller.seekSlider.setSliderPosition(display.vlcMediaPlayer.get_time())
controller.seekSlider.blockSignals(False)
def get_info(self): def get_info(self):
return(translate('Media.player', 'VLC is an external player which ' return(translate('Media.player', 'VLC is an external player which '

View File

@ -423,7 +423,9 @@ class WebkitPlayer(MediaPlayer):
controller.media_info.length = length controller.media_info.length = length
controller.seekSlider.setMaximum(length) controller.seekSlider.setMaximum(length)
if not controller.seekSlider.isSliderDown(): if not controller.seekSlider.isSliderDown():
controller.seekSlider.blockSignals(True)
controller.seekSlider.setSliderPosition(currentTime) controller.seekSlider.setSliderPosition(currentTime)
controller.seekSlider.blockSignals(False)
def get_info(self): def get_info(self):
return(translate('Media.player', 'Webkit is a media player which runs ' return(translate('Media.player', 'Webkit is a media player which runs '

View File

@ -69,7 +69,7 @@ class ScreenList(object):
screen_list.screen_list = [] screen_list.screen_list = []
screen_list.display_count = 0 screen_list.display_count = 0
screen_list.screen_count_changed() 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'resized(int)'), screen_list.screen_resolution_changed)
QtCore.QObject.connect(desktop, QtCore.SIGNAL(u'screenCountChanged(int)'), screen_list.screen_count_changed) QtCore.QObject.connect(desktop, QtCore.SIGNAL(u'screenCountChanged(int)'), screen_list.screen_count_changed)
return screen_list return screen_list
@ -237,7 +237,7 @@ class ScreenList(object):
y >= size.y() and y <= (size.y() + size.height()): y >= size.y() and y <= (size.y() + size.height()):
return screen[u'number'] 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. Loads the screen size and the monitor number from the settings.
""" """

View File

@ -114,6 +114,7 @@ class ServiceManager(QtGui.QWidget):
# is a new service and has not been saved # is a new service and has not been saved
self._modified = False self._modified = False
self._fileName = u'' self._fileName = u''
self.service_has_all_original_files = True
self.serviceNoteForm = ServiceNoteForm(self.mainwindow) self.serviceNoteForm = ServiceNoteForm(self.mainwindow)
self.serviceItemEditForm = ServiceItemEditForm(self.mainwindow) self.serviceItemEditForm = ServiceItemEditForm(self.mainwindow)
self.startTimeForm = StartTimeForm(self.mainwindow) self.startTimeForm = StartTimeForm(self.mainwindow)
@ -458,7 +459,7 @@ class ServiceManager(QtGui.QWidget):
for item in list(self.serviceItems): for item in list(self.serviceItems):
self.mainwindow.incrementProgressBar() self.mainwindow.incrementProgressBar()
item[u'service_item'].remove_invalid_frames(missing_list) 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) self.serviceItems.remove(item)
else: else:
service_item = item[u'service_item'].get_service_repr(self._saveLite) service_item = item[u'service_item'].get_service_repr(self._saveLite)
@ -620,7 +621,7 @@ class ServiceManager(QtGui.QWidget):
path = os.path.join(directory, default_filename) path = os.path.join(directory, default_filename)
# SaveAs from osz to oszl is not valid as the files will be deleted # 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. # 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, fileName = QtGui.QFileDialog.getSaveFileName(self.mainwindow, UiStrings().SaveService, path,
translate('OpenLP.ServiceManager', translate('OpenLP.ServiceManager',
'OpenLP Service Files (*.osz);; OpenLP Service Files - lite (*.oszl)')) 'OpenLP Service Files (*.osz);; OpenLP Service Files - lite (*.oszl)'))
@ -693,7 +694,7 @@ class ServiceManager(QtGui.QWidget):
serviceItem.set_from_service(item) serviceItem.set_from_service(item)
else: else:
serviceItem.set_from_service(item, self.servicePath) serviceItem.set_from_service(item, self.servicePath)
self.validateItem(serviceItem) serviceItem.validate_item(self.suffixes)
self.load_item_uuid = 0 self.load_item_uuid = 0
if serviceItem.is_capable(ItemCapabilities.OnLoadUpdate): if serviceItem.is_capable(ItemCapabilities.OnLoadUpdate):
Receiver.send_message(u'%s_service_load' % serviceItem.name.lower(), serviceItem) Receiver.send_message(u'%s_service_load' % serviceItem.name.lower(), serviceItem)
@ -1032,9 +1033,12 @@ class ServiceManager(QtGui.QWidget):
""" """
# Correct order of items in array # Correct order of items in array
count = 1 count = 1
self.service_has_all_original_files = True
for item in self.serviceItems: for item in self.serviceItems:
item[u'order'] = count item[u'order'] = count
count += 1 count += 1
if not item[u'service_item'].has_original_files:
self.service_has_all_original_files = False
# Repaint the screen # Repaint the screen
self.serviceManagerList.clear() self.serviceManagerList.clear()
for itemcount, item in enumerate(self.serviceItems): for itemcount, item in enumerate(self.serviceItems):
@ -1093,18 +1097,6 @@ class ServiceManager(QtGui.QWidget):
self.serviceManagerList.setCurrentItem(treewidgetitem) self.serviceManagerList.setCurrentItem(treewidgetitem)
treewidgetitem.setExpanded(item[u'expanded']) 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): def cleanUp(self):
""" """
Empties the servicePath of temporary files on system exit. Empties the servicePath of temporary files on system exit.
@ -1144,7 +1136,7 @@ class ServiceManager(QtGui.QWidget):
Receiver.send_message(u'cursor_busy') Receiver.send_message(u'cursor_busy')
log.debug(u'regenerateServiceItems') log.debug(u'regenerateServiceItems')
# force reset of renderer as theme data has changed # force reset of renderer as theme data has changed
self.mainwindow.renderer.themedata = None self.service_has_all_original_files = True
if self.serviceItems: if self.serviceItems:
for item in self.serviceItems: for item in self.serviceItems:
item[u'selected'] = False item[u'selected'] = False

View File

@ -31,7 +31,7 @@ The :mod:`settingsform` provides a user interface for the OpenLP settings
""" """
import logging import logging
from PyQt4 import QtCore, QtGui from PyQt4 import QtGui
from openlp.core.lib import Receiver, build_icon, PluginStatus from openlp.core.lib import Receiver, build_icon, PluginStatus
from openlp.core.ui import AdvancedTab, GeneralTab, ThemesTab from openlp.core.ui import AdvancedTab, GeneralTab, ThemesTab

View File

@ -933,8 +933,7 @@ class SlideController(DisplayController):
else: else:
if not self.serviceItem.is_command(): if not self.serviceItem.is_command():
Receiver.send_message(u'live_display_show') Receiver.send_message(u'live_display_show')
Receiver.send_message(u'%s_unblank' % self.serviceItem.name.lower(), Receiver.send_message(u'%s_unblank' % self.serviceItem.name.lower(), [self.serviceItem, self.isLive])
[self.serviceItem, self.isLive])
else: else:
if hide_mode: if hide_mode:
Receiver.send_message(u'live_display_hide', hide_mode) Receiver.send_message(u'live_display_hide', hide_mode)
@ -949,13 +948,11 @@ class SlideController(DisplayController):
if self.serviceItem is not None: if self.serviceItem is not None:
if hide: if hide:
Receiver.send_message(u'live_display_hide', HideMode.Screen) Receiver.send_message(u'live_display_hide', HideMode.Screen)
Receiver.send_message(u'%s_hide' % self.serviceItem.name.lower(), Receiver.send_message(u'%s_hide' % self.serviceItem.name.lower(), [self.serviceItem, self.isLive])
[self.serviceItem, self.isLive])
else: else:
if not self.serviceItem.is_command(): if not self.serviceItem.is_command():
Receiver.send_message(u'live_display_show') Receiver.send_message(u'live_display_show')
Receiver.send_message(u'%s_unblank' % self.serviceItem.name.lower(), Receiver.send_message(u'%s_unblank' % self.serviceItem.name.lower(), [self.serviceItem, self.isLive])
[self.serviceItem, self.isLive])
else: else:
if hide: if hide:
Receiver.send_message(u'live_display_hide', HideMode.Screen) Receiver.send_message(u'live_display_hide', HideMode.Screen)
@ -995,6 +992,7 @@ class SlideController(DisplayController):
self.selectedRow = row self.selectedRow = row
self.__checkUpdateSelectedSlide(row) self.__checkUpdateSelectedSlide(row)
Receiver.send_message(u'slidecontroller_%s_changed' % self.typePrefix, row) Receiver.send_message(u'slidecontroller_%s_changed' % self.typePrefix, row)
self.display.setFocus()
def onSlideChange(self, row): def onSlideChange(self, row):
""" """

View File

@ -29,7 +29,7 @@
""" """
The :mod:`openlp.core.utils` module provides the utility libraries for OpenLP. 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 from distutils.version import LooseVersion
import logging import logging
import locale import locale
@ -277,17 +277,28 @@ def check_latest_version(current_version):
``current_version`` ``current_version``
The current version of OpenLP. 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'] version_string = current_version[u'full']
# set to prod in the distribution config file. # set to prod in the distribution config file.
settings = Settings() settings = Settings()
settings.beginGroup(u'general') 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() this_test = datetime.now().date()
settings.setValue(u'last version test', this_test) settings.setValue(u'last version test', this_test)
settings.endGroup() settings.endGroup()
if last_test != this_test: if last_test != this_test:
if current_version[u'build']: if current_version[u'build']:
req = urllib2.Request(u'http://www.openlp.org/files/nightly_version.txt')
else:
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') req = urllib2.Request(u'http://www.openlp.org/files/dev_version.txt')
else: else:
req = urllib2.Request(u'http://www.openlp.org/files/version.txt') req = urllib2.Request(u'http://www.openlp.org/files/version.txt')

View File

@ -26,5 +26,31 @@
# with this program; if not, write to the Free Software Foundation, Inc., 59 # # with this program; if not, write to the Free Software Foundation, Inc., 59 #
# Temple Place, Suite 330, Boston, MA 02111-1307 USA # # 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 from alertform import AlertForm

View File

@ -26,6 +26,10 @@
# with this program; if not, write to the Free Software Foundation, Inc., 59 # # with this program; if not, write to the Free Software Foundation, Inc., 59 #
# Temple Place, Suite 330, Boston, MA 02111-1307 USA # # 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 import logging

View File

@ -29,7 +29,7 @@
import logging 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 import Plugin, StringContent, build_icon, translate, Settings
from openlp.core.lib.ui import create_action, UiStrings from openlp.core.lib.ui import create_action, UiStrings

View File

@ -34,7 +34,7 @@ import logging
import re import re
from openlp.core.lib import translate, Settings from openlp.core.lib import translate, Settings
from openlp.plugins.bibles.lib.db import BiblesResourcesDB
log = logging.getLogger(__name__) log = logging.getLogger(__name__)

View File

@ -30,8 +30,6 @@
import logging import logging
import os import os
from PyQt4 import QtCore
from openlp.core.lib import Receiver, SettingsManager, translate, Settings from openlp.core.lib import Receiver, SettingsManager, translate, Settings
from openlp.core.utils import AppLocation, delete_file from openlp.core.utils import AppLocation, delete_file
from openlp.plugins.bibles.lib import parse_reference, get_reference_separator, LanguageSelection from openlp.plugins.bibles.lib import parse_reference, get_reference_separator, LanguageSelection

View File

@ -26,6 +26,10 @@
# with this program; if not, write to the Free Software Foundation, Inc., 59 # # with this program; if not, write to the Free Software Foundation, Inc., 59 #
# Temple Place, Suite 330, Boston, MA 02111-1307 USA # # 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 import logging

View File

@ -26,6 +26,10 @@
# with this program; if not, write to the Free Software Foundation, Inc., 59 # # with this program; if not, write to the Free Software Foundation, Inc., 59 #
# Temple Place, Suite 330, Boston, MA 02111-1307 USA # # 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 from PyQt4 import QtCore, QtGui
@ -66,6 +70,9 @@ class CustomTab(SettingsTab):
'Import missing custom slides from service files')) 'Import missing custom slides from service files'))
def onDisplayFooterCheckBoxChanged(self, check_state): def onDisplayFooterCheckBoxChanged(self, check_state):
"""
Toggle the setting for displaying the footer.
"""
self.displayFooter = False self.displayFooter = False
# we have a set value convert to True/False # we have a set value convert to True/False
if check_state == QtCore.Qt.Checked: if check_state == QtCore.Qt.Checked:

View File

@ -50,6 +50,7 @@ from lxml import etree, objectify
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
#TODO: These classes need to be refactored into a single class.
class CustomXMLBuilder(object): class CustomXMLBuilder(object):
""" """
This class builds the XML used to describe songs. This class builds the XML used to describe songs.
@ -84,11 +85,11 @@ class CustomXMLBuilder(object):
self.lyrics.setAttribute(u'language', u'en') self.lyrics.setAttribute(u'language', u'en')
self.song.appendChild(self.lyrics) 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. Add a verse to the ``<lyrics>`` tag.
``type`` ``verse_type``
A string denoting the type of verse. Possible values are "Chorus", A string denoting the type of verse. Possible values are "Chorus",
"Verse", "Bridge", and "Custom". "Verse", "Bridge", and "Custom".
@ -99,7 +100,7 @@ class CustomXMLBuilder(object):
The actual text of the verse to be stored. The actual text of the verse to be stored.
""" """
verse = self.custom_xml.createElement(u'verse') verse = self.custom_xml.createElement(u'verse')
verse.setAttribute(u'type', type) verse.setAttribute(u'type', verse_type)
verse.setAttribute(u'label', number) verse.setAttribute(u'label', number)
self.lyrics.appendChild(verse) self.lyrics.appendChild(verse)
# add data as a CDATA section to protect the XML from special chars # add data as a CDATA section to protect the XML from special chars

View File

@ -33,11 +33,11 @@ import os
from PyQt4 import QtCore, QtGui from PyQt4 import QtCore, QtGui
from openlp.core.lib import MediaManagerItem, build_icon, ItemCapabilities, SettingsManager, translate, \ 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.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 import DisplayController, Display, DisplayControllerType
from openlp.core.ui.media import get_media_players, set_media_players 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__) log = logging.getLogger(__name__)
@ -130,8 +130,7 @@ class MediaMediaItem(MediaManagerItem):
""" """
Called to reset the Live background with the media selected, Called to reset the Live background with the media selected,
""" """
self.plugin.liveController.mediaController.media_reset( self.plugin.liveController.mediaController.media_reset(self.plugin.liveController)
self.plugin.liveController)
self.resetAction.setVisible(False) self.resetAction.setVisible(False)
def videobackgroundReplaced(self): def videobackgroundReplaced(self):
@ -145,8 +144,7 @@ class MediaMediaItem(MediaManagerItem):
Called to replace Live background with the media selected. Called to replace Live background with the media selected.
""" """
if check_item_selected(self.listView, if check_item_selected(self.listView,
translate('MediaPlugin.MediaItem', translate('MediaPlugin.MediaItem', 'You must select a media file to replace the background with.')):
'You must select a media file to replace the background with.')):
item = self.listView.currentItem() item = self.listView.currentItem()
filename = item.data(QtCore.Qt.UserRole) filename = item.data(QtCore.Qt.UserRole)
if os.path.exists(filename): if os.path.exists(filename):
@ -166,8 +164,8 @@ class MediaMediaItem(MediaManagerItem):
translate('MediaPlugin.MediaItem', translate('MediaPlugin.MediaItem',
'There was a problem replacing your background, the media file "%s" no longer exists.') % filename) 'There was a problem replacing your background, the media file "%s" no longer exists.') % filename)
def generateSlideData(self, service_item, item=None, xmlVersion=False, def generateSlideData(self, service_item, item=None, xmlVersion=False, remote=False,
remote=False, context=ServiceItemContext.Live): context=ServiceItemContext.Live):
if item is None: if item is None:
item = self.listView.currentItem() item = self.listView.currentItem()
if item is None: if item is None:
@ -201,6 +199,8 @@ class MediaMediaItem(MediaManagerItem):
def initialise(self): def initialise(self):
self.listView.clear() self.listView.clear()
self.listView.setIconSize(QtCore.QSize(88, 50)) 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.loadList(SettingsManager.load_list(self.settingsSection, u'media'))
self.populateDisplayTypes() self.populateDisplayTypes()
@ -247,14 +247,13 @@ class MediaMediaItem(MediaManagerItem):
""" """
Remove a media item from the list. Remove a media item from the list.
""" """
if check_item_selected(self.listView, translate('MediaPlugin.MediaItem', if check_item_selected(self.listView,
'You must select a media file to delete.')): translate('MediaPlugin.MediaItem', 'You must select a media file to delete.')):
row_list = [item.row() for item in self.listView.selectedIndexes()] row_list = [item.row() for item in self.listView.selectedIndexes()]
row_list.sort(reverse=True) row_list.sort(reverse=True)
for row in row_list: for row in row_list:
self.listView.takeItem(row) self.listView.takeItem(row)
SettingsManager.set_list(self.settingsSection, SettingsManager.set_list(self.settingsSection, u'media', self.getFileList())
u'media', self.getFileList())
def loadList(self, media): def loadList(self, media):
# Sort the media by its filename considering language specific # Sort the media by its filename considering language specific

View File

@ -28,7 +28,6 @@
############################################################################### ###############################################################################
import logging import logging
import os
from PyQt4 import QtCore from PyQt4 import QtCore

View File

@ -77,7 +77,7 @@ class PresentationPlugin(Plugin):
if self.controllers[controller].enabled(): if self.controllers[controller].enabled():
try: try:
self.controllers[controller].start_process() self.controllers[controller].start_process()
except: except Exception:
log.warn(u'Failed to start controller process') log.warn(u'Failed to start controller process')
self.controllers[controller].available = False self.controllers[controller].available = False
self.mediaItem.buildFileMaskString() self.mediaItem.buildFileMaskString()

View File

@ -26,7 +26,6 @@
# with this program; if not, write to the Free Software Foundation, Inc., 59 # # with this program; if not, write to the Free Software Foundation, Inc., 59 #
# Temple Place, Suite 330, Boston, MA 02111-1307 USA # # Temple Place, Suite 330, Boston, MA 02111-1307 USA #
############################################################################### ###############################################################################
""" """
Forms in OpenLP are made up of two classes. One class holds all the graphical 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 elements, like buttons and lists, and the other class holds all the functional

View File

@ -26,6 +26,10 @@
# with this program; if not, write to the Free Software Foundation, Inc., 59 # # with this program; if not, write to the Free Software Foundation, Inc., 59 #
# Temple Place, Suite 330, Boston, MA 02111-1307 USA # # 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 logging
import re 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 import SongXML, VerseType, clean_song
from openlp.plugins.songs.lib.db import Book, Song, Author, Topic, MediaFile from openlp.plugins.songs.lib.db import Book, Song, Author, Topic, MediaFile
from openlp.plugins.songs.lib.ui import SongStrings 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__) log = logging.getLogger(__name__)
@ -56,7 +60,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
""" """
Constructor Constructor
""" """
QtGui.QDialog.__init__(self, parent) super(EditSongForm, self).__init__(parent)
self.mediaitem = mediaitem self.mediaitem = mediaitem
self.song = None self.song = None
# can this be automated? # can this be automated?
@ -113,12 +117,18 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
self.whitespace = re.compile(r'\W+', re.UNICODE) self.whitespace = re.compile(r'\W+', re.UNICODE)
def initialise(self): def initialise(self):
"""
Set up the form for when it is displayed.
"""
self.verseEditButton.setEnabled(False) self.verseEditButton.setEnabled(False)
self.verseDeleteButton.setEnabled(False) self.verseDeleteButton.setEnabled(False)
self.authorRemoveButton.setEnabled(False) self.authorRemoveButton.setEnabled(False)
self.topicRemoveButton.setEnabled(False) self.topicRemoveButton.setEnabled(False)
def loadAuthors(self): def loadAuthors(self):
"""
Load the authors from the database into the combobox.
"""
authors = self.manager.get_all_objects(Author, authors = self.manager.get_all_objects(Author,
order_by_ref=Author.display_name) order_by_ref=Author.display_name)
self.authorsComboBox.clear() self.authorsComboBox.clear()
@ -132,14 +142,23 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
set_case_insensitive_completer(self.authors, self.authorsComboBox) set_case_insensitive_completer(self.authors, self.authorsComboBox)
def loadTopics(self): def loadTopics(self):
"""
Load the topics into the combobox.
"""
self.topics = [] self.topics = []
self.__loadObjects(Topic, self.topicsComboBox, self.topics) self.__loadObjects(Topic, self.topicsComboBox, self.topics)
def loadBooks(self): def loadBooks(self):
"""
Load the song books into the combobox
"""
self.books = [] self.books = []
self.__loadObjects(Book, self.songBookComboBox, self.books) self.__loadObjects(Book, self.songBookComboBox, self.books)
def __loadObjects(self, cls, combo, cache): 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) objects = self.manager.get_all_objects(cls, order_by_ref=cls.name)
combo.clear() combo.clear()
combo.addItem(u'') combo.addItem(u'')
@ -151,6 +170,9 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
set_case_insensitive_completer(cache, combo) set_case_insensitive_completer(cache, combo)
def loadThemes(self, theme_list): def loadThemes(self, theme_list):
"""
Load the themes into a combobox.
"""
self.themeComboBox.clear() self.themeComboBox.clear()
self.themeComboBox.addItem(u'') self.themeComboBox.addItem(u'')
self.themes = theme_list self.themes = theme_list
@ -158,6 +180,9 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
set_case_insensitive_completer(self.themes, self.themeComboBox) set_case_insensitive_completer(self.themes, self.themeComboBox)
def loadMediaFiles(self): def loadMediaFiles(self):
"""
Load the media files into a combobox.
"""
self.audioAddFromMediaButton.setVisible(False) self.audioAddFromMediaButton.setVisible(False)
for plugin in self.parent().pluginManager.plugins: for plugin in self.parent().pluginManager.plugins:
if plugin.name == u'media' and plugin.status == PluginStatus.Active: if plugin.name == u'media' and plugin.status == PluginStatus.Active:
@ -166,6 +191,9 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
break break
def newSong(self): def newSong(self):
"""
Blank the edit form out in preparation for a new song.
"""
log.debug(u'New Song') log.debug(u'New Song')
self.song = None self.song = None
self.initialise() self.initialise()
@ -313,6 +341,9 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
self.verseListWidget.repaint() self.verseListWidget.repaint()
def onAuthorAddButtonClicked(self): 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()) item = int(self.authorsComboBox.currentIndex())
text = self.authorsComboBox.currentText().strip(u' \r\n\t') text = self.authorsComboBox.currentText().strip(u' \r\n\t')
# This if statement is for OS X, which doesn't seem to work well with # 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) self.authorsListView.addItem(author_item)
def onAuthorsListViewClicked(self): 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: if self.authorsListView.count() > 1:
self.authorRemoveButton.setEnabled(True) self.authorRemoveButton.setEnabled(True)
def onAuthorRemoveButtonClicked(self): def onAuthorRemoveButtonClicked(self):
"""
Remove the author from the list when the delete button is clicked.
"""
self.authorRemoveButton.setEnabled(False) self.authorRemoveButton.setEnabled(False)
item = self.authorsListView.currentItem() item = self.authorsListView.currentItem()
row = self.authorsListView.row(item) row = self.authorsListView.row(item)

View File

@ -28,7 +28,7 @@
############################################################################### ###############################################################################
import re import re
from PyQt4 import QtGui, QtCore from PyQt4 import QtGui
from openlp.core.lib import translate from openlp.core.lib import translate
from openlp.core.utils import CONTROL_CHARS, locale_direct_compare from openlp.core.utils import CONTROL_CHARS, locale_direct_compare

View File

@ -36,7 +36,6 @@ import re
from sqlalchemy import Column, ForeignKey, Table, types from sqlalchemy import Column, ForeignKey, Table, types
from sqlalchemy.orm import mapper, relation, reconstructor from sqlalchemy.orm import mapper, relation, reconstructor
from sqlalchemy.sql.expression import func from sqlalchemy.sql.expression import func
from PyQt4 import QtCore
from openlp.core.lib.db import BaseModel, init_db from openlp.core.lib.db import BaseModel, init_db

View File

@ -30,8 +30,6 @@
The :mod:`dreambeamimport` module provides the functionality for importing The :mod:`dreambeamimport` module provides the functionality for importing
DreamBeam songs into the OpenLP database. DreamBeam songs into the OpenLP database.
""" """
import os
import sys
import logging import logging
from lxml import etree, objectify from lxml import etree, objectify

View File

@ -30,8 +30,6 @@
The :mod:`mediashoutimport` module provides the functionality for importing The :mod:`mediashoutimport` module provides the functionality for importing
a MediaShout database into the OpenLP database. a MediaShout database into the OpenLP database.
""" """
import re
import os
import pyodbc import pyodbc
from openlp.core.lib import translate from openlp.core.lib import translate

View File

@ -31,9 +31,7 @@ The :mod:`songproimport` module provides the functionality for importing SongPro
songs into the OpenLP database. songs into the OpenLP database.
""" """
import re import re
import os
from openlp.core.lib import translate
from openlp.plugins.songs.lib import strip_rtf from openlp.plugins.songs.lib import strip_rtf
from openlp.plugins.songs.lib.songimport import SongImport from openlp.plugins.songs.lib.songimport import SongImport

View File

@ -107,13 +107,13 @@ class WowImport(SongImport):
""" """
if isinstance(self.importSource, list): if isinstance(self.importSource, list):
self.importWizard.progressBar.setMaximum(len(self.importSource)) self.importWizard.progressBar.setMaximum(len(self.importSource))
for file in self.importSource: for source in self.importSource:
if self.stopImportFlag: if self.stopImportFlag:
return return
self.setDefaults() self.setDefaults()
song_data = open(file, 'rb') song_data = open(source, 'rb')
if song_data.read(19) != u'WoW File\nSong Words': 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.')))) ('Invalid Words of Worship song file. Missing "Wow File\\nSong Words" header.'))))
continue continue
# Seek to byte which stores number of blocks in the song # 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)) no_of_blocks = ord(song_data.read(1))
song_data.seek(66) song_data.seek(66)
if song_data.read(16) != u'CSongDoc::CBlock': 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.')))) ('Invalid Words of Worship song file. Missing "CSongDoc::CBlock" string.'))))
continue continue
# Seek to the beginning of the first block # Seek to the beginning of the first block
@ -150,9 +150,9 @@ class WowImport(SongImport):
copyright_length = ord(song_data.read(1)) copyright_length = ord(song_data.read(1))
if copyright_length: if copyright_length:
self.addCopyright(unicode(song_data.read(copyright_length), u'cp1252')) 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 # Get the song title
self.title = file_name.rpartition(u'.')[0] self.title = file_name.rpartition(u'.')[0]
song_data.close() song_data.close()
if not self.finish(): if not self.finish():
self.logError(file) self.logError(source)

View File

@ -26,6 +26,10 @@
# with this program; if not, write to the Free Software Foundation, Inc., 59 # # with this program; if not, write to the Free Software Foundation, Inc., 59 #
# Temple Place, Suite 330, Boston, MA 02111-1307 USA # # 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 logging
import os import os

View File

@ -38,6 +38,7 @@ modules, simply run this script::
""" """
import os import os
import sys import sys
from distutils.version import LooseVersion
is_win = sys.platform.startswith('win') is_win = sys.platform.startswith('win')
@ -89,15 +90,13 @@ OPTIONAL_MODULES = [
w = sys.stdout.write w = sys.stdout.write
def check_vers(version, required, text): def check_vers(version, required, text):
if type(version) is str: if type(version) is not str:
version = version.split('.') version = '.'.join(map(str, version))
version = map(int, version) if type(required) is not str:
if type(required) is str: required = '.'.join(map(str, required))
required = required.split('.') w(' %s >= %s ... ' % (text, required))
required = map(int, required) if LooseVersion(version) >= LooseVersion(required):
w(' %s >= %s ... ' % (text, '.'.join(map(str, required)))) w(version + os.linesep)
if version >= required:
w('.'.join(map(str, version)) + os.linesep)
return True return True
else: else:
w('FAIL' + os.linesep) w('FAIL' + os.linesep)

Binary file not shown.

After

Width:  |  Height:  |  Size: 213 KiB

View File

@ -3,7 +3,7 @@ Package to test the openlp.core.lib package.
""" """
from unittest import TestCase from unittest import TestCase
from mock import MagicMock, patch, call from mock import MagicMock, patch
from openlp.core.lib import str_to_bool, translate, check_directory_exists, get_text_file_string, build_icon from openlp.core.lib import str_to_bool, translate, check_directory_exists, get_text_file_string, build_icon

View 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'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'

View 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_()

Binary file not shown.

After

Width:  |  Height:  |  Size: 213 KiB

View File

@ -34,4 +34,4 @@ from openlp.core.ui.mainwindow import MainWindow
def test_start_app(openlpapp): def test_start_app(openlpapp):
assert type(openlpapp) == OpenLP assert type(openlpapp) == OpenLP
assert type(openlpapp.mainWindow) == MainWindow assert type(openlpapp.mainWindow) == MainWindow
assert unicode(openlpapp.mainWindow.windowTitle()) == u'OpenLP 2.0' assert unicode(openlpapp.mainWindow.windowTitle()) == u'OpenLP 2.1'