forked from openlp/openlp
HEAD
This commit is contained in:
commit
831f2f3b69
|
@ -28,3 +28,4 @@ tests.kdev4
|
|||
*.nja
|
||||
*.orig
|
||||
__pycache__
|
||||
*.dll
|
||||
|
|
|
@ -43,7 +43,7 @@ from traceback import format_exception
|
|||
|
||||
from PyQt4 import QtCore, QtGui
|
||||
|
||||
from openlp.core.common import Registry, AppLocation, Settings, UiStrings, check_directory_exists
|
||||
from openlp.core.common import Registry, OpenLPMixin, AppLocation, Settings, UiStrings, check_directory_exists
|
||||
from openlp.core.lib import ScreenList
|
||||
from openlp.core.resources import qInitResources
|
||||
from openlp.core.ui.mainwindow import MainWindow
|
||||
|
@ -58,6 +58,7 @@ __all__ = ['OpenLP', 'main']
|
|||
|
||||
|
||||
log = logging.getLogger()
|
||||
|
||||
NT_REPAIR_STYLESHEET = """
|
||||
QMainWindow::separator
|
||||
{
|
||||
|
@ -81,7 +82,7 @@ QToolBar
|
|||
"""
|
||||
|
||||
|
||||
class OpenLP(QtGui.QApplication):
|
||||
class OpenLP(OpenLPMixin, QtGui.QApplication):
|
||||
"""
|
||||
The core application class. This class inherits from Qt's QApplication
|
||||
class in order to provide the core of the application.
|
||||
|
@ -101,6 +102,8 @@ class OpenLP(QtGui.QApplication):
|
|||
def run(self, args):
|
||||
"""
|
||||
Run the OpenLP application.
|
||||
|
||||
:param args: Some Args
|
||||
"""
|
||||
self.is_event_loop_active = False
|
||||
# On Windows, the args passed into the constructor are ignored. Not very handy, so set the ones we want to use.
|
||||
|
@ -137,6 +140,7 @@ class OpenLP(QtGui.QApplication):
|
|||
self.main_window = MainWindow()
|
||||
Registry().execute('bootstrap_initialise')
|
||||
Registry().execute('bootstrap_post_set_up')
|
||||
Registry().initialise = False
|
||||
self.main_window.show()
|
||||
if show_splash:
|
||||
# now kill the splashscreen
|
||||
|
@ -171,25 +175,20 @@ class OpenLP(QtGui.QApplication):
|
|||
self.shared_memory.create(1)
|
||||
return False
|
||||
|
||||
def hook_exception(self, exctype, value, traceback):
|
||||
def hook_exception(self, exc_type, value, traceback):
|
||||
"""
|
||||
Add an exception hook so that any uncaught exceptions are displayed in this window rather than somewhere where
|
||||
users cannot see it and cannot report when we encounter these problems.
|
||||
|
||||
``exctype``
|
||||
The class of exception.
|
||||
|
||||
``value``
|
||||
The actual exception object.
|
||||
|
||||
``traceback``
|
||||
A traceback object with the details of where the exception occurred.
|
||||
:param exc_type: The class of exception.
|
||||
:param value: The actual exception object.
|
||||
:param traceback: A traceback object with the details of where the exception occurred.
|
||||
"""
|
||||
# We can't log.exception here because the last exception no longer exists, we're actually busy handling it.
|
||||
log.critical(''.join(format_exception(exctype, value, traceback)))
|
||||
log.critical(''.join(format_exception(exc_type, value, traceback)))
|
||||
if not hasattr(self, 'exception_form'):
|
||||
self.exception_form = ExceptionForm()
|
||||
self.exception_form.exception_text_edit.setPlainText(''.join(format_exception(exctype, value, traceback)))
|
||||
self.exception_form.exception_text_edit.setPlainText(''.join(format_exception(exc_type, value, traceback)))
|
||||
self.set_normal_cursor()
|
||||
self.exception_form.exec_()
|
||||
|
||||
|
@ -197,7 +196,6 @@ class OpenLP(QtGui.QApplication):
|
|||
"""
|
||||
Wrapper to make ProcessEvents visible and named correctly
|
||||
"""
|
||||
log.debug('processing event flush')
|
||||
self.processEvents()
|
||||
|
||||
def set_busy_cursor(self):
|
||||
|
@ -217,6 +215,8 @@ class OpenLP(QtGui.QApplication):
|
|||
def event(self, event):
|
||||
"""
|
||||
Enables direct file opening on OS X
|
||||
|
||||
:param event: The event
|
||||
"""
|
||||
if event.type() == QtCore.QEvent.FileOpen:
|
||||
file_name = event.file()
|
||||
|
@ -230,10 +230,12 @@ class OpenLP(QtGui.QApplication):
|
|||
def set_up_logging(log_path):
|
||||
"""
|
||||
Setup our logging using log_path
|
||||
|
||||
:param log_path: the path
|
||||
"""
|
||||
check_directory_exists(log_path, True)
|
||||
filename = os.path.join(log_path, 'openlp.log')
|
||||
logfile = logging.FileHandler(filename, 'w')
|
||||
logfile = logging.FileHandler(filename, 'w', encoding="UTF-8")
|
||||
logfile.setFormatter(logging.Formatter('%(asctime)s %(name)-55s %(levelname)-8s %(message)s'))
|
||||
log.addHandler(logfile)
|
||||
if log.isEnabledFor(logging.DEBUG):
|
||||
|
@ -243,7 +245,8 @@ def set_up_logging(log_path):
|
|||
def main(args=None):
|
||||
"""
|
||||
The main function which parses command line options and then runs
|
||||
the PyQt4 Application.
|
||||
|
||||
:param args: Some args
|
||||
"""
|
||||
# Set up command line options.
|
||||
usage = 'Usage: %prog [options] [qt-options]'
|
||||
|
|
|
@ -38,7 +38,7 @@ import traceback
|
|||
|
||||
from PyQt4 import QtCore
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
log = logging.getLogger(__name__+'.__init__')
|
||||
|
||||
|
||||
FIRST_CAMEL_REGEX = re.compile('(.)([A-Z][a-z]+)')
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
###############################################################################
|
||||
# OpenLP - Open Source Lyrics Projection #
|
||||
# --------------------------------------------------------------------------- #
|
||||
# Copyright (c) 2008-2013 Raoul Snyman #
|
||||
# Portions copyright (c) 2008-2013 Tim Bentley, Gerald Britton, Jonathan #
|
||||
# Copyright (c) 2008-2014 Raoul Snyman #
|
||||
# Portions copyright (c) 2008-2014 Tim Bentley, Gerald Britton, Jonathan #
|
||||
# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, #
|
||||
# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. #
|
||||
# Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru, #
|
||||
|
@ -33,7 +33,8 @@ import logging
|
|||
import inspect
|
||||
|
||||
from openlp.core.common import trace_error_handler
|
||||
DO_NOT_TRACE_EVENTS = ['timerEvent', 'paintEvent']
|
||||
DO_NOT_TRACE_EVENTS = ['timerEvent', 'paintEvent', 'drag_enter_event', 'drop_event', 'on_controller_size_changed',
|
||||
'preview_size_changed', 'resizeEvent']
|
||||
|
||||
|
||||
class OpenLPMixin(object):
|
||||
|
@ -68,13 +69,13 @@ class OpenLPMixin(object):
|
|||
|
||||
def log_debug(self, message):
|
||||
"""
|
||||
Common log debug handler which prints the calling path
|
||||
Common log debug handler
|
||||
"""
|
||||
self.logger.debug(message)
|
||||
|
||||
def log_info(self, message):
|
||||
"""
|
||||
Common log info handler which prints the calling path
|
||||
Common log info handler
|
||||
"""
|
||||
self.logger.info(message)
|
||||
|
||||
|
|
|
@ -63,6 +63,7 @@ class Registry(object):
|
|||
registry.service_list = {}
|
||||
registry.functions_list = {}
|
||||
registry.running_under_test = False
|
||||
registry.initialising = True
|
||||
# Allow the tests to remove Registry entries but not the live system
|
||||
if 'nose' in sys.argv[0]:
|
||||
registry.running_under_test = True
|
||||
|
@ -78,9 +79,10 @@ class Registry(object):
|
|||
if key in self.service_list:
|
||||
return self.service_list[key]
|
||||
else:
|
||||
trace_error_handler(log)
|
||||
log.error('Service %s not found in list' % key)
|
||||
#raise KeyError('Service %s not found in list' % key)
|
||||
if not self.initialising:
|
||||
trace_error_handler(log)
|
||||
log.error('Service %s not found in list' % key)
|
||||
raise KeyError('Service %s not found in list' % key)
|
||||
|
||||
def register(self, key, reference):
|
||||
"""
|
||||
|
@ -170,5 +172,5 @@ class Registry(object):
|
|||
log.exception('Exception for function %s', function)
|
||||
else:
|
||||
trace_error_handler(log)
|
||||
log.error("Event %s not called by not registered" % event)
|
||||
log.error("Event %s called but not registered" % event)
|
||||
return results
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
###############################################################################
|
||||
# OpenLP - Open Source Lyrics Projection #
|
||||
# --------------------------------------------------------------------------- #
|
||||
# Copyright (c) 2008-2013 Raoul Snyman #
|
||||
# Portions copyright (c) 2008-2013 Tim Bentley, Gerald Britton, Jonathan #
|
||||
# Copyright (c) 2008-2014 Raoul Snyman #
|
||||
# Portions copyright (c) 2008-2014 Tim Bentley, Gerald Britton, Jonathan #
|
||||
# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, #
|
||||
# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. #
|
||||
# Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru, #
|
||||
|
|
|
@ -39,7 +39,7 @@ from PyQt4 import QtCore, QtGui, Qt
|
|||
|
||||
from openlp.core.common import translate
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
log = logging.getLogger(__name__+'.__init__')
|
||||
|
||||
|
||||
class ServiceItemContext(object):
|
||||
|
@ -89,8 +89,8 @@ def get_text_file_string(text_file):
|
|||
returns False. If there is an error loading the file or the content can't be decoded then the function will return
|
||||
None.
|
||||
|
||||
``textfile``
|
||||
The name of the file.
|
||||
:param text_file: The name of the file.
|
||||
:return The file as a single string
|
||||
"""
|
||||
if not os.path.isfile(text_file):
|
||||
return False
|
||||
|
@ -114,8 +114,8 @@ def str_to_bool(string_value):
|
|||
"""
|
||||
Convert a string version of a boolean into a real boolean.
|
||||
|
||||
``string_value``
|
||||
The string value to examine and convert to a boolean type.
|
||||
:param string_value: The string value to examine and convert to a boolean type.
|
||||
:return The correct boolean value
|
||||
"""
|
||||
if isinstance(string_value, bool):
|
||||
return string_value
|
||||
|
@ -127,9 +127,10 @@ def build_icon(icon):
|
|||
Build a QIcon instance from an existing QIcon, a resource location, or a physical file location. If the icon is a
|
||||
QIcon instance, that icon is simply returned. If not, it builds a QIcon instance from the resource or file name.
|
||||
|
||||
``icon``
|
||||
:param icon:
|
||||
The icon to build. This can be a QIcon, a resource string in the form ``:/resource/file.png``, or a file
|
||||
location like ``/path/to/file.png``. However, the **recommended** way is to specify a resource string.
|
||||
:return The build icon.
|
||||
"""
|
||||
button_icon = QtGui.QIcon()
|
||||
if isinstance(icon, QtGui.QIcon):
|
||||
|
@ -148,8 +149,7 @@ def image_to_byte(image):
|
|||
"""
|
||||
Resize an image to fit on the current screen for the web and returns it as a byte stream.
|
||||
|
||||
``image``
|
||||
The image to converted.
|
||||
:param image: The image to converted.
|
||||
"""
|
||||
log.debug('image_to_byte - start')
|
||||
byte_array = QtCore.QByteArray()
|
||||
|
@ -166,18 +166,12 @@ def create_thumb(image_path, thumb_path, return_icon=True, size=None):
|
|||
"""
|
||||
Create a thumbnail from the given image path and depending on ``return_icon`` it returns an icon from this thumb.
|
||||
|
||||
``image_path``
|
||||
The image file to create the icon from.
|
||||
|
||||
``thumb_path``
|
||||
The filename to save the thumbnail to.
|
||||
|
||||
``return_icon``
|
||||
States if an icon should be build and returned from the thumb. Defaults to ``True``.
|
||||
|
||||
``size``
|
||||
Allows to state a own size (QtCore.QSize) to use. Defaults to ``None``, which means that a default height of 88
|
||||
is used.
|
||||
:param image_path: The image file to create the icon from.
|
||||
:param thumb_path: The filename to save the thumbnail to.
|
||||
:param return_icon: States if an icon should be build and returned from the thumb. Defaults to ``True``.
|
||||
:param size: Allows to state a own size (QtCore.QSize) to use. Defaults to ``None``, which means that a default
|
||||
height of 88 is used.
|
||||
:return The final icon.
|
||||
"""
|
||||
ext = os.path.splitext(thumb_path)[1].lower()
|
||||
reader = QtGui.QImageReader(image_path)
|
||||
|
@ -191,9 +185,9 @@ def create_thumb(image_path, thumb_path, return_icon=True, size=None):
|
|||
if not return_icon:
|
||||
return
|
||||
if os.path.exists(thumb_path):
|
||||
return build_icon(str(thumb_path))
|
||||
return build_icon(thumb_path)
|
||||
# Fallback for files with animation support.
|
||||
return build_icon(str(image_path))
|
||||
return build_icon(image_path)
|
||||
|
||||
|
||||
def validate_thumb(file_path, thumb_path):
|
||||
|
@ -201,11 +195,9 @@ def validate_thumb(file_path, thumb_path):
|
|||
Validates whether an file's thumb still exists and if is up to date. **Note**, you must **not** call this function,
|
||||
before checking the existence of the file.
|
||||
|
||||
``file_path``
|
||||
The path to the file. The file **must** exist!
|
||||
|
||||
``thumb_path``
|
||||
The path to the thumb.
|
||||
:param file_path: The path to the file. The file **must** exist!
|
||||
:param thumb_path: The path to the thumb.
|
||||
:return True, False if the image has changed since the thumb was created.
|
||||
"""
|
||||
if not os.path.exists(thumb_path):
|
||||
return False
|
||||
|
@ -218,19 +210,12 @@ def resize_image(image_path, width, height, background='#000000'):
|
|||
"""
|
||||
Resize an image to fit on the current screen.
|
||||
|
||||
``image_path``
|
||||
The path to the image to resize.
|
||||
|
||||
``width``
|
||||
The new image width.
|
||||
|
||||
``height``
|
||||
The new image height.
|
||||
|
||||
``background``
|
||||
The background colour. Defaults to black.
|
||||
|
||||
DO NOT REMOVE THE DEFAULT BACKGROUND VALUE!
|
||||
|
||||
:param image_path: The path to the image to resize.
|
||||
:param width: The new image width.
|
||||
:param height: The new image height.
|
||||
:param background: The background colour. Defaults to black.
|
||||
"""
|
||||
log.debug('resize_image - start')
|
||||
reader = QtGui.QImageReader(image_path)
|
||||
|
@ -265,11 +250,8 @@ def check_item_selected(list_widget, message):
|
|||
"""
|
||||
Check if a list item is selected so an action may be performed on it
|
||||
|
||||
``list_widget``
|
||||
The list to check for selected items
|
||||
|
||||
``message``
|
||||
The message to give the user if no item is selected
|
||||
:param list_widget: The list to check for selected items
|
||||
:param message: The message to give the user if no item is selected
|
||||
"""
|
||||
if not list_widget.selectedIndexes():
|
||||
QtGui.QMessageBox.information(list_widget.parent(),
|
||||
|
@ -281,6 +263,8 @@ def check_item_selected(list_widget, message):
|
|||
def clean_tags(text):
|
||||
"""
|
||||
Remove Tags from text for display
|
||||
|
||||
:param text: Text to be cleaned
|
||||
"""
|
||||
text = text.replace('<br>', '\n')
|
||||
text = text.replace('{br}', '\n')
|
||||
|
@ -294,6 +278,8 @@ def clean_tags(text):
|
|||
def expand_tags(text):
|
||||
"""
|
||||
Expand tags HTML for display
|
||||
|
||||
:param text: The text to be expanded.
|
||||
"""
|
||||
for tag in FormattingTags.get_html_tags():
|
||||
text = text.replace(tag['start tag'], tag['start html'])
|
||||
|
@ -304,11 +290,11 @@ def expand_tags(text):
|
|||
def create_separated_list(string_list):
|
||||
"""
|
||||
Returns a string that represents a join of a list of strings with a localized separator. This function corresponds
|
||||
|
||||
to QLocale::createSeparatedList which was introduced in Qt 4.8 and implements the algorithm from
|
||||
http://www.unicode.org/reports/tr35/#ListPatterns
|
||||
|
||||
``string_list``
|
||||
List of unicode strings
|
||||
:param string_list: List of unicode strings
|
||||
"""
|
||||
if LooseVersion(Qt.PYQT_VERSION_STR) >= LooseVersion('4.9') and \
|
||||
LooseVersion(Qt.qVersion()) >= LooseVersion('4.8'):
|
||||
|
|
|
@ -332,8 +332,7 @@ class MediaManagerItem(QtGui.QWidget):
|
|||
"""
|
||||
Turn file from Drag and Drop into an array so the Validate code can run it.
|
||||
|
||||
``data``
|
||||
A dictionary containing the list of files to be loaded and the target
|
||||
:param data: A dictionary containing the list of files to be loaded and the target
|
||||
"""
|
||||
new_files = []
|
||||
error_shown = False
|
||||
|
@ -353,9 +352,8 @@ class MediaManagerItem(QtGui.QWidget):
|
|||
def dnd_move_internal(self, target):
|
||||
"""
|
||||
Handle internal moving of media manager items
|
||||
|
||||
``target``
|
||||
The target of the DnD action
|
||||
s
|
||||
:param target: The target of the DnD action
|
||||
"""
|
||||
pass
|
||||
|
||||
|
@ -364,11 +362,8 @@ class MediaManagerItem(QtGui.QWidget):
|
|||
Process a list for files either from the File Dialog or from Drag and
|
||||
Drop
|
||||
|
||||
``files``
|
||||
The files to be loaded.
|
||||
|
||||
``target_group``
|
||||
The QTreeWidgetItem of the group that will be the parent of the added files
|
||||
:param files: The files to be loaded.
|
||||
:param target_group: The QTreeWidgetItem of the group that will be the parent of the added files
|
||||
"""
|
||||
names = []
|
||||
full_list = []
|
||||
|
@ -388,7 +383,7 @@ class MediaManagerItem(QtGui.QWidget):
|
|||
if target_group is None:
|
||||
self.list_view.clear()
|
||||
self.load_list(full_list, target_group)
|
||||
last_dir = os.path.split(str(files[0]))[0]
|
||||
last_dir = os.path.split(files[0])[0]
|
||||
Settings().setValue(self.settings_section + '/last directory', last_dir)
|
||||
Settings().setValue('%s/%s files' % (self.settings_section, self.settings_section), self.get_file_list())
|
||||
if duplicates_found:
|
||||
|
@ -399,6 +394,8 @@ class MediaManagerItem(QtGui.QWidget):
|
|||
def context_menu(self, point):
|
||||
"""
|
||||
Display a context menu
|
||||
|
||||
:param point: The point the cursor was at
|
||||
"""
|
||||
item = self.list_view.itemAt(point)
|
||||
# Decide if we have to show the context menu or not.
|
||||
|
@ -422,6 +419,9 @@ class MediaManagerItem(QtGui.QWidget):
|
|||
def load_list(self, load_list, target_group):
|
||||
"""
|
||||
Load a list. Needs to be implemented by the plugin.
|
||||
|
||||
:param load_list: List object to load
|
||||
:param target_group: Group to load
|
||||
"""
|
||||
raise NotImplementedError('MediaManagerItem.loadList needs to be defined by the plugin')
|
||||
|
||||
|
@ -454,6 +454,11 @@ class MediaManagerItem(QtGui.QWidget):
|
|||
context=ServiceItemContext.Live):
|
||||
"""
|
||||
Generate the slide data. Needs to be implemented by the plugin.
|
||||
:param service_item: The service Item to be processed
|
||||
:param item: The database item to be used to build the service item
|
||||
:param xml_version:
|
||||
:param remote: Was this remote triggered (False)
|
||||
:param context: The service context
|
||||
"""
|
||||
raise NotImplementedError('MediaManagerItem.generate_slide_data needs to be defined by the plugin')
|
||||
|
||||
|
@ -471,12 +476,14 @@ class MediaManagerItem(QtGui.QWidget):
|
|||
Allows the change of current item in the list to be actioned
|
||||
"""
|
||||
if Settings().value('advanced/single click preview') and self.quick_preview_allowed \
|
||||
and self.list_view.selectedIndexes() and self.auto_select_id == -1:
|
||||
and self.list_view.selectedIndexes() and self.auto_select_id == -1:
|
||||
self.on_preview_click(True)
|
||||
|
||||
def on_preview_click(self, keep_focus=False):
|
||||
"""
|
||||
Preview an item by building a service item then adding that service item to the preview slide controller.
|
||||
|
||||
:param keep_focus: Do we keep focus (False)
|
||||
"""
|
||||
if not self.list_view.selectedIndexes() and not self.remote_triggered:
|
||||
QtGui.QMessageBox.information(self, UiStrings().NISp,
|
||||
|
@ -506,14 +513,16 @@ class MediaManagerItem(QtGui.QWidget):
|
|||
"""
|
||||
Remote Call wrapper
|
||||
|
||||
``message``
|
||||
The passed data item_id:Remote.
|
||||
:param message: The passed data item_id:Remote.
|
||||
"""
|
||||
self.go_live(message[0], remote=message[1])
|
||||
|
||||
def go_live(self, item_id=None, remote=False):
|
||||
"""
|
||||
Make the currently selected item go live.
|
||||
|
||||
:param item_id: item to make live
|
||||
:param remote: From Remote
|
||||
"""
|
||||
log.debug('%s Live requested', self.plugin.name)
|
||||
item = None
|
||||
|
@ -530,6 +539,8 @@ class MediaManagerItem(QtGui.QWidget):
|
|||
def create_item_from_id(self, item_id):
|
||||
"""
|
||||
Create a media item from an item id.
|
||||
|
||||
:param item_id: Id to make live
|
||||
"""
|
||||
item = QtGui.QListWidgetItem()
|
||||
item.setData(QtCore.Qt.UserRole, item_id)
|
||||
|
@ -558,14 +569,17 @@ class MediaManagerItem(QtGui.QWidget):
|
|||
"""
|
||||
Remote Call wrapper
|
||||
|
||||
``message``
|
||||
The passed data item:Remote.
|
||||
:param message: The passed data item:Remote.
|
||||
"""
|
||||
self.add_to_service(message[0], remote=message[1])
|
||||
|
||||
def add_to_service(self, item=None, replace=None, remote=False):
|
||||
"""
|
||||
Add this item to the current service.
|
||||
|
||||
:param item: Item to be processed
|
||||
:param replace: Replace the existing item
|
||||
:param remote: Triggered from remote
|
||||
"""
|
||||
service_item = self.build_service_item(item, True, remote=remote, context=ServiceItemContext.Service)
|
||||
if service_item:
|
||||
|
@ -598,6 +612,10 @@ class MediaManagerItem(QtGui.QWidget):
|
|||
def build_service_item(self, item=None, xml_version=False, remote=False, context=ServiceItemContext.Live):
|
||||
"""
|
||||
Common method for generating a service item
|
||||
:param item: Service Item to be built.
|
||||
:param xml_version: version of XML (False)
|
||||
:param remote: Remote triggered (False)
|
||||
:param context: The context on which this is called
|
||||
"""
|
||||
service_item = ServiceItem(self.plugin)
|
||||
service_item.add_icon(self.plugin.icon_path)
|
||||
|
@ -611,8 +629,7 @@ class MediaManagerItem(QtGui.QWidget):
|
|||
Method to add processing when a service has been loaded and individual service items need to be processed by the
|
||||
plugins.
|
||||
|
||||
``item``
|
||||
The item to be processed and returned.
|
||||
:param item: The item to be processed and returned.
|
||||
"""
|
||||
return item
|
||||
|
||||
|
@ -634,11 +651,8 @@ class MediaManagerItem(QtGui.QWidget):
|
|||
"""
|
||||
Utility method to check items being submitted for slide generation.
|
||||
|
||||
``item``
|
||||
The item to check.
|
||||
|
||||
``remote_item``
|
||||
The id to assign if the slide generation was remotely triggered.
|
||||
:param item: The item to check.
|
||||
:param remote_item: The id to assign if the slide generation was remotely triggered.
|
||||
"""
|
||||
if item is None:
|
||||
if self.remote_triggered is None:
|
||||
|
@ -665,6 +679,9 @@ class MediaManagerItem(QtGui.QWidget):
|
|||
def search(self, string, show_error=True):
|
||||
"""
|
||||
Performs a plugin specific search for items containing ``string``
|
||||
|
||||
:param string: String to be displayed
|
||||
:param show_error: Should the error be shown (True)
|
||||
"""
|
||||
raise NotImplementedError('Plugin.search needs to be defined by the plugin')
|
||||
|
||||
|
|
|
@ -27,18 +27,15 @@
|
|||
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||
###############################################################################
|
||||
|
||||
import logging
|
||||
|
||||
from PyQt4 import QtGui, QtCore, QtWebKit
|
||||
|
||||
from openlp.core.common import Registry, Settings
|
||||
from openlp.core.common import Registry, OpenLPMixin, RegistryMixin, Settings
|
||||
from openlp.core.lib import FormattingTags, ImageSource, ItemCapabilities, ScreenList, ServiceItem, expand_tags, \
|
||||
build_lyrics_format_css, build_lyrics_outline_css
|
||||
from openlp.core.common import ThemeLevel
|
||||
from openlp.core.ui import MainDisplay
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
VERSE = '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' \
|
||||
|
@ -50,43 +47,47 @@ VERSE_FOR_LINE_COUNT = '\n'.join(map(str, range(100)))
|
|||
FOOTER = ['Arky Arky (Unknown)', 'Public Domain', 'CCLI 123456']
|
||||
|
||||
|
||||
class Renderer(object):
|
||||
class Renderer(OpenLPMixin, RegistryMixin):
|
||||
"""
|
||||
Class to pull all Renderer interactions into one place. The plugins will call helper methods to do the rendering but
|
||||
this class will provide display defense code.
|
||||
"""
|
||||
log.info('Renderer Loaded')
|
||||
|
||||
def __init__(self):
|
||||
"""
|
||||
Initialise the renderer.
|
||||
"""
|
||||
log.debug('Initialisation started')
|
||||
super(Renderer, self).__init__(None)
|
||||
# Need live behaviour if this is also working as a pseudo MainDisplay.
|
||||
self.is_live = True
|
||||
self.screens = ScreenList()
|
||||
Registry().register('renderer', self)
|
||||
self.theme_level = ThemeLevel.Global
|
||||
self.global_theme_name = ''
|
||||
self.service_theme_name = ''
|
||||
self.item_theme_name = ''
|
||||
self.force_page = False
|
||||
self.display = MainDisplay(None, False, self)
|
||||
self.display.setup()
|
||||
self._theme_dimensions = {}
|
||||
self._calculate_default()
|
||||
Registry().register_function('theme_update_global', self.set_global_theme)
|
||||
self.web = QtWebKit.QWebView()
|
||||
self.web.setVisible(False)
|
||||
self.web_frame = self.web.page().mainFrame()
|
||||
Registry().register_function('theme_update_global', self.set_global_theme)
|
||||
|
||||
def bootstrap_initialise(self):
|
||||
"""
|
||||
Initialise functions
|
||||
"""
|
||||
self.display = MainDisplay(self)
|
||||
self.display.setup()
|
||||
|
||||
def update_display(self):
|
||||
"""
|
||||
Updates the renderer's information about the current screen.
|
||||
"""
|
||||
log.debug('Update Display')
|
||||
self._calculate_default()
|
||||
if self.display:
|
||||
self.display.close()
|
||||
self.display = MainDisplay(None, False, self)
|
||||
self.display = MainDisplay(self)
|
||||
self.display.setup()
|
||||
self._theme_dimensions = {}
|
||||
|
||||
|
@ -94,15 +95,11 @@ class Renderer(object):
|
|||
"""
|
||||
This method updates the theme in ``_theme_dimensions`` when a theme has been edited or renamed.
|
||||
|
||||
``theme_name``
|
||||
The current theme name.
|
||||
|
||||
``old_theme_name``
|
||||
The old theme name. Has only to be passed, when the theme has been renamed. Defaults to *None*.
|
||||
|
||||
``only_delete``
|
||||
Only remove the given ``theme_name`` from the ``_theme_dimensions`` list. This can be used when a theme is
|
||||
permanently deleted.
|
||||
:param theme_name: The current theme name.
|
||||
:param old_theme_name: The old theme name. Has only to be passed, when the theme has been renamed.
|
||||
Defaults to *None*.
|
||||
:param only_delete: Only remove the given ``theme_name`` from the ``_theme_dimensions`` list. This can be
|
||||
used when a theme is permanently deleted.
|
||||
"""
|
||||
if old_theme_name is not None and old_theme_name in self._theme_dimensions:
|
||||
del self._theme_dimensions[old_theme_name]
|
||||
|
@ -115,9 +112,9 @@ class Renderer(object):
|
|||
"""
|
||||
Helper method to save theme names and theme data.
|
||||
|
||||
``theme_name``
|
||||
The theme name.
|
||||
:param theme_name: The theme name
|
||||
"""
|
||||
self.log_debug("_set_theme with theme %s" % theme_name)
|
||||
if theme_name not in self._theme_dimensions:
|
||||
theme_data = self.theme_manager.get_theme_data(theme_name)
|
||||
main_rect = self.get_main_rectangle(theme_data)
|
||||
|
@ -128,16 +125,15 @@ class Renderer(object):
|
|||
# if No file do not update cache
|
||||
if theme_data.background_filename:
|
||||
self.image_manager.add_image(theme_data.background_filename,
|
||||
ImageSource.Theme, QtGui.QColor(theme_data.background_border_color))
|
||||
ImageSource.Theme, QtGui.QColor(theme_data.background_border_color))
|
||||
|
||||
def pre_render(self, override_theme_data=None):
|
||||
"""
|
||||
Set up the theme to be used before rendering an item.
|
||||
|
||||
``override_theme_data``
|
||||
The theme data should be passed, when we want to use our own theme data, regardless of the theme level. This
|
||||
should for example be used in the theme manager. **Note**, this is **not** to be mixed up with the
|
||||
``set_item_theme`` method.
|
||||
:param override_theme_data: The theme data should be passed, when we want to use our own theme data, regardless
|
||||
of the theme level. This should for example be used in the theme manager. **Note**, this is **not** to
|
||||
be mixed up with the ``set_item_theme`` method.
|
||||
"""
|
||||
# Just assume we use the global theme.
|
||||
theme_to_use = self.global_theme_name
|
||||
|
@ -167,17 +163,13 @@ class Renderer(object):
|
|||
"""
|
||||
Sets the theme level.
|
||||
|
||||
``theme_level``
|
||||
The theme level to be used.
|
||||
:param theme_level: The theme level to be used.
|
||||
"""
|
||||
self.theme_level = theme_level
|
||||
|
||||
def set_global_theme(self):
|
||||
"""
|
||||
Set the global-level theme name.
|
||||
|
||||
``global_theme_name``
|
||||
The global-level theme's name.
|
||||
"""
|
||||
global_theme_name = Settings().value('themes/global theme')
|
||||
self._set_theme(global_theme_name)
|
||||
|
@ -187,8 +179,7 @@ class Renderer(object):
|
|||
"""
|
||||
Set the service-level theme.
|
||||
|
||||
``service_theme_name``
|
||||
The service level theme's name.
|
||||
:param service_theme_name: The service level theme's name.
|
||||
"""
|
||||
self._set_theme(service_theme_name)
|
||||
self.service_theme_name = service_theme_name
|
||||
|
@ -197,9 +188,9 @@ class Renderer(object):
|
|||
"""
|
||||
Set the item-level theme. **Note**, this has to be done for each item we are rendering.
|
||||
|
||||
``item_theme_name``
|
||||
The item theme's name.
|
||||
:param item_theme_name: The item theme's name.
|
||||
"""
|
||||
self.log_debug("set_item_theme with theme %s" % item_theme_name)
|
||||
self._set_theme(item_theme_name)
|
||||
self.item_theme_name = item_theme_name
|
||||
|
||||
|
@ -207,13 +198,9 @@ class Renderer(object):
|
|||
"""
|
||||
Generate a preview of a theme.
|
||||
|
||||
``theme_data``
|
||||
The theme to generated a preview for.
|
||||
|
||||
``force_page``
|
||||
Flag to tell message lines per page need to be generated.
|
||||
:param theme_data: The theme to generated a preview for.
|
||||
:param force_page: Flag to tell message lines per page need to be generated.
|
||||
"""
|
||||
log.debug('generate preview')
|
||||
# save value for use in format_slide
|
||||
self.force_page = force_page
|
||||
# build a service item to generate preview
|
||||
|
@ -245,13 +232,11 @@ class Renderer(object):
|
|||
"""
|
||||
Calculate how much text can fit on a slide.
|
||||
|
||||
``text``
|
||||
The words to go on the slides.
|
||||
:param text: The words to go on the slides.
|
||||
:param item: The :class:`~openlp.core.lib.serviceitem.ServiceItem` item object.
|
||||
|
||||
``item``
|
||||
The :class:`~openlp.core.lib.serviceitem.ServiceItem` item object.
|
||||
"""
|
||||
log.debug('format slide')
|
||||
self.log_debug('format slide')
|
||||
# Add line endings after each line of text used for bibles.
|
||||
line_end = '<br>'
|
||||
if item.is_capable(ItemCapabilities.NoLineBreaks):
|
||||
|
@ -329,7 +314,7 @@ class Renderer(object):
|
|||
self.width = screen_size.width()
|
||||
self.height = screen_size.height()
|
||||
self.screen_ratio = self.height / self.width
|
||||
log.debug('_calculate default %s, %f' % (screen_size, self.screen_ratio))
|
||||
self.log_debug('_calculate default %s, %f' % (screen_size, self.screen_ratio))
|
||||
# 90% is start of footer
|
||||
self.footer_start = int(self.height * 0.90)
|
||||
|
||||
|
@ -337,8 +322,7 @@ class Renderer(object):
|
|||
"""
|
||||
Calculates the placement and size of the main rectangle.
|
||||
|
||||
``theme_data``
|
||||
The theme information
|
||||
:param theme_data: The theme information
|
||||
"""
|
||||
if not theme_data.font_main_override:
|
||||
return QtCore.QRect(10, 0, self.width - 20, self.footer_start)
|
||||
|
@ -350,8 +334,7 @@ class Renderer(object):
|
|||
"""
|
||||
Calculates the placement and size of the footer rectangle.
|
||||
|
||||
``theme_data``
|
||||
The theme data.
|
||||
:param theme_data: The theme data.
|
||||
"""
|
||||
if not theme_data.font_footer_override:
|
||||
return QtCore.QRect(10, self.footer_start, self.width - 20, self.height - self.footer_start)
|
||||
|
@ -364,16 +347,11 @@ class Renderer(object):
|
|||
"""
|
||||
Sets the rectangle within which text should be rendered.
|
||||
|
||||
``theme_data``
|
||||
The theme data.
|
||||
|
||||
``rect_main``
|
||||
The main text block.
|
||||
|
||||
``rect_footer``
|
||||
The footer text block.
|
||||
:param theme_data: The theme data.
|
||||
:param rect_main: The main text block.
|
||||
:param rect_footer: The footer text block.
|
||||
"""
|
||||
log.debug('_set_text_rectangle %s , %s' % (rect_main, rect_footer))
|
||||
self.log_debug('_set_text_rectangle %s , %s' % (rect_main, rect_footer))
|
||||
self._rect = rect_main
|
||||
self._rect_footer = rect_footer
|
||||
self.page_width = self._rect.width()
|
||||
|
@ -409,16 +387,13 @@ class Renderer(object):
|
|||
def _paginate_slide(self, lines, line_end):
|
||||
"""
|
||||
Figure out how much text can appear on a slide, using the current theme settings.
|
||||
|
||||
**Note:** The smallest possible "unit" of text for a slide is one line. If the line is too long it will be cut
|
||||
off when displayed.
|
||||
|
||||
``lines``
|
||||
The text to be fitted on the slide split into lines.
|
||||
|
||||
``line_end``
|
||||
The text added after each line. Either ``u' '`` or ``u'<br>``.
|
||||
:param lines: The text to be fitted on the slide split into lines.
|
||||
:param line_end: The text added after each line. Either ``u' '`` or ``u'<br>``.
|
||||
"""
|
||||
log.debug('_paginate_slide - Start')
|
||||
formatted = []
|
||||
previous_html = ''
|
||||
previous_raw = ''
|
||||
|
@ -431,22 +406,18 @@ class Renderer(object):
|
|||
else:
|
||||
previous_raw = separator.join(lines)
|
||||
formatted.append(previous_raw)
|
||||
log.debug('_paginate_slide - End')
|
||||
return formatted
|
||||
|
||||
def _paginate_slide_words(self, lines, line_end):
|
||||
"""
|
||||
Figure out how much text can appear on a slide, using the current theme settings.
|
||||
|
||||
**Note:** The smallest possible "unit" of text for a slide is one word. If one line is too long it will be
|
||||
processed word by word. This is sometimes need for **bible** verses.
|
||||
|
||||
``lines``
|
||||
The text to be fitted on the slide split into lines.
|
||||
|
||||
``line_end``
|
||||
The text added after each line. Either ``u' '`` or ``u'<br>``. This is needed for **bibles**.
|
||||
:param lines: The text to be fitted on the slide split into lines.
|
||||
:param line_end: The text added after each line. Either ``u' '`` or ``u'<br>``. This is needed for **bibles**.
|
||||
"""
|
||||
log.debug('_paginate_slide_words - Start')
|
||||
formatted = []
|
||||
previous_html = ''
|
||||
previous_raw = ''
|
||||
|
@ -476,7 +447,6 @@ class Renderer(object):
|
|||
previous_html += html_line + line_end
|
||||
previous_raw += line + line_end
|
||||
formatted.append(previous_raw)
|
||||
log.debug('_paginate_slide_words - End')
|
||||
return formatted
|
||||
|
||||
def _get_start_tags(self, raw_text):
|
||||
|
@ -488,9 +458,8 @@ class Renderer(object):
|
|||
The first unicode string is the text, with correct closing tags. The second unicode string are OpenLP's opening
|
||||
formatting tags and the third unicode string the html opening formatting tags.
|
||||
|
||||
``raw_text``
|
||||
The text to test. The text must **not** contain html tags, only OpenLP formatting tags are allowed::
|
||||
|
||||
:param raw_text: The text to test. The text must **not** contain html tags, only OpenLP formatting tags
|
||||
are allowed::
|
||||
{st}{r}Text text text
|
||||
"""
|
||||
raw_tags = []
|
||||
|
@ -522,29 +491,18 @@ class Renderer(object):
|
|||
and word based (word by word). It is assumed that this method is **only** called, when the lines/words to be
|
||||
rendered do **not** fit as a whole.
|
||||
|
||||
``formatted``
|
||||
The list to append any slides.
|
||||
|
||||
``previous_html``
|
||||
The html text which is know to fit on a slide, but is not yet added to the list of slides. (unicode string)
|
||||
|
||||
``previous_raw``
|
||||
The raw text (with formatting tags) which is know to fit on a slide, but is not yet added to the list of
|
||||
slides. (unicode string)
|
||||
|
||||
``html_list``
|
||||
The elements which do not fit on a slide and needs to be processed using the binary chop. The text contains
|
||||
html.
|
||||
|
||||
``raw_list``
|
||||
The elements which do not fit on a slide and needs to be processed using the binary chop. The elements can
|
||||
contain formatting tags.
|
||||
|
||||
``separator``
|
||||
The separator for the elements. For lines this is ``u'<br>'`` and for words this is ``u' '``.
|
||||
|
||||
``line_end``
|
||||
The text added after each "element line". Either ``u' '`` or ``u'<br>``. This is needed for bibles.
|
||||
:param formatted: The list to append any slides.
|
||||
:param previous_html: The html text which is know to fit on a slide, but is not yet added to the list of
|
||||
slides. (unicode string)
|
||||
:param previous_raw: The raw text (with formatting tags) which is know to fit on a slide, but is not yet added
|
||||
to the list of slides. (unicode string)
|
||||
:param html_list: The elements which do not fit on a slide and needs to be processed using the binary chop.
|
||||
The text contains html.
|
||||
:param raw_list: The elements which do not fit on a slide and needs to be processed using the binary chop.
|
||||
The elements can contain formatting tags.
|
||||
:param separator: The separator for the elements. For lines this is ``u'<br>'`` and for words this is ``u' '``.
|
||||
:param line_end: The text added after each "element line". Either ``u' '`` or ``u'<br>``. This is needed for
|
||||
bibles.
|
||||
"""
|
||||
smallest_index = 0
|
||||
highest_index = len(html_list) - 1
|
||||
|
@ -591,8 +549,7 @@ class Renderer(object):
|
|||
"""
|
||||
Checks if the given ``text`` fits on a slide. If it does ``True`` is returned, otherwise ``False``.
|
||||
|
||||
``text``
|
||||
The text to check. It may contain HTML tags.
|
||||
:param text: The text to check. It may contain HTML tags.
|
||||
"""
|
||||
self.web_frame.evaluateJavaScript('show_text("%s")' % text.replace('\\', '\\\\').replace('\"', '\\\"'))
|
||||
return self.web_frame.contentsSize().height() <= self.empty_height
|
||||
|
@ -600,6 +557,8 @@ class Renderer(object):
|
|||
def _words_split(self, line):
|
||||
"""
|
||||
Split the slide up by word so can wrap better
|
||||
|
||||
:param line: Line to be split
|
||||
"""
|
||||
# this parse we are to be wordy
|
||||
line = line.replace('\n', ' ')
|
||||
|
@ -619,7 +578,7 @@ class Renderer(object):
|
|||
"""
|
||||
Adds the theme manager to the class dynamically
|
||||
"""
|
||||
if not hasattr(self, '_theme_manager'):
|
||||
if not hasattr(self, '_theme_manager') or not self._theme_manager :
|
||||
self._theme_manager = Registry().get('theme_manager')
|
||||
return self._theme_manager
|
||||
|
||||
|
|
|
@ -31,8 +31,8 @@ The :mod:`serviceitem` provides the service item functionality including the
|
|||
type and capability of an item.
|
||||
"""
|
||||
|
||||
import cgi
|
||||
import datetime
|
||||
import html
|
||||
import logging
|
||||
import os
|
||||
import uuid
|
||||
|
@ -241,9 +241,8 @@ class ServiceItem(object):
|
|||
self.theme_data, self.main, self.footer = self.renderer.pre_render()
|
||||
if self.service_item_type == ServiceItemType.Text:
|
||||
log.debug('Formatting slides: %s' % self.title)
|
||||
# Save rendered pages to this dict. In the case that a slide is used
|
||||
# twice we can use the pages saved to the dict instead of rendering
|
||||
# them again.
|
||||
# Save rendered pages to this dict. In the case that a slide is used twice we can use the pages saved to
|
||||
# the dict instead of rendering them again.
|
||||
previous_pages = {}
|
||||
for slide in self._raw_frames:
|
||||
verse_tag = slide['verseTag']
|
||||
|
@ -254,11 +253,11 @@ class ServiceItem(object):
|
|||
previous_pages[verse_tag] = (slide['raw_slide'], pages)
|
||||
for page in pages:
|
||||
page = page.replace('<br>', '{br}')
|
||||
html = expand_tags(cgi.escape(page.rstrip()))
|
||||
html_data = expand_tags(html.escape(page.rstrip()))
|
||||
self._display_frames.append({
|
||||
'title': clean_tags(page),
|
||||
'text': clean_tags(page.rstrip()),
|
||||
'html': html.replace('&nbsp;', ' '),
|
||||
'html': html_data.replace('&nbsp;', ' '),
|
||||
'verseTag': verse_tag
|
||||
})
|
||||
elif self.service_item_type == ServiceItemType.Image or self.service_item_type == ServiceItemType.Command:
|
||||
|
|
|
@ -342,18 +342,16 @@ def create_valign_selection_widgets(parent):
|
|||
return label, combo_box
|
||||
|
||||
|
||||
def find_and_set_in_combo_box(combo_box, value_to_find):
|
||||
def find_and_set_in_combo_box(combo_box, value_to_find, set_missing=True):
|
||||
"""
|
||||
Find a string in a combo box and set it as the selected item if present
|
||||
|
||||
``combo_box``
|
||||
The combo box to check for selected items
|
||||
|
||||
``value_to_find``
|
||||
The value to find
|
||||
:param combo_box: The combo box to check for selected items
|
||||
:param value_to_find: The value to find
|
||||
:param set_missing: if not found leave value as current
|
||||
"""
|
||||
index = combo_box.findText(value_to_find, QtCore.Qt.MatchExactly)
|
||||
if index == -1:
|
||||
# Not Found.
|
||||
index = 0
|
||||
index = 0 if set_missing else combo_box.currentIndex()
|
||||
combo_box.setCurrentIndex(index)
|
||||
|
|
|
@ -123,12 +123,10 @@ from .formattingtagcontroller import FormattingTagController
|
|||
from .shortcutlistform import ShortcutListForm
|
||||
from .mediadockmanager import MediaDockManager
|
||||
from .servicemanager import ServiceManager
|
||||
from .thememanagerhelper import ThemeManagerHelper
|
||||
from .thememanager import ThemeManager
|
||||
|
||||
__all__ = ['SplashScreen', 'AboutForm', 'SettingsForm', 'MainDisplay', 'SlideController', 'ServiceManager', 'ThemeForm',
|
||||
'ThemeManager', 'MediaDockManager', 'ServiceItemEditForm', 'FirstTimeForm', 'FirstTimeLanguageForm',
|
||||
'Display', 'ServiceNoteForm', 'ThemeLayoutForm', 'FileRenameForm', 'StartTimeForm', 'MainDisplay',
|
||||
'SlideController', 'DisplayController', 'GeneralTab', 'ThemesTab', 'AdvancedTab', 'PluginForm',
|
||||
'FormattingTagForm', 'ShortcutListForm', 'FormattingTagController', 'ThemeManagerHelper',
|
||||
'SingleColumnTableWidget']
|
||||
'FormattingTagForm', 'ShortcutListForm', 'FormattingTagController', 'SingleColumnTableWidget']
|
||||
|
|
|
@ -44,7 +44,7 @@ import sys
|
|||
from PyQt4 import QtCore, QtGui, QtWebKit, QtOpenGL
|
||||
from PyQt4.phonon import Phonon
|
||||
|
||||
from openlp.core.common import Registry, Settings, translate
|
||||
from openlp.core.common import Registry, OpenLPMixin, Settings, translate
|
||||
from openlp.core.lib import ServiceItem, ImageSource, build_html, expand_tags, image_to_byte
|
||||
from openlp.core.lib.theme import BackgroundType
|
||||
|
||||
|
@ -56,22 +56,23 @@ log = logging.getLogger(__name__)
|
|||
|
||||
class Display(QtGui.QGraphicsView):
|
||||
"""
|
||||
This is a general display screen class. Here the general display settings
|
||||
will done. It will be used as specialized classes by Main Display and
|
||||
Preview display.
|
||||
This is a general display screen class. Here the general display settings will done. It will be used as
|
||||
specialized classes by Main Display and Preview display.
|
||||
"""
|
||||
def __init__(self, parent, live, controller):
|
||||
def __init__(self, parent):
|
||||
"""
|
||||
Constructor
|
||||
"""
|
||||
if live:
|
||||
self.is_live = False
|
||||
if hasattr(parent, 'is_live') and parent.is_live:
|
||||
self.is_live = True
|
||||
if self.is_live:
|
||||
super(Display, self).__init__()
|
||||
# Overwrite the parent() method.
|
||||
self.parent = lambda: parent
|
||||
else:
|
||||
super(Display, self).__init__(parent)
|
||||
self.is_live = live
|
||||
self.controller = controller
|
||||
self.controller = parent
|
||||
self.screen = {}
|
||||
# FIXME: On Mac OS X (tested on 10.7) the display screen is corrupt with
|
||||
# OpenGL. Only white blank screen is shown on the 2nd monitor all the
|
||||
|
@ -84,9 +85,7 @@ class Display(QtGui.QGraphicsView):
|
|||
"""
|
||||
Set up and build the screen base
|
||||
"""
|
||||
log.debug('Start Display base setup (live = %s)' % self.is_live)
|
||||
self.setGeometry(self.screen['size'])
|
||||
log.debug('Setup webView')
|
||||
self.web_view = QtWebKit.QWebView(self)
|
||||
self.web_view.setGeometry(0, 0, self.screen['size'].width(), self.screen['size'].height())
|
||||
self.web_view.settings().setAttribute(QtWebKit.QWebSettings.PluginsEnabled, True)
|
||||
|
@ -107,33 +106,34 @@ class Display(QtGui.QGraphicsView):
|
|||
def resizeEvent(self, event):
|
||||
"""
|
||||
React to resizing of this display
|
||||
|
||||
:param event: The event to be handled
|
||||
"""
|
||||
self.web_view.setGeometry(0, 0, self.width(), self.height())
|
||||
|
||||
def is_web_loaded(self):
|
||||
def is_web_loaded(self, field=None):
|
||||
"""
|
||||
Called by webView event to show display is fully loaded
|
||||
"""
|
||||
log.debug('is web loaded')
|
||||
self.web_loaded = True
|
||||
|
||||
|
||||
class MainDisplay(Display):
|
||||
class MainDisplay(OpenLPMixin, Display):
|
||||
"""
|
||||
This is the display screen as a specialized class from the Display class
|
||||
"""
|
||||
def __init__(self, parent, live, controller):
|
||||
def __init__(self, parent):
|
||||
"""
|
||||
Constructor
|
||||
"""
|
||||
super(MainDisplay, self).__init__(parent, live, controller)
|
||||
super(MainDisplay, self).__init__(parent)
|
||||
self.screens = ScreenList()
|
||||
self.rebuild_css = False
|
||||
self.hide_mode = None
|
||||
self.override = {}
|
||||
self.retranslateUi()
|
||||
self.media_object = None
|
||||
if live:
|
||||
if self.is_live:
|
||||
self.audio_player = AudioPlayer(self)
|
||||
else:
|
||||
self.audio_player = None
|
||||
|
@ -164,6 +164,8 @@ class MainDisplay(Display):
|
|||
def set_transparency(self, enabled):
|
||||
"""
|
||||
Set the transparency of the window
|
||||
|
||||
:param enabled: Is transparency enabled
|
||||
"""
|
||||
if enabled:
|
||||
self.setAutoFillBackground(False)
|
||||
|
@ -189,7 +191,7 @@ class MainDisplay(Display):
|
|||
"""
|
||||
Set up and build the output screen
|
||||
"""
|
||||
log.debug('Start MainDisplay setup (live = %s)' % self.is_live)
|
||||
self.log_debug('Start MainDisplay setup (live = %s)' % self.is_live)
|
||||
self.screen = self.screens.current
|
||||
self.setVisible(False)
|
||||
Display.setup(self)
|
||||
|
@ -216,20 +218,15 @@ class MainDisplay(Display):
|
|||
service_item.bg_image_bytes = image_to_byte(self.initial_fame)
|
||||
self.web_view.setHtml(build_html(service_item, self.screen, self.is_live, None,
|
||||
plugins=self.plugin_manager.plugins))
|
||||
self.__hideMouse()
|
||||
log.debug('Finished MainDisplay setup')
|
||||
self._hide_mouse()
|
||||
|
||||
def text(self, slide, animate=True):
|
||||
"""
|
||||
Add the slide text from slideController
|
||||
|
||||
``slide``
|
||||
The slide text to be displayed
|
||||
|
||||
``animate``
|
||||
Perform transitions if applicable when setting the text
|
||||
:param slide: The slide text to be displayed
|
||||
:param animate: Perform transitions if applicable when setting the text
|
||||
"""
|
||||
log.debug('text to display')
|
||||
# Wait for the webview to update before displaying text.
|
||||
while not self.web_loaded:
|
||||
self.application.process_events()
|
||||
|
@ -248,10 +245,9 @@ class MainDisplay(Display):
|
|||
"""
|
||||
Display an alert.
|
||||
|
||||
``text``
|
||||
The text to be displayed.
|
||||
:param text: The text to be displayed.
|
||||
:param location: Where on the screen is the text to be displayed
|
||||
"""
|
||||
log.debug('alert to display')
|
||||
# First we convert <>& marks to html variants, then apply
|
||||
# formattingtags, finally we double all backslashes for JavaScript.
|
||||
text_prepared = expand_tags(cgi.escape(text)).replace('\\', '\\\\').replace('\"', '\\\"')
|
||||
|
@ -278,6 +274,9 @@ class MainDisplay(Display):
|
|||
def direct_image(self, path, background):
|
||||
"""
|
||||
API for replacement backgrounds so Images are added directly to cache.
|
||||
|
||||
:param path: Path to Image
|
||||
:param background: The background color
|
||||
"""
|
||||
self.image_manager.add_image(path, ImageSource.ImagePlugin, background)
|
||||
if not hasattr(self, 'service_item'):
|
||||
|
@ -295,12 +294,9 @@ class MainDisplay(Display):
|
|||
Add an image as the background. The image has already been added to the
|
||||
cache.
|
||||
|
||||
``path``
|
||||
The path to the image to be displayed. **Note**, the path is only
|
||||
passed to identify the image. If the image has changed it has to be
|
||||
re-added to the image manager.
|
||||
:param path: The path to the image to be displayed. **Note**, the path is only passed to identify the image.
|
||||
If the image has changed it has to be re-added to the image manager.
|
||||
"""
|
||||
log.debug('image to display')
|
||||
image = self.image_manager.get_image_bytes(path, ImageSource.ImagePlugin)
|
||||
self.controller.media_controller.media_reset(self.controller)
|
||||
self.display_image(image)
|
||||
|
@ -308,6 +304,8 @@ class MainDisplay(Display):
|
|||
def display_image(self, image):
|
||||
"""
|
||||
Display an image, as is.
|
||||
|
||||
:param image: The image to be displayed
|
||||
"""
|
||||
self.setGeometry(self.screen['size'])
|
||||
if image:
|
||||
|
@ -318,10 +316,8 @@ class MainDisplay(Display):
|
|||
|
||||
def reset_image(self):
|
||||
"""
|
||||
Reset the background image to the service item image. Used after the
|
||||
image plugin has changed the background.
|
||||
Reset the background image to the service item image. Used after the image plugin has changed the background.
|
||||
"""
|
||||
log.debug('reset_image')
|
||||
if hasattr(self, 'service_item'):
|
||||
self.display_image(self.service_item.bg_image_bytes)
|
||||
else:
|
||||
|
@ -336,7 +332,6 @@ class MainDisplay(Display):
|
|||
"""
|
||||
Generates a preview of the image displayed.
|
||||
"""
|
||||
log.debug('preview for %s', self.is_live)
|
||||
was_visible = self.isVisible()
|
||||
self.application.process_events()
|
||||
# We must have a service item to preview.
|
||||
|
@ -368,10 +363,11 @@ class MainDisplay(Display):
|
|||
|
||||
def build_html(self, service_item, image_path=''):
|
||||
"""
|
||||
Store the service_item and build the new HTML from it. Add the
|
||||
HTML to the display
|
||||
Store the service_item and build the new HTML from it. Add the HTML to the display
|
||||
|
||||
:param service_item: The Service item to be used
|
||||
:param image_path: Where the image resides.
|
||||
"""
|
||||
log.debug('build_html')
|
||||
self.web_loaded = False
|
||||
self.initial_fame = None
|
||||
self.service_item = service_item
|
||||
|
@ -393,17 +389,14 @@ class MainDisplay(Display):
|
|||
BackgroundType.to_string(BackgroundType.Transparent))
|
||||
if self.service_item.theme_data.background_filename:
|
||||
self.service_item.bg_image_bytes = self.image_manager.get_image_bytes(
|
||||
self.service_item.theme_data.background_filename, ImageSource.Theme
|
||||
)
|
||||
self.service_item.theme_data.background_filename, ImageSource.Theme)
|
||||
if image_path:
|
||||
image_bytes = self.image_manager.get_image_bytes(image_path, ImageSource.ImagePlugin)
|
||||
else:
|
||||
image_bytes = None
|
||||
html = build_html(self.service_item, self.screen, self.is_live, background, image_bytes,
|
||||
plugins=self.plugin_manager.plugins)
|
||||
log.debug('buildHtml - pre setHtml')
|
||||
self.web_view.setHtml(html)
|
||||
log.debug('buildHtml - post setHtml')
|
||||
if service_item.foot_text:
|
||||
self.footer(service_item.foot_text)
|
||||
# if was hidden keep it hidden
|
||||
|
@ -412,22 +405,24 @@ class MainDisplay(Display):
|
|||
Registry().execute('slidecontroller_live_unblank')
|
||||
else:
|
||||
self.hide_display(self.hide_mode)
|
||||
self.__hideMouse()
|
||||
self._hide_mouse()
|
||||
|
||||
def footer(self, text):
|
||||
"""
|
||||
Display the Footer
|
||||
|
||||
:param text: footer text to be displayed
|
||||
"""
|
||||
log.debug('footer')
|
||||
js = 'show_footer(\'' + text.replace('\\', '\\\\').replace('\'', '\\\'') + '\')'
|
||||
self.frame.evaluateJavaScript(js)
|
||||
|
||||
def hide_display(self, mode=HideMode.Screen):
|
||||
"""
|
||||
Hide the display by making all layers transparent
|
||||
Store the images so they can be replaced when required
|
||||
Hide the display by making all layers transparent Store the images so they can be replaced when required
|
||||
|
||||
:param mode: How the screen is to be hidden
|
||||
"""
|
||||
log.debug('hide_display mode = %d', mode)
|
||||
self.log_debug('hide_display mode = %d' % mode)
|
||||
if self.screens.display_count == 1:
|
||||
# Only make visible if setting enabled.
|
||||
if not Settings().value('core/display on monitor'):
|
||||
|
@ -450,7 +445,6 @@ class MainDisplay(Display):
|
|||
Show the stored layers so the screen reappears as it was originally.
|
||||
Make the stored images None to release memory.
|
||||
"""
|
||||
log.debug('show_display')
|
||||
if self.screens.display_count == 1:
|
||||
# Only make visible if setting enabled.
|
||||
if not Settings().value('core/display on monitor'):
|
||||
|
@ -463,7 +457,7 @@ class MainDisplay(Display):
|
|||
if self.is_live:
|
||||
Registry().execute('live_display_active')
|
||||
|
||||
def __hideMouse(self):
|
||||
def _hide_mouse(self):
|
||||
"""
|
||||
Hide mouse cursor when moved over display.
|
||||
"""
|
||||
|
@ -519,12 +513,10 @@ class MainDisplay(Display):
|
|||
live_controller = property(_get_live_controller)
|
||||
|
||||
|
||||
class AudioPlayer(QtCore.QObject):
|
||||
class AudioPlayer(OpenLPMixin, QtCore.QObject):
|
||||
"""
|
||||
This Class will play audio only allowing components to work with a
|
||||
soundtrack independent of the user interface.
|
||||
This Class will play audio only allowing components to work with a soundtrack independent of the user interface.
|
||||
"""
|
||||
log.info('AudioPlayer Loaded')
|
||||
|
||||
def __init__(self, parent):
|
||||
"""
|
||||
|
@ -533,7 +525,6 @@ class AudioPlayer(QtCore.QObject):
|
|||
``parent``
|
||||
The parent widget.
|
||||
"""
|
||||
log.debug('AudioPlayer Initialisation started')
|
||||
super(AudioPlayer, self).__init__(parent)
|
||||
self.current_index = -1
|
||||
self.playlist = []
|
||||
|
@ -567,7 +558,7 @@ class AudioPlayer(QtCore.QObject):
|
|||
When the audio track finishes.
|
||||
"""
|
||||
if self.repeat:
|
||||
log.debug('Repeat is enabled... here we go again!')
|
||||
self.log_debug('Repeat is enabled... here we go again!')
|
||||
self.media_object.clearQueue()
|
||||
self.media_object.clear()
|
||||
self.current_index = -1
|
||||
|
@ -592,7 +583,6 @@ class AudioPlayer(QtCore.QObject):
|
|||
"""
|
||||
We want to play the file so start it
|
||||
"""
|
||||
log.debug('AudioPlayer.play() called')
|
||||
if self.current_index == -1:
|
||||
self.on_about_to_finish()
|
||||
self.media_object.play()
|
||||
|
@ -601,22 +591,19 @@ class AudioPlayer(QtCore.QObject):
|
|||
"""
|
||||
Pause the Audio
|
||||
"""
|
||||
log.debug('AudioPlayer.pause() called')
|
||||
self.media_object.pause()
|
||||
|
||||
def stop(self):
|
||||
"""
|
||||
Stop the Audio and clean up
|
||||
"""
|
||||
log.debug('AudioPlayer.stop() called')
|
||||
self.media_object.stop()
|
||||
|
||||
def add_to_playlist(self, file_names):
|
||||
"""
|
||||
Add another file to the playlist.
|
||||
|
||||
``file_names``
|
||||
A list with files to be added to the playlist.
|
||||
:param file_names: A list with files to be added to the playlist.
|
||||
"""
|
||||
if not isinstance(file_names, list):
|
||||
file_names = [file_names]
|
||||
|
@ -641,6 +628,8 @@ class AudioPlayer(QtCore.QObject):
|
|||
def go_to(self, index):
|
||||
"""
|
||||
Go to a particular track in the list
|
||||
|
||||
:param index: The track to go to
|
||||
"""
|
||||
is_playing = self.media_object.state() == Phonon.PlayingState
|
||||
self.media_object.clearQueue()
|
||||
|
@ -653,6 +642,9 @@ class AudioPlayer(QtCore.QObject):
|
|||
def connectSlot(self, signal, slot):
|
||||
"""
|
||||
Connect a slot to a signal on the media object. Used by slidecontroller to connect to audio object.
|
||||
|
||||
:param slot: The slot the signal is attached to.
|
||||
:param signal: The signal to be fired
|
||||
"""
|
||||
QtCore.QObject.connect(self.media_object, signal, slot)
|
||||
|
||||
|
|
|
@ -293,7 +293,7 @@ class Ui_MainWindow(object):
|
|||
self.about_item.setMenuRole(QtGui.QAction.AboutRole)
|
||||
if os.name == 'nt':
|
||||
self.local_help_file = os.path.join(AppLocation.get_directory(AppLocation.AppDir), 'OpenLP.chm')
|
||||
self.offline_help_item = create_action(main_window, 'offline_help_item',
|
||||
self.offline_help_item = create_action(main_window, 'offlineHelpItem',
|
||||
icon=':/system/system_help_contents.png',
|
||||
can_shortcuts=True,
|
||||
category=UiStrings().Help, triggers=self.on_offline_help_clicked)
|
||||
|
@ -313,8 +313,8 @@ class Ui_MainWindow(object):
|
|||
self.export_language_item, None))
|
||||
add_actions(self.file_menu, (self.file_new_item, self.file_open_item,
|
||||
self.file_save_item, self.file_save_as_item, self.recent_files_menu.menuAction(), None,
|
||||
self.file_import_menu.menuAction(), self.file_export_menu.menuAction(), None, self.print_service_order_item,
|
||||
self.file_exit_item))
|
||||
self.file_import_menu.menuAction(), self.file_export_menu.menuAction(), None,
|
||||
self.print_service_order_item, self.file_exit_item))
|
||||
add_actions(self.view_mode_menu, (self.mode_default_item, self.mode_setup_item, self.mode_live_item))
|
||||
add_actions(self.view_menu, (self.view_mode_menu.menuAction(), None, self.view_media_manager_item,
|
||||
self.view_service_manager_item, self.view_theme_manager_item, None, self.view_preview_panel,
|
||||
|
@ -547,6 +547,7 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
|
|||
self.live_controller.panel.setVisible(Settings().value('user interface/live panel'))
|
||||
self.load_settings()
|
||||
self.restore_current_media_manager_item()
|
||||
Registry().execute('theme_update_global')
|
||||
|
||||
def restore_current_media_manager_item(self):
|
||||
"""
|
||||
|
@ -675,7 +676,7 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
|
|||
else:
|
||||
self.active_plugin.toggle_status(PluginStatus.Inactive)
|
||||
# Set global theme and
|
||||
Registry().execute('theme_update_global', self.theme_manager_contents.global_theme)
|
||||
Registry().execute('theme_update_global')
|
||||
self.theme_manager_contents.load_first_time_themes()
|
||||
# Check if any Bibles downloaded. If there are, they will be processed.
|
||||
Registry().execute('bibles_load_list', True)
|
||||
|
|
|
@ -35,7 +35,7 @@ from openlp.core.common import Settings
|
|||
|
||||
from PyQt4 import QtCore
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
log = logging.getLogger(__name__+'.__init__')
|
||||
|
||||
|
||||
class MediaState(object):
|
||||
|
|
|
@ -35,7 +35,7 @@ import os
|
|||
import datetime
|
||||
from PyQt4 import QtCore, QtGui
|
||||
|
||||
from openlp.core.common import Registry, Settings, UiStrings, translate
|
||||
from openlp.core.common import Registry, RegistryMixin, Settings, UiStrings, translate
|
||||
from openlp.core.lib import OpenLPToolbar
|
||||
from openlp.core.lib.ui import critical_error_message_box
|
||||
from openlp.core.ui.media import MediaState, MediaInfo, MediaType, get_media_players, set_media_players
|
||||
|
@ -99,7 +99,7 @@ class MediaController(object):
|
|||
Constructor
|
||||
"""
|
||||
Registry().register('media_controller', self)
|
||||
Registry().register_function('bootstrap_initialise', self.check_available_media_players)
|
||||
Registry().register_function('bootstrap_initialise', self.bootstrap_initialise)
|
||||
self.media_players = {}
|
||||
self.display_controllers = {}
|
||||
self.current_media_players = {}
|
||||
|
@ -134,20 +134,22 @@ class MediaController(object):
|
|||
"""
|
||||
Set the active players and available media files
|
||||
"""
|
||||
suffix_list = []
|
||||
self.audio_extensions_list = []
|
||||
for player in list(self.media_players.values()):
|
||||
if player.is_active:
|
||||
for item in player.audio_extensions_list:
|
||||
if not item in self.audio_extensions_list:
|
||||
self.audio_extensions_list.append(item)
|
||||
self.service_manager.supported_suffixes(item[2:])
|
||||
suffix_list.append(item[2:])
|
||||
self.video_extensions_list = []
|
||||
for player in list(self.media_players.values()):
|
||||
if player.is_active:
|
||||
for item in player.video_extensions_list:
|
||||
if item not in self.video_extensions_list:
|
||||
self.video_extensions_list.extend(item)
|
||||
self.service_manager.supported_suffixes(item[2:])
|
||||
suffix_list.append(item[2:])
|
||||
self.service_manager.supported_suffixes(suffix_list)
|
||||
|
||||
def register_players(self, player):
|
||||
"""
|
||||
|
@ -159,7 +161,7 @@ class MediaController(object):
|
|||
"""
|
||||
self.media_players[player.name] = player
|
||||
|
||||
def check_available_media_players(self):
|
||||
def bootstrap_initialise(self):
|
||||
"""
|
||||
Check to see if we have any media Player's available.
|
||||
"""
|
||||
|
@ -169,27 +171,28 @@ class MediaController(object):
|
|||
if filename.endswith('player.py') and not filename == 'mediaplayer.py':
|
||||
path = os.path.join(controller_dir, filename)
|
||||
if os.path.isfile(path):
|
||||
modulename = 'openlp.core.ui.media.' + os.path.splitext(filename)[0]
|
||||
log.debug('Importing controller %s', modulename)
|
||||
module_name = 'openlp.core.ui.media.' + os.path.splitext(filename)[0]
|
||||
log.debug('Importing controller %s', module_name)
|
||||
try:
|
||||
__import__(modulename, globals(), locals(), [])
|
||||
__import__(module_name, globals(), locals(), [])
|
||||
# On some platforms importing vlc.py might cause
|
||||
# also OSError exceptions. (e.g. Mac OS X)
|
||||
except (ImportError, OSError):
|
||||
log.warn('Failed to import %s on path %s', modulename, path)
|
||||
log.warn('Failed to import %s on path %s', module_name, path)
|
||||
player_classes = MediaPlayer.__subclasses__()
|
||||
for player_class in player_classes:
|
||||
player = player_class(self)
|
||||
self.register_players(player)
|
||||
if not self.media_players:
|
||||
return False
|
||||
savedPlayers, overriddenPlayer = get_media_players()
|
||||
invalid_media_players = [mediaPlayer for mediaPlayer in savedPlayers
|
||||
if not mediaPlayer in self.media_players or not self.media_players[mediaPlayer].check_available()]
|
||||
saved_players, overridden_player = get_media_players()
|
||||
invalid_media_players = \
|
||||
[mediaPlayer for mediaPlayer in saved_players if not mediaPlayer in self.media_players or
|
||||
not self.media_players[mediaPlayer].check_available()]
|
||||
if invalid_media_players:
|
||||
for invalidPlayer in invalid_media_players:
|
||||
savedPlayers.remove(invalidPlayer)
|
||||
set_media_players(savedPlayers, overriddenPlayer)
|
||||
saved_players.remove(invalidPlayer)
|
||||
set_media_players(saved_players, overridden_player)
|
||||
self._set_active_players()
|
||||
self._generate_extensions_lists()
|
||||
return True
|
||||
|
@ -270,14 +273,17 @@ class MediaController(object):
|
|||
# Build a Media ToolBar
|
||||
controller.mediabar = OpenLPToolbar(controller)
|
||||
controller.mediabar.add_toolbar_action('playbackPlay', text='media_playback_play',
|
||||
icon=':/slides/media_playback_start.png',
|
||||
tooltip=translate('OpenLP.SlideController', 'Start playing media.'), triggers=controller.send_to_plugins)
|
||||
icon=':/slides/media_playback_start.png',
|
||||
tooltip=translate('OpenLP.SlideController', 'Start playing media.'),
|
||||
triggers=controller.send_to_plugins)
|
||||
controller.mediabar.add_toolbar_action('playbackPause', text='media_playback_pause',
|
||||
icon=':/slides/media_playback_pause.png',
|
||||
tooltip=translate('OpenLP.SlideController', 'Pause playing media.'), triggers=controller.send_to_plugins)
|
||||
icon=':/slides/media_playback_pause.png',
|
||||
tooltip=translate('OpenLP.SlideController', 'Pause playing media.'),
|
||||
triggers=controller.send_to_plugins)
|
||||
controller.mediabar.add_toolbar_action('playbackStop', text='media_playback_stop',
|
||||
icon=':/slides/media_playback_stop.png',
|
||||
tooltip=translate('OpenLP.SlideController', 'Stop playing media.'), triggers=controller.send_to_plugins)
|
||||
icon=':/slides/media_playback_stop.png',
|
||||
tooltip=translate('OpenLP.SlideController', 'Stop playing media.'),
|
||||
triggers=controller.send_to_plugins)
|
||||
# Build the seek_slider.
|
||||
controller.seek_slider = MediaSlider(QtCore.Qt.Horizontal, self, controller)
|
||||
controller.seek_slider.setMaximum(1000)
|
||||
|
@ -445,11 +451,11 @@ class MediaController(object):
|
|||
if not self._check_file_type(controller, display, service_item):
|
||||
# Media could not be loaded correctly
|
||||
critical_error_message_box(translate('MediaPlugin.MediaItem', 'Unsupported File'),
|
||||
translate('MediaPlugin.MediaItem', 'Unsupported File'))
|
||||
translate('MediaPlugin.MediaItem', 'Unsupported File'))
|
||||
return False
|
||||
if not self.media_play(controller):
|
||||
critical_error_message_box(translate('MediaPlugin.MediaItem', 'Unsupported File'),
|
||||
translate('MediaPlugin.MediaItem', 'Unsupported File'))
|
||||
translate('MediaPlugin.MediaItem', 'Unsupported File'))
|
||||
return False
|
||||
service_item.set_media_length(controller.media_info.length)
|
||||
self.media_stop(controller)
|
||||
|
|
|
@ -37,8 +37,7 @@ from openlp.core.ui.media import MediaState
|
|||
|
||||
class MediaPlayer(object):
|
||||
"""
|
||||
This is the base class media Player class to provide OpenLP with a
|
||||
pluggable media display framework.
|
||||
This is the base class media Player class to provide OpenLP with a pluggable media display framework.
|
||||
"""
|
||||
|
||||
def __init__(self, parent, name='media_player'):
|
||||
|
|
|
@ -80,12 +80,12 @@ class PhononPlayer(MediaPlayer):
|
|||
self.parent = parent
|
||||
self.additional_extensions = ADDITIONAL_EXT
|
||||
mimetypes.init()
|
||||
for mimetype in Phonon.BackendCapabilities.availableMimeTypes():
|
||||
mimetype = str(mimetype)
|
||||
if mimetype.startswith('audio/'):
|
||||
self._addToList(self.audio_extensions_list, mimetype)
|
||||
elif mimetype.startswith('video/'):
|
||||
self._addToList(self.video_extensions_list, mimetype)
|
||||
for mime_type in Phonon.BackendCapabilities.availableMimeTypes():
|
||||
mime_type = str(mime_type)
|
||||
if mime_type.startswith('audio/'):
|
||||
self._addToList(self.audio_extensions_list, mime_type)
|
||||
elif mime_type.startswith('video/'):
|
||||
self._addToList(self.video_extensions_list, mime_type)
|
||||
|
||||
def _addToList(self, mimetype_list, mimetype):
|
||||
"""
|
||||
|
@ -144,14 +144,14 @@ class PhononPlayer(MediaPlayer):
|
|||
self.volume(display, volume)
|
||||
return True
|
||||
|
||||
def media_state_wait(self, display, mediaState):
|
||||
def media_state_wait(self, display, media_state):
|
||||
"""
|
||||
Wait for the video to change its state
|
||||
Wait no longer than 5 seconds.
|
||||
"""
|
||||
start = datetime.now()
|
||||
current_state = display.media_object.state()
|
||||
while current_state != mediaState:
|
||||
while current_state != media_state:
|
||||
current_state = display.media_object.state()
|
||||
if current_state == Phonon.ErrorState:
|
||||
return False
|
||||
|
@ -172,8 +172,7 @@ class PhononPlayer(MediaPlayer):
|
|||
"""
|
||||
controller = display.controller
|
||||
start_time = 0
|
||||
if display.media_object.state() != Phonon.PausedState and \
|
||||
controller.media_info.start_time > 0:
|
||||
if display.media_object.state() != Phonon.PausedState and controller.media_info.start_time > 0:
|
||||
start_time = controller.media_info.start_time
|
||||
display.media_object.play()
|
||||
if not self.media_state_wait(display, Phonon.PlayingState):
|
||||
|
@ -262,8 +261,8 @@ class PhononPlayer(MediaPlayer):
|
|||
Return some info about this player
|
||||
"""
|
||||
return(translate('Media.player', 'Phonon is a media player which '
|
||||
'interacts with the operating system to provide media capabilities.') +
|
||||
'<br/> <strong>' + translate('Media.player', 'Audio') +
|
||||
'</strong><br/>' + str(self.audio_extensions_list) +
|
||||
'<br/><strong>' + translate('Media.player', 'Video') +
|
||||
'</strong><br/>' + str(self.video_extensions_list) + '<br/>')
|
||||
'interacts with the operating system to provide media capabilities.') +
|
||||
'<br/> <strong>' + translate('Media.player', 'Audio') +
|
||||
'</strong><br/>' + str(self.audio_extensions_list) +
|
||||
'<br/><strong>' + translate('Media.player', 'Video') +
|
||||
'</strong><br/>' + str(self.video_extensions_list) + '<br/>')
|
||||
|
|
|
@ -39,13 +39,13 @@ from openlp.core.ui.media import get_media_players, set_media_players
|
|||
|
||||
class MediaQCheckBox(QtGui.QCheckBox):
|
||||
"""
|
||||
MediaQCheckBox adds an extra property, playerName to the QCheckBox class.
|
||||
MediaQCheckBox adds an extra property, player_name to the QCheckBox class.
|
||||
"""
|
||||
def set_player_name(self, name):
|
||||
"""
|
||||
Set the player name
|
||||
"""
|
||||
self.playerName = name
|
||||
self.player_name = name
|
||||
|
||||
|
||||
class PlayerTab(SettingsTab):
|
||||
|
@ -113,9 +113,9 @@ class PlayerTab(SettingsTab):
|
|||
self.ordering_button_layout.setObjectName('ordering_button_layout')
|
||||
self.ordering_button_layout.addStretch(1)
|
||||
self.ordering_up_button = create_button(self, 'ordering_up_button', role='up',
|
||||
click=self.on_up_button_clicked)
|
||||
click=self.on_up_button_clicked)
|
||||
self.ordering_down_button = create_button(self, 'ordering_down_button', role='down',
|
||||
click=self.on_down_button_clicked)
|
||||
click=self.on_down_button_clicked)
|
||||
self.ordering_button_layout.addWidget(self.ordering_up_button)
|
||||
self.ordering_button_layout.addWidget(self.ordering_down_button)
|
||||
self.ordering_button_layout.addStretch(1)
|
||||
|
@ -135,8 +135,8 @@ class PlayerTab(SettingsTab):
|
|||
self.background_color_group_box.setTitle(UiStrings().BackgroundColor)
|
||||
self.background_color_label.setText(UiStrings().DefaultColor)
|
||||
self.information_label.setText(translate('OpenLP.PlayerTab',
|
||||
'Visible background for videos with aspect ratio different to screen.'))
|
||||
self.retranslatePlayers()
|
||||
'Visible background for videos with aspect ratio different to screen.'))
|
||||
self.retranslate_players()
|
||||
|
||||
def on_background_color_button_clicked(self):
|
||||
"""
|
||||
|
@ -151,7 +151,7 @@ class PlayerTab(SettingsTab):
|
|||
"""
|
||||
Add or remove players depending on their status
|
||||
"""
|
||||
player = self.sender().playerName
|
||||
player = self.sender().player_name
|
||||
if check_state == QtCore.Qt.Checked:
|
||||
if player not in self.used_players:
|
||||
self.used_players.append(player)
|
||||
|
@ -249,9 +249,9 @@ class PlayerTab(SettingsTab):
|
|||
else:
|
||||
checkbox.setChecked(False)
|
||||
self.update_player_list()
|
||||
self.retranslatePlayers()
|
||||
self.retranslate_players()
|
||||
|
||||
def retranslatePlayers(self):
|
||||
def retranslate_players(self):
|
||||
"""
|
||||
Translations for players is dependent on their setup as well
|
||||
"""
|
||||
|
|
|
@ -118,35 +118,35 @@ class VlcPlayer(MediaPlayer):
|
|||
"""
|
||||
Set up the media player
|
||||
"""
|
||||
display.vlcWidget = QtGui.QFrame(display)
|
||||
display.vlcWidget.setFrameStyle(QtGui.QFrame.NoFrame)
|
||||
display.vlc_widget = QtGui.QFrame(display)
|
||||
display.vlc_widget.setFrameStyle(QtGui.QFrame.NoFrame)
|
||||
# creating a basic vlc instance
|
||||
command_line_options = '--no-video-title-show'
|
||||
if not display.has_audio:
|
||||
command_line_options += ' --no-audio --no-video-title-show'
|
||||
if Settings().value('advanced/hide mouse') and display.controller.is_live:
|
||||
command_line_options += ' --mouse-hide-timeout=0'
|
||||
display.vlcInstance = vlc.Instance(command_line_options)
|
||||
display.vlc_instance = vlc.Instance(command_line_options)
|
||||
# creating an empty vlc media player
|
||||
display.vlcMediaPlayer = display.vlcInstance.media_player_new()
|
||||
display.vlcWidget.resize(display.size())
|
||||
display.vlcWidget.raise_()
|
||||
display.vlcWidget.hide()
|
||||
display.vlc_media_player = display.vlc_instance.media_player_new()
|
||||
display.vlc_widget.resize(display.size())
|
||||
display.vlc_widget.raise_()
|
||||
display.vlc_widget.hide()
|
||||
# The media player has to be 'connected' to the QFrame.
|
||||
# (otherwise a video would be displayed in it's own window)
|
||||
# This is platform specific!
|
||||
# You have to give the id of the QFrame (or similar object)
|
||||
# to vlc, different platforms have different functions for this.
|
||||
win_id = int(display.vlcWidget.winId())
|
||||
win_id = int(display.vlc_widget.winId())
|
||||
if sys.platform == "win32":
|
||||
display.vlcMediaPlayer.set_hwnd(win_id)
|
||||
display.vlc_media_player.set_hwnd(win_id)
|
||||
elif sys.platform == "darwin":
|
||||
# We have to use 'set_nsobject' since Qt4 on OSX uses Cocoa
|
||||
# framework and not the old Carbon.
|
||||
display.vlcMediaPlayer.set_nsobject(win_id)
|
||||
display.vlc_media_player.set_nsobject(win_id)
|
||||
else:
|
||||
# for Linux using the X Server
|
||||
display.vlcMediaPlayer.set_xwindow(win_id)
|
||||
display.vlc_media_player.set_xwindow(win_id)
|
||||
self.has_own_widget = True
|
||||
|
||||
def check_available(self):
|
||||
|
@ -165,18 +165,18 @@ class VlcPlayer(MediaPlayer):
|
|||
file_path = str(controller.media_info.file_info.absoluteFilePath())
|
||||
path = os.path.normcase(file_path)
|
||||
# create the media
|
||||
display.vlcMedia = display.vlcInstance.media_new_path(path)
|
||||
display.vlc_media = display.vlc_instance.media_new_path(path)
|
||||
# put the media in the media player
|
||||
display.vlcMediaPlayer.set_media(display.vlcMedia)
|
||||
display.vlc_media_player.set_media(display.vlc_media)
|
||||
# parse the metadata of the file
|
||||
display.vlcMedia.parse()
|
||||
display.vlc_media.parse()
|
||||
self.volume(display, volume)
|
||||
# We need to set media_info.length during load because we want
|
||||
# to avoid start and stop the video twice. Once for real playback
|
||||
# and once to just get media length.
|
||||
#
|
||||
# Media plugin depends on knowing media length before playback.
|
||||
controller.media_info.length = int(display.vlcMediaPlayer.get_media().get_duration() / 1000)
|
||||
controller.media_info.length = int(display.vlc_media_player.get_media().get_duration() / 1000)
|
||||
return True
|
||||
|
||||
def media_state_wait(self, display, media_state):
|
||||
|
@ -185,8 +185,8 @@ class VlcPlayer(MediaPlayer):
|
|||
Wait no longer than 60 seconds. (loading an iso file needs a long time)
|
||||
"""
|
||||
start = datetime.now()
|
||||
while not media_state == display.vlcMedia.get_state():
|
||||
if display.vlcMedia.get_state() == vlc.State.Error:
|
||||
while not media_state == display.vlc_media.get_state():
|
||||
if display.vlc_media.get_state() == vlc.State.Error:
|
||||
return False
|
||||
self.application.process_events()
|
||||
if (datetime.now() - start).seconds > 60:
|
||||
|
@ -197,7 +197,7 @@ class VlcPlayer(MediaPlayer):
|
|||
"""
|
||||
Resize the player
|
||||
"""
|
||||
display.vlcWidget.resize(display.size())
|
||||
display.vlc_widget.resize(display.size())
|
||||
|
||||
def play(self, display):
|
||||
"""
|
||||
|
@ -207,25 +207,25 @@ class VlcPlayer(MediaPlayer):
|
|||
start_time = 0
|
||||
if self.state != MediaState.Paused and controller.media_info.start_time > 0:
|
||||
start_time = controller.media_info.start_time
|
||||
display.vlcMediaPlayer.play()
|
||||
display.vlc_media_player.play()
|
||||
if not self.media_state_wait(display, vlc.State.Playing):
|
||||
return False
|
||||
self.volume(display, controller.media_info.volume)
|
||||
if start_time > 0:
|
||||
self.seek(display, controller.media_info.start_time * 1000)
|
||||
controller.media_info.length = int(display.vlcMediaPlayer.get_media().get_duration() / 1000)
|
||||
controller.media_info.length = int(display.vlc_media_player.get_media().get_duration() / 1000)
|
||||
controller.seek_slider.setMaximum(controller.media_info.length * 1000)
|
||||
self.state = MediaState.Playing
|
||||
display.vlcWidget.raise_()
|
||||
display.vlc_widget.raise_()
|
||||
return True
|
||||
|
||||
def pause(self, display):
|
||||
"""
|
||||
Pause the current item
|
||||
"""
|
||||
if display.vlcMedia.get_state() != vlc.State.Playing:
|
||||
if display.vlc_media.get_state() != vlc.State.Playing:
|
||||
return
|
||||
display.vlcMediaPlayer.pause()
|
||||
display.vlc_media_player.pause()
|
||||
if self.media_state_wait(display, vlc.State.Paused):
|
||||
self.state = MediaState.Paused
|
||||
|
||||
|
@ -233,7 +233,7 @@ class VlcPlayer(MediaPlayer):
|
|||
"""
|
||||
Stop the current item
|
||||
"""
|
||||
display.vlcMediaPlayer.stop()
|
||||
display.vlc_media_player.stop()
|
||||
self.state = MediaState.Stopped
|
||||
|
||||
def volume(self, display, vol):
|
||||
|
@ -241,21 +241,21 @@ class VlcPlayer(MediaPlayer):
|
|||
Set the volume
|
||||
"""
|
||||
if display.has_audio:
|
||||
display.vlcMediaPlayer.audio_set_volume(vol)
|
||||
display.vlc_media_player.audio_set_volume(vol)
|
||||
|
||||
def seek(self, display, seek_value):
|
||||
"""
|
||||
Go to a particular position
|
||||
"""
|
||||
if display.vlcMediaPlayer.is_seekable():
|
||||
display.vlcMediaPlayer.set_time(seek_value)
|
||||
if display.vlc_media_player.is_seekable():
|
||||
display.vlc_media_player.set_time(seek_value)
|
||||
|
||||
def reset(self, display):
|
||||
"""
|
||||
Reset the player
|
||||
"""
|
||||
display.vlcMediaPlayer.stop()
|
||||
display.vlcWidget.setVisible(False)
|
||||
display.vlc_media_player.stop()
|
||||
display.vlc_widget.setVisible(False)
|
||||
self.state = MediaState.Off
|
||||
|
||||
def set_visible(self, display, status):
|
||||
|
@ -263,23 +263,23 @@ class VlcPlayer(MediaPlayer):
|
|||
Set the visibility
|
||||
"""
|
||||
if self.has_own_widget:
|
||||
display.vlcWidget.setVisible(status)
|
||||
display.vlc_widget.setVisible(status)
|
||||
|
||||
def update_ui(self, display):
|
||||
"""
|
||||
Update the UI
|
||||
"""
|
||||
# Stop video if playback is finished.
|
||||
if display.vlcMedia.get_state() == vlc.State.Ended:
|
||||
if display.vlc_media.get_state() == vlc.State.Ended:
|
||||
self.stop(display)
|
||||
controller = display.controller
|
||||
if controller.media_info.end_time > 0:
|
||||
if display.vlcMediaPlayer.get_time() > controller.media_info.end_time * 1000:
|
||||
if display.vlc_media_player.get_time() > controller.media_info.end_time * 1000:
|
||||
self.stop(display)
|
||||
self.set_visible(display, False)
|
||||
if not controller.seek_slider.isSliderDown():
|
||||
controller.seek_slider.blockSignals(True)
|
||||
controller.seek_slider.setSliderPosition(display.vlcMediaPlayer.get_time())
|
||||
controller.seek_slider.setSliderPosition(display.vlc_media_player.get_time())
|
||||
controller.seek_slider.blockSignals(False)
|
||||
|
||||
def get_info(self):
|
||||
|
@ -287,8 +287,8 @@ class VlcPlayer(MediaPlayer):
|
|||
Return some information about this player
|
||||
"""
|
||||
return(translate('Media.player', 'VLC is an external player which '
|
||||
'supports a number of different formats.') +
|
||||
'<br/> <strong>' + translate('Media.player', 'Audio') +
|
||||
'</strong><br/>' + str(AUDIO_EXT) + '<br/><strong>' +
|
||||
translate('Media.player', 'Video') + '</strong><br/>' +
|
||||
str(VIDEO_EXT) + '<br/>')
|
||||
'supports a number of different formats.') +
|
||||
'<br/> <strong>' + translate('Media.player', 'Audio') +
|
||||
'</strong><br/>' + str(AUDIO_EXT) + '<br/><strong>' +
|
||||
translate('Media.player', 'Video') + '</strong><br/>' +
|
||||
str(VIDEO_EXT) + '<br/>')
|
|
@ -376,9 +376,9 @@ class WebkitPlayer(MediaPlayer):
|
|||
else:
|
||||
is_visible = "hidden"
|
||||
if controller.media_info.is_flash:
|
||||
display.frame.evaluateJavaScript('show_flash("setVisible", null, null, "%s");' % (is_visible))
|
||||
display.frame.evaluateJavaScript('show_flash("setVisible", null, null, "%s");' % is_visible)
|
||||
else:
|
||||
display.frame.evaluateJavaScript('show_video("setVisible", null, null, null, "%s");' % (is_visible))
|
||||
display.frame.evaluateJavaScript('show_video("setVisible", null, null, null, "%s");' % is_visible)
|
||||
|
||||
def update_ui(self, display):
|
||||
"""
|
||||
|
@ -412,9 +412,9 @@ class WebkitPlayer(MediaPlayer):
|
|||
Return some information about this player
|
||||
"""
|
||||
return(translate('Media.player', 'Webkit is a media player which runs '
|
||||
'inside a web browser. This player allows text over video to be '
|
||||
'rendered.') +
|
||||
'<br/> <strong>' + translate('Media.player', 'Audio') +
|
||||
'</strong><br/>' + str(AUDIO_EXT) + '<br/><strong>' +
|
||||
translate('Media.player', 'Video') + '</strong><br/>' +
|
||||
str(VIDEO_EXT) + '<br/>')
|
||||
'inside a web browser. This player allows text over video to be '
|
||||
'rendered.') +
|
||||
'<br/> <strong>' + translate('Media.player', 'Audio') +
|
||||
'</strong><br/>' + str(AUDIO_EXT) + '<br/><strong>' +
|
||||
translate('Media.player', 'Video') + '</strong><br/>' +
|
||||
str(VIDEO_EXT) + '<br/>')
|
||||
|
|
|
@ -31,9 +31,10 @@ The actual print service dialog
|
|||
"""
|
||||
import datetime
|
||||
import os
|
||||
import html
|
||||
import lxml.html
|
||||
|
||||
from PyQt4 import QtCore, QtGui
|
||||
from lxml import html
|
||||
|
||||
from openlp.core.common import Registry, Settings, UiStrings, translate
|
||||
from openlp.core.lib import get_text_file_string
|
||||
|
@ -183,7 +184,7 @@ class PrintServiceForm(QtGui.QDialog, Ui_PrintServiceDialog):
|
|||
self._add_element(
|
||||
'span', translate('OpenLP.ServiceManager', 'Custom Service Notes: '), div, classId='customNotesTitle')
|
||||
self._add_element('span', html.escape(self.footer_text_edit.toPlainText()), div, classId='customNotesText')
|
||||
self.document.setHtml(html.tostring(html_data).decode())
|
||||
self.document.setHtml(lxml.html.tostring(html_data).decode())
|
||||
self.preview_widget.updatePreview()
|
||||
|
||||
def _add_preview_item(self, body, item, index):
|
||||
|
@ -257,9 +258,9 @@ class PrintServiceForm(QtGui.QDialog, Ui_PrintServiceDialog):
|
|||
Tuple name/value pair to add as an optional attribute
|
||||
"""
|
||||
if text is not None:
|
||||
element = html.fragment_fromstring(str(text), create_parent=tag)
|
||||
element = lxml.html.fragment_fromstring(str(text), create_parent=tag)
|
||||
else:
|
||||
element = html.Element(tag)
|
||||
element = lxml.html.Element(tag)
|
||||
if parent is not None:
|
||||
parent.append(element)
|
||||
if classId is not None:
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
###############################################################################
|
||||
# OpenLP - Open Source Lyrics Projection #
|
||||
# --------------------------------------------------------------------------- #
|
||||
# Copyright (c) 2008-2013 Raoul Snyman #
|
||||
# Portions copyright (c) 2008-2013 Tim Bentley, Gerald Britton, Jonathan #
|
||||
# Copyright (c) 2008-2014 Raoul Snyman #
|
||||
# Portions copyright (c) 2008-2014 Tim Bentley, Gerald Britton, Jonathan #
|
||||
# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, #
|
||||
# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. #
|
||||
# Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru, #
|
||||
|
@ -30,7 +30,6 @@
|
|||
The service manager sets up, loads, saves and manages services.
|
||||
"""
|
||||
import html
|
||||
import logging
|
||||
import os
|
||||
import shutil
|
||||
import zipfile
|
||||
|
@ -38,11 +37,10 @@ import json
|
|||
from tempfile import mkstemp
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
from PyQt4 import QtCore, QtGui
|
||||
|
||||
from openlp.core.common import Registry, AppLocation, Settings, ThemeLevel, check_directory_exists, UiStrings, translate
|
||||
from openlp.core.common import Registry, AppLocation, Settings, ThemeLevel, OpenLPMixin, RegistryMixin, \
|
||||
check_directory_exists, UiStrings, translate
|
||||
from openlp.core.lib import OpenLPToolbar, ServiceItem, ItemCapabilities, PluginStatus, build_icon
|
||||
from openlp.core.lib.ui import critical_error_message_box, create_widget_action, find_and_set_in_combo_box
|
||||
from openlp.core.ui import ServiceNoteForm, ServiceItemEditForm, StartTimeForm
|
||||
|
@ -65,6 +63,7 @@ class ServiceManagerList(QtGui.QTreeWidget):
|
|||
def keyPressEvent(self, event):
|
||||
"""
|
||||
Capture Key press and respond accordingly.
|
||||
:param event:
|
||||
"""
|
||||
if isinstance(event, QtGui.QKeyEvent):
|
||||
# here accept the event and do something
|
||||
|
@ -75,7 +74,7 @@ class ServiceManagerList(QtGui.QTreeWidget):
|
|||
self.service_manager.on_move_selection_down()
|
||||
event.accept()
|
||||
elif event.key() == QtCore.Qt.Key_Delete:
|
||||
self.service_manager.onDeleteFromService()
|
||||
self.service_manager.on_delete_from_service()
|
||||
event.accept()
|
||||
event.ignore()
|
||||
else:
|
||||
|
@ -85,6 +84,7 @@ class ServiceManagerList(QtGui.QTreeWidget):
|
|||
"""
|
||||
Drag and drop event does not care what data is selected as the recipient will use events to request the data
|
||||
move just tell it what plugin to call
|
||||
:param event:
|
||||
"""
|
||||
if event.buttons() != QtCore.Qt.LeftButton:
|
||||
event.ignore()
|
||||
|
@ -99,16 +99,21 @@ class ServiceManagerList(QtGui.QTreeWidget):
|
|||
drag.start(QtCore.Qt.CopyAction)
|
||||
|
||||
|
||||
class ServiceManagerDialog(object):
|
||||
class Ui_ServiceManager(object):
|
||||
"""
|
||||
UI part of the Service Manager
|
||||
"""
|
||||
def setup_ui(self, widget):
|
||||
"""
|
||||
Define the UI
|
||||
:param widget:
|
||||
"""
|
||||
# start with the layout
|
||||
self.layout = QtGui.QVBoxLayout(widget)
|
||||
self.layout.setSpacing(0)
|
||||
self.layout.setMargin(0)
|
||||
# Create the top toolbar
|
||||
self.toolbar = OpenLPToolbar(self)
|
||||
self.toolbar = OpenLPToolbar(widget)
|
||||
self.toolbar.add_toolbar_action('newService', text=UiStrings().NewService, icon=':/general/general_new.png',
|
||||
tooltip=UiStrings().CreateService, triggers=self.on_new_service_clicked)
|
||||
self.toolbar.add_toolbar_action('openService', text=UiStrings().OpenService,
|
||||
|
@ -120,7 +125,7 @@ class ServiceManagerDialog(object):
|
|||
tooltip=translate('OpenLP.ServiceManager', 'Save this service.'),
|
||||
triggers=self.decide_save_method)
|
||||
self.toolbar.addSeparator()
|
||||
self.theme_label = QtGui.QLabel('%s:' % UiStrings().Theme, self)
|
||||
self.theme_label = QtGui.QLabel('%s:' % UiStrings().Theme, widget)
|
||||
self.theme_label.setMargin(3)
|
||||
self.theme_label.setObjectName('theme_label')
|
||||
self.toolbar.add_toolbar_widget(self.theme_label)
|
||||
|
@ -133,7 +138,7 @@ class ServiceManagerDialog(object):
|
|||
self.toolbar.setObjectName('toolbar')
|
||||
self.layout.addWidget(self.toolbar)
|
||||
# Create the service manager list
|
||||
self.service_manager_list = ServiceManagerList(self)
|
||||
self.service_manager_list = ServiceManagerList(widget)
|
||||
self.service_manager_list.setEditTriggers(
|
||||
QtGui.QAbstractItemView.CurrentChanged |
|
||||
QtGui.QAbstractItemView.DoubleClicked |
|
||||
|
@ -151,51 +156,61 @@ class ServiceManagerDialog(object):
|
|||
self.service_manager_list.__class__.dropEvent = self.drop_event
|
||||
self.layout.addWidget(self.service_manager_list)
|
||||
# Add the bottom toolbar
|
||||
self.order_toolbar = OpenLPToolbar(self)
|
||||
self.order_toolbar = OpenLPToolbar(widget)
|
||||
action_list = ActionList.get_instance()
|
||||
action_list.add_category(UiStrings().Service, CategoryOrder.standard_toolbar)
|
||||
self.service_manager_list.move_top = self.order_toolbar.add_toolbar_action('moveTop',
|
||||
self.service_manager_list.move_top = self.order_toolbar.add_toolbar_action(
|
||||
'moveTop',
|
||||
text=translate('OpenLP.ServiceManager', 'Move to &top'), icon=':/services/service_top.png',
|
||||
tooltip=translate('OpenLP.ServiceManager', 'Move item to the top of the service.'),
|
||||
can_shortcuts=True, category=UiStrings().Service, triggers=self.onServiceTop)
|
||||
self.service_manager_list.move_up = self.order_toolbar.add_toolbar_action('moveUp',
|
||||
can_shortcuts=True, category=UiStrings().Service, triggers=self.on_service_top)
|
||||
self.service_manager_list.move_up = self.order_toolbar.add_toolbar_action(
|
||||
'moveUp',
|
||||
text=translate('OpenLP.ServiceManager', 'Move &up'), icon=':/services/service_up.png',
|
||||
tooltip=translate('OpenLP.ServiceManager', 'Move item up one position in the service.'),
|
||||
can_shortcuts=True, category=UiStrings().Service, triggers=self.onServiceUp)
|
||||
self.service_manager_list.move_down = self.order_toolbar.add_toolbar_action('moveDown',
|
||||
can_shortcuts=True, category=UiStrings().Service, triggers=self.on_service_up)
|
||||
self.service_manager_list.move_down = self.order_toolbar.add_toolbar_action(
|
||||
'moveDown',
|
||||
text=translate('OpenLP.ServiceManager', 'Move &down'), icon=':/services/service_down.png',
|
||||
tooltip=translate('OpenLP.ServiceManager', 'Move item down one position in the service.'),
|
||||
can_shortcuts=True, category=UiStrings().Service, triggers=self.onServiceDown)
|
||||
self.service_manager_list.move_bottom = self.order_toolbar.add_toolbar_action('moveBottom',
|
||||
can_shortcuts=True, category=UiStrings().Service, triggers=self.on_service_down)
|
||||
self.service_manager_list.move_bottom = self.order_toolbar.add_toolbar_action(
|
||||
'moveBottom',
|
||||
text=translate('OpenLP.ServiceManager', 'Move to &bottom'), icon=':/services/service_bottom.png',
|
||||
tooltip=translate('OpenLP.ServiceManager', 'Move item to the end of the service.'),
|
||||
can_shortcuts=True, category=UiStrings().Service, triggers=self.onServiceEnd)
|
||||
self.service_manager_list.down = self.order_toolbar.add_toolbar_action('down',
|
||||
can_shortcuts=True, category=UiStrings().Service, triggers=self.on_service_end)
|
||||
self.service_manager_list.down = self.order_toolbar.add_toolbar_action(
|
||||
'down',
|
||||
text=translate('OpenLP.ServiceManager', 'Move &down'), can_shortcuts=True,
|
||||
tooltip=translate('OpenLP.ServiceManager', 'Moves the selection down the window.'), visible=False,
|
||||
triggers=self.on_move_selection_down)
|
||||
action_list.add_action(self.service_manager_list.down)
|
||||
self.service_manager_list.up = self.order_toolbar.add_toolbar_action('up',
|
||||
self.service_manager_list.up = self.order_toolbar.add_toolbar_action(
|
||||
'up',
|
||||
text=translate('OpenLP.ServiceManager', 'Move up'), can_shortcuts=True,
|
||||
tooltip=translate('OpenLP.ServiceManager', 'Moves the selection up the window.'), visible=False,
|
||||
triggers=self.on_move_selection_up)
|
||||
action_list.add_action(self.service_manager_list.up)
|
||||
self.order_toolbar.addSeparator()
|
||||
self.service_manager_list.delete = self.order_toolbar.add_toolbar_action('delete', can_shortcuts=True,
|
||||
self.service_manager_list.delete = self.order_toolbar.add_toolbar_action(
|
||||
'delete', can_shortcuts=True,
|
||||
text=translate('OpenLP.ServiceManager', '&Delete From Service'), icon=':/general/general_delete.png',
|
||||
tooltip=translate('OpenLP.ServiceManager', 'Delete the selected item from the service.'),
|
||||
triggers=self.onDeleteFromService)
|
||||
triggers=self.on_delete_from_service)
|
||||
self.order_toolbar.addSeparator()
|
||||
self.service_manager_list.expand = self.order_toolbar.add_toolbar_action('expand', can_shortcuts=True,
|
||||
self.service_manager_list.expand = self.order_toolbar.add_toolbar_action(
|
||||
'expand', can_shortcuts=True,
|
||||
text=translate('OpenLP.ServiceManager', '&Expand all'), icon=':/services/service_expand_all.png',
|
||||
tooltip=translate('OpenLP.ServiceManager', 'Expand all the service items.'),
|
||||
category=UiStrings().Service, triggers=self.on_expand_all)
|
||||
self.service_manager_list.collapse = self.order_toolbar.add_toolbar_action('collapse', can_shortcuts=True,
|
||||
self.service_manager_list.collapse = self.order_toolbar.add_toolbar_action(
|
||||
'collapse', can_shortcuts=True,
|
||||
text=translate('OpenLP.ServiceManager', '&Collapse all'), icon=':/services/service_collapse_all.png',
|
||||
tooltip=translate('OpenLP.ServiceManager', 'Collapse all the service items.'),
|
||||
category=UiStrings().Service, triggers=self.on_collapse_all)
|
||||
self.order_toolbar.addSeparator()
|
||||
self.service_manager_list.make_live = self.order_toolbar.add_toolbar_action('make_live', can_shortcuts=True,
|
||||
self.service_manager_list.make_live = self.order_toolbar.add_toolbar_action(
|
||||
'make_live', can_shortcuts=True,
|
||||
text=translate('OpenLP.ServiceManager', 'Go Live'), icon=':/general/general_live.png',
|
||||
tooltip=translate('OpenLP.ServiceManager', 'Send the selected item to Live.'),
|
||||
category=UiStrings().Service,
|
||||
|
@ -276,7 +291,7 @@ class ServiceManagerDialog(object):
|
|||
self.service_manager_list.down,
|
||||
self.service_manager_list.expand,
|
||||
self.service_manager_list.collapse
|
||||
])
|
||||
])
|
||||
Registry().register_function('theme_update_list', self.update_theme_list)
|
||||
Registry().register_function('config_screen_changed', self.regenerate_service_items)
|
||||
Registry().register_function('theme_update_global', self.theme_change)
|
||||
|
@ -286,13 +301,12 @@ class ServiceManagerDialog(object):
|
|||
"""
|
||||
Accept Drag events
|
||||
|
||||
``event``
|
||||
Handle of the event pint passed
|
||||
:param event: Handle of the event passed
|
||||
"""
|
||||
event.accept()
|
||||
|
||||
|
||||
class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
||||
class ServiceManager(OpenLPMixin, RegistryMixin, QtGui.QWidget, Ui_ServiceManager):
|
||||
"""
|
||||
Manages the services. This involves taking text strings from plugins and adding them to the service. This service
|
||||
can then be zipped up with all the resources used into one OSZ or oszl file for use on any OpenLP v2 installation.
|
||||
|
@ -305,7 +319,6 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
|||
super(ServiceManager, self).__init__(parent)
|
||||
self.active = build_icon(':/media/auto-start_active.png')
|
||||
self.inactive = build_icon(':/media/auto-start_inactive.png')
|
||||
Registry().register('service_manager', self)
|
||||
self.service_items = []
|
||||
self.suffixes = []
|
||||
self.drop_position = 0
|
||||
|
@ -314,20 +327,28 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
|||
self._modified = False
|
||||
self._file_name = ''
|
||||
self.service_has_all_original_files = True
|
||||
self.service_note_form = ServiceNoteForm()
|
||||
self.service_item_edit_form = ServiceItemEditForm()
|
||||
self.start_time_form = StartTimeForm()
|
||||
# start with the layout
|
||||
self.layout = QtGui.QVBoxLayout(self)
|
||||
self.layout.setSpacing(0)
|
||||
self.layout.setMargin(0)
|
||||
|
||||
def bootstrap_initialise(self):
|
||||
"""
|
||||
To be called as part of initialisation
|
||||
"""
|
||||
self.setup_ui(self)
|
||||
# Need to use event as called across threads and UI is updated
|
||||
QtCore.QObject.connect(self, QtCore.SIGNAL('servicemanager_set_item'), self.on_set_item)
|
||||
|
||||
def bootstrap_post_set_up(self):
|
||||
"""
|
||||
Can be set up as a late setup
|
||||
"""
|
||||
self.service_note_form = ServiceNoteForm()
|
||||
self.service_item_edit_form = ServiceItemEditForm()
|
||||
self.start_time_form = StartTimeForm()
|
||||
|
||||
def set_modified(self, modified=True):
|
||||
"""
|
||||
Setter for property "modified". Sets whether or not the current service has been modified.
|
||||
|
||||
:param modified: Indicates if the service has new or removed items. Used to trigger a remote update.
|
||||
"""
|
||||
if modified:
|
||||
self.service_id += 1
|
||||
|
@ -344,6 +365,8 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
|||
def set_file_name(self, file_name):
|
||||
"""
|
||||
Setter for service file.
|
||||
|
||||
:param file_name: The service file name
|
||||
"""
|
||||
self._file_name = str(file_name)
|
||||
self.main_window.set_service_modified(self.is_modified(), self.short_file_name())
|
||||
|
@ -369,19 +392,20 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
|||
"""
|
||||
self.suffixes = []
|
||||
|
||||
def supported_suffixes(self, suffix):
|
||||
def supported_suffixes(self, suffix_list):
|
||||
"""
|
||||
Adds Suffixes supported to the master list. Called from Plugins.
|
||||
|
||||
``suffix``
|
||||
New Suffix to be supported
|
||||
:param suffix_list: New Suffix's to be supported
|
||||
"""
|
||||
if not suffix in self.suffixes:
|
||||
self.suffixes.append(suffix)
|
||||
for suffix in suffix_list:
|
||||
if not suffix in self.suffixes:
|
||||
self.suffixes.append(suffix)
|
||||
|
||||
def on_new_service_clicked(self):
|
||||
def on_new_service_clicked(self, field=None):
|
||||
"""
|
||||
Create a new service.
|
||||
:param field:
|
||||
"""
|
||||
if self.is_modified():
|
||||
result = self.save_modified_service()
|
||||
|
@ -396,8 +420,7 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
|||
"""
|
||||
Loads the service file and saves the existing one it there is one unchanged.
|
||||
|
||||
``load_file``
|
||||
The service file to the loaded. Will be None is from menu so selection will be required.
|
||||
:param load_file: The service file to the loaded. Will be None is from menu so selection will be required.
|
||||
"""
|
||||
if self.is_modified():
|
||||
result = self.save_modified_service()
|
||||
|
@ -432,9 +455,10 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
|||
QtGui.QMessageBox.Save | QtGui.QMessageBox.Discard |
|
||||
QtGui.QMessageBox.Cancel, QtGui.QMessageBox.Save)
|
||||
|
||||
def on_recent_service_clicked(self):
|
||||
def on_recent_service_clicked(self, field=None):
|
||||
"""
|
||||
Load a recent file as the service triggered by mainwindow recent service list.
|
||||
:param field:
|
||||
"""
|
||||
sender = self.sender()
|
||||
self.load_file(sender.data())
|
||||
|
@ -451,6 +475,19 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
|||
Settings().setValue('servicemanager/last file', '')
|
||||
self.plugin_manager.new_service_created()
|
||||
|
||||
def create_basic_service(self):
|
||||
"""
|
||||
Create the initial service array with the base items to be saved.
|
||||
|
||||
:return service array
|
||||
"""
|
||||
service = []
|
||||
core = {'lite-service': self._save_lite,
|
||||
'service-theme': self.service_theme
|
||||
}
|
||||
service.append({'openlp_core': core})
|
||||
return service
|
||||
|
||||
def save_file(self):
|
||||
"""
|
||||
Save the current service file.
|
||||
|
@ -464,14 +501,14 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
|||
temp_file, temp_file_name = mkstemp('.osz', 'openlp_')
|
||||
# We don't need the file handle.
|
||||
os.close(temp_file)
|
||||
log.debug(temp_file_name)
|
||||
self.log_debug(temp_file_name)
|
||||
path_file_name = str(self.file_name())
|
||||
path, file_name = os.path.split(path_file_name)
|
||||
base_name = os.path.splitext(file_name)[0]
|
||||
service_file_name = '%s.osj' % base_name
|
||||
log.debug('ServiceManager.save_file - %s', path_file_name)
|
||||
self.log_debug('ServiceManager.save_file - %s' % path_file_name)
|
||||
Settings().setValue(self.main_window.service_manager_settings_section + '/last directory', path)
|
||||
service = []
|
||||
service = self.create_basic_service()
|
||||
write_list = []
|
||||
missing_list = []
|
||||
audio_files = []
|
||||
|
@ -522,11 +559,11 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
|||
for file_item in write_list:
|
||||
file_size = os.path.getsize(file_item)
|
||||
total_size += file_size
|
||||
log.debug('ServiceManager.save_file - ZIP contents size is %i bytes' % total_size)
|
||||
self.log_debug('ServiceManager.save_file - ZIP contents size is %i bytes' % total_size)
|
||||
service_content = json.dumps(service)
|
||||
# Usual Zip file cannot exceed 2GiB, file with Zip64 cannot be extracted using unzip in UNIX.
|
||||
allow_zip_64 = (total_size > 2147483648 + len(service_content))
|
||||
log.debug('ServiceManager.save_file - allowZip64 is %s' % allow_zip_64)
|
||||
self.log_debug('ServiceManager.save_file - allowZip64 is %s' % allow_zip_64)
|
||||
zip_file = None
|
||||
success = True
|
||||
self.main_window.increment_progress_bar()
|
||||
|
@ -549,7 +586,7 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
|||
shutil.copy(audio_from, save_file)
|
||||
zip_file.write(audio_from, audio_to)
|
||||
except IOError:
|
||||
log.exception('Failed to save service to disk: %s', temp_file_name)
|
||||
self.log_exception('Failed to save service to disk: %s' % temp_file_name)
|
||||
self.main_window.error_message(translate('OpenLP.ServiceManager', 'Error Saving File'),
|
||||
translate('OpenLP.ServiceManager', 'There was an error saving your file.'))
|
||||
success = False
|
||||
|
@ -578,14 +615,14 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
|||
temp_file, temp_file_name = mkstemp('.oszl', 'openlp_')
|
||||
# We don't need the file handle.
|
||||
os.close(temp_file)
|
||||
log.debug(temp_file_name)
|
||||
self.log_debug(temp_file_name)
|
||||
path_file_name = str(self.file_name())
|
||||
path, file_name = os.path.split(path_file_name)
|
||||
base_name = os.path.splitext(file_name)[0]
|
||||
service_file_name = '%s.osj' % base_name
|
||||
log.debug('ServiceManager.save_file - %s', path_file_name)
|
||||
self.log_debug('ServiceManager.save_file - %s' % path_file_name)
|
||||
Settings().setValue(self.main_window.service_manager_settings_section + '/last directory', path)
|
||||
service = []
|
||||
service = self.create_basic_service()
|
||||
self.application.set_busy_cursor()
|
||||
# Number of items + 1 to zip it
|
||||
self.main_window.display_progress_bar(len(self.service_items) + 1)
|
||||
|
@ -604,7 +641,7 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
|||
# First we add service contents.
|
||||
zip_file.writestr(service_file_name, service_content)
|
||||
except IOError:
|
||||
log.exception('Failed to save service to disk: %s', temp_file_name)
|
||||
self.log_exception('Failed to save service to disk: %s', temp_file_name)
|
||||
self.main_window.error_message(translate('OpenLP.ServiceManager', 'Error Saving File'),
|
||||
translate('OpenLP.ServiceManager', 'There was an error saving your file.'))
|
||||
success = False
|
||||
|
@ -623,7 +660,7 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
|||
delete_file(temp_file_name)
|
||||
return success
|
||||
|
||||
def save_file_as(self):
|
||||
def save_file_as(self, field=None):
|
||||
"""
|
||||
Get a file name and then call :func:`ServiceManager.save_file` to save the file.
|
||||
"""
|
||||
|
@ -656,8 +693,8 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
|||
'(*.oszl)'))
|
||||
else:
|
||||
file_name = QtGui.QFileDialog.getSaveFileName(self.main_window, UiStrings().SaveService, path,
|
||||
translate('OpenLP.ServiceManager', 'OpenLP Service Files (*'
|
||||
'.osz);;'))
|
||||
translate('OpenLP.ServiceManager',
|
||||
'OpenLP Service Files (*.osz);;'))
|
||||
if not file_name:
|
||||
return False
|
||||
if os.path.splitext(file_name)[1] == '':
|
||||
|
@ -668,9 +705,10 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
|||
self.set_file_name(file_name)
|
||||
self.decide_save_method()
|
||||
|
||||
def decide_save_method(self):
|
||||
def decide_save_method(self, field=None):
|
||||
"""
|
||||
Determine which type of save method to use.
|
||||
:param field:
|
||||
"""
|
||||
if not self.file_name():
|
||||
return self.save_file_as()
|
||||
|
@ -682,6 +720,7 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
|||
def load_file(self, file_name):
|
||||
"""
|
||||
Load an existing service file
|
||||
:param file_name:
|
||||
"""
|
||||
if not file_name:
|
||||
return False
|
||||
|
@ -697,14 +736,14 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
|||
try:
|
||||
ucs_file = zip_info.filename
|
||||
except UnicodeDecodeError:
|
||||
log.exception('file_name "%s" is not valid UTF-8' % zip_info.file_name)
|
||||
self.log_exception('file_name "%s" is not valid UTF-8' % zip_info.file_name)
|
||||
critical_error_message_box(message=translate('OpenLP.ServiceManager',
|
||||
'File is not a valid service.\n The content encoding is not UTF-8.'))
|
||||
continue
|
||||
os_file = ucs_file.replace('/', os.path.sep)
|
||||
if not os_file.startswith('audio'):
|
||||
os_file = os.path.split(os_file)[1]
|
||||
log.debug('Extract file: %s', os_file)
|
||||
self.log_debug('Extract file: %s' % os_file)
|
||||
zip_info.filename = os_file
|
||||
zip_file.extract(zip_info, self.service_path)
|
||||
if os_file.endswith('osj') or os_file.endswith('osd'):
|
||||
|
@ -722,38 +761,26 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
|||
self.new_file()
|
||||
self.set_file_name(file_name)
|
||||
self.main_window.display_progress_bar(len(items))
|
||||
for item in items:
|
||||
self.main_window.increment_progress_bar()
|
||||
service_item = ServiceItem()
|
||||
if self._save_lite:
|
||||
service_item.set_from_service(item)
|
||||
else:
|
||||
service_item.set_from_service(item, self.service_path)
|
||||
service_item.validate_item(self.suffixes)
|
||||
if service_item.is_capable(ItemCapabilities.OnLoadUpdate):
|
||||
new_item = Registry().get(service_item.name).service_load(service_item)
|
||||
if new_item:
|
||||
service_item = new_item
|
||||
self.add_service_item(service_item, repaint=False)
|
||||
self.process_service_items(items)
|
||||
delete_file(p_file)
|
||||
self.main_window.add_recent_file(file_name)
|
||||
self.set_modified(False)
|
||||
Settings().setValue('servicemanager/last file', file_name)
|
||||
else:
|
||||
critical_error_message_box(message=translate('OpenLP.ServiceManager', 'File is not a valid service.'))
|
||||
log.error('File contains no service data')
|
||||
self.log_error('File contains no service data')
|
||||
except (IOError, NameError, zipfile.BadZipfile):
|
||||
log.exception('Problem loading service file %s' % file_name)
|
||||
self.log_exception('Problem loading service file %s' % file_name)
|
||||
critical_error_message_box(message=translate('OpenLP.ServiceManager',
|
||||
'File could not be opened because it is corrupt.'))
|
||||
except zipfile.BadZipfile:
|
||||
if os.path.getsize(file_name) == 0:
|
||||
log.exception('Service file is zero sized: %s' % file_name)
|
||||
self.log_exception('Service file is zero sized: %s' % file_name)
|
||||
QtGui.QMessageBox.information(self, translate('OpenLP.ServiceManager', 'Empty File'),
|
||||
translate('OpenLP.ServiceManager', 'This service file does not contain '
|
||||
'any data.'))
|
||||
else:
|
||||
log.exception('Service file is cannot be extracted as zip: %s' % file_name)
|
||||
self.log_exception('Service file is cannot be extracted as zip: %s' % file_name)
|
||||
QtGui.QMessageBox.information(self, translate('OpenLP.ServiceManager', 'Corrupt File'),
|
||||
translate('OpenLP.ServiceManager',
|
||||
'This file is either corrupt or it is not an OpenLP 2 service file.'))
|
||||
|
@ -768,6 +795,34 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
|||
self.application.set_normal_cursor()
|
||||
self.repaint_service_list(-1, -1)
|
||||
|
||||
def process_service_items(self, service_items):
|
||||
"""
|
||||
Process all the array of service items loaded from the saved service
|
||||
|
||||
:param service_items: list of service_items
|
||||
"""
|
||||
for item in service_items:
|
||||
self.main_window.increment_progress_bar()
|
||||
service_item = ServiceItem()
|
||||
if 'openlp_core' in item:
|
||||
item = item.get('openlp_core')
|
||||
theme = item.get('service-theme', None)
|
||||
if theme:
|
||||
find_and_set_in_combo_box(self.theme_combo_box, theme, set_missing=False)
|
||||
if theme == self.theme_combo_box.currentText():
|
||||
self.renderer.set_service_theme(theme)
|
||||
else:
|
||||
if self._save_lite:
|
||||
service_item.set_from_service(item)
|
||||
else:
|
||||
service_item.set_from_service(item, self.service_path)
|
||||
service_item.validate_item(self.suffixes)
|
||||
if service_item.is_capable(ItemCapabilities.OnLoadUpdate):
|
||||
new_item = Registry().get(service_item.name).service_load(service_item)
|
||||
if new_item:
|
||||
service_item = new_item
|
||||
self.add_service_item(service_item, repaint=False)
|
||||
|
||||
def load_last_file(self):
|
||||
"""
|
||||
Load the last service item from the service manager when the service was last closed. Can be blank if there was
|
||||
|
@ -780,6 +835,8 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
|||
def context_menu(self, point):
|
||||
"""
|
||||
The Right click context menu from the Serviceitem list
|
||||
|
||||
:param point: The location of the cursor.
|
||||
"""
|
||||
item = self.service_manager_list.itemAt(point)
|
||||
if item is None:
|
||||
|
@ -843,9 +900,10 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
|||
theme_action.setChecked(True)
|
||||
self.menu.exec_(self.service_manager_list.mapToGlobal(point))
|
||||
|
||||
def on_service_item_note_form(self):
|
||||
def on_service_item_note_form(self, field=None):
|
||||
"""
|
||||
Allow the service note to be edited
|
||||
:param field:
|
||||
"""
|
||||
item = self.find_service_item()[0]
|
||||
self.service_note_form.text_edit.setPlainText(self.service_items[item]['service_item'].notes)
|
||||
|
@ -854,18 +912,21 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
|||
self.repaint_service_list(item, -1)
|
||||
self.set_modified()
|
||||
|
||||
def on_start_time_form(self):
|
||||
def on_start_time_form(self, field=None):
|
||||
"""
|
||||
Opens a dialog to type in service item notes.
|
||||
:param field:
|
||||
"""
|
||||
item = self.find_service_item()[0]
|
||||
self.start_time_form.item = self.service_items[item]
|
||||
if self.start_time_form.exec_():
|
||||
self.repaint_service_list(item, -1)
|
||||
|
||||
def toggle_auto_play_slides_once(self):
|
||||
def toggle_auto_play_slides_once(self, field=None):
|
||||
"""
|
||||
Toggle Auto play slide once. Inverts auto play once option for the item
|
||||
|
||||
:param field:
|
||||
"""
|
||||
item = self.find_service_item()[0]
|
||||
service_item = self.service_items[item]['service_item']
|
||||
|
@ -878,9 +939,11 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
|||
self.main_window.general_settings_section + '/loop delay')
|
||||
self.set_modified()
|
||||
|
||||
def toggle_auto_play_slides_loop(self):
|
||||
def toggle_auto_play_slides_loop(self, field=None):
|
||||
"""
|
||||
Toggle Auto play slide loop.
|
||||
|
||||
:param field:
|
||||
"""
|
||||
item = self.find_service_item()[0]
|
||||
service_item = self.service_items[item]['service_item']
|
||||
|
@ -893,9 +956,10 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
|||
self.main_window.general_settings_section + '/loop delay')
|
||||
self.set_modified()
|
||||
|
||||
def on_timed_slide_interval(self):
|
||||
def on_timed_slide_interval(self, field=None):
|
||||
"""
|
||||
Shows input dialog for enter interval in seconds for delay
|
||||
:param field:
|
||||
"""
|
||||
item = self.find_service_item()[0]
|
||||
service_item = self.service_items[item]['service_item']
|
||||
|
@ -926,9 +990,10 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
|||
self.service_items[item]['service_item'].will_auto_start = \
|
||||
not self.service_items[item]['service_item'].will_auto_start
|
||||
|
||||
def on_service_item_edit_form(self):
|
||||
def on_service_item_edit_form(self, field=None):
|
||||
"""
|
||||
Opens a dialog to edit the service item and update the service display if changes are saved.
|
||||
:param field:
|
||||
"""
|
||||
item = self.find_service_item()[0]
|
||||
self.service_item_edit_form.set_service_item(self.service_items[item]['service_item'])
|
||||
|
@ -941,11 +1006,8 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
|||
Called by the SlideController to request a preview item be made live and allows the next preview to be updated
|
||||
if relevant.
|
||||
|
||||
``unique_identifier``
|
||||
Reference to the service_item
|
||||
|
||||
``row``
|
||||
individual row number
|
||||
:param unique_identifier: Reference to the service_item
|
||||
:param row: individual row number
|
||||
"""
|
||||
for sitem in self.service_items:
|
||||
if sitem['service_item'].unique_identifier == unique_identifier:
|
||||
|
@ -958,9 +1020,9 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
|||
"""
|
||||
Called by the SlideController to select the next service item.
|
||||
"""
|
||||
if not self.service_manager_list.selected_items():
|
||||
if not self.service_manager_list.selectedItems():
|
||||
return
|
||||
selected = self.service_manager_list.selected_items()[0]
|
||||
selected = self.service_manager_list.selectedItems()[0]
|
||||
look_for = 0
|
||||
service_iterator = QtGui.QTreeWidgetItemIterator(self.service_manager_list)
|
||||
while service_iterator.value():
|
||||
|
@ -976,13 +1038,11 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
|||
"""
|
||||
Called by the SlideController to select the previous service item.
|
||||
|
||||
``last_slide``
|
||||
Is this the last slide in the service_item
|
||||
|
||||
:param last_slide: Is this the last slide in the service_item.
|
||||
"""
|
||||
if not self.service_manager_list.selected_items():
|
||||
if not self.service_manager_list.selectedItems():
|
||||
return
|
||||
selected = self.service_manager_list.selected_items()[0]
|
||||
selected = self.service_manager_list.selectedItems()[0]
|
||||
prev_item = None
|
||||
prev_item_last_slide = None
|
||||
service_iterator = QtGui.QTreeWidgetItemIterator(self.service_manager_list)
|
||||
|
@ -1006,17 +1066,22 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
|||
prev_item_last_slide = service_iterator.value()
|
||||
service_iterator += 1
|
||||
|
||||
def on_set_item(self, message):
|
||||
def on_set_item(self, message, field=None):
|
||||
"""
|
||||
Called by a signal to select a specific item and make it live usually from remote.
|
||||
|
||||
:param field:
|
||||
:param message: The data passed in from a remove message
|
||||
"""
|
||||
self.set_item(int(message))
|
||||
|
||||
def set_item(self, index):
|
||||
"""
|
||||
Makes a specific item in the service live.
|
||||
|
||||
:param index: The index of the service item list to be actioned.
|
||||
"""
|
||||
if index >= 0 and index < self.service_manager_list.topLevelItemCount():
|
||||
if 0 >= index < self.service_manager_list.topLevelItemCount():
|
||||
item = self.service_manager_list.topLevelItem(index)
|
||||
self.service_manager_list.setCurrentItem(item)
|
||||
self.make_live()
|
||||
|
@ -1041,9 +1106,10 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
|||
return
|
||||
self.service_manager_list.setCurrentItem(item_after)
|
||||
|
||||
def on_collapse_all(self):
|
||||
def on_collapse_all(self, field=None):
|
||||
"""
|
||||
Collapse all the service items.
|
||||
:param field:
|
||||
"""
|
||||
for item in self.service_items:
|
||||
item['expanded'] = False
|
||||
|
@ -1052,13 +1118,16 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
|||
def collapsed(self, item):
|
||||
"""
|
||||
Record if an item is collapsed. Used when repainting the list to get the correct state.
|
||||
|
||||
:param item: The service item to be checked
|
||||
"""
|
||||
pos = item.data(0, QtCore.Qt.UserRole)
|
||||
self.service_items[pos - 1]['expanded'] = False
|
||||
|
||||
def on_expand_all(self):
|
||||
def on_expand_all(self, field=None):
|
||||
"""
|
||||
Collapse all the service items.
|
||||
:param field:
|
||||
"""
|
||||
for item in self.service_items:
|
||||
item['expanded'] = True
|
||||
|
@ -1067,13 +1136,16 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
|||
def expanded(self, item):
|
||||
"""
|
||||
Record if an item is collapsed. Used when repainting the list to get the correct state.
|
||||
|
||||
:param item: The service item to be checked
|
||||
"""
|
||||
pos = item.data(0, QtCore.Qt.UserRole)
|
||||
self.service_items[pos - 1]['expanded'] = True
|
||||
|
||||
def onServiceTop(self):
|
||||
def on_service_top(self, field=None):
|
||||
"""
|
||||
Move the current ServiceItem to the top of the list.
|
||||
:param field:
|
||||
"""
|
||||
item, child = self.find_service_item()
|
||||
if item < len(self.service_items) and item != -1:
|
||||
|
@ -1083,9 +1155,10 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
|||
self.repaint_service_list(0, child)
|
||||
self.set_modified()
|
||||
|
||||
def onServiceUp(self):
|
||||
def on_service_up(self, field=None):
|
||||
"""
|
||||
Move the current ServiceItem one position up in the list.
|
||||
:param field:
|
||||
"""
|
||||
item, child = self.find_service_item()
|
||||
if item > 0:
|
||||
|
@ -1095,9 +1168,10 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
|||
self.repaint_service_list(item - 1, child)
|
||||
self.set_modified()
|
||||
|
||||
def onServiceDown(self):
|
||||
def on_service_down(self, field=None):
|
||||
"""
|
||||
Move the current ServiceItem one position down in the list.
|
||||
:param field:
|
||||
"""
|
||||
item, child = self.find_service_item()
|
||||
if item < len(self.service_items) and item != -1:
|
||||
|
@ -1107,9 +1181,10 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
|||
self.repaint_service_list(item + 1, child)
|
||||
self.set_modified()
|
||||
|
||||
def onServiceEnd(self):
|
||||
def on_service_end(self, field=None):
|
||||
"""
|
||||
Move the current ServiceItem to the bottom of the list.
|
||||
:param field:
|
||||
"""
|
||||
item, child = self.find_service_item()
|
||||
if item < len(self.service_items) and item != -1:
|
||||
|
@ -1119,9 +1194,10 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
|||
self.repaint_service_list(len(self.service_items) - 1, child)
|
||||
self.set_modified()
|
||||
|
||||
def onDeleteFromService(self):
|
||||
def on_delete_from_service(self, field=None):
|
||||
"""
|
||||
Remove the current ServiceItem from the list.
|
||||
:param field:
|
||||
"""
|
||||
item = self.find_service_item()[0]
|
||||
if item != -1:
|
||||
|
@ -1134,11 +1210,8 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
|||
Clear the existing service list and prepaint all the items. This is used when moving items as the move takes
|
||||
place in a supporting list, and when regenerating all the items due to theme changes.
|
||||
|
||||
``service_item``
|
||||
The item which changed. (int)
|
||||
|
||||
``service_item_child``
|
||||
The child of the ``service_item``, which will be selected. (int)
|
||||
:param service_item: The item which changed. (int)
|
||||
:param service_item_child: The child of the ``service_item``, which will be selected. (int)
|
||||
"""
|
||||
# Correct order of items in array
|
||||
count = 1
|
||||
|
@ -1211,18 +1284,18 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
|||
"""
|
||||
Empties the service_path of temporary files on system exit.
|
||||
"""
|
||||
log.debug('Cleaning up service_path')
|
||||
for file_name in os.listdir(self.service_path):
|
||||
file_path = os.path.join(self.service_path, file_name)
|
||||
delete_file(file_path)
|
||||
if os.path.exists(os.path.join(self.service_path, 'audio')):
|
||||
shutil.rmtree(os.path.join(self.service_path, 'audio'), True)
|
||||
|
||||
def on_theme_combo_box_selected(self, currentIndex):
|
||||
def on_theme_combo_box_selected(self, current_index):
|
||||
"""
|
||||
Set the theme for the current service.
|
||||
|
||||
:param current_index: The combo box index for the selected item
|
||||
"""
|
||||
log.debug('on_theme_combo_box_selected')
|
||||
self.service_theme = self.theme_combo_box.currentText()
|
||||
self.renderer.set_service_theme(self.service_theme)
|
||||
Settings().setValue(self.main_window.service_manager_settings_section + '/service theme', self.service_theme)
|
||||
|
@ -1232,7 +1305,6 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
|||
"""
|
||||
The theme may have changed in the settings dialog so make sure the theme combo box is in the correct state.
|
||||
"""
|
||||
log.debug('theme_change')
|
||||
visible = self.renderer.theme_level == ThemeLevel.Global
|
||||
self.theme_label.setVisible(visible)
|
||||
self.theme_combo_box.setVisible(visible)
|
||||
|
@ -1240,9 +1312,10 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
|||
def regenerate_service_items(self, changed=False):
|
||||
"""
|
||||
Rebuild the service list as things have changed and a repaint is the easiest way to do this.
|
||||
|
||||
:param changed: True if the list has changed for new / removed items. False for a theme change.
|
||||
"""
|
||||
self.application.set_busy_cursor()
|
||||
log.debug('regenerate_service_items')
|
||||
# force reset of renderer as theme data has changed
|
||||
self.service_has_all_original_files = True
|
||||
if self.service_items:
|
||||
|
@ -1274,46 +1347,49 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
|||
self.repaint_service_list(-1, -1)
|
||||
self.application.set_normal_cursor()
|
||||
|
||||
def replace_service_item(self, newItem):
|
||||
def replace_service_item(self, new_item):
|
||||
"""
|
||||
Using the service item passed replace the one with the same edit id if found.
|
||||
|
||||
:param new_item: a new service item to up date an existing one.
|
||||
"""
|
||||
for item_count, item in enumerate(self.service_items):
|
||||
if item['service_item'].edit_id == newItem.edit_id and item['service_item'].name == newItem.name:
|
||||
newItem.render()
|
||||
newItem.merge(item['service_item'])
|
||||
item['service_item'] = newItem
|
||||
if item['service_item'].edit_id == new_item.edit_id and item['service_item'].name == new_item.name:
|
||||
new_item.render()
|
||||
new_item.merge(item['service_item'])
|
||||
item['service_item'] = new_item
|
||||
self.repaint_service_list(item_count + 1, 0)
|
||||
self.live_controller.replace_service_manager_item(newItem)
|
||||
self.live_controller.replace_service_manager_item(new_item)
|
||||
self.set_modified()
|
||||
|
||||
def add_service_item(self, item, rebuild=False, expand=None, replace=False, repaint=True, selected=False):
|
||||
"""
|
||||
Add a Service item to the list
|
||||
|
||||
``item``
|
||||
Service Item to be added
|
||||
|
||||
``expand``
|
||||
Override the default expand settings. (Tristate)
|
||||
:param item: Service Item to be added
|
||||
:param rebuild: Do we need to rebuild the live display (Default False)
|
||||
:param expand: Override the default expand settings. (Tristate)
|
||||
:param replace: Is the service item a replacement (Default False)
|
||||
:param repaint: Do we need to repaint the service item list (Default True)
|
||||
:param selected: Has the item been selected (Default False)
|
||||
"""
|
||||
# if not passed set to config value
|
||||
if expand is None:
|
||||
expand = Settings().value('advanced/expand service item')
|
||||
item.from_service = True
|
||||
if replace:
|
||||
sitem, child = self.find_service_item()
|
||||
item.merge(self.service_items[sitem]['service_item'])
|
||||
self.service_items[sitem]['service_item'] = item
|
||||
self.repaint_service_list(sitem, child)
|
||||
s_item, child = self.find_service_item()
|
||||
item.merge(self.service_items[s_item]['service_item'])
|
||||
self.service_items[s_item]['service_item'] = item
|
||||
self.repaint_service_list(s_item, child)
|
||||
self.live_controller.replace_service_manager_item(item)
|
||||
else:
|
||||
item.render()
|
||||
# nothing selected for dnd
|
||||
if self.drop_position == 0:
|
||||
if isinstance(item, list):
|
||||
for inditem in item:
|
||||
self.service_items.append({'service_item': inditem,
|
||||
for ind_item in item:
|
||||
self.service_items.append({'service_item': ind_item,
|
||||
'order': len(self.service_items) + 1,
|
||||
'expanded': expand, 'selected': selected})
|
||||
else:
|
||||
|
@ -1324,8 +1400,8 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
|||
self.repaint_service_list(len(self.service_items) - 1, -1)
|
||||
else:
|
||||
self.service_items.insert(self.drop_position,
|
||||
{'service_item': item, 'order': self.drop_position,
|
||||
'expanded': expand, 'selected': selected})
|
||||
{'service_item': item, 'order': self.drop_position,
|
||||
'expanded': expand, 'selected': selected})
|
||||
self.repaint_service_list(self.drop_position, -1)
|
||||
# if rebuilding list make sure live is fixed.
|
||||
if rebuild:
|
||||
|
@ -1357,9 +1433,10 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
|||
else:
|
||||
return self.service_items[item]['service_item']
|
||||
|
||||
def on_make_live(self):
|
||||
def on_make_live(self, field=None):
|
||||
"""
|
||||
Send the current item to the Live slide controller but triggered by a tablewidget click event.
|
||||
:param field:
|
||||
"""
|
||||
self.make_live()
|
||||
|
||||
|
@ -1367,8 +1444,7 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
|||
"""
|
||||
Send the current item to the Live slide controller
|
||||
|
||||
``row``
|
||||
Row number to be displayed if from preview. -1 is passed if the value is not set
|
||||
:param row: Row number to be displayed if from preview. -1 is passed if the value is not set
|
||||
"""
|
||||
item, child = self.find_service_item()
|
||||
# No items in service
|
||||
|
@ -1394,9 +1470,10 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
|||
'is missing or inactive'))
|
||||
self.application.set_normal_cursor()
|
||||
|
||||
def remote_edit(self):
|
||||
def remote_edit(self, field=None):
|
||||
"""
|
||||
Triggers a remote edit to a plugin to allow item to be edited.
|
||||
:param field:
|
||||
"""
|
||||
item = self.find_service_item()[0]
|
||||
if self.service_items[item]['service_item'].is_capable(ItemCapabilities.CanEdit):
|
||||
|
@ -1405,9 +1482,10 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
|||
if new_item:
|
||||
self.add_service_item(new_item, replace=True)
|
||||
|
||||
def create_custom(self):
|
||||
def create_custom(self, field=None):
|
||||
"""
|
||||
Saves the current text item as a custom slide
|
||||
:param field:
|
||||
"""
|
||||
item = self.find_service_item()[0]
|
||||
Registry().execute('custom_create_from_service', self.service_items[item]['service_item'])
|
||||
|
@ -1441,8 +1519,7 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
|||
Receive drop event and trigger an internal event to get the plugins to build and push the correct service item.
|
||||
The drag event payload carries the plugin name
|
||||
|
||||
``event``
|
||||
Handle of the event pint passed
|
||||
:param event: Handle of the event passed
|
||||
"""
|
||||
link = event.mimeData()
|
||||
if link.hasUrls():
|
||||
|
@ -1501,8 +1578,7 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
|||
"""
|
||||
Called from ThemeManager when the Themes have changed
|
||||
|
||||
``theme_list``
|
||||
A list of current themes to be displayed
|
||||
:param theme_list: A list of current themes to be displayed
|
||||
"""
|
||||
self.theme_combo_box.clear()
|
||||
self.theme_menu.clear()
|
||||
|
@ -1525,9 +1601,11 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
|||
self.renderer.set_service_theme(self.service_theme)
|
||||
self.regenerate_service_items()
|
||||
|
||||
def on_theme_change_action(self):
|
||||
def on_theme_change_action(self, field=None):
|
||||
"""
|
||||
Handles theme change events
|
||||
|
||||
:param field:
|
||||
"""
|
||||
theme = self.sender().objectName()
|
||||
# No object name means that the "Default" theme is supposed to be used.
|
||||
|
@ -1540,6 +1618,8 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
|||
def _get_parent_item_data(self, item):
|
||||
"""
|
||||
Finds and returns the parent item for any item
|
||||
|
||||
:param item: The service item list item to be checked.
|
||||
"""
|
||||
parent_item = item.parent()
|
||||
if parent_item is None:
|
||||
|
@ -1547,7 +1627,7 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
|||
else:
|
||||
return parent_item.data(0, QtCore.Qt.UserRole)
|
||||
|
||||
def print_service_order(self):
|
||||
def print_service_order(self, field=None):
|
||||
"""
|
||||
Print a Service Order Sheet.
|
||||
"""
|
||||
|
|
|
@ -51,7 +51,7 @@ class SettingsForm(QtGui.QDialog, Ui_SettingsDialog):
|
|||
Initialise the settings form
|
||||
"""
|
||||
Registry().register('settings_form', self)
|
||||
Registry().register_function('bootstrap_post_set_up', self.post_set_up)
|
||||
Registry().register_function('bootstrap_post_set_up', self.bootstrap_post_set_up)
|
||||
super(SettingsForm, self).__init__(parent)
|
||||
self.processes = []
|
||||
self.setupUi(self)
|
||||
|
@ -117,7 +117,7 @@ class SettingsForm(QtGui.QDialog, Ui_SettingsDialog):
|
|||
self.stacked_layout.widget(tabIndex).cancel()
|
||||
return QtGui.QDialog.reject(self)
|
||||
|
||||
def post_set_up(self):
|
||||
def bootstrap_post_set_up(self):
|
||||
"""
|
||||
Run any post-setup code for the tabs on the form
|
||||
"""
|
||||
|
|
|
@ -72,6 +72,11 @@ WIDE_MENU = [
|
|||
'desktop_screen_button'
|
||||
]
|
||||
|
||||
NON_TEXT_MENU = [
|
||||
'blank_screen_button',
|
||||
'desktop_screen_button'
|
||||
]
|
||||
|
||||
|
||||
class DisplayController(QtGui.QWidget):
|
||||
"""
|
||||
|
@ -88,8 +93,7 @@ class DisplayController(QtGui.QWidget):
|
|||
|
||||
def send_to_plugins(self, *args):
|
||||
"""
|
||||
This is the generic function to send signal for control widgets,
|
||||
created from within other plugins
|
||||
This is the generic function to send signal for control widgets, created from within other plugins
|
||||
This function is needed to catch the current controller
|
||||
"""
|
||||
sender = self.sender().objectName() if self.sender().objectName() else self.sender().text()
|
||||
|
@ -116,6 +120,9 @@ class SlideController(DisplayController):
|
|||
self.screen_size_changed()
|
||||
|
||||
def initialise(self):
|
||||
"""
|
||||
Initialise the UI elements of the controller
|
||||
"""
|
||||
self.screens = ScreenList()
|
||||
try:
|
||||
self.ratio = self.screens.current['size'].width() / self.screens.current['size'].height()
|
||||
|
@ -269,7 +276,8 @@ class SlideController(DisplayController):
|
|||
self.toolbar.add_toolbar_widget(self.song_menu)
|
||||
# Stuff for items with background audio.
|
||||
# FIXME: object name should be changed. But this requires that we migrate the shortcut.
|
||||
self.audio_pause_item = self.toolbar.add_toolbar_action('audioPauseItem',
|
||||
self.audio_pause_item = self.toolbar.add_toolbar_action(
|
||||
'audioPauseItem',
|
||||
icon=':/slides/media_playback_pause.png', text=translate('OpenLP.SlideController', 'Pause Audio'),
|
||||
tooltip=translate('OpenLP.SlideController', 'Pause audio.'),
|
||||
checked=False, visible=False, category=self.category, context=QtCore.Qt.WindowShortcut,
|
||||
|
@ -310,7 +318,7 @@ class SlideController(DisplayController):
|
|||
self.slide_layout.setSpacing(0)
|
||||
self.slide_layout.setMargin(0)
|
||||
self.slide_layout.setObjectName('SlideLayout')
|
||||
self.preview_display = Display(self, self.is_live, self)
|
||||
self.preview_display = Display(self)
|
||||
self.preview_display.setGeometry(QtCore.QRect(0, 0, 300, 300))
|
||||
self.preview_display.screen = {'size': self.preview_display.geometry()}
|
||||
self.preview_display.setup()
|
||||
|
@ -442,6 +450,8 @@ class SlideController(DisplayController):
|
|||
def set_live_hot_keys(self, parent=None):
|
||||
"""
|
||||
Set the live hotkeys
|
||||
|
||||
:param parent: The parent UI object for actions to be added to.
|
||||
"""
|
||||
self.previous_service = create_action(parent, 'previousService',
|
||||
text=translate('OpenLP.SlideController', 'Previous Service'),
|
||||
|
@ -469,6 +479,8 @@ class SlideController(DisplayController):
|
|||
def toggle_display(self, action):
|
||||
"""
|
||||
Toggle the display settings triggered from remote messages.
|
||||
|
||||
:param action: The blank action to be processed.
|
||||
"""
|
||||
if action == 'blank' or action == 'hide':
|
||||
self.on_blank_display(True)
|
||||
|
@ -520,7 +532,7 @@ class SlideController(DisplayController):
|
|||
# rebuild display as screen size changed
|
||||
if self.display:
|
||||
self.display.close()
|
||||
self.display = MainDisplay(self, self.is_live, self)
|
||||
self.display = MainDisplay(self)
|
||||
self.display.setup()
|
||||
if self.is_live:
|
||||
self.__add_actions_to_widget(self.display)
|
||||
|
@ -544,6 +556,8 @@ class SlideController(DisplayController):
|
|||
def __add_actions_to_widget(self, widget):
|
||||
"""
|
||||
Add actions to the widget specified by `widget`
|
||||
|
||||
:param widget: The UI widget for the actions
|
||||
"""
|
||||
widget.addActions([
|
||||
self.previous_item, self.next_item,
|
||||
|
@ -552,17 +566,15 @@ class SlideController(DisplayController):
|
|||
|
||||
def preview_size_changed(self):
|
||||
"""
|
||||
Takes care of the SlidePreview's size. Is called when one of the the
|
||||
splitters is moved or when the screen size is changed. Note, that this
|
||||
method is (also) called frequently from the mainwindow *paintEvent*.
|
||||
Takes care of the SlidePreview's size. Is called when one of the the splitters is moved or when the screen
|
||||
size is changed. Note, that this method is (also) called frequently from the mainwindow *paintEvent*.
|
||||
"""
|
||||
if self.ratio < self.preview_frame.width() / self.preview_frame.height():
|
||||
# We have to take the height as limit.
|
||||
max_height = self.preview_frame.height() - self.grid.margin() * 2
|
||||
self.slide_preview.setFixedSize(QtCore.QSize(max_height * self.ratio, max_height))
|
||||
self.preview_display.setFixedSize(QtCore.QSize(max_height * self.ratio, max_height))
|
||||
self.preview_display.screen = {
|
||||
'size': self.preview_display.geometry()}
|
||||
self.preview_display.screen = {'size': self.preview_display.geometry()}
|
||||
else:
|
||||
# We have to take the width as limit.
|
||||
max_width = self.preview_frame.width() - self.grid.margin() * 2
|
||||
|
@ -574,6 +586,8 @@ class SlideController(DisplayController):
|
|||
def on_controller_size_changed(self, width):
|
||||
"""
|
||||
Change layout of display control buttons on controller size change
|
||||
|
||||
:param width: the new width of the display
|
||||
"""
|
||||
if self.is_live:
|
||||
# Space used by the toolbar.
|
||||
|
@ -581,12 +595,24 @@ class SlideController(DisplayController):
|
|||
# Add the threshold to prevent flickering.
|
||||
if width > used_space + HIDE_MENU_THRESHOLD and self.hide_menu.isVisible():
|
||||
self.toolbar.set_widget_visible(NARROW_MENU, False)
|
||||
self.toolbar.set_widget_visible(WIDE_MENU)
|
||||
self.set_blank_menu()
|
||||
# Take away a threshold to prevent flickering.
|
||||
elif width < used_space - HIDE_MENU_THRESHOLD and not self.hide_menu.isVisible():
|
||||
self.toolbar.set_widget_visible(WIDE_MENU, False)
|
||||
self.set_blank_menu(False)
|
||||
self.toolbar.set_widget_visible(NARROW_MENU)
|
||||
|
||||
def set_blank_menu(self, visible=True):
|
||||
"""
|
||||
Set the correct menu type dependent on the service item type
|
||||
|
||||
:param visible: Do I need to hide the menu?
|
||||
"""
|
||||
self.toolbar.set_widget_visible(WIDE_MENU, False)
|
||||
if self.service_item and self.service_item.is_text():
|
||||
self.toolbar.set_widget_visible(WIDE_MENU, visible)
|
||||
else:
|
||||
self.toolbar.set_widget_visible(NON_TEXT_MENU, visible)
|
||||
|
||||
def on_song_bar_handler(self):
|
||||
"""
|
||||
Some song handler
|
||||
|
@ -612,6 +638,8 @@ class SlideController(DisplayController):
|
|||
def enable_tool_bar(self, item):
|
||||
"""
|
||||
Allows the toolbars to be reconfigured based on Controller Type and ServiceItem Type
|
||||
|
||||
:param item: current service item being processed
|
||||
"""
|
||||
if self.is_live:
|
||||
self.enable_live_tool_bar(item)
|
||||
|
@ -621,6 +649,8 @@ class SlideController(DisplayController):
|
|||
def enable_live_tool_bar(self, item):
|
||||
"""
|
||||
Allows the live toolbar to be customised
|
||||
|
||||
:param item: The current service item
|
||||
"""
|
||||
# Work-around for OS X, hide and then show the toolbar
|
||||
# See bug #791050
|
||||
|
@ -643,6 +673,7 @@ class SlideController(DisplayController):
|
|||
self.mediabar.show()
|
||||
self.previous_item.setVisible(not item.is_media())
|
||||
self.next_item.setVisible(not item.is_media())
|
||||
self.set_blank_menu()
|
||||
# Work-around for OS X, hide and then show the toolbar
|
||||
# See bug #791050
|
||||
self.toolbar.show()
|
||||
|
@ -650,6 +681,8 @@ class SlideController(DisplayController):
|
|||
def enable_preview_tool_bar(self, item):
|
||||
"""
|
||||
Allows the Preview toolbar to be customised
|
||||
|
||||
:param item: The current service item
|
||||
"""
|
||||
# Work-around for OS X, hide and then show the toolbar
|
||||
# See bug #791050
|
||||
|
@ -679,6 +712,8 @@ class SlideController(DisplayController):
|
|||
"""
|
||||
Method to install the service item into the controller
|
||||
Called by plugins
|
||||
|
||||
:param item: The current service item
|
||||
"""
|
||||
item.render()
|
||||
slide_no = 0
|
||||
|
@ -690,6 +725,8 @@ class SlideController(DisplayController):
|
|||
def replace_service_manager_item(self, item):
|
||||
"""
|
||||
Replacement item following a remote edit
|
||||
|
||||
:param item: The current service item
|
||||
"""
|
||||
if item == self.service_item:
|
||||
self._process_item(item, self.preview_widget.current_slide_number())
|
||||
|
@ -698,6 +735,9 @@ class SlideController(DisplayController):
|
|||
"""
|
||||
Method to install the service item into the controller and request the correct toolbar for the plugin. Called by
|
||||
:class:`~openlp.core.ui.ServiceManager`
|
||||
|
||||
:param item: The current service item
|
||||
:param slide_no: The slide number to select
|
||||
"""
|
||||
# If no valid slide number is specified we take the first one, but we remember the initial value to see if we
|
||||
# should reload the song or not
|
||||
|
@ -722,6 +762,9 @@ class SlideController(DisplayController):
|
|||
def _process_item(self, service_item, slide_no):
|
||||
"""
|
||||
Loads a ServiceItem into the system from ServiceManager. Display the slide number passed.
|
||||
|
||||
:param service_item: The current service item
|
||||
:param slide_no: The slide number to select
|
||||
"""
|
||||
self.on_stop_loop()
|
||||
old_item = self.service_item
|
||||
|
@ -729,8 +772,9 @@ class SlideController(DisplayController):
|
|||
self.service_item = copy.copy(service_item)
|
||||
if old_item and self.is_live and old_item.is_capable(ItemCapabilities.ProvidesOwnDisplay):
|
||||
self._reset_blank()
|
||||
Registry().execute(
|
||||
'%s_start' % service_item.name.lower(), [service_item, self.is_live, self.hide_mode(), slide_no])
|
||||
if service_item.is_command():
|
||||
Registry().execute(
|
||||
'%s_start' % service_item.name.lower(), [service_item, self.is_live, self.hide_mode(), slide_no])
|
||||
self.slide_list = {}
|
||||
if self.is_live:
|
||||
self.song_menu.menu().clear()
|
||||
|
@ -756,7 +800,7 @@ class SlideController(DisplayController):
|
|||
self.set_audio_items_visibility(True)
|
||||
row = 0
|
||||
width = self.main_window.control_splitter.sizes()[self.split]
|
||||
for framenumber, frame in enumerate(self.service_item.get_frames()):
|
||||
for frame_number, frame in enumerate(self.service_item.get_frames()):
|
||||
if self.service_item.is_text():
|
||||
if frame['verseTag']:
|
||||
# These tags are already translated.
|
||||
|
@ -765,7 +809,7 @@ class SlideController(DisplayController):
|
|||
two_line_def = '%s\n%s' % (verse_def[0], verse_def[1:])
|
||||
row = two_line_def
|
||||
if verse_def not in self.slide_list:
|
||||
self.slide_list[verse_def] = framenumber
|
||||
self.slide_list[verse_def] = frame_number
|
||||
if self.is_live:
|
||||
self.song_menu.menu().addAction(verse_def, self.on_song_bar_handler)
|
||||
else:
|
||||
|
@ -775,7 +819,7 @@ class SlideController(DisplayController):
|
|||
row += 1
|
||||
self.slide_list[str(row)] = row - 1
|
||||
# If current slide set background to image
|
||||
if not self.service_item.is_command() and framenumber == slide_no:
|
||||
if not self.service_item.is_command() and frame_number == slide_no:
|
||||
self.service_item.bg_image_bytes = \
|
||||
self.image_manager.get_image_bytes(frame['path'], ImageSource.ImagePlugin)
|
||||
self.preview_widget.replace_service_item(self.service_item, width, slide_no)
|
||||
|
@ -799,10 +843,11 @@ class SlideController(DisplayController):
|
|||
self.on_media_close()
|
||||
Registry().execute('slidecontroller_%s_started' % self.type_prefix, [service_item])
|
||||
|
||||
# Screen event methods
|
||||
def on_slide_selected_index(self, message):
|
||||
"""
|
||||
Go to the requested slide
|
||||
|
||||
:param message: remote message to be processed.
|
||||
"""
|
||||
index = int(message[0])
|
||||
if not self.service_item:
|
||||
|
@ -847,6 +892,8 @@ class SlideController(DisplayController):
|
|||
def on_blank_display(self, checked=None):
|
||||
"""
|
||||
Handle the blank screen button actions
|
||||
|
||||
:param checked: the new state of the of the widget
|
||||
"""
|
||||
if checked is None:
|
||||
checked = self.blank_screen.isChecked()
|
||||
|
@ -866,6 +913,8 @@ class SlideController(DisplayController):
|
|||
def on_theme_display(self, checked=None):
|
||||
"""
|
||||
Handle the Theme screen button
|
||||
|
||||
:param checked: the new state of the of the widget
|
||||
"""
|
||||
if checked is None:
|
||||
checked = self.theme_screen.isChecked()
|
||||
|
@ -885,6 +934,8 @@ class SlideController(DisplayController):
|
|||
def on_hide_display(self, checked=None):
|
||||
"""
|
||||
Handle the Hide screen button
|
||||
|
||||
:param checked: the new state of the of the widget
|
||||
"""
|
||||
if checked is None:
|
||||
checked = self.desktop_screen.isChecked()
|
||||
|
@ -951,8 +1002,9 @@ class SlideController(DisplayController):
|
|||
|
||||
def slide_selected(self, start=False):
|
||||
"""
|
||||
Generate the preview when you click on a slide.
|
||||
if this is the Live Controller also display on the screen
|
||||
Generate the preview when you click on a slide. If this is the Live Controller also display on the screen
|
||||
|
||||
:param start:
|
||||
"""
|
||||
row = self.preview_widget.current_slide_number()
|
||||
self.selected_row = 0
|
||||
|
@ -975,12 +1027,13 @@ class SlideController(DisplayController):
|
|||
self.update_preview()
|
||||
self.selected_row = row
|
||||
self.preview_widget.change_slide(row)
|
||||
Registry().execute('slidecontroller_%s_changed' % self.type_prefix, row)
|
||||
self.display.setFocus()
|
||||
|
||||
def on_slide_change(self, row):
|
||||
"""
|
||||
The slide has been changed. Update the slidecontroller accordingly
|
||||
|
||||
:param row: Row to be selected
|
||||
"""
|
||||
self.preview_widget.change_slide(row)
|
||||
self.update_preview()
|
||||
|
@ -1013,20 +1066,24 @@ class SlideController(DisplayController):
|
|||
|
||||
def on_slide_selected_next_action(self, checked):
|
||||
"""
|
||||
Wrapper function from create_action so we can throw away the
|
||||
incorrect parameter
|
||||
Wrapper function from create_action so we can throw away the incorrect parameter
|
||||
|
||||
:param checked: the new state of the of the widget
|
||||
"""
|
||||
self.on_slide_selected_next()
|
||||
|
||||
def on_slide_selected_next(self, wrap=None):
|
||||
"""
|
||||
Go to the next slide.
|
||||
|
||||
:param wrap: Are we wrapping round the service item
|
||||
"""
|
||||
if not self.service_item:
|
||||
return
|
||||
Registry().execute('%s_next' % self.service_item.name.lower(), [self.service_item, self.is_live])
|
||||
if self.service_item.is_command() and self.is_live:
|
||||
self.update_preview()
|
||||
if self.service_item.is_command():
|
||||
Registry().execute('%s_next' % self.service_item.name.lower(), [self.service_item, self.is_live])
|
||||
if self.is_live:
|
||||
self.update_preview()
|
||||
else:
|
||||
row = self.preview_widget.current_slide_number() + 1
|
||||
if row == self.preview_widget.slide_count():
|
||||
|
@ -1051,9 +1108,10 @@ class SlideController(DisplayController):
|
|||
"""
|
||||
if not self.service_item:
|
||||
return
|
||||
Registry().execute('%s_previous' % self.service_item.name.lower(), [self.service_item, self.is_live])
|
||||
if self.service_item.is_command() and self.is_live:
|
||||
self.update_preview()
|
||||
if self.service_item.is_command():
|
||||
Registry().execute('%s_previous' % self.service_item.name.lower(), [self.service_item, self.is_live])
|
||||
if self.is_live:
|
||||
self.update_preview()
|
||||
else:
|
||||
row = self.preview_widget.current_slide_number() - 1
|
||||
if row == -1:
|
||||
|
@ -1096,6 +1154,8 @@ class SlideController(DisplayController):
|
|||
def on_play_slides_loop(self, checked=None):
|
||||
"""
|
||||
Start or stop 'Play Slides in Loop'
|
||||
|
||||
:param checked: is the check box checked.
|
||||
"""
|
||||
if checked is None:
|
||||
checked = self.play_slides_loop.isChecked()
|
||||
|
@ -1117,6 +1177,8 @@ class SlideController(DisplayController):
|
|||
def on_play_slides_once(self, checked=None):
|
||||
"""
|
||||
Start or stop 'Play Slides to End'
|
||||
|
||||
:param checked: is the check box checked.
|
||||
"""
|
||||
if checked is None:
|
||||
checked = self.play_slides_once.isChecked()
|
||||
|
@ -1144,6 +1206,8 @@ class SlideController(DisplayController):
|
|||
def set_audio_pause_clicked(self, checked):
|
||||
"""
|
||||
Pause the audio player
|
||||
|
||||
:param checked: is the check box checked.
|
||||
"""
|
||||
if not self.audio_pause_item.isVisible():
|
||||
return
|
||||
|
@ -1155,6 +1219,8 @@ class SlideController(DisplayController):
|
|||
def timerEvent(self, event):
|
||||
"""
|
||||
If the timer event is for this window select next slide
|
||||
|
||||
:param event: The triggered event
|
||||
"""
|
||||
if event.timerId() == self.timer_id:
|
||||
self.on_slide_selected_next(self.play_slides_loop.isChecked())
|
||||
|
@ -1202,6 +1268,8 @@ class SlideController(DisplayController):
|
|||
def on_media_start(self, item):
|
||||
"""
|
||||
Respond to the arrival of a media service item
|
||||
|
||||
:param item: The service item to be processed
|
||||
"""
|
||||
self.media_controller.video(self.controller_type, item, self.hide_mode())
|
||||
if not self.is_live:
|
||||
|
@ -1255,6 +1323,8 @@ class SlideController(DisplayController):
|
|||
def on_audio_time_remaining(self, time):
|
||||
"""
|
||||
Update how much time is remaining
|
||||
|
||||
:param time: the time remainings
|
||||
"""
|
||||
seconds = self.display.audio_player.media_object.remainingTime() // 1000
|
||||
minutes = seconds // 60
|
||||
|
@ -1302,7 +1372,7 @@ class SlideController(DisplayController):
|
|||
"""
|
||||
Adds the service manager to the class dynamically
|
||||
"""
|
||||
if not hasattr(self, '_service_manager'):
|
||||
if not hasattr(self, '_service_manager') or not self._service_manager:
|
||||
self._service_manager = Registry().get('service_manager')
|
||||
return self._service_manager
|
||||
|
||||
|
|
|
@ -42,28 +42,25 @@ from openlp.core.lib import FileDialog, ImageSource, OpenLPToolbar, get_text_fil
|
|||
check_item_selected, create_thumb, validate_thumb
|
||||
from openlp.core.lib.theme import ThemeXML, BackgroundType
|
||||
from openlp.core.lib.ui import critical_error_message_box, create_widget_action
|
||||
from openlp.core.ui import FileRenameForm, ThemeForm, ThemeManagerHelper
|
||||
from openlp.core.ui import FileRenameForm, ThemeForm
|
||||
from openlp.core.utils import delete_file, get_locale_key, get_filesystem_encoding
|
||||
|
||||
|
||||
class ThemeManager(RegistryMixin, OpenLPMixin, QtGui.QWidget, ThemeManagerHelper):
|
||||
class Ui_ThemeManager(object):
|
||||
"""
|
||||
Manages the orders of Theme.
|
||||
UI part of the Theme Manager
|
||||
"""
|
||||
def __init__(self, parent=None):
|
||||
def setup_ui(self, widget):
|
||||
"""
|
||||
Constructor
|
||||
Define the UI
|
||||
:param widget: The screen object the the dialog is to be attached to.
|
||||
"""
|
||||
super(ThemeManager, self).__init__(parent)
|
||||
self.settings_section = 'themes'
|
||||
self.theme_form = ThemeForm(self)
|
||||
self.file_rename_form = FileRenameForm()
|
||||
# start with the layout
|
||||
self.layout = QtGui.QVBoxLayout(self)
|
||||
self.layout = QtGui.QVBoxLayout(widget)
|
||||
self.layout.setSpacing(0)
|
||||
self.layout.setMargin(0)
|
||||
self.layout.setObjectName('layout')
|
||||
self.toolbar = OpenLPToolbar(self)
|
||||
self.toolbar = OpenLPToolbar(widget)
|
||||
self.toolbar.setObjectName('toolbar')
|
||||
self.toolbar.add_toolbar_action('newTheme',
|
||||
text=UiStrings().NewTheme, icon=':/themes/theme_new.png',
|
||||
|
@ -96,7 +93,7 @@ class ThemeManager(RegistryMixin, OpenLPMixin, QtGui.QWidget, ThemeManagerHelper
|
|||
self.theme_widget = QtGui.QWidgetAction(self.toolbar)
|
||||
self.theme_widget.setObjectName('theme_widget')
|
||||
# create theme manager list
|
||||
self.theme_list_widget = QtGui.QListWidget(self)
|
||||
self.theme_list_widget = QtGui.QListWidget(widget)
|
||||
self.theme_list_widget.setAlternatingRowColors(True)
|
||||
self.theme_list_widget.setIconSize(QtCore.QSize(88, 50))
|
||||
self.theme_list_widget.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
|
||||
|
@ -128,7 +125,18 @@ class ThemeManager(RegistryMixin, OpenLPMixin, QtGui.QWidget, ThemeManagerHelper
|
|||
# Signals
|
||||
self.theme_list_widget.doubleClicked.connect(self.change_global_from_screen)
|
||||
self.theme_list_widget.currentItemChanged.connect(self.check_list_state)
|
||||
Registry().register_function('theme_update_global', self.change_global_from_tab)
|
||||
|
||||
|
||||
class ThemeManager(OpenLPMixin, RegistryMixin, QtGui.QWidget, Ui_ThemeManager):
|
||||
"""
|
||||
Manages the orders of Theme.
|
||||
"""
|
||||
def __init__(self, parent=None):
|
||||
"""
|
||||
Constructor
|
||||
"""
|
||||
super(ThemeManager, self).__init__(parent)
|
||||
self.settings_section = 'themes'
|
||||
# Variables
|
||||
self.theme_list = []
|
||||
self.old_background_image = None
|
||||
|
@ -137,7 +145,7 @@ class ThemeManager(RegistryMixin, OpenLPMixin, QtGui.QWidget, ThemeManagerHelper
|
|||
"""
|
||||
process the bootstrap initialise setup request
|
||||
"""
|
||||
self.log_debug('initialise called')
|
||||
self.setup_ui(self)
|
||||
self.global_theme = Settings().value(self.settings_section + '/global theme')
|
||||
self.build_theme_path()
|
||||
self.load_first_time_themes()
|
||||
|
@ -146,12 +154,28 @@ class ThemeManager(RegistryMixin, OpenLPMixin, QtGui.QWidget, ThemeManagerHelper
|
|||
"""
|
||||
process the bootstrap post setup request
|
||||
"""
|
||||
self._push_themes()
|
||||
self.theme_form = ThemeForm(self)
|
||||
self.theme_form.path = self.path
|
||||
self.file_rename_form = FileRenameForm()
|
||||
Registry().register_function('theme_update_global', self.change_global_from_tab)
|
||||
self.load_themes()
|
||||
|
||||
def build_theme_path(self):
|
||||
"""
|
||||
Set up the theme path variables
|
||||
"""
|
||||
self.path = AppLocation.get_section_data_path(self.settings_section)
|
||||
check_directory_exists(self.path)
|
||||
self.thumb_path = os.path.join(self.path, 'thumbnails')
|
||||
check_directory_exists(self.thumb_path)
|
||||
|
||||
def check_list_state(self, item, field=None):
|
||||
"""
|
||||
If Default theme selected remove delete button.
|
||||
Note for some reason a dummy field is required. Nothing is passed!
|
||||
|
||||
:param field:
|
||||
:param item: Service Item to process
|
||||
"""
|
||||
if item is None:
|
||||
return
|
||||
|
@ -165,8 +189,9 @@ class ThemeManager(RegistryMixin, OpenLPMixin, QtGui.QWidget, ThemeManagerHelper
|
|||
|
||||
def context_menu(self, point):
|
||||
"""
|
||||
Build the Right Click Context menu and set state depending on
|
||||
the type of theme.
|
||||
Build the Right Click Context menu and set state depending on the type of theme.
|
||||
|
||||
:param point: The position of the mouse so the correct item can be found.
|
||||
"""
|
||||
item = self.theme_list_widget.itemAt(point)
|
||||
if item is None:
|
||||
|
@ -200,8 +225,9 @@ class ThemeManager(RegistryMixin, OpenLPMixin, QtGui.QWidget, ThemeManagerHelper
|
|||
|
||||
def change_global_from_screen(self, index=-1):
|
||||
"""
|
||||
Change the global theme when a theme is double clicked upon in the
|
||||
Theme Manager list
|
||||
Change the global theme when a theme is double clicked upon in the Theme Manager list.
|
||||
|
||||
:param index:
|
||||
"""
|
||||
selected_row = self.theme_list_widget.currentRow()
|
||||
for count in range(0, self.theme_list_widget.count()):
|
||||
|
@ -221,8 +247,9 @@ class ThemeManager(RegistryMixin, OpenLPMixin, QtGui.QWidget, ThemeManagerHelper
|
|||
|
||||
def on_add_theme(self, field=None):
|
||||
"""
|
||||
Loads a new theme with the default settings and then launches the theme
|
||||
editing form for the user to make their customisations.
|
||||
Loads a new theme with the default settings and then launches the theme editing form for the user to make
|
||||
their customisations.
|
||||
:param field:
|
||||
"""
|
||||
theme = ThemeXML()
|
||||
theme.set_default_header_footer()
|
||||
|
@ -233,6 +260,7 @@ class ThemeManager(RegistryMixin, OpenLPMixin, QtGui.QWidget, ThemeManagerHelper
|
|||
def on_rename_theme(self, field=None):
|
||||
"""
|
||||
Renames an existing theme to a new name
|
||||
:param field:
|
||||
"""
|
||||
if self._validate_theme_action(translate('OpenLP.ThemeManager', 'You must select a theme to rename.'),
|
||||
translate('OpenLP.ThemeManager', 'Rename Confirmation'),
|
||||
|
@ -257,6 +285,7 @@ class ThemeManager(RegistryMixin, OpenLPMixin, QtGui.QWidget, ThemeManagerHelper
|
|||
def on_copy_theme(self, field=None):
|
||||
"""
|
||||
Copies an existing theme to a new name
|
||||
:param field:
|
||||
"""
|
||||
item = self.theme_list_widget.currentItem()
|
||||
old_theme_name = item.data(QtCore.Qt.UserRole)
|
||||
|
@ -271,6 +300,9 @@ class ThemeManager(RegistryMixin, OpenLPMixin, QtGui.QWidget, ThemeManagerHelper
|
|||
def clone_theme_data(self, theme_data, new_theme_name):
|
||||
"""
|
||||
Takes a theme and makes a new copy of it as well as saving it.
|
||||
|
||||
:param theme_data: The theme to be used
|
||||
:param new_theme_name: The new theme name to save the data to
|
||||
"""
|
||||
save_to = None
|
||||
save_from = None
|
||||
|
@ -286,6 +318,7 @@ class ThemeManager(RegistryMixin, OpenLPMixin, QtGui.QWidget, ThemeManagerHelper
|
|||
"""
|
||||
Loads the settings for the theme that is to be edited and launches the
|
||||
theme editing form so the user can make their changes.
|
||||
:param field:
|
||||
"""
|
||||
if check_item_selected(self.theme_list_widget,
|
||||
translate('OpenLP.ThemeManager', 'You must select a theme to edit.')):
|
||||
|
@ -301,11 +334,12 @@ class ThemeManager(RegistryMixin, OpenLPMixin, QtGui.QWidget, ThemeManagerHelper
|
|||
|
||||
def on_delete_theme(self, field=None):
|
||||
"""
|
||||
Delete a theme
|
||||
Delete a theme triggered by the UI.
|
||||
:param field:
|
||||
"""
|
||||
if self._validate_theme_action(translate('OpenLP.ThemeManager', 'You must select a theme to delete.'),
|
||||
translate('OpenLP.ThemeManager', 'Delete Confirmation'),
|
||||
translate('OpenLP.ThemeManager', 'Delete %s theme?')):
|
||||
translate('OpenLP.ThemeManager', 'Delete Confirmation'),
|
||||
translate('OpenLP.ThemeManager', 'Delete %s theme?')):
|
||||
item = self.theme_list_widget.currentItem()
|
||||
theme = item.text()
|
||||
row = self.theme_list_widget.row(item)
|
||||
|
@ -320,8 +354,7 @@ class ThemeManager(RegistryMixin, OpenLPMixin, QtGui.QWidget, ThemeManagerHelper
|
|||
"""
|
||||
Delete a theme.
|
||||
|
||||
``theme``
|
||||
The theme to delete.
|
||||
:param theme: The theme to delete.
|
||||
"""
|
||||
self.theme_list.remove(theme)
|
||||
thumb = '%s.png' % theme
|
||||
|
@ -337,6 +370,7 @@ class ThemeManager(RegistryMixin, OpenLPMixin, QtGui.QWidget, ThemeManagerHelper
|
|||
def on_export_theme(self, field=None):
|
||||
"""
|
||||
Export the theme in a zip file
|
||||
:param field:
|
||||
"""
|
||||
item = self.theme_list_widget.currentItem()
|
||||
if item is None:
|
||||
|
@ -378,6 +412,7 @@ class ThemeManager(RegistryMixin, OpenLPMixin, QtGui.QWidget, ThemeManagerHelper
|
|||
"""
|
||||
Opens a file dialog to select the theme file(s) to import before attempting to extract OpenLP themes from
|
||||
those files. This process will load both OpenLP version 1 and version 2 themes.
|
||||
:param field:
|
||||
"""
|
||||
files = FileDialog.getOpenFileNames(self,
|
||||
translate('OpenLP.ThemeManager', 'Select Theme Import File'),
|
||||
|
@ -411,7 +446,6 @@ class ThemeManager(RegistryMixin, OpenLPMixin, QtGui.QWidget, ThemeManagerHelper
|
|||
self._write_theme(theme, None, None)
|
||||
Settings().setValue(self.settings_section + '/global theme', theme.theme_name)
|
||||
self.application.set_normal_cursor()
|
||||
self.load_themes()
|
||||
|
||||
def load_themes(self):
|
||||
"""
|
||||
|
@ -462,8 +496,8 @@ class ThemeManager(RegistryMixin, OpenLPMixin, QtGui.QWidget, ThemeManagerHelper
|
|||
"""
|
||||
Returns a theme object from an XML file
|
||||
|
||||
``theme_name``
|
||||
Name of the theme to load from file
|
||||
:param theme_name: Name of the theme to load from file
|
||||
:return The theme object.
|
||||
"""
|
||||
self.log_debug('get theme data for theme %s' % theme_name)
|
||||
xml_file = os.path.join(self.path, str(theme_name), str(theme_name) + '.xml')
|
||||
|
@ -472,11 +506,14 @@ class ThemeManager(RegistryMixin, OpenLPMixin, QtGui.QWidget, ThemeManagerHelper
|
|||
self.log_debug('No theme data - using default theme')
|
||||
return ThemeXML()
|
||||
else:
|
||||
return self._create_theme_from_Xml(xml, self.path)
|
||||
return self._create_theme_from_xml(xml, self.path)
|
||||
|
||||
def over_write_message_box(self, theme_name):
|
||||
"""
|
||||
Display a warning box to the user that a theme already exists
|
||||
|
||||
:param theme_name: Name of the theme.
|
||||
:return Confirm if the theme is to be overwritten.
|
||||
"""
|
||||
ret = QtGui.QMessageBox.question(self, translate('OpenLP.ThemeManager', 'Theme Already Exists'),
|
||||
translate('OpenLP.ThemeManager',
|
||||
|
@ -489,11 +526,12 @@ class ThemeManager(RegistryMixin, OpenLPMixin, QtGui.QWidget, ThemeManagerHelper
|
|||
|
||||
def unzip_theme(self, file_name, directory):
|
||||
"""
|
||||
Unzip the theme, remove the preview file if stored
|
||||
Generate a new preview file. Check the XML theme version and upgrade if
|
||||
necessary.
|
||||
Unzip the theme, remove the preview file if stored. Generate a new preview file. Check the XML theme version
|
||||
and upgrade if necessary.
|
||||
:param file_name:
|
||||
:param directory:
|
||||
"""
|
||||
self.log_debug('Unzipping theme %s', file_name)
|
||||
self.log_debug('Unzipping theme %s' % file_name)
|
||||
file_name = str(file_name)
|
||||
theme_zip = None
|
||||
out_file = None
|
||||
|
@ -548,8 +586,8 @@ class ThemeManager(RegistryMixin, OpenLPMixin, QtGui.QWidget, ThemeManagerHelper
|
|||
if not abort_import:
|
||||
# As all files are closed, we can create the Theme.
|
||||
if file_xml:
|
||||
theme = self._create_theme_from_Xml(file_xml, self.path)
|
||||
self.generate_and_save_image(directory, theme_name, theme)
|
||||
theme = self._create_theme_from_xml(file_xml, self.path)
|
||||
self.generate_and_save_image(theme_name, theme)
|
||||
# Only show the error message, when IOError was not raised (in
|
||||
# this case the error message has already been shown).
|
||||
elif theme_zip is not None:
|
||||
|
@ -562,8 +600,8 @@ class ThemeManager(RegistryMixin, OpenLPMixin, QtGui.QWidget, ThemeManagerHelper
|
|||
"""
|
||||
Check if theme already exists and displays error message
|
||||
|
||||
``theme_name``
|
||||
Name of the Theme to test
|
||||
:param theme_name: Name of the Theme to test
|
||||
:return True or False if theme exists
|
||||
"""
|
||||
theme_dir = os.path.join(self.path, theme_name)
|
||||
if os.path.exists(theme_dir):
|
||||
|
@ -575,7 +613,11 @@ class ThemeManager(RegistryMixin, OpenLPMixin, QtGui.QWidget, ThemeManagerHelper
|
|||
|
||||
def save_theme(self, theme, image_from, image_to):
|
||||
"""
|
||||
Called by thememaintenance Dialog to save the theme and to trigger the reload of the theme list
|
||||
Called by theme maintenance Dialog to save the theme and to trigger the reload of the theme list
|
||||
|
||||
:param theme: The theme data object.
|
||||
:param image_from: Where the theme image is currently located.
|
||||
:param image_to: Where the Theme Image is to be saved to
|
||||
"""
|
||||
self._write_theme(theme, image_from, image_to)
|
||||
if theme.background_type == BackgroundType.to_string(BackgroundType.Image):
|
||||
|
@ -587,6 +629,10 @@ class ThemeManager(RegistryMixin, OpenLPMixin, QtGui.QWidget, ThemeManagerHelper
|
|||
def _write_theme(self, theme, image_from, image_to):
|
||||
"""
|
||||
Writes the theme to the disk and handles the background image if necessary
|
||||
|
||||
:param theme: The theme data object.
|
||||
:param image_from: Where the theme image is currently located.
|
||||
:param image_to: Where the Theme Image is to be saved to
|
||||
"""
|
||||
name = theme.theme_name
|
||||
theme_pretty_xml = theme.extract_formatted_xml()
|
||||
|
@ -611,11 +657,14 @@ class ThemeManager(RegistryMixin, OpenLPMixin, QtGui.QWidget, ThemeManagerHelper
|
|||
except IOError as xxx_todo_changeme:
|
||||
shutil.Error = xxx_todo_changeme
|
||||
self.log_exception('Failed to save theme image')
|
||||
self.generate_and_save_image(self.path, name, theme)
|
||||
self.generate_and_save_image(name, theme)
|
||||
|
||||
def generate_and_save_image(self, directory, name, theme):
|
||||
def generate_and_save_image(self, name, theme):
|
||||
"""
|
||||
Generate and save a preview image
|
||||
|
||||
:param name: The name of the theme.
|
||||
:param theme: The theme data object.
|
||||
"""
|
||||
frame = self.generate_image(theme)
|
||||
sample_path_name = os.path.join(self.path, name + '.png')
|
||||
|
@ -632,7 +681,7 @@ class ThemeManager(RegistryMixin, OpenLPMixin, QtGui.QWidget, ThemeManagerHelper
|
|||
self.main_window.display_progress_bar(len(self.theme_list))
|
||||
for theme in self.theme_list:
|
||||
self.main_window.increment_progress_bar()
|
||||
self.generate_and_save_image(self.path, theme, self.get_theme_data(theme))
|
||||
self.generate_and_save_image(theme, self.get_theme_data(theme))
|
||||
self.main_window.finished_progress_bar()
|
||||
self.load_themes()
|
||||
|
||||
|
@ -640,11 +689,8 @@ class ThemeManager(RegistryMixin, OpenLPMixin, QtGui.QWidget, ThemeManagerHelper
|
|||
"""
|
||||
Call the renderer to build a Sample Image
|
||||
|
||||
``theme_data``
|
||||
The theme to generated a preview for.
|
||||
|
||||
``force_page``
|
||||
Flag to tell message lines per page need to be generated.
|
||||
:param theme_data: The theme to generated a preview for.
|
||||
:param force_page: Flag to tell message lines per page need to be generated.
|
||||
"""
|
||||
return self.renderer.generate_preview(theme_data, force_page)
|
||||
|
||||
|
@ -652,26 +698,33 @@ class ThemeManager(RegistryMixin, OpenLPMixin, QtGui.QWidget, ThemeManagerHelper
|
|||
"""
|
||||
Return an image representing the look of the theme
|
||||
|
||||
``theme``
|
||||
The theme to return the image for
|
||||
:param theme: The theme to return the image for.
|
||||
"""
|
||||
return os.path.join(self.path, theme + '.png')
|
||||
|
||||
def _create_theme_from_Xml(self, theme_xml, path):
|
||||
def _create_theme_from_xml(self, theme_xml, image_path):
|
||||
"""
|
||||
Return a theme object using information parsed from XML
|
||||
|
||||
``theme_xml``
|
||||
The XML data to load into the theme
|
||||
:param theme_xml: The Theme data object.
|
||||
:param image_path: Where the theme image is stored
|
||||
:return Theme data.
|
||||
"""
|
||||
theme = ThemeXML()
|
||||
theme.parse(theme_xml)
|
||||
theme.extend_image_filename(path)
|
||||
theme.extend_image_filename(image_path)
|
||||
return theme
|
||||
|
||||
def _validate_theme_action(self, select_text, confirm_title, confirm_text, test_plugin=True, confirm=True):
|
||||
"""
|
||||
Check to see if theme has been selected and the destructive action is allowed.
|
||||
|
||||
:param select_text: Text for message box if no item selected.
|
||||
:param confirm_title: Confirm message title to be displayed.
|
||||
:param confirm_text: Confirm message text to be displayed.
|
||||
:param test_plugin: Do we check the plugins for theme usage.
|
||||
:param confirm: Do we display a confirm box before run checks.
|
||||
:return True or False depending on the validity.
|
||||
"""
|
||||
self.global_theme = Settings().value(self.settings_section + '/global theme')
|
||||
if check_item_selected(self.theme_list_widget, select_text):
|
||||
|
|
|
@ -156,12 +156,6 @@ class ThemesTab(SettingsTab):
|
|||
self.settings_form.register_post_process('theme_update_global')
|
||||
self.tab_visited = False
|
||||
|
||||
def post_set_up(self):
|
||||
"""
|
||||
After setting things up...
|
||||
"""
|
||||
Registry().execute('theme_update_global')
|
||||
|
||||
def on_song_level_button_clicked(self):
|
||||
"""
|
||||
Set the theme level
|
||||
|
|
|
@ -56,7 +56,8 @@ if sys.platform != 'win32' and sys.platform != 'darwin':
|
|||
|
||||
from openlp.core.common import translate
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
log = logging.getLogger(__name__+'.__init__')
|
||||
|
||||
APPLICATION_VERSION = {}
|
||||
IMAGES_FILTER = None
|
||||
ICU_COLLATOR = None
|
||||
|
|
|
@ -127,9 +127,15 @@ __default_settings__ = {
|
|||
|
||||
|
||||
class AlertsPlugin(Plugin):
|
||||
"""
|
||||
The Alerts Plugin Class
|
||||
"""
|
||||
log.info('Alerts Plugin loaded')
|
||||
|
||||
def __init__(self):
|
||||
"""
|
||||
Class __init__ method
|
||||
"""
|
||||
super(AlertsPlugin, self).__init__('alerts', __default_settings__, settings_tab_class=AlertsTab)
|
||||
self.weight = -3
|
||||
self.icon_path = ':/plugins/plugin_alerts.png'
|
||||
|
@ -142,17 +148,20 @@ class AlertsPlugin(Plugin):
|
|||
"""
|
||||
Give the alerts plugin the opportunity to add items to the **Tools** menu.
|
||||
|
||||
``tools_menu``
|
||||
The actual **Tools** menu item, so that your actions can use it as their parent.
|
||||
:param tools_menu: The actual **Tools** menu item, so that your actions can use it as their parent.
|
||||
"""
|
||||
log.info('add tools menu')
|
||||
self.tools_alert_item = create_action(tools_menu, 'toolsAlertItem',
|
||||
text=translate('AlertsPlugin', '&Alert'), icon=':/plugins/plugin_alerts.png',
|
||||
statustip=translate('AlertsPlugin', 'Show an alert message.'),
|
||||
visible=False, can_shortcuts=True, triggers=self.on_alerts_trigger)
|
||||
text=translate('AlertsPlugin', '&Alert'),
|
||||
icon=':/plugins/plugin_alerts.png',
|
||||
statustip=translate('AlertsPlugin', 'Show an alert message.'),
|
||||
visible=False, can_shortcuts=True, triggers=self.on_alerts_trigger)
|
||||
self.main_window.tools_menu.addAction(self.tools_alert_item)
|
||||
|
||||
def initialise(self):
|
||||
"""
|
||||
Initialise plugin
|
||||
"""
|
||||
log.info('Alerts Initialising')
|
||||
super(AlertsPlugin, self).initialise()
|
||||
self.tools_alert_item.setVisible(True)
|
||||
|
@ -171,16 +180,27 @@ class AlertsPlugin(Plugin):
|
|||
action_list.remove_action(self.tools_alert_item, 'Tools')
|
||||
|
||||
def toggle_alerts_state(self):
|
||||
"""
|
||||
Switch the alerts state
|
||||
"""
|
||||
self.alerts_active = not self.alerts_active
|
||||
Settings().setValue(self.settings_section + '/active', self.alerts_active)
|
||||
|
||||
def on_alerts_trigger(self):
|
||||
"""
|
||||
Start of the Alerts dialog triggered from the main menu.
|
||||
"""
|
||||
self.alert_form.load_list()
|
||||
self.alert_form.exec_()
|
||||
|
||||
def about(self):
|
||||
"""
|
||||
Plugin Alerts about method
|
||||
|
||||
:return: text
|
||||
"""
|
||||
about_text = translate('AlertsPlugin', '<strong>Alerts Plugin</strong>'
|
||||
'<br />The alert plugin controls the displaying of nursery alerts on the display screen.')
|
||||
'<br />The alert plugin controls the displaying of alerts on the display screen.')
|
||||
return about_text
|
||||
|
||||
def set_plugin_text_strings(self):
|
||||
|
@ -209,7 +229,7 @@ class AlertsPlugin(Plugin):
|
|||
"""
|
||||
align = VerticalType.Names[self.settings_tab.location]
|
||||
return CSS % (align, self.settings_tab.font_face, self.settings_tab.font_size, self.settings_tab.font_color,
|
||||
self.settings_tab.background_color)
|
||||
self.settings_tab.background_color)
|
||||
|
||||
def get_display_html(self):
|
||||
"""
|
||||
|
@ -219,12 +239,11 @@ class AlertsPlugin(Plugin):
|
|||
|
||||
def refresh_css(self, frame):
|
||||
"""
|
||||
Trigger an update of the CSS in the maindisplay.
|
||||
Trigger an update of the CSS in the main display.
|
||||
|
||||
``frame``
|
||||
The Web frame holding the page.
|
||||
:param frame: The Web frame holding the page.
|
||||
"""
|
||||
align = VerticalType.Names[self.settings_tab.location]
|
||||
frame.evaluateJavaScript('update_css("%s", "%s", "%s", "%s", "%s")' %
|
||||
(align, self.settings_tab.font_face, self.settings_tab.font_size,
|
||||
self.settings_tab.font_color, self.settings_tab.background_color))
|
||||
(align, self.settings_tab.font_face, self.settings_tab.font_size,
|
||||
self.settings_tab.font_color, self.settings_tab.background_color))
|
||||
|
|
|
@ -35,7 +35,15 @@ from openlp.core.lib.ui import create_button, create_button_box
|
|||
|
||||
|
||||
class Ui_AlertDialog(object):
|
||||
"""
|
||||
Alert UI Class
|
||||
"""
|
||||
def setupUi(self, alert_dialog):
|
||||
"""
|
||||
Setup the Alert UI dialog
|
||||
|
||||
:param alert_dialog: The dialog
|
||||
"""
|
||||
alert_dialog.setObjectName('alert_dialog')
|
||||
alert_dialog.resize(400, 300)
|
||||
alert_dialog.setWindowIcon(build_icon(':/icon/openlp-logo-16x16.png'))
|
||||
|
@ -72,20 +80,25 @@ class Ui_AlertDialog(object):
|
|||
self.save_button.setObjectName('save_button')
|
||||
self.manage_button_layout.addWidget(self.save_button)
|
||||
self.delete_button = create_button(alert_dialog, 'delete_button', role='delete', enabled=False,
|
||||
click=alert_dialog.on_delete_button_clicked)
|
||||
click=alert_dialog.on_delete_button_clicked)
|
||||
self.manage_button_layout.addWidget(self.delete_button)
|
||||
self.manage_button_layout.addStretch()
|
||||
self.alert_dialog_layout.addLayout(self.manage_button_layout, 1, 1)
|
||||
display_icon = build_icon(':/general/general_live.png')
|
||||
self.display_button = create_button(alert_dialog, 'display_button', icon=display_icon, enabled=False)
|
||||
self.display_close_button = create_button(alert_dialog, 'display_close_button', icon=display_icon,
|
||||
enabled=False)
|
||||
enabled=False)
|
||||
self.button_box = create_button_box(alert_dialog, 'button_box', ['close'],
|
||||
[self.display_button, self.display_close_button])
|
||||
[self.display_button, self.display_close_button])
|
||||
self.alert_dialog_layout.addWidget(self.button_box, 2, 0, 1, 2)
|
||||
self.retranslateUi(alert_dialog)
|
||||
|
||||
def retranslateUi(self, alert_dialog):
|
||||
"""
|
||||
Retranslate the UI strings
|
||||
|
||||
:param alert_dialog: The dialog
|
||||
"""
|
||||
alert_dialog.setWindowTitle(translate('AlertsPlugin.AlertForm', 'Alert Message'))
|
||||
self.alert_entry_label.setText(translate('AlertsPlugin.AlertForm', 'Alert &text:'))
|
||||
self.alert_parameter.setText(translate('AlertsPlugin.AlertForm', '&Parameter:'))
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
|
||||
from PyQt4 import QtGui, QtCore
|
||||
|
||||
from openlp.core.common import translate
|
||||
from openlp.core.common import Registry, translate
|
||||
from openlp.plugins.alerts.lib.db import AlertItem
|
||||
|
||||
from .alertdialog import Ui_AlertDialog
|
||||
|
@ -46,8 +46,7 @@ class AlertForm(QtGui.QDialog, Ui_AlertDialog):
|
|||
self.manager = plugin.manager
|
||||
self.plugin = plugin
|
||||
self.item_id = None
|
||||
# TODO: Use Registry()
|
||||
super(AlertForm, self).__init__(self.plugin.main_window)
|
||||
super(AlertForm, self).__init__( Registry().get('main_window'))
|
||||
self.setupUi(self)
|
||||
self.display_button.clicked.connect(self.on_display_clicked)
|
||||
self.display_close_button.clicked.connect(self.on_display_close_clicked)
|
||||
|
@ -113,9 +112,10 @@ class AlertForm(QtGui.QDialog, Ui_AlertDialog):
|
|||
"""
|
||||
if not self.alert_text_edit.text():
|
||||
QtGui.QMessageBox.information(self,
|
||||
translate('AlertsPlugin.AlertForm', 'New Alert'),
|
||||
translate('AlertsPlugin.AlertForm', 'You haven\'t specified any text for your alert. \n'
|
||||
'Please type in some text before clicking New.'))
|
||||
translate('AlertsPlugin.AlertForm', 'New Alert'),
|
||||
translate('AlertsPlugin.AlertForm',
|
||||
'You haven\'t specified any text for your alert. \n'
|
||||
'Please type in some text before clicking New.'))
|
||||
else:
|
||||
alert = AlertItem()
|
||||
alert.text = self.alert_text_edit.text()
|
||||
|
@ -153,10 +153,10 @@ class AlertForm(QtGui.QDialog, Ui_AlertDialog):
|
|||
List item has been double clicked to display it.
|
||||
"""
|
||||
item = self.alert_list_widget.selectedIndexes()[0]
|
||||
bitem = self.alert_list_widget.item(item.row())
|
||||
self.trigger_alert(bitem.text())
|
||||
self.alert_text_edit.setText(bitem.text())
|
||||
self.item_id = bitem.data(QtCore.Qt.UserRole)
|
||||
list_item = self.alert_list_widget.item(item.row())
|
||||
self.trigger_alert(list_item.text())
|
||||
self.alert_text_edit.setText(list_item.text())
|
||||
self.item_id = list_item.data(QtCore.Qt.UserRole)
|
||||
self.save_button.setEnabled(False)
|
||||
|
||||
def on_single_click(self):
|
||||
|
@ -164,9 +164,9 @@ class AlertForm(QtGui.QDialog, Ui_AlertDialog):
|
|||
List item has been single clicked to add it to the edit field so it can be changed.
|
||||
"""
|
||||
item = self.alert_list_widget.selectedIndexes()[0]
|
||||
bitem = self.alert_list_widget.item(item.row())
|
||||
self.alert_text_edit.setText(bitem.text())
|
||||
self.item_id = bitem.data(QtCore.Qt.UserRole)
|
||||
list_item = self.alert_list_widget.item(item.row())
|
||||
self.alert_text_edit.setText(list_item.text())
|
||||
self.item_id = list_item.data(QtCore.Qt.UserRole)
|
||||
# If the alert does not contain '<>' we clear the ParameterEdit field.
|
||||
if self.alert_text_edit.text().find('<>') == -1:
|
||||
self.parameter_edit.setText('')
|
||||
|
@ -176,26 +176,30 @@ class AlertForm(QtGui.QDialog, Ui_AlertDialog):
|
|||
"""
|
||||
Prepares the alert text for displaying.
|
||||
|
||||
``text``
|
||||
The alert text (unicode).
|
||||
:param text: The alert text.
|
||||
"""
|
||||
if not text:
|
||||
return False
|
||||
# We found '<>' in the alert text, but the ParameterEdit field is empty.
|
||||
if text.find('<>') != -1 and not self.parameter_edit.text() and QtGui.QMessageBox.question(self,
|
||||
translate('AlertsPlugin.AlertForm', 'No Parameter Found'),
|
||||
translate('AlertsPlugin.AlertForm', 'You have not entered a parameter to be replaced.\n'
|
||||
'Do you want to continue anyway?'),
|
||||
QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.No | QtGui.QMessageBox.Yes)) == QtGui.QMessageBox.No:
|
||||
if text.find('<>') != -1 and not self.parameter_edit.text() and \
|
||||
QtGui.QMessageBox.question(self,
|
||||
translate('AlertsPlugin.AlertForm', 'No Parameter Found'),
|
||||
translate('AlertsPlugin.AlertForm',
|
||||
'You have not entered a parameter to be replaced.\n'
|
||||
'Do you want to continue anyway?'),
|
||||
QtGui.QMessageBox.StandardButtons(
|
||||
QtGui.QMessageBox.No | QtGui.QMessageBox.Yes)) == QtGui.QMessageBox.No:
|
||||
self.parameter_edit.setFocus()
|
||||
return False
|
||||
# The ParameterEdit field is not empty, but we have not found '<>'
|
||||
# in the alert text.
|
||||
elif text.find('<>') == -1 and self.parameter_edit.text() and QtGui.QMessageBox.question(self,
|
||||
translate('AlertsPlugin.AlertForm', 'No Placeholder Found'),
|
||||
translate('AlertsPlugin.AlertForm', 'The alert text does not contain \'<>\'.\n'
|
||||
'Do you want to continue anyway?'),
|
||||
QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.No | QtGui.QMessageBox.Yes)) == QtGui.QMessageBox.No:
|
||||
elif text.find('<>') == -1 and self.parameter_edit.text() and \
|
||||
QtGui.QMessageBox.question(self,
|
||||
translate('AlertsPlugin.AlertForm', 'No Placeholder Found'),
|
||||
translate('AlertsPlugin.AlertForm', 'The alert text does not contain \'<>\'.\n'
|
||||
'Do you want to continue anyway?'),
|
||||
QtGui.QMessageBox.StandardButtons(
|
||||
QtGui.QMessageBox.No | QtGui.QMessageBox.Yes)) == QtGui.QMessageBox.No:
|
||||
self.parameter_edit.setFocus()
|
||||
return False
|
||||
text = text.replace('<>', self.parameter_edit.text())
|
||||
|
@ -207,8 +211,7 @@ class AlertForm(QtGui.QDialog, Ui_AlertDialog):
|
|||
Called when the *alert_list_widget*'s current row has been changed. This enables or disables buttons which
|
||||
require an item to act on.
|
||||
|
||||
``row``
|
||||
The row (int). If there is no current row, the value is -1.
|
||||
:param row: The row (int). If there is no current row, the value is -1.
|
||||
"""
|
||||
if row == -1:
|
||||
self.display_button.setEnabled(False)
|
||||
|
|
|
@ -31,25 +31,17 @@ The :mod:`~openlp.plugins.alerts.lib.alertsmanager` module contains the part of
|
|||
displaying of alerts.
|
||||
"""
|
||||
|
||||
import logging
|
||||
|
||||
from PyQt4 import QtCore
|
||||
|
||||
from openlp.core.common import Registry, translate
|
||||
from openlp.core.common import OpenLPMixin, RegistryMixin, Registry, translate
|
||||
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class AlertsManager(QtCore.QObject):
|
||||
class AlertsManager(OpenLPMixin, RegistryMixin, QtCore.QObject):
|
||||
"""
|
||||
AlertsManager manages the settings of Alerts.
|
||||
"""
|
||||
log.info('Alert Manager loaded')
|
||||
|
||||
def __init__(self, parent):
|
||||
super(AlertsManager, self).__init__(parent)
|
||||
Registry().register('alerts_manager', self)
|
||||
self.timer_id = 0
|
||||
self.alert_list = []
|
||||
Registry().register_function('live_display_active', self.generate_alert)
|
||||
|
@ -59,6 +51,8 @@ class AlertsManager(QtCore.QObject):
|
|||
def alert_text(self, message):
|
||||
"""
|
||||
Called via a alerts_text event. Message is single element array containing text.
|
||||
|
||||
:param message: The message text to be displayed
|
||||
"""
|
||||
if message:
|
||||
self.display_alert(message[0])
|
||||
|
@ -67,10 +61,9 @@ class AlertsManager(QtCore.QObject):
|
|||
"""
|
||||
Called from the Alert Tab to display an alert.
|
||||
|
||||
``text``
|
||||
display text
|
||||
:param text: The text to display
|
||||
"""
|
||||
log.debug('display alert called %s' % text)
|
||||
self.log_debug('display alert called %s' % text)
|
||||
if text:
|
||||
self.alert_list.append(text)
|
||||
if self.timer_id != 0:
|
||||
|
@ -84,7 +77,6 @@ class AlertsManager(QtCore.QObject):
|
|||
"""
|
||||
Format and request the Alert and start the timer.
|
||||
"""
|
||||
log.debug('Generate Alert called')
|
||||
if not self.alert_list:
|
||||
return
|
||||
text = self.alert_list.pop(0)
|
||||
|
@ -98,13 +90,11 @@ class AlertsManager(QtCore.QObject):
|
|||
"""
|
||||
Time has finished so if our time then request the next Alert if there is one and reset the timer.
|
||||
|
||||
``event``
|
||||
the QT event that has been triggered.
|
||||
:param event: the QT event that has been triggered.
|
||||
"""
|
||||
log.debug('timer event')
|
||||
if event.timerId() == self.timer_id:
|
||||
alertTab = self.parent().settings_tab
|
||||
self.live_controller.display.alert('', alertTab.location)
|
||||
alert_tab = self.parent().settings_tab
|
||||
self.live_controller.display.alert('', alert_tab.location)
|
||||
self.killTimer(self.timer_id)
|
||||
self.timer_id = 0
|
||||
self.generate_alert()
|
||||
|
|
|
@ -114,6 +114,9 @@ class AlertsTab(SettingsTab):
|
|||
self.font_preview.setText(UiStrings().OLPV2x)
|
||||
|
||||
def on_background_color_button_clicked(self):
|
||||
"""
|
||||
The background color has been changed.
|
||||
"""
|
||||
new_color = QtGui.QColorDialog.getColor(QtGui.QColor(self.background_color), self)
|
||||
if new_color.isValid():
|
||||
self.background_color = new_color.name()
|
||||
|
@ -121,9 +124,15 @@ class AlertsTab(SettingsTab):
|
|||
self.update_display()
|
||||
|
||||
def on_font_combo_box_clicked(self):
|
||||
"""
|
||||
The Font Combo was changed.
|
||||
"""
|
||||
self.update_display()
|
||||
|
||||
def on_font_color_button_clicked(self):
|
||||
"""
|
||||
The Font Color button has clicked.
|
||||
"""
|
||||
new_color = QtGui.QColorDialog.getColor(QtGui.QColor(self.font_color), self)
|
||||
if new_color.isValid():
|
||||
self.font_color = new_color.name()
|
||||
|
@ -131,14 +140,24 @@ class AlertsTab(SettingsTab):
|
|||
self.update_display()
|
||||
|
||||
def on_timeout_spin_box_changed(self):
|
||||
"""
|
||||
The Time out spin box has changed.
|
||||
|
||||
"""
|
||||
self.timeout = self.timeout_spin_box.value()
|
||||
self.changed = True
|
||||
|
||||
def on_font_size_spin_box_changed(self):
|
||||
"""
|
||||
The font size spin box has changed.
|
||||
"""
|
||||
self.font_size = self.font_size_spin_box.value()
|
||||
self.update_display()
|
||||
|
||||
def load(self):
|
||||
"""
|
||||
Load the settings into the UI.
|
||||
"""
|
||||
settings = Settings()
|
||||
settings.beginGroup(self.settings_section)
|
||||
self.timeout = settings.value('timeout')
|
||||
|
@ -160,6 +179,9 @@ class AlertsTab(SettingsTab):
|
|||
self.changed = False
|
||||
|
||||
def save(self):
|
||||
"""
|
||||
Save the changes on exit of the Settings dialog.
|
||||
"""
|
||||
settings = Settings()
|
||||
settings.beginGroup(self.settings_section)
|
||||
# Check value has changed as no event handles this field
|
||||
|
@ -179,6 +201,9 @@ class AlertsTab(SettingsTab):
|
|||
self.changed = False
|
||||
|
||||
def update_display(self):
|
||||
"""
|
||||
Update the preview display after changes have been made,
|
||||
"""
|
||||
font = QtGui.QFont()
|
||||
font.setFamily(self.font_combo_box.currentFont().family())
|
||||
font.setBold(True)
|
||||
|
|
|
@ -47,14 +47,14 @@ def init_schema(url):
|
|||
"""
|
||||
Setup the alerts database connection and initialise the database schema
|
||||
|
||||
``url``
|
||||
:param url:
|
||||
The database to setup
|
||||
"""
|
||||
session, metadata = init_db(url)
|
||||
|
||||
alerts_table = Table('alerts', metadata,
|
||||
Column('id', types.Integer(), primary_key=True),
|
||||
Column('text', types.UnicodeText, nullable=False))
|
||||
Column('id', types.Integer(), primary_key=True),
|
||||
Column('text', types.UnicodeText, nullable=False))
|
||||
|
||||
mapper(AlertItem, alerts_table)
|
||||
|
||||
|
|
|
@ -36,6 +36,10 @@ from openlp.core.lib.ui import create_button_box, create_button
|
|||
|
||||
class Ui_CustomEditDialog(object):
|
||||
def setupUi(self, custom_edit_dialog):
|
||||
"""
|
||||
Build the Edit Dialog UI
|
||||
:param custom_edit_dialog: The Dialog
|
||||
"""
|
||||
custom_edit_dialog.setObjectName('custom_edit_dialog')
|
||||
custom_edit_dialog.resize(450, 350)
|
||||
custom_edit_dialog.setWindowIcon(build_icon(':/icon/openlp-logo-16x16.png'))
|
||||
|
@ -100,7 +104,7 @@ class Ui_CustomEditDialog(object):
|
|||
self.dialog_layout.addLayout(self.bottom_form_layout)
|
||||
self.preview_button = QtGui.QPushButton()
|
||||
self.button_box = create_button_box(custom_edit_dialog, 'button_box', ['cancel', 'save'],
|
||||
[self.preview_button])
|
||||
[self.preview_button])
|
||||
self.dialog_layout.addWidget(self.button_box)
|
||||
self.retranslateUi(custom_edit_dialog)
|
||||
|
||||
|
|
|
@ -70,8 +70,7 @@ class EditCustomForm(QtGui.QDialog, Ui_CustomEditDialog):
|
|||
"""
|
||||
Load a list of themes into the themes combo box.
|
||||
|
||||
``theme_list``
|
||||
The list of themes to load.
|
||||
:param theme_list: The list of themes to load.
|
||||
"""
|
||||
self.theme_combo_box.clear()
|
||||
self.theme_combo_box.addItem('')
|
||||
|
@ -81,12 +80,8 @@ class EditCustomForm(QtGui.QDialog, Ui_CustomEditDialog):
|
|||
"""
|
||||
Called when editing or creating a new custom.
|
||||
|
||||
``id``
|
||||
The custom's id. If zero, then a new custom is created.
|
||||
|
||||
``preview``
|
||||
States whether the custom is edited while being previewed in the
|
||||
preview panel.
|
||||
:param id: The custom's id. If zero, then a new custom is created.
|
||||
:param preview: States whether the custom is edited while being previewed in the preview panel.
|
||||
"""
|
||||
self.slide_list_view.clear()
|
||||
if id == 0:
|
||||
|
@ -195,11 +190,8 @@ class EditCustomForm(QtGui.QDialog, Ui_CustomEditDialog):
|
|||
"""
|
||||
Updates the slide list after editing slides.
|
||||
|
||||
``slides``
|
||||
A list of all slides which have been edited.
|
||||
|
||||
``edit_all``
|
||||
Indicates if all slides or only one slide has been edited.
|
||||
:param slides: A list of all slides which have been edited.
|
||||
:param edit_all: Indicates if all slides or only one slide has been edited.
|
||||
"""
|
||||
if edit_all:
|
||||
self.slide_list_view.clear()
|
||||
|
@ -229,8 +221,7 @@ class EditCustomForm(QtGui.QDialog, Ui_CustomEditDialog):
|
|||
Called when the *slide_list_view*'s current row has been changed. This
|
||||
enables or disables buttons which require an slide to act on.
|
||||
|
||||
``row``
|
||||
The row (int). If there is no current row, the value is -1.
|
||||
:param row: The row (int). If there is no current row, the value is -1.
|
||||
"""
|
||||
if row == -1:
|
||||
self.delete_button.setEnabled(False)
|
||||
|
|
|
@ -57,8 +57,7 @@ class EditCustomSlideForm(QtGui.QDialog, Ui_CustomSlideEditDialog):
|
|||
"""
|
||||
Set the text for slide_text_edit.
|
||||
|
||||
``text``
|
||||
The text (unicode).
|
||||
:param text: The text (unicode).
|
||||
"""
|
||||
self.slide_text_edit.clear()
|
||||
if text:
|
||||
|
@ -87,7 +86,9 @@ class EditCustomSlideForm(QtGui.QDialog, Ui_CustomSlideEditDialog):
|
|||
|
||||
def insert_single_line_text_at_cursor(self, text):
|
||||
"""
|
||||
Adds ``text`` in a single line at the cursor position.
|
||||
Adds a single line at the cursor position.
|
||||
|
||||
:param text: The text to be inserted
|
||||
"""
|
||||
full_text = self.slide_text_edit.toPlainText()
|
||||
position = self.slide_text_edit.textCursor().position()
|
||||
|
|
|
@ -72,6 +72,8 @@ class CustomTab(SettingsTab):
|
|||
def on_display_footer_check_box_changed(self, check_state):
|
||||
"""
|
||||
Toggle the setting for displaying the footer.
|
||||
|
||||
:param check_state: The current check box state
|
||||
"""
|
||||
self.display_footer = False
|
||||
# we have a set value convert to True/False
|
||||
|
@ -79,9 +81,18 @@ class CustomTab(SettingsTab):
|
|||
self.display_footer = True
|
||||
|
||||
def on_add_from_service_check_box_changed(self, check_state):
|
||||
"""
|
||||
Allows service items to create Custom items.
|
||||
|
||||
:param check_state: The current check box state
|
||||
"""
|
||||
self.update_load = (check_state == QtCore.Qt.Checked)
|
||||
|
||||
def load(self):
|
||||
"""
|
||||
|
||||
Load the settings into the dialog
|
||||
"""
|
||||
settings = Settings()
|
||||
settings.beginGroup(self.settings_section)
|
||||
self.display_footer = settings.value('display footer')
|
||||
|
@ -91,6 +102,9 @@ class CustomTab(SettingsTab):
|
|||
settings.endGroup()
|
||||
|
||||
def save(self):
|
||||
"""
|
||||
Save the Dialog settings
|
||||
"""
|
||||
settings = Settings()
|
||||
settings.beginGroup(self.settings_section)
|
||||
settings.setValue('display footer', self.display_footer)
|
||||
|
|
|
@ -50,6 +50,7 @@ from lxml import etree, objectify
|
|||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
#TODO: These classes need to be refactored into a single class.
|
||||
class CustomXMLBuilder(object):
|
||||
"""
|
||||
|
@ -89,15 +90,11 @@ class CustomXMLBuilder(object):
|
|||
"""
|
||||
Add a verse to the ``<lyrics>`` tag.
|
||||
|
||||
``verse_type``
|
||||
A string denoting the type of verse. Possible values are "Chorus",
|
||||
"Verse", "Bridge", and "Custom".
|
||||
:param verse_type: A string denoting the type of verse. Possible values are "Chorus", "Verse", "Bridge",
|
||||
and "Custom".
|
||||
:param number: An integer denoting the number of the item, for example: verse 1.
|
||||
:param content: The actual text of the verse to be stored.
|
||||
|
||||
``number``
|
||||
An integer denoting the number of the item, for example: verse 1.
|
||||
|
||||
``content``
|
||||
The actual text of the verse to be stored.
|
||||
"""
|
||||
verse = self.custom_xml.createElement('verse')
|
||||
verse.setAttribute('type', verse_type)
|
||||
|
|
|
@ -71,7 +71,7 @@ def init_schema(url):
|
|||
Column('text', types.UnicodeText, nullable=False),
|
||||
Column('credits', types.UnicodeText),
|
||||
Column('theme_name', types.Unicode(128))
|
||||
)
|
||||
)
|
||||
|
||||
mapper(CustomSlide, custom_slide_table)
|
||||
|
||||
|
|
|
@ -73,6 +73,9 @@ class CustomMediaItem(MediaManagerItem):
|
|||
self.remote_custom = -1
|
||||
|
||||
def add_end_header_bar(self):
|
||||
"""
|
||||
Add the Custom End Head bar and register events and functions
|
||||
"""
|
||||
self.toolbar.addSeparator()
|
||||
self.add_search_to_toolbar()
|
||||
# Signals and slots
|
||||
|
@ -91,10 +94,17 @@ class CustomMediaItem(MediaManagerItem):
|
|||
self.add_custom_from_service = Settings().value(self.settings_section + '/add custom from service')
|
||||
|
||||
def retranslateUi(self):
|
||||
"""
|
||||
|
||||
|
||||
"""
|
||||
self.search_text_label.setText('%s:' % UiStrings().Search)
|
||||
self.search_text_button.setText(UiStrings().Search)
|
||||
|
||||
def initialise(self):
|
||||
"""
|
||||
Initialise the UI so it can provide Searches
|
||||
"""
|
||||
self.search_text_edit.set_search_types([(CustomSearch.Titles, ':/songs/song_search_title.png',
|
||||
translate('SongsPlugin.MediaItem', 'Titles'),
|
||||
translate('SongsPlugin.MediaItem', 'Search Titles...')),
|
||||
|
@ -107,6 +117,11 @@ class CustomMediaItem(MediaManagerItem):
|
|||
|
||||
def load_list(self, custom_slides, target_group=None):
|
||||
# Sort out what custom we want to select after loading the list.
|
||||
"""
|
||||
|
||||
:param custom_slides:
|
||||
:param target_group:
|
||||
"""
|
||||
self.save_auto_select_id()
|
||||
self.list_view.clear()
|
||||
custom_slides.sort()
|
||||
|
@ -123,6 +138,9 @@ class CustomMediaItem(MediaManagerItem):
|
|||
# active trigger it and clean up so it will not update again.
|
||||
|
||||
def on_new_click(self):
|
||||
"""
|
||||
Handle the New item event
|
||||
"""
|
||||
self.edit_custom_form.load_custom(0)
|
||||
self.edit_custom_form.exec_()
|
||||
self.on_clear_text_button_click()
|
||||
|
@ -132,6 +150,9 @@ class CustomMediaItem(MediaManagerItem):
|
|||
"""
|
||||
Called by ServiceManager or SlideController by event passing the custom Id in the payload along with an
|
||||
indicator to say which type of display is required.
|
||||
|
||||
:param custom_id: The id of the item to be edited
|
||||
:param preview: Do we need to update the Preview after the edit. (Default False)
|
||||
"""
|
||||
custom_id = int(custom_id)
|
||||
valid = self.plugin.db_manager.get_object(CustomSlide, custom_id)
|
||||
|
@ -184,12 +205,20 @@ class CustomMediaItem(MediaManagerItem):
|
|||
self.on_search_text_button_clicked()
|
||||
|
||||
def on_focus(self):
|
||||
"""
|
||||
Set the focus
|
||||
"""
|
||||
self.search_text_edit.setFocus()
|
||||
|
||||
def generate_slide_data(self, service_item, item=None, xml_version=False,
|
||||
remote=False, context=ServiceItemContext.Service):
|
||||
"""
|
||||
Generate the slide data. Needs to be implemented by the plugin.
|
||||
:param service_item: To be updated
|
||||
:param item: The custom database item to be used
|
||||
:param xml_version: No used
|
||||
:param remote: Is this triggered by the Preview Controller or Service Manager.
|
||||
:param context: Why is this item required to be build (Default Service).
|
||||
"""
|
||||
item_id = self._get_id_of_item_to_generate(item, self.remote_custom)
|
||||
service_item.add_capability(ItemCapabilities.CanEdit)
|
||||
|
@ -243,6 +272,8 @@ class CustomMediaItem(MediaManagerItem):
|
|||
"""
|
||||
If search as type enabled invoke the search on each key press. If the Title is being searched do not start until
|
||||
2 characters have been entered.
|
||||
|
||||
:param text: The search text
|
||||
"""
|
||||
search_length = 2
|
||||
if len(text) > search_length:
|
||||
|
@ -253,6 +284,8 @@ class CustomMediaItem(MediaManagerItem):
|
|||
def service_load(self, item):
|
||||
"""
|
||||
Triggered by a custom item being loaded by the service manager.
|
||||
|
||||
:param item: The service Item from the service to load found in the database.
|
||||
"""
|
||||
log.debug('service_load')
|
||||
if self.plugin.status != PluginStatus.Active:
|
||||
|
@ -271,6 +304,8 @@ class CustomMediaItem(MediaManagerItem):
|
|||
def create_from_service_item(self, item):
|
||||
"""
|
||||
Create a custom slide from a text service item
|
||||
|
||||
:param item: the service item to be converted to a Custom item
|
||||
"""
|
||||
custom = CustomSlide()
|
||||
custom.title = item.title
|
||||
|
@ -303,6 +338,9 @@ class CustomMediaItem(MediaManagerItem):
|
|||
def search(self, string, show_error):
|
||||
"""
|
||||
Search the database for a given item.
|
||||
|
||||
:param string: The search string
|
||||
:param show_error: The error string to be show.
|
||||
"""
|
||||
search = '%' + string.lower() + '%'
|
||||
search_results = self.plugin.db_manager.get_all_objects(CustomSlide,
|
||||
|
|
|
@ -74,7 +74,7 @@ class MediaMediaItem(MediaManagerItem):
|
|||
self.display_controller.controller_layout = QtGui.QVBoxLayout()
|
||||
self.media_controller.register_controller(self.display_controller)
|
||||
self.media_controller.set_controls_visible(self.display_controller, False)
|
||||
self.display_controller.preview_display = Display(self.display_controller, False, self.display_controller)
|
||||
self.display_controller.preview_display = Display(self.display_controller)
|
||||
self.display_controller.preview_display.hide()
|
||||
self.display_controller.preview_display.setGeometry(QtCore.QRect(0, 0, 300, 300))
|
||||
self.display_controller.preview_display.screen = {'size': self.display_controller.preview_display.geometry()}
|
||||
|
@ -94,7 +94,7 @@ class MediaMediaItem(MediaManagerItem):
|
|||
self.reset_action.setToolTip(UiStrings().ResetLiveBG)
|
||||
self.automatic = UiStrings().Automatic
|
||||
self.display_type_label.setText(translate('MediaPlugin.MediaItem', 'Use Player:'))
|
||||
self.rebuild_players()
|
||||
#self.rebuild_players()
|
||||
|
||||
def required_icons(self):
|
||||
"""
|
||||
|
@ -112,9 +112,9 @@ class MediaMediaItem(MediaManagerItem):
|
|||
def add_end_header_bar(self):
|
||||
# Replace backgrounds do not work at present so remove functionality.
|
||||
self.replace_action = self.toolbar.add_toolbar_action('replace_action', icon=':/slides/slide_blank.png',
|
||||
triggers=self.onReplaceClick)
|
||||
triggers=self.on_replace_click)
|
||||
self.reset_action = self.toolbar.add_toolbar_action('reset_action', icon=':/system/system_close.png',
|
||||
visible=False, triggers=self.onResetClick)
|
||||
visible=False, triggers=self.on_reset_click)
|
||||
self.media_widget = QtGui.QWidget(self)
|
||||
self.media_widget.setObjectName('media_widget')
|
||||
self.display_layout = QtGui.QFormLayout(self.media_widget)
|
||||
|
@ -128,16 +128,16 @@ class MediaMediaItem(MediaManagerItem):
|
|||
self.display_layout.addRow(self.display_type_label, self.display_type_combo_box)
|
||||
# Add the Media widget to the page layout.
|
||||
self.page_layout.addWidget(self.media_widget)
|
||||
self.display_type_combo_box.currentIndexChanged.connect(self.overridePlayerChanged)
|
||||
self.display_type_combo_box.currentIndexChanged.connect(self.override_player_changed)
|
||||
|
||||
def overridePlayerChanged(self, index):
|
||||
def override_player_changed(self, index):
|
||||
player = get_media_players()[0]
|
||||
if index == 0:
|
||||
set_media_players(player)
|
||||
else:
|
||||
set_media_players(player, player[index-1])
|
||||
|
||||
def onResetClick(self):
|
||||
def on_reset_click(self):
|
||||
"""
|
||||
Called to reset the Live background with the media selected,
|
||||
"""
|
||||
|
@ -150,12 +150,13 @@ class MediaMediaItem(MediaManagerItem):
|
|||
"""
|
||||
self.reset_action.setVisible(False)
|
||||
|
||||
def onReplaceClick(self):
|
||||
def on_replace_click(self):
|
||||
"""
|
||||
Called to replace Live background with the media selected.
|
||||
"""
|
||||
if check_item_selected(self.list_view,
|
||||
translate('MediaPlugin.MediaItem', 'You must select a media file to replace the background with.')):
|
||||
translate('MediaPlugin.MediaItem',
|
||||
'You must select a media file to replace the background with.')):
|
||||
item = self.list_view.currentItem()
|
||||
filename = item.data(QtCore.Qt.UserRole)
|
||||
if os.path.exists(filename):
|
||||
|
@ -168,14 +169,16 @@ class MediaMediaItem(MediaManagerItem):
|
|||
self.reset_action.setVisible(True)
|
||||
else:
|
||||
critical_error_message_box(UiStrings().LiveBGError,
|
||||
translate('MediaPlugin.MediaItem', 'There was no display item to amend.'))
|
||||
translate('MediaPlugin.MediaItem',
|
||||
'There was no display item to amend.'))
|
||||
else:
|
||||
critical_error_message_box(UiStrings().LiveBGError,
|
||||
translate('MediaPlugin.MediaItem',
|
||||
'There was a problem replacing your background, the media file "%s" no longer exists.') % filename)
|
||||
translate('MediaPlugin.MediaItem',
|
||||
'There was a problem replacing your background, '
|
||||
'the media file "%s" no longer exists.') % filename)
|
||||
|
||||
def generate_slide_data(self, service_item, item=None, xml_version=False, remote=False,
|
||||
context=ServiceItemContext.Live):
|
||||
context=ServiceItemContext.Live):
|
||||
"""
|
||||
Generate the slide data. Needs to be implemented by the plugin.
|
||||
"""
|
||||
|
@ -211,16 +214,16 @@ class MediaMediaItem(MediaManagerItem):
|
|||
def initialise(self):
|
||||
self.list_view.clear()
|
||||
self.list_view.setIconSize(QtCore.QSize(88, 50))
|
||||
self.servicePath = os.path.join(AppLocation.get_section_data_path(self.settings_section), 'thumbnails')
|
||||
check_directory_exists(self.servicePath)
|
||||
self.service_path = os.path.join(AppLocation.get_section_data_path(self.settings_section), 'thumbnails')
|
||||
check_directory_exists(self.service_path)
|
||||
self.load_list(Settings().value(self.settings_section + '/media files'))
|
||||
self.populateDisplayTypes()
|
||||
self.populate_display_types()
|
||||
|
||||
def rebuild_players(self):
|
||||
"""
|
||||
Rebuild the tab in the media manager when changes are made in the settings.
|
||||
"""
|
||||
self.populateDisplayTypes()
|
||||
self.populate_display_types()
|
||||
self.on_new_file_masks = translate('MediaPlugin.MediaItem', 'Videos (%s);;Audio (%s);;%s (*)') % (
|
||||
' '.join(self.media_controller.video_extensions_list),
|
||||
' '.join(self.media_controller.audio_extensions_list), UiStrings().AllFiles)
|
||||
|
@ -228,25 +231,25 @@ class MediaMediaItem(MediaManagerItem):
|
|||
def display_setup(self):
|
||||
self.media_controller.setup_display(self.display_controller.preview_display, False)
|
||||
|
||||
def populateDisplayTypes(self):
|
||||
def populate_display_types(self):
|
||||
"""
|
||||
Load the combobox with the enabled media players, allowing user to select a specific player if settings allow.
|
||||
"""
|
||||
# block signals to avoid unnecessary overridePlayerChanged Signals while combo box creation
|
||||
# block signals to avoid unnecessary override_player_changed Signals while combo box creation
|
||||
self.display_type_combo_box.blockSignals(True)
|
||||
self.display_type_combo_box.clear()
|
||||
usedPlayers, overridePlayer = get_media_players()
|
||||
used_players, override_player = get_media_players()
|
||||
media_players = self.media_controller.media_players
|
||||
currentIndex = 0
|
||||
for player in usedPlayers:
|
||||
current_index = 0
|
||||
for player in used_players:
|
||||
# load the drop down selection
|
||||
self.display_type_combo_box.addItem(media_players[player].original_name)
|
||||
if overridePlayer == player:
|
||||
currentIndex = len(self.display_type_combo_box)
|
||||
if override_player == player:
|
||||
current_index = len(self.display_type_combo_box)
|
||||
if self.display_type_combo_box.count() > 1:
|
||||
self.display_type_combo_box.insertItem(0, self.automatic)
|
||||
self.display_type_combo_box.setCurrentIndex(currentIndex)
|
||||
if overridePlayer:
|
||||
self.display_type_combo_box.setCurrentIndex(current_index)
|
||||
if override_player:
|
||||
self.media_widget.show()
|
||||
else:
|
||||
self.media_widget.hide()
|
||||
|
@ -257,7 +260,7 @@ class MediaMediaItem(MediaManagerItem):
|
|||
Remove a media item from the list.
|
||||
"""
|
||||
if check_item_selected(self.list_view,
|
||||
translate('MediaPlugin.MediaItem', 'You must select a media file to delete.')):
|
||||
translate('MediaPlugin.MediaItem', 'You must select a media file to delete.')):
|
||||
row_list = [item.row() for item in self.list_view.selectedIndexes()]
|
||||
row_list.sort(reverse=True)
|
||||
for row in row_list:
|
||||
|
@ -266,25 +269,25 @@ class MediaMediaItem(MediaManagerItem):
|
|||
|
||||
def load_list(self, media, target_group=None):
|
||||
# Sort the media by its filename considering language specific characters.
|
||||
media.sort(key=lambda filename: get_locale_key(os.path.split(str(filename))[1]))
|
||||
media.sort(key=lambda file_name: get_locale_key(os.path.split(str(file_name))[1]))
|
||||
for track in media:
|
||||
track_info = QtCore.QFileInfo(track)
|
||||
if not os.path.exists(track):
|
||||
filename = os.path.split(str(track))[1]
|
||||
item_name = QtGui.QListWidgetItem(filename)
|
||||
file_name = os.path.split(str(track))[1]
|
||||
item_name = QtGui.QListWidgetItem(file_name)
|
||||
item_name.setIcon(ERROR_ICON)
|
||||
item_name.setData(QtCore.Qt.UserRole, track)
|
||||
elif track_info.isFile():
|
||||
filename = os.path.split(str(track))[1]
|
||||
item_name = QtGui.QListWidgetItem(filename)
|
||||
if '*.%s' % (filename.split('.')[-1].lower()) in self.media_controller.audio_extensions_list:
|
||||
file_name = os.path.split(str(track))[1]
|
||||
item_name = QtGui.QListWidgetItem(file_name)
|
||||
if '*.%s' % (file_name.split('.')[-1].lower()) in self.media_controller.audio_extensions_list:
|
||||
item_name.setIcon(AUDIO_ICON)
|
||||
else:
|
||||
item_name.setIcon(VIDEO_ICON)
|
||||
item_name.setData(QtCore.Qt.UserRole, track)
|
||||
else:
|
||||
filename = os.path.split(str(track))[1]
|
||||
item_name = QtGui.QListWidgetItem(filename)
|
||||
file_name = os.path.split(str(track))[1]
|
||||
item_name = QtGui.QListWidgetItem(file_name)
|
||||
item_name.setIcon(build_icon(DVD_ICON))
|
||||
item_name.setData(QtCore.Qt.UserRole, track)
|
||||
item_name.setToolTip(track)
|
||||
|
@ -302,7 +305,7 @@ class MediaMediaItem(MediaManagerItem):
|
|||
media = [x for x in media if os.path.splitext(x)[1] in extension]
|
||||
return media
|
||||
|
||||
def search(self, string, showError):
|
||||
def search(self, string, show_error):
|
||||
files = Settings().value(self.settings_section + '/media files')
|
||||
results = []
|
||||
string = string.lower()
|
||||
|
|
|
@ -166,7 +166,7 @@ class PresentationMediaItem(MediaManagerItem):
|
|||
self.main_window.increment_progress_bar()
|
||||
if current_list.count(file) > 0:
|
||||
continue
|
||||
filename = os.path.split(str(file))[1]
|
||||
filename = os.path.split(file)[1]
|
||||
if not os.path.exists(file):
|
||||
item_name = QtGui.QListWidgetItem(filename)
|
||||
item_name.setIcon(build_icon(ERROR_IMAGE))
|
||||
|
@ -184,7 +184,7 @@ class PresentationMediaItem(MediaManagerItem):
|
|||
controller_name = self.findControllerByType(filename)
|
||||
if controller_name:
|
||||
controller = self.controllers[controller_name]
|
||||
doc = controller.add_document(str(file))
|
||||
doc = controller.add_document(file)
|
||||
thumb = os.path.join(doc.get_thumbnail_folder(), 'icon.png')
|
||||
preview = doc.get_thumbnail_path(1, True)
|
||||
if not preview and not initial_load:
|
||||
|
|
|
@ -27,8 +27,8 @@
|
|||
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||
###############################################################################
|
||||
|
||||
import os
|
||||
import logging
|
||||
import os
|
||||
|
||||
if os.name == 'nt':
|
||||
from ctypes import cdll
|
||||
|
@ -121,12 +121,17 @@ class PptviewDocument(PresentationDocument):
|
|||
the background PptView task started earlier.
|
||||
"""
|
||||
log.debug('LoadPresentation')
|
||||
temp_folder = self.get_temp_folder()
|
||||
size = ScreenList().current['size']
|
||||
rect = RECT(size.x(), size.y(), size.right(), size.bottom())
|
||||
filepath = str(self.filepath.replace('/', '\\'))
|
||||
if not os.path.isdir(self.get_temp_folder()):
|
||||
os.makedirs(self.get_temp_folder())
|
||||
self.ppt_id = self.controller.process.OpenPPT(filepath, None, rect, str(self.get_temp_folder()) + '\\slide')
|
||||
file_path = os.path.normpath(self.filepath)
|
||||
preview_path = os.path.join(temp_folder, 'slide')
|
||||
# Ensure that the paths are null terminated
|
||||
file_path = file_path.encode('utf-16-le') + b'\0'
|
||||
preview_path = preview_path.encode('utf-16-le') + b'\0'
|
||||
if not os.path.isdir(temp_folder):
|
||||
os.makedirs(temp_folder)
|
||||
self.ppt_id = self.controller.process.OpenPPT(file_path, None, rect, preview_path)
|
||||
if self.ppt_id >= 0:
|
||||
self.create_thumbnails()
|
||||
self.stop_presentation()
|
||||
|
|
|
@ -61,18 +61,18 @@ BOOL APIENTRY DllMain(HMODULE hModule, DWORD ulReasonForCall,
|
|||
switch(ulReasonForCall)
|
||||
{
|
||||
case DLL_PROCESS_ATTACH:
|
||||
DEBUG("PROCESS_ATTACH\n");
|
||||
DEBUG(L"PROCESS_ATTACH\n");
|
||||
break;
|
||||
case DLL_THREAD_ATTACH:
|
||||
//DEBUG("THREAD_ATTACH\n");
|
||||
//DEBUG(L"THREAD_ATTACH\n");
|
||||
break;
|
||||
case DLL_THREAD_DETACH:
|
||||
//DEBUG("THREAD_DETACH\n");
|
||||
//DEBUG(L"THREAD_DETACH\n");
|
||||
break;
|
||||
case DLL_PROCESS_DETACH:
|
||||
// Clean up... hopefully there is only the one process attached?
|
||||
// We'll find out soon enough during tests!
|
||||
DEBUG("PROCESS_DETACH\n");
|
||||
DEBUG(L"PROCESS_DETACH\n");
|
||||
for (int i = 0; i < MAX_PPTS; i++)
|
||||
ClosePPT(i);
|
||||
break;
|
||||
|
@ -84,18 +84,18 @@ DllExport void SetDebug(BOOL onOff)
|
|||
{
|
||||
printf("SetDebug\n");
|
||||
debug = onOff;
|
||||
DEBUG("enabled\n");
|
||||
DEBUG(L"enabled\n");
|
||||
}
|
||||
|
||||
DllExport BOOL CheckInstalled()
|
||||
{
|
||||
char cmdLine[MAX_PATH * 2];
|
||||
wchar_t cmdLine[MAX_PATH * 2];
|
||||
|
||||
DEBUG("CheckInstalled\n");
|
||||
DEBUG(L"CheckInstalled\n");
|
||||
BOOL found = GetPPTViewerPath(cmdLine, sizeof(cmdLine));
|
||||
if(found)
|
||||
{
|
||||
DEBUG("Exe: %s\n", cmdLine);
|
||||
DEBUG(L"Exe: %s\n", cmdLine);
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
@ -106,20 +106,20 @@ DllExport BOOL CheckInstalled()
|
|||
// "<n>.bmp" will be appended to complete the path. E.g. "c:\temp\slide" would
|
||||
// create "c:\temp\slide1.bmp" slide2.bmp, slide3.bmp etc.
|
||||
// It will also create a *info.txt containing information about the ppt
|
||||
DllExport int OpenPPT(char *filename, HWND hParentWnd, RECT rect,
|
||||
char *previewPath)
|
||||
DllExport int OpenPPT(wchar_t *filename, HWND hParentWnd, RECT rect,
|
||||
wchar_t *previewPath)
|
||||
{
|
||||
STARTUPINFO si;
|
||||
PROCESS_INFORMATION pi;
|
||||
char cmdLine[MAX_PATH * 2];
|
||||
wchar_t cmdLine[MAX_PATH * 2];
|
||||
int id;
|
||||
|
||||
DEBUG("OpenPPT start: %s; %s\n", filename, previewPath);
|
||||
DEBUG("OpenPPT start: %u; %i, %i, %i, %i\n", hParentWnd, rect.top,
|
||||
DEBUG(L"OpenPPT start: %s; %s\n", filename, previewPath);
|
||||
DEBUG(L"OpenPPT start: %u; %i, %i, %i, %i\n", hParentWnd, rect.top,
|
||||
rect.left, rect.bottom, rect.right);
|
||||
if (GetPPTViewerPath(cmdLine, sizeof(cmdLine)) == FALSE)
|
||||
{
|
||||
DEBUG("OpenPPT: GetPPTViewerPath failed\n");
|
||||
DEBUG(L"OpenPPT: GetPPTViewerPath failed\n");
|
||||
return -1;
|
||||
}
|
||||
id = -1;
|
||||
|
@ -133,12 +133,12 @@ DllExport int OpenPPT(char *filename, HWND hParentWnd, RECT rect,
|
|||
}
|
||||
if (id < 0)
|
||||
{
|
||||
DEBUG("OpenPPT: Too many PPTs\n");
|
||||
DEBUG(L"OpenPPT: Too many PPTs\n");
|
||||
return -1;
|
||||
}
|
||||
memset(&pptView[id], 0, sizeof(PPTVIEW));
|
||||
strcpy_s(pptView[id].filename, MAX_PATH, filename);
|
||||
strcpy_s(pptView[id].previewPath, MAX_PATH, previewPath);
|
||||
wcscpy_s(pptView[id].filename, MAX_PATH, filename);
|
||||
wcscpy_s(pptView[id].previewPath, MAX_PATH, previewPath);
|
||||
pptView[id].state = PPT_CLOSED;
|
||||
pptView[id].slideCount = 0;
|
||||
pptView[id].currentSlide = 0;
|
||||
|
@ -169,9 +169,9 @@ DllExport int OpenPPT(char *filename, HWND hParentWnd, RECT rect,
|
|||
pptView[id].rect.bottom = rect.bottom;
|
||||
pptView[id].rect.right = rect.right;
|
||||
}
|
||||
strcat_s(cmdLine, MAX_PATH * 2, " /F /S \"");
|
||||
strcat_s(cmdLine, MAX_PATH * 2, filename);
|
||||
strcat_s(cmdLine, MAX_PATH * 2, "\"");
|
||||
wcscat_s(cmdLine, MAX_PATH * 2, L" /F /S \"");
|
||||
wcscat_s(cmdLine, MAX_PATH * 2, filename);
|
||||
wcscat_s(cmdLine, MAX_PATH * 2, L"\"");
|
||||
memset(&si, 0, sizeof(si));
|
||||
memset(&pi, 0, sizeof(pi));
|
||||
BOOL gotInfo = GetPPTInfo(id);
|
||||
|
@ -190,7 +190,7 @@ DllExport int OpenPPT(char *filename, HWND hParentWnd, RECT rect,
|
|||
globalHook = SetWindowsHookEx(WH_CBT, CbtProc, hInstance, NULL);
|
||||
if (globalHook == 0)
|
||||
{
|
||||
DEBUG("OpenPPT: SetWindowsHookEx failed\n");
|
||||
DEBUG(L"OpenPPT: SetWindowsHookEx failed\n");
|
||||
ClosePPT(id);
|
||||
return -1;
|
||||
}
|
||||
|
@ -198,7 +198,7 @@ DllExport int OpenPPT(char *filename, HWND hParentWnd, RECT rect,
|
|||
Sleep(10);
|
||||
if (!CreateProcess(NULL, cmdLine, NULL, NULL, FALSE, 0, 0, NULL, &si, &pi))
|
||||
{
|
||||
DEBUG("OpenPPT: CreateProcess failed: %s\n", cmdLine);
|
||||
DEBUG(L"OpenPPT: CreateProcess failed: %s\n", cmdLine);
|
||||
ClosePPT(id);
|
||||
return -1;
|
||||
}
|
||||
|
@ -210,32 +210,32 @@ DllExport int OpenPPT(char *filename, HWND hParentWnd, RECT rect,
|
|||
Sleep(10);
|
||||
if (gotInfo)
|
||||
{
|
||||
DEBUG("OpenPPT: Info loaded, no refresh\n");
|
||||
DEBUG(L"OpenPPT: Info loaded, no refresh\n");
|
||||
pptView[id].state = PPT_LOADED;
|
||||
Resume(id);
|
||||
}
|
||||
else
|
||||
{
|
||||
DEBUG("OpenPPT: Get info\n");
|
||||
DEBUG(L"OpenPPT: Get info\n");
|
||||
pptView[id].steps = 0;
|
||||
int steps = 0;
|
||||
while (pptView[id].state == PPT_OPENED)
|
||||
{
|
||||
if (steps <= pptView[id].steps)
|
||||
{
|
||||
Sleep(20);
|
||||
DEBUG("OpenPPT: Step %d/%d\n", steps, pptView[id].steps);
|
||||
Sleep(100);
|
||||
DEBUG(L"OpenPPT: Step %d/%d\n", steps, pptView[id].steps);
|
||||
steps++;
|
||||
NextStep(id);
|
||||
}
|
||||
Sleep(10);
|
||||
}
|
||||
DEBUG("OpenPPT: Slides %d, Steps %d, first slide steps %d\n",
|
||||
DEBUG(L"OpenPPT: Slides %d, Steps %d, first slide steps %d\n",
|
||||
pptView[id].slideCount, pptView[id].steps,
|
||||
pptView[id].firstSlideSteps);
|
||||
for(int i = 1; i <= pptView[id].slideCount; i++)
|
||||
{
|
||||
DEBUG("OpenPPT: Slide %d = %d\n", i, pptView[id].slideNos[i]);
|
||||
DEBUG(L"OpenPPT: Slide %d = %d\n", i, pptView[id].slideNos[i]);
|
||||
}
|
||||
SavePPTInfo(id);
|
||||
if (pptView[id].state == PPT_CLOSING
|
||||
|
@ -257,7 +257,7 @@ DllExport int OpenPPT(char *filename, HWND hParentWnd, RECT rect,
|
|||
}
|
||||
pptView[id].msgHook = NULL;
|
||||
}
|
||||
DEBUG("OpenPPT: Exit: id=%i\n", id);
|
||||
DEBUG(L"OpenPPT: Exit: id=%i\n", id);
|
||||
return id;
|
||||
}
|
||||
// Load information about the ppt from an info.txt file.
|
||||
|
@ -270,75 +270,75 @@ DllExport int OpenPPT(char *filename, HWND hParentWnd, RECT rect,
|
|||
BOOL GetPPTInfo(int id)
|
||||
{
|
||||
struct _stat fileStats;
|
||||
char info[MAX_PATH];
|
||||
wchar_t info[MAX_PATH];
|
||||
FILE* pFile;
|
||||
char buf[100];
|
||||
wchar_t buf[100];
|
||||
|
||||
DEBUG("GetPPTInfo: start\n");
|
||||
if (_stat(pptView[id].filename, &fileStats) != 0)
|
||||
DEBUG(L"GetPPTInfo: start\n");
|
||||
if (_wstat(pptView[id].filename, &fileStats) != 0)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
sprintf_s(info, MAX_PATH, "%sinfo.txt", pptView[id].previewPath);
|
||||
int err = fopen_s(&pFile, info, "r");
|
||||
swprintf_s(info, MAX_PATH, L"%sinfo.txt", pptView[id].previewPath);
|
||||
int err = _wfopen_s(&pFile, info, L"r");
|
||||
if (err != 0)
|
||||
{
|
||||
DEBUG("GetPPTInfo: file open failed - %d\n", err);
|
||||
DEBUG(L"GetPPTInfo: file open failed - %d\n", err);
|
||||
return FALSE;
|
||||
}
|
||||
fgets(buf, 100, pFile); // version == 1
|
||||
fgets(buf, 100, pFile);
|
||||
if (fileStats.st_mtime != atoi(buf))
|
||||
fgetws(buf, 100, pFile); // version == 1
|
||||
fgetws(buf, 100, pFile);
|
||||
if (fileStats.st_mtime != _wtoi(buf))
|
||||
{
|
||||
DEBUG("GetPPTInfo: date changed\n");
|
||||
DEBUG(L"GetPPTInfo: date changed\n");
|
||||
fclose (pFile);
|
||||
return FALSE;
|
||||
}
|
||||
fgets(buf, 100, pFile);
|
||||
if (fileStats.st_size != atoi(buf))
|
||||
fgetws(buf, 100, pFile);
|
||||
if (fileStats.st_size != _wtoi(buf))
|
||||
{
|
||||
DEBUG("GetPPTInfo: size changed\n");
|
||||
DEBUG(L"GetPPTInfo: size changed\n");
|
||||
fclose (pFile);
|
||||
return FALSE;
|
||||
}
|
||||
fgets(buf, 100, pFile); // slidecount
|
||||
int slideCount = atoi(buf);
|
||||
fgets(buf, 100, pFile); // first slide steps
|
||||
int firstSlideSteps = atoi(buf);
|
||||
fgetws(buf, 100, pFile); // slidecount
|
||||
int slideCount = _wtoi(buf);
|
||||
fgetws(buf, 100, pFile); // first slide steps
|
||||
int firstSlideSteps = _wtoi(buf);
|
||||
// check all the preview images still exist
|
||||
for (int i = 1; i <= slideCount; i++)
|
||||
{
|
||||
sprintf_s(info, MAX_PATH, "%s%i.bmp", pptView[id].previewPath, i);
|
||||
swprintf_s(info, MAX_PATH, L"%s%i.bmp", pptView[id].previewPath, i);
|
||||
if (GetFileAttributes(info) == INVALID_FILE_ATTRIBUTES)
|
||||
{
|
||||
DEBUG("GetPPTInfo: bmp not found\n");
|
||||
DEBUG(L"GetPPTInfo: bmp not found\n");
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
fclose(pFile);
|
||||
pptView[id].slideCount = slideCount;
|
||||
pptView[id].firstSlideSteps = firstSlideSteps;
|
||||
DEBUG("GetPPTInfo: exit ok\n");
|
||||
DEBUG(L"GetPPTInfo: exit ok\n");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL SavePPTInfo(int id)
|
||||
{
|
||||
struct _stat fileStats;
|
||||
char info[MAX_PATH];
|
||||
wchar_t info[MAX_PATH];
|
||||
FILE* pFile;
|
||||
|
||||
DEBUG("SavePPTInfo: start\n");
|
||||
if (_stat(pptView[id].filename, &fileStats) != 0)
|
||||
DEBUG(L"SavePPTInfo: start\n");
|
||||
if (_wstat(pptView[id].filename, &fileStats) != 0)
|
||||
{
|
||||
DEBUG("SavePPTInfo: stat of %s failed\n", pptView[id].filename);
|
||||
DEBUG(L"SavePPTInfo: stat of %s failed\n", pptView[id].filename);
|
||||
return FALSE;
|
||||
}
|
||||
sprintf_s(info, MAX_PATH, "%sinfo.txt", pptView[id].previewPath);
|
||||
int err = fopen_s(&pFile, info, "w");
|
||||
swprintf_s(info, MAX_PATH, L"%sinfo.txt", pptView[id].previewPath);
|
||||
int err = _wfopen_s(&pFile, info, L"w");
|
||||
if (err != 0)
|
||||
{
|
||||
DEBUG("SavePPTInfo: fopen of %s failed%i\n", info, err);
|
||||
DEBUG(L"SavePPTInfo: fopen of %s failed%i\n", info, err);
|
||||
return FALSE;
|
||||
}
|
||||
fprintf(pFile, "1\n");
|
||||
|
@ -347,21 +347,21 @@ BOOL SavePPTInfo(int id)
|
|||
fprintf(pFile, "%u\n", pptView[id].slideCount);
|
||||
fprintf(pFile, "%u\n", pptView[id].firstSlideSteps);
|
||||
fclose(pFile);
|
||||
DEBUG("SavePPTInfo: exit ok\n");
|
||||
DEBUG(L"SavePPTInfo: exit ok\n");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// Get the path of the PowerPoint viewer from the registry
|
||||
BOOL GetPPTViewerPath(char *pptViewerPath, int stringSize)
|
||||
BOOL GetPPTViewerPath(wchar_t *pptViewerPath, int stringSize)
|
||||
{
|
||||
char cwd[MAX_PATH];
|
||||
wchar_t cwd[MAX_PATH];
|
||||
|
||||
DEBUG("GetPPTViewerPath: start\n");
|
||||
DEBUG(L"GetPPTViewerPath: start\n");
|
||||
if(GetPPTViewerPathFromReg(pptViewerPath, stringSize))
|
||||
{
|
||||
if(_access(pptViewerPath, 0) != -1)
|
||||
if(_waccess(pptViewerPath, 0) != -1)
|
||||
{
|
||||
DEBUG("GetPPTViewerPath: exit registry\n");
|
||||
DEBUG(L"GetPPTViewerPath: exit registry\n");
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
@ -370,37 +370,37 @@ BOOL GetPPTViewerPath(char *pptViewerPath, int stringSize)
|
|||
// upset those who like to put things somewhere else
|
||||
|
||||
// Viewer 2007 in 64bit Windows:
|
||||
if(_access("C:\\Program Files (x86)\\Microsoft Office\\Office12\\PPTVIEW.EXE",
|
||||
if(_waccess(L"C:\\Program Files (x86)\\Microsoft Office\\Office12\\PPTVIEW.EXE",
|
||||
0) != -1)
|
||||
{
|
||||
strcpy_s(
|
||||
"C:\\Program Files (x86)\\Microsoft Office\\Office12\\PPTVIEW.EXE",
|
||||
wcscpy_s(
|
||||
L"C:\\Program Files (x86)\\Microsoft Office\\Office12\\PPTVIEW.EXE",
|
||||
stringSize, pptViewerPath);
|
||||
DEBUG("GetPPTViewerPath: exit 64bit 2007\n");
|
||||
DEBUG(L"GetPPTViewerPath: exit 64bit 2007\n");
|
||||
return TRUE;
|
||||
}
|
||||
// Viewer 2007 in 32bit Windows:
|
||||
if(_access("C:\\Program Files\\Microsoft Office\\Office12\\PPTVIEW.EXE", 0)
|
||||
if(_waccess(L"C:\\Program Files\\Microsoft Office\\Office12\\PPTVIEW.EXE", 0)
|
||||
!= -1)
|
||||
{
|
||||
strcpy_s("C:\\Program Files\\Microsoft Office\\Office12\\PPTVIEW.EXE",
|
||||
wcscpy_s(L"C:\\Program Files\\Microsoft Office\\Office12\\PPTVIEW.EXE",
|
||||
stringSize, pptViewerPath);
|
||||
DEBUG("GetPPTViewerPath: exit 32bit 2007\n");
|
||||
DEBUG(L"GetPPTViewerPath: exit 32bit 2007\n");
|
||||
return TRUE;
|
||||
}
|
||||
// Give them the opportunity to place it in the same folder as the app
|
||||
_getcwd(cwd, MAX_PATH);
|
||||
strcat_s(cwd, MAX_PATH, "\\PPTVIEW.EXE");
|
||||
if(_access(cwd, 0) != -1)
|
||||
_wgetcwd(cwd, MAX_PATH);
|
||||
wcscat_s(cwd, MAX_PATH, L"\\PPTVIEW.EXE");
|
||||
if(_waccess(cwd, 0) != -1)
|
||||
{
|
||||
strcpy_s(pptViewerPath, stringSize, cwd);
|
||||
DEBUG("GetPPTViewerPath: exit local\n");
|
||||
wcscpy_s(pptViewerPath, stringSize, cwd);
|
||||
DEBUG(L"GetPPTViewerPath: exit local\n");
|
||||
return TRUE;
|
||||
}
|
||||
DEBUG("GetPPTViewerPath: exit fail\n");
|
||||
DEBUG(L"GetPPTViewerPath: exit fail\n");
|
||||
return FALSE;
|
||||
}
|
||||
BOOL GetPPTViewerPathFromReg(char *pptViewerPath, int stringSize)
|
||||
BOOL GetPPTViewerPathFromReg(wchar_t *pptViewerPath, int stringSize)
|
||||
{
|
||||
HKEY hKey;
|
||||
DWORD dwType, dwSize;
|
||||
|
@ -411,17 +411,17 @@ BOOL GetPPTViewerPathFromReg(char *pptViewerPath, int stringSize)
|
|||
// PPT Viewer 2003 (recent versions)
|
||||
// PPT Viewer 2003 (older versions)
|
||||
// PPT Viewer 97
|
||||
if ((RegOpenKeyEx(HKEY_CLASSES_ROOT,
|
||||
"PowerPointViewer.Show.12\\shell\\Show\\command", 0, KEY_READ, &hKey)
|
||||
if ((RegOpenKeyExW(HKEY_CLASSES_ROOT,
|
||||
L"PowerPointViewer.Show.12\\shell\\Show\\command", 0, KEY_READ, &hKey)
|
||||
!= ERROR_SUCCESS)
|
||||
&& (RegOpenKeyEx(HKEY_CLASSES_ROOT,
|
||||
"PowerPointViewer.Show.11\\shell\\Show\\command", 0, KEY_READ, &hKey)
|
||||
&& (RegOpenKeyExW(HKEY_CLASSES_ROOT,
|
||||
L"PowerPointViewer.Show.11\\shell\\Show\\command", 0, KEY_READ, &hKey)
|
||||
!= ERROR_SUCCESS)
|
||||
&& (RegOpenKeyEx(HKEY_CLASSES_ROOT,
|
||||
"Applications\\PPTVIEW.EXE\\shell\\open\\command", 0, KEY_READ, &hKey)
|
||||
&& (RegOpenKeyExW(HKEY_CLASSES_ROOT,
|
||||
L"Applications\\PPTVIEW.EXE\\shell\\open\\command", 0, KEY_READ, &hKey)
|
||||
!= ERROR_SUCCESS)
|
||||
&& (RegOpenKeyEx(HKEY_CLASSES_ROOT,
|
||||
"Applications\\PPTVIEW.EXE\\shell\\Show\\command", 0, KEY_READ, &hKey)
|
||||
&& (RegOpenKeyExW(HKEY_CLASSES_ROOT,
|
||||
L"Applications\\PPTVIEW.EXE\\shell\\Show\\command", 0, KEY_READ, &hKey)
|
||||
!= ERROR_SUCCESS))
|
||||
{
|
||||
return FALSE;
|
||||
|
@ -436,14 +436,14 @@ BOOL GetPPTViewerPathFromReg(char *pptViewerPath, int stringSize)
|
|||
return FALSE;
|
||||
}
|
||||
// remove "%1" from end of key value
|
||||
pptViewerPath[strlen(pptViewerPath) - 4] = '\0';
|
||||
pptViewerPath[wcslen(pptViewerPath) - 4] = '\0';
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// Unhook the Windows hook
|
||||
void Unhook(int id)
|
||||
{
|
||||
DEBUG("Unhook: start %d\n", id);
|
||||
DEBUG(L"Unhook: start %d\n", id);
|
||||
if (pptView[id].hook != NULL)
|
||||
{
|
||||
UnhookWindowsHookEx(pptView[id].hook);
|
||||
|
@ -454,13 +454,13 @@ void Unhook(int id)
|
|||
}
|
||||
pptView[id].hook = NULL;
|
||||
pptView[id].msgHook = NULL;
|
||||
DEBUG("Unhook: exit ok\n");
|
||||
DEBUG(L"Unhook: exit ok\n");
|
||||
}
|
||||
|
||||
// Close the PowerPoint viewer, release resources
|
||||
DllExport void ClosePPT(int id)
|
||||
{
|
||||
DEBUG("ClosePPT: start%d\n", id);
|
||||
DEBUG(L"ClosePPT: start%d\n", id);
|
||||
pptView[id].state = PPT_CLOSED;
|
||||
Unhook(id);
|
||||
if (pptView[id].hWnd == 0)
|
||||
|
@ -474,13 +474,13 @@ DllExport void ClosePPT(int id)
|
|||
CloseHandle(pptView[id].hThread);
|
||||
CloseHandle(pptView[id].hProcess);
|
||||
memset(&pptView[id], 0, sizeof(PPTVIEW));
|
||||
DEBUG("ClosePPT: exit ok\n");
|
||||
DEBUG(L"ClosePPT: exit ok\n");
|
||||
return;
|
||||
}
|
||||
// Moves the show back onto the display
|
||||
DllExport void Resume(int id)
|
||||
{
|
||||
DEBUG("Resume: %d\n", id);
|
||||
DEBUG(L"Resume: %d\n", id);
|
||||
MoveWindow(pptView[id].hWnd, pptView[id].rect.left,
|
||||
pptView[id].rect.top,
|
||||
pptView[id].rect.right - pptView[id].rect.left,
|
||||
|
@ -490,7 +490,7 @@ DllExport void Resume(int id)
|
|||
// Moves the show off the screen so it can't be seen
|
||||
DllExport void Stop(int id)
|
||||
{
|
||||
DEBUG("Stop:%d\n", id);
|
||||
DEBUG(L"Stop:%d\n", id);
|
||||
MoveWindow(pptView[id].hWnd, -32000, -32000,
|
||||
pptView[id].rect.right - pptView[id].rect.left,
|
||||
pptView[id].rect.bottom - pptView[id].rect.top, TRUE);
|
||||
|
@ -499,7 +499,7 @@ DllExport void Stop(int id)
|
|||
// Return the total number of slides
|
||||
DllExport int GetSlideCount(int id)
|
||||
{
|
||||
DEBUG("GetSlideCount:%d\n", id);
|
||||
DEBUG(L"GetSlideCount:%d\n", id);
|
||||
if (pptView[id].state == 0)
|
||||
{
|
||||
return -1;
|
||||
|
@ -513,7 +513,7 @@ DllExport int GetSlideCount(int id)
|
|||
// Return the number of the slide currently viewing
|
||||
DllExport int GetCurrentSlide(int id)
|
||||
{
|
||||
DEBUG("GetCurrentSlide:%d\n", id);
|
||||
DEBUG(L"GetCurrentSlide:%d\n", id);
|
||||
if (pptView[id].state == 0)
|
||||
{
|
||||
return -1;
|
||||
|
@ -527,7 +527,7 @@ DllExport int GetCurrentSlide(int id)
|
|||
// Take a step forwards through the show
|
||||
DllExport void NextStep(int id)
|
||||
{
|
||||
DEBUG("NextStep:%d (%d)\n", id, pptView[id].currentSlide);
|
||||
DEBUG(L"NextStep:%d (%d)\n", id, pptView[id].currentSlide);
|
||||
if (pptView[id].currentSlide > pptView[id].slideCount) return;
|
||||
if (pptView[id].currentSlide < pptView[id].slideCount)
|
||||
{
|
||||
|
@ -540,7 +540,7 @@ DllExport void NextStep(int id)
|
|||
// Take a step backwards through the show
|
||||
DllExport void PrevStep(int id)
|
||||
{
|
||||
DEBUG("PrevStep:%d (%d)\n", id, pptView[id].currentSlide);
|
||||
DEBUG(L"PrevStep:%d (%d)\n", id, pptView[id].currentSlide);
|
||||
if (pptView[id].currentSlide > 1)
|
||||
{
|
||||
pptView[id].guess = pptView[id].currentSlide - 1;
|
||||
|
@ -556,7 +556,7 @@ DllExport void Blank(int id)
|
|||
// So send random unmapped letter first (say 'A'), then we can
|
||||
// better guarantee B will blank instead of trying to guess
|
||||
// whether it was already blank or not.
|
||||
DEBUG("Blank:%d\n", id);
|
||||
DEBUG(L"Blank:%d\n", id);
|
||||
HWND h1 = GetForegroundWindow();
|
||||
HWND h2 = GetFocus();
|
||||
SetForegroundWindow(pptView[id].hWnd);
|
||||
|
@ -573,7 +573,7 @@ DllExport void Blank(int id)
|
|||
// Unblank the show
|
||||
DllExport void Unblank(int id)
|
||||
{
|
||||
DEBUG("Unblank:%d\n", id);
|
||||
DEBUG(L"Unblank:%d\n", id);
|
||||
// Pressing any key resumes.
|
||||
// For some reason SendMessage works for unblanking, but not blanking.
|
||||
SendMessage(pptView[id].hWnd2, WM_CHAR, 'A', 0);
|
||||
|
@ -582,7 +582,7 @@ DllExport void Unblank(int id)
|
|||
// Go directly to a slide
|
||||
DllExport void GotoSlide(int id, int slideNo)
|
||||
{
|
||||
DEBUG("GotoSlide %i %i:\n", id, slideNo);
|
||||
DEBUG(L"GotoSlide %i %i:\n", id, slideNo);
|
||||
// Did try WM_KEYDOWN/WM_CHAR/WM_KEYUP with SendMessage but didn't work
|
||||
// perhaps I was sending to the wrong window? No idea.
|
||||
// Anyway fall back to keybd_event, which is OK as long we makesure
|
||||
|
@ -619,7 +619,7 @@ DllExport void RestartShow(int id)
|
|||
// Only way I've found to get around this is to step backwards all the way
|
||||
// through. Lets move the window out of the way first so the audience
|
||||
// doesn't see this.
|
||||
DEBUG("RestartShow:%d\n", id);
|
||||
DEBUG(L"RestartShow:%d\n", id);
|
||||
Stop(id);
|
||||
GotoSlide(id, pptView[id].slideCount);
|
||||
for (int i=0; i <= pptView[id].steps - pptView[id].lastSlideSteps; i++)
|
||||
|
@ -644,12 +644,12 @@ LRESULT CALLBACK CbtProc(int nCode, WPARAM wParam, LPARAM lParam)
|
|||
HHOOK hook = globalHook;
|
||||
if (nCode == HCBT_CREATEWND)
|
||||
{
|
||||
char csClassName[16];
|
||||
wchar_t csClassName[32];
|
||||
HWND hCurrWnd = (HWND)wParam;
|
||||
DWORD retProcId = NULL;
|
||||
GetClassName(hCurrWnd, csClassName, sizeof(csClassName));
|
||||
if ((strcmp(csClassName, "paneClassDC") == 0)
|
||||
||(strcmp(csClassName, "screenClass") == 0))
|
||||
if ((wcscmp(csClassName, L"paneClassDC") == 0)
|
||||
||(wcscmp(csClassName, L"screenClass") == 0))
|
||||
{
|
||||
int id = -1;
|
||||
DWORD windowThread = GetWindowThreadProcessId(hCurrWnd, NULL);
|
||||
|
@ -663,7 +663,7 @@ LRESULT CALLBACK CbtProc(int nCode, WPARAM wParam, LPARAM lParam)
|
|||
}
|
||||
if (id >= 0)
|
||||
{
|
||||
if (strcmp(csClassName, "paneClassDC") == 0)
|
||||
if (wcscmp(csClassName, L"paneClassDC") == 0)
|
||||
{
|
||||
pptView[id].hWnd2 = hCurrWnd;
|
||||
}
|
||||
|
@ -737,7 +737,7 @@ LRESULT CALLBACK CwpProc(int nCode, WPARAM wParam, LPARAM lParam){
|
|||
CWPSTRUCT *cwp;
|
||||
cwp = (CWPSTRUCT *)lParam;
|
||||
HHOOK hook = NULL;
|
||||
char filename[MAX_PATH];
|
||||
wchar_t filename[MAX_PATH];
|
||||
|
||||
DWORD windowThread = GetWindowThreadProcessId(cwp->hwnd, NULL);
|
||||
int id = -1;
|
||||
|
@ -758,9 +758,9 @@ LRESULT CALLBACK CwpProc(int nCode, WPARAM wParam, LPARAM lParam){
|
|||
{
|
||||
if ((pptView[id].currentSlide > 0)
|
||||
&& (pptView[id].previewPath != NULL
|
||||
&& strlen(pptView[id].previewPath) > 0))
|
||||
&& wcslen(pptView[id].previewPath) > 0))
|
||||
{
|
||||
sprintf_s(filename, MAX_PATH, "%s%i.bmp",
|
||||
swprintf_s(filename, MAX_PATH, L"%s%i.bmp",
|
||||
pptView[id].previewPath,
|
||||
pptView[id].currentSlide);
|
||||
CaptureAndSaveWindow(cwp->hwnd, filename);
|
||||
|
@ -820,7 +820,7 @@ LRESULT CALLBACK CwpProc(int nCode, WPARAM wParam, LPARAM lParam){
|
|||
return CallNextHookEx(hook, nCode, wParam, lParam);
|
||||
}
|
||||
|
||||
VOID CaptureAndSaveWindow(HWND hWnd, CHAR* filename)
|
||||
VOID CaptureAndSaveWindow(HWND hWnd, wchar_t* filename)
|
||||
{
|
||||
HBITMAP hBmp;
|
||||
if ((hBmp = CaptureWindow(hWnd)) == NULL)
|
||||
|
@ -863,7 +863,7 @@ VOID CaptureAndSaveWindow(HWND hWnd, CHAR* filename)
|
|||
|
||||
// Writing:
|
||||
FILE* pFile;
|
||||
int err = fopen_s(&pFile, filename, "wb");
|
||||
int err = _wfopen_s(&pFile, filename, L"wb");
|
||||
if (err == 0)
|
||||
{
|
||||
fwrite(&bmf, sizeof(bmf), 1, pFile);
|
||||
|
@ -893,7 +893,7 @@ HBITMAP CaptureWindow(HWND hWnd)
|
|||
if ((hMemDC = CreateCompatibleDC(hDC)) != NULL)
|
||||
{
|
||||
hDCBmp = (HBITMAP)SelectObject(hMemDC, hImage);
|
||||
HMODULE hLib = LoadLibrary("User32");
|
||||
HMODULE hLib = LoadLibrary(L"User32");
|
||||
// PrintWindow works for windows outside displayable area
|
||||
// but was only introduced in WinXP. BitBlt requires the window to
|
||||
// be topmost and within the viewable area of the display
|
||||
|
|
|
@ -26,13 +26,13 @@
|
|||
|
||||
#define DllExport extern "C" __declspec( dllexport )
|
||||
|
||||
#define DEBUG(...) if (debug) printf(__VA_ARGS__)
|
||||
#define DEBUG(...) if (debug) wprintf(__VA_ARGS__)
|
||||
|
||||
enum PPTVIEWSTATE {PPT_CLOSED, PPT_STARTED, PPT_OPENED, PPT_LOADED,
|
||||
PPT_CLOSING};
|
||||
|
||||
DllExport int OpenPPT(char *filename, HWND hParentWnd, RECT rect,
|
||||
char *previewPath);
|
||||
DllExport int OpenPPT(wchar_t *filename, HWND hParentWnd, RECT rect,
|
||||
wchar_t *previewPath);
|
||||
DllExport BOOL CheckInstalled();
|
||||
DllExport void ClosePPT(int id);
|
||||
DllExport int GetCurrentSlide(int id);
|
||||
|
@ -50,11 +50,11 @@ DllExport void SetDebug(BOOL onOff);
|
|||
LRESULT CALLBACK CbtProc(int nCode, WPARAM wParam, LPARAM lParam);
|
||||
LRESULT CALLBACK CwpProc(int nCode, WPARAM wParam, LPARAM lParam);
|
||||
LRESULT CALLBACK GetMsgProc(int nCode, WPARAM wParam, LPARAM lParam);
|
||||
BOOL GetPPTViewerPath(char *pptViewerPath, int stringSize);
|
||||
BOOL GetPPTViewerPathFromReg(char *pptViewerPath, int stringSize);
|
||||
BOOL GetPPTViewerPath(wchar_t *pptViewerPath, int stringSize);
|
||||
BOOL GetPPTViewerPathFromReg(wchar_t *pptViewerPath, int stringSize);
|
||||
HBITMAP CaptureWindow(HWND hWnd);
|
||||
VOID SaveBitmap(CHAR* filename, HBITMAP hBmp) ;
|
||||
VOID CaptureAndSaveWindow(HWND hWnd, CHAR* filename);
|
||||
VOID SaveBitmap(wchar_t* filename, HBITMAP hBmp) ;
|
||||
VOID CaptureAndSaveWindow(HWND hWnd, wchar_t* filename);
|
||||
BOOL GetPPTInfo(int id);
|
||||
BOOL SavePPTInfo(int id);
|
||||
void Unhook(int id);
|
||||
|
@ -80,8 +80,8 @@ struct PPTVIEW
|
|||
int lastSlideSteps;
|
||||
int steps;
|
||||
int guess;
|
||||
char filename[MAX_PATH];
|
||||
char previewPath[MAX_PATH];
|
||||
wchar_t filename[MAX_PATH];
|
||||
wchar_t previewPath[MAX_PATH];
|
||||
int slideNos[MAX_SLIDES];
|
||||
PPTVIEWSTATE state;
|
||||
};
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
|
||||
IntermediateDirectory="$(ConfigurationName)"
|
||||
ConfigurationType="2"
|
||||
CharacterSet="2"
|
||||
CharacterSet="1"
|
||||
>
|
||||
<Tool
|
||||
Name="VCPreBuildEventTool"
|
||||
|
@ -93,7 +93,7 @@
|
|||
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
|
||||
IntermediateDirectory="$(ConfigurationName)"
|
||||
ConfigurationType="2"
|
||||
CharacterSet="2"
|
||||
CharacterSet="1"
|
||||
WholeProgramOptimization="1"
|
||||
>
|
||||
<Tool
|
||||
|
|
|
@ -96,8 +96,14 @@ class PresentationDocument(object):
|
|||
"""
|
||||
Constructor for the PresentationController class
|
||||
"""
|
||||
self.slidenumber = 0
|
||||
self.controller = controller
|
||||
self._setup(name)
|
||||
|
||||
def _setup(self, name):
|
||||
"""
|
||||
Run some initial setup. This method is separate from __init__ in order to mock it out in tests.
|
||||
"""
|
||||
self.slidenumber = 0
|
||||
self.filepath = name
|
||||
check_directory_exists(self.get_thumbnail_folder())
|
||||
|
||||
|
|
|
@ -34,7 +34,15 @@ from openlp.core.lib.ui import create_button_box
|
|||
|
||||
|
||||
class Ui_SongUsageDeleteDialog(object):
|
||||
"""
|
||||
The Song Usage delete dialog
|
||||
"""
|
||||
def setupUi(self, song_usage_delete_dialog):
|
||||
"""
|
||||
Setup the UI
|
||||
|
||||
:param song_usage_delete_dialog:
|
||||
"""
|
||||
song_usage_delete_dialog.setObjectName('song_usage_delete_dialog')
|
||||
song_usage_delete_dialog.resize(291, 243)
|
||||
self.vertical_layout = QtGui.QVBoxLayout(song_usage_delete_dialog)
|
||||
|
@ -55,8 +63,12 @@ class Ui_SongUsageDeleteDialog(object):
|
|||
self.retranslateUi(song_usage_delete_dialog)
|
||||
|
||||
def retranslateUi(self, song_usage_delete_dialog):
|
||||
"""
|
||||
Retranslate the strings
|
||||
:param song_usage_delete_dialog:
|
||||
"""
|
||||
song_usage_delete_dialog.setWindowTitle(
|
||||
translate('SongUsagePlugin.SongUsageDeleteForm', 'Delete Song Usage Data'))
|
||||
self.delete_label.setText(
|
||||
translate('SongUsagePlugin.SongUsageDeleteForm', 'Select the date up to which the song usage data '
|
||||
'should be deleted. All data recorded before this date will be permanently deleted.'))
|
||||
'should be deleted. \nAll data recorded before this date will be permanently deleted.'))
|
||||
|
|
|
@ -48,19 +48,25 @@ class SongUsageDeleteForm(QtGui.QDialog, Ui_SongUsageDeleteDialog):
|
|||
self.button_box.clicked.connect(self.on_button_box_clicked)
|
||||
|
||||
def on_button_box_clicked(self, button):
|
||||
"""
|
||||
The button event has been triggered
|
||||
|
||||
:param button: The button pressed
|
||||
"""
|
||||
if self.button_box.standardButton(button) == QtGui.QDialogButtonBox.Ok:
|
||||
ret = QtGui.QMessageBox.question(self,
|
||||
ret = QtGui.QMessageBox.question(
|
||||
self,
|
||||
translate('SongUsagePlugin.SongUsageDeleteForm', 'Delete Selected Song Usage Events?'),
|
||||
translate('SongUsagePlugin.SongUsageDeleteForm',
|
||||
'Are you sure you want to delete selected Song Usage data?'),
|
||||
'Are you sure you want to delete selected Song Usage data?'),
|
||||
QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Yes | QtGui.QMessageBox.No), QtGui.QMessageBox.No)
|
||||
if ret == QtGui.QMessageBox.Yes:
|
||||
delete_date = self.delete_calendar.selectedDate().toPyDate()
|
||||
self.manager.delete_all_objects(SongUsageItem, SongUsageItem.usagedate <= delete_date)
|
||||
self.main_window.information_message(
|
||||
translate('SongUsagePlugin.SongUsageDeleteForm', 'Deletion Successful'),
|
||||
translate(
|
||||
'SongUsagePlugin.SongUsageDeleteForm', 'All requested data has been deleted successfully.')
|
||||
translate('SongUsagePlugin.SongUsageDeleteForm',
|
||||
'All requested data has been deleted successfully.')
|
||||
)
|
||||
self.accept()
|
||||
else:
|
||||
|
|
|
@ -35,7 +35,15 @@ from openlp.core.lib.ui import create_button_box
|
|||
|
||||
|
||||
class Ui_SongUsageDetailDialog(object):
|
||||
"""
|
||||
The Song Usage report details
|
||||
"""
|
||||
def setupUi(self, song_usage_detail_dialog):
|
||||
"""
|
||||
Set up the UI
|
||||
|
||||
:param song_usage_detail_dialog:
|
||||
"""
|
||||
song_usage_detail_dialog.setObjectName('song_usage_detail_dialog')
|
||||
song_usage_detail_dialog.resize(609, 413)
|
||||
self.vertical_layout = QtGui.QVBoxLayout(song_usage_detail_dialog)
|
||||
|
@ -82,6 +90,11 @@ class Ui_SongUsageDetailDialog(object):
|
|||
self.save_file_push_button.clicked.connect(song_usage_detail_dialog.define_output_location)
|
||||
|
||||
def retranslateUi(self, song_usage_detail_dialog):
|
||||
"""
|
||||
Retranslate the UI
|
||||
|
||||
:param song_usage_detail_dialog:
|
||||
"""
|
||||
song_usage_detail_dialog.setWindowTitle(
|
||||
translate('SongUsagePlugin.SongUsageDetailForm', 'Song Usage Extraction'))
|
||||
self.date_range_group_box.setTitle(translate('SongUsagePlugin.SongUsageDetailForm', 'Select Date Range'))
|
||||
|
|
|
@ -66,8 +66,8 @@ class SongUsageDetailForm(QtGui.QDialog, Ui_SongUsageDetailDialog):
|
|||
"""
|
||||
Triggered when the Directory selection button is clicked
|
||||
"""
|
||||
path = QtGui.QFileDialog.getExistingDirectory(self,
|
||||
translate('SongUsagePlugin.SongUsageDetailForm', 'Output File Location'),
|
||||
path = QtGui.QFileDialog.getExistingDirectory(
|
||||
self, translate('SongUsagePlugin.SongUsageDetailForm', 'Output File Location'),
|
||||
Settings().value(self.plugin.settings_section + '/last directory export'))
|
||||
if path:
|
||||
Settings().setValue(self.plugin.settings_section + '/last directory export', path)
|
||||
|
@ -83,7 +83,7 @@ class SongUsageDetailForm(QtGui.QDialog, Ui_SongUsageDetailDialog):
|
|||
self.main_window.error_message(
|
||||
translate('SongUsagePlugin.SongUsageDetailForm', 'Output Path Not Selected'),
|
||||
translate('SongUsagePlugin.SongUsageDetailForm', 'You have not set a valid output location for your'
|
||||
' song usage report. Please select an existing path on your computer.')
|
||||
' song usage report. \nPlease select an existing path on your computer.')
|
||||
)
|
||||
return
|
||||
check_directory_exists(path)
|
||||
|
@ -109,8 +109,8 @@ class SongUsageDetailForm(QtGui.QDialog, Ui_SongUsageDetailDialog):
|
|||
file_handle.write(record.encode('utf-8'))
|
||||
self.main_window.information_message(
|
||||
translate('SongUsagePlugin.SongUsageDetailForm', 'Report Creation'),
|
||||
translate('SongUsagePlugin.SongUsageDetailForm', 'Report \n%s \n'
|
||||
'has been successfully created. ') % report_file_name
|
||||
translate('SongUsagePlugin.SongUsageDetailForm',
|
||||
'Report \n%s \nhas been successfully created. ') % report_file_name
|
||||
)
|
||||
except IOError:
|
||||
log.exception('Failed to write out song usage records')
|
||||
|
|
|
@ -48,21 +48,20 @@ def init_schema(url):
|
|||
"""
|
||||
Setup the songusage database connection and initialise the database schema
|
||||
|
||||
``url``
|
||||
The database to setup
|
||||
:param url: The database to setup
|
||||
"""
|
||||
session, metadata = init_db(url)
|
||||
|
||||
songusage_table = Table('songusage_data', metadata,
|
||||
Column('id', types.Integer(), primary_key=True),
|
||||
Column('usagedate', types.Date, index=True, nullable=False),
|
||||
Column('usagetime', types.Time, index=True, nullable=False),
|
||||
Column('title', types.Unicode(255), nullable=False),
|
||||
Column('authors', types.Unicode(255), nullable=False),
|
||||
Column('copyright', types.Unicode(255)),
|
||||
Column('ccl_number', types.Unicode(65)),
|
||||
Column('plugin_name', types.Unicode(20)),
|
||||
Column('source', types.Unicode(10))
|
||||
Column('id', types.Integer(), primary_key=True),
|
||||
Column('usagedate', types.Date, index=True, nullable=False),
|
||||
Column('usagetime', types.Time, index=True, nullable=False),
|
||||
Column('title', types.Unicode(255), nullable=False),
|
||||
Column('authors', types.Unicode(255), nullable=False),
|
||||
Column('copyright', types.Unicode(255)),
|
||||
Column('ccl_number', types.Unicode(65)),
|
||||
Column('plugin_name', types.Unicode(20)),
|
||||
Column('source', types.Unicode(10))
|
||||
)
|
||||
|
||||
mapper(SongUsageItem, songusage_table)
|
||||
|
|
|
@ -58,6 +58,9 @@ __default_settings__ = {
|
|||
|
||||
|
||||
class SongUsagePlugin(Plugin):
|
||||
"""
|
||||
Song Usage Plugin class
|
||||
"""
|
||||
log.info('SongUsage Plugin loaded')
|
||||
|
||||
def __init__(self):
|
||||
|
@ -79,31 +82,33 @@ class SongUsagePlugin(Plugin):
|
|||
"""
|
||||
Give the SongUsage plugin the opportunity to add items to the **Tools** menu.
|
||||
|
||||
``tools_menu``
|
||||
The actual **Tools** menu item, so that your actions can use it as their parent.
|
||||
:param tools_menu: The actual **Tools** menu item, so that your actions can use it as their parent.
|
||||
"""
|
||||
log.info('add tools menu')
|
||||
self.toolsMenu = tools_menu
|
||||
self.tools_menu = tools_menu
|
||||
self.song_usage_menu = QtGui.QMenu(tools_menu)
|
||||
self.song_usage_menu.setObjectName('song_usage_menu')
|
||||
self.song_usage_menu.setTitle(translate('SongUsagePlugin', '&Song Usage Tracking'))
|
||||
# SongUsage Delete
|
||||
self.song_usage_delete = create_action(tools_menu, 'songUsageDelete',
|
||||
self.song_usage_delete = create_action(
|
||||
tools_menu, 'songUsageDelete',
|
||||
text=translate('SongUsagePlugin', '&Delete Tracking Data'),
|
||||
statustip=translate('SongUsagePlugin', 'Delete song usage data up to a specified date.'),
|
||||
triggers=self.on_song_usage_delete)
|
||||
# SongUsage Report
|
||||
self.song_usage_report = create_action(tools_menu, 'songUsageReport',
|
||||
self.song_usage_report = create_action(
|
||||
tools_menu, 'songUsageReport',
|
||||
text=translate('SongUsagePlugin', '&Extract Tracking Data'),
|
||||
statustip=translate('SongUsagePlugin', 'Generate a report on song usage.'),
|
||||
triggers=self.on_song_usage_report)
|
||||
# SongUsage activation
|
||||
self.song_usage_status = create_action(tools_menu, 'songUsageStatus',
|
||||
self.song_usage_status = create_action(
|
||||
tools_menu, 'songUsageStatus',
|
||||
text=translate('SongUsagePlugin', 'Toggle Tracking'),
|
||||
statustip=translate('SongUsagePlugin', 'Toggle the tracking of song usage.'), checked=False,
|
||||
can_shortcuts=True, triggers=self.toggle_song_usage_state)
|
||||
# Add Menus together
|
||||
self.toolsMenu.addAction(self.song_usage_menu.menuAction())
|
||||
self.tools_menu.addAction(self.song_usage_menu.menuAction())
|
||||
self.song_usage_menu.addAction(self.song_usage_status)
|
||||
self.song_usage_menu.addSeparator()
|
||||
self.song_usage_menu.addAction(self.song_usage_report)
|
||||
|
@ -117,7 +122,7 @@ class SongUsagePlugin(Plugin):
|
|||
self.song_usage_active_button.hide()
|
||||
# Signals and slots
|
||||
QtCore.QObject.connect(self.song_usage_status, QtCore.SIGNAL('visibilityChanged(bool)'),
|
||||
self.song_usage_status.setChecked)
|
||||
self.song_usage_status.setChecked)
|
||||
self.song_usage_active_button.toggled.connect(self.toggle_song_usage_state)
|
||||
self.song_usage_menu.menuAction().setVisible(False)
|
||||
|
||||
|
@ -165,8 +170,7 @@ class SongUsagePlugin(Plugin):
|
|||
|
||||
def set_button_state(self):
|
||||
"""
|
||||
Keep buttons inline. Turn of signals to stop dead loop but we need the
|
||||
button and check box set correctly.
|
||||
Keep buttons inline. Turn of signals to stop dead loop but we need the button and check box set correctly.
|
||||
"""
|
||||
self.song_usage_active_button.blockSignals(True)
|
||||
self.song_usage_status.blockSignals(True)
|
||||
|
@ -186,12 +190,16 @@ class SongUsagePlugin(Plugin):
|
|||
def display_song_usage(self, item):
|
||||
"""
|
||||
Song Usage for which has been displayed
|
||||
|
||||
:param item: Item displayed
|
||||
"""
|
||||
self._add_song_usage(translate('SongUsagePlugin', 'display'), item)
|
||||
|
||||
def print_song_usage(self, item):
|
||||
"""
|
||||
Song Usage for which has been printed
|
||||
|
||||
:param item: Item printed
|
||||
"""
|
||||
self._add_song_usage(translate('SongUsagePlugin', 'printed'), item)
|
||||
|
||||
|
@ -210,15 +218,28 @@ class SongUsagePlugin(Plugin):
|
|||
self.manager.save_object(song_usage_item)
|
||||
|
||||
def on_song_usage_delete(self):
|
||||
"""
|
||||
Request the delete form to be displayed
|
||||
"""
|
||||
self.song_usage_delete_form.exec_()
|
||||
|
||||
def on_song_usage_report(self):
|
||||
"""
|
||||
Display the song usage report generator screen
|
||||
|
||||
"""
|
||||
self.song_usage_detail_form.initialise()
|
||||
self.song_usage_detail_form.exec_()
|
||||
|
||||
def about(self):
|
||||
"""
|
||||
The plugin about text
|
||||
|
||||
:return: the text to be displayed
|
||||
"""
|
||||
about_text = translate('SongUsagePlugin',
|
||||
'<strong>SongUsage Plugin</strong><br />This plugin tracks the usage of songs in services.')
|
||||
'<strong>SongUsage Plugin</strong><br />'
|
||||
'This plugin tracks the usage of songs in services.')
|
||||
return about_text
|
||||
|
||||
def set_plugin_text_strings(self):
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
###############################################################################
|
||||
# OpenLP - Open Source Lyrics Projection #
|
||||
# --------------------------------------------------------------------------- #
|
||||
# Copyright (c) 2008-2013 Raoul Snyman #
|
||||
# Portions copyright (c) 2008-2013 Tim Bentley, Gerald Britton, Jonathan #
|
||||
# Copyright (c) 2008-2014 Raoul Snyman #
|
||||
# Portions copyright (c) 2008-2014 Tim Bentley, Gerald Britton, Jonathan #
|
||||
# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, #
|
||||
# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. #
|
||||
# Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru, #
|
||||
|
|
|
@ -59,22 +59,18 @@ class TestRegistry(TestCase):
|
|||
with self.assertRaises(KeyError) as context:
|
||||
Registry().register('test1', mock_1)
|
||||
self.assertEqual(context.exception.args[0], 'Duplicate service exception test1',
|
||||
'KeyError exception should have been thrown for duplicate service')
|
||||
'KeyError exception should have been thrown for duplicate service')
|
||||
|
||||
# WHEN I try to get back a non existent component
|
||||
# THEN I will get an exception
|
||||
with self.assertRaises(KeyError) as context:
|
||||
temp = Registry().get('test2')
|
||||
self.assertEqual(context.exception.args[0], 'Service test2 not found in list',
|
||||
'KeyError exception should have been thrown for missing service')
|
||||
temp = Registry().get('test2')
|
||||
self.assertEqual(temp, None, 'None should have been returned for missing service')
|
||||
|
||||
# WHEN I try to replace a component I should be allowed (testing only)
|
||||
Registry().remove('test1')
|
||||
# THEN I will get an exception
|
||||
with self.assertRaises(KeyError) as context:
|
||||
temp = Registry().get('test1')
|
||||
self.assertEqual(context.exception.args[0], 'Service test1 not found in list',
|
||||
'KeyError exception should have been thrown for deleted service')
|
||||
temp = Registry().get('test1')
|
||||
self.assertEqual(temp, None, 'None should have been returned for deleted service')
|
||||
|
||||
def registry_function_test(self):
|
||||
"""
|
||||
|
|
|
@ -71,7 +71,7 @@ class TestPluginManager(TestCase):
|
|||
|
||||
# THEN: The create_media_manager_item() method should have been called
|
||||
self.assertEqual(0, mocked_plugin.create_media_manager_item.call_count,
|
||||
'The create_media_manager_item() method should not have been called.')
|
||||
'The create_media_manager_item() method should not have been called.')
|
||||
|
||||
def hook_media_manager_with_active_plugin_test(self):
|
||||
"""
|
||||
|
@ -104,7 +104,7 @@ class TestPluginManager(TestCase):
|
|||
|
||||
# THEN: The hook_settings_tabs() method should have been called
|
||||
self.assertEqual(0, mocked_plugin.create_media_manager_item.call_count,
|
||||
'The create_media_manager_item() method should not have been called.')
|
||||
'The create_media_manager_item() method should not have been called.')
|
||||
|
||||
def hook_settings_tabs_with_disabled_plugin_and_mocked_form_test(self):
|
||||
"""
|
||||
|
@ -124,9 +124,9 @@ class TestPluginManager(TestCase):
|
|||
|
||||
# THEN: The create_settings_tab() method should not have been called, but the plugins lists should be the same
|
||||
self.assertEqual(0, mocked_plugin.create_settings_tab.call_count,
|
||||
'The create_media_manager_item() method should not have been called.')
|
||||
'The create_media_manager_item() method should not have been called.')
|
||||
self.assertEqual(mocked_settings_form.plugin_manager.plugins, plugin_manager.plugins,
|
||||
'The plugins on the settings form should be the same as the plugins in the plugin manager')
|
||||
'The plugins on the settings form should be the same as the plugins in the plugin manager')
|
||||
|
||||
def hook_settings_tabs_with_active_plugin_and_mocked_form_test(self):
|
||||
"""
|
||||
|
@ -146,9 +146,9 @@ class TestPluginManager(TestCase):
|
|||
|
||||
# THEN: The create_media_manager_item() method should have been called with the mocked settings form
|
||||
self.assertEqual(1, mocked_plugin.create_settings_tab.call_count,
|
||||
'The create_media_manager_item() method should have been called once.')
|
||||
'The create_media_manager_item() method should have been called once.')
|
||||
self.assertEqual(plugin_manager.plugins, mocked_settings_form.plugin_manager.plugins,
|
||||
'The plugins on the settings form should be the same as the plugins in the plugin manager')
|
||||
'The plugins on the settings form should be the same as the plugins in the plugin manager')
|
||||
|
||||
def hook_settings_tabs_with_active_plugin_and_no_form_test(self):
|
||||
"""
|
||||
|
@ -181,7 +181,7 @@ class TestPluginManager(TestCase):
|
|||
|
||||
# THEN: The create_media_manager_item() method should have been called
|
||||
self.assertEqual(0, mocked_plugin.add_import_menu_item.call_count,
|
||||
'The add_import_menu_item() method should not have been called.')
|
||||
'The add_import_menu_item() method should not have been called.')
|
||||
|
||||
def hook_import_menu_with_active_plugin_test(self):
|
||||
"""
|
||||
|
@ -214,7 +214,7 @@ class TestPluginManager(TestCase):
|
|||
|
||||
# THEN: The add_export_menu_Item() method should not have been called
|
||||
self.assertEqual(0, mocked_plugin.add_export_menu_Item.call_count,
|
||||
'The add_export_menu_Item() method should not have been called.')
|
||||
'The add_export_menu_Item() method should not have been called.')
|
||||
|
||||
def hook_export_menu_with_active_plugin_test(self):
|
||||
"""
|
||||
|
@ -248,7 +248,7 @@ class TestPluginManager(TestCase):
|
|||
|
||||
# THEN: The upgrade_settings() method should not have been called
|
||||
self.assertEqual(0, mocked_plugin.upgrade_settings.call_count,
|
||||
'The upgrade_settings() method should not have been called.')
|
||||
'The upgrade_settings() method should not have been called.')
|
||||
|
||||
def hook_upgrade_plugin_settings_with_active_plugin_test(self):
|
||||
"""
|
||||
|
@ -282,7 +282,7 @@ class TestPluginManager(TestCase):
|
|||
|
||||
# THEN: The add_tools_menu_item() method should have been called
|
||||
self.assertEqual(0, mocked_plugin.add_tools_menu_item.call_count,
|
||||
'The add_tools_menu_item() method should not have been called.')
|
||||
'The add_tools_menu_item() method should not have been called.')
|
||||
|
||||
def hook_tools_menu_with_active_plugin_test(self):
|
||||
"""
|
||||
|
|
|
@ -0,0 +1,89 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
|
||||
|
||||
###############################################################################
|
||||
# OpenLP - Open Source Lyrics Projection #
|
||||
# --------------------------------------------------------------------------- #
|
||||
# Copyright (c) 2008-2014 Raoul Snyman #
|
||||
# Portions copyright (c) 2008-2014 Tim Bentley, Gerald Britton, Jonathan #
|
||||
# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, #
|
||||
# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. #
|
||||
# Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru, #
|
||||
# Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, #
|
||||
# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Dave Warnock, #
|
||||
# Frode Woldsund, Martin Zibricky, Patrick Zimmermann #
|
||||
# --------------------------------------------------------------------------- #
|
||||
# This program is free software; you can redistribute it and/or modify it #
|
||||
# under the terms of the GNU General Public License as published by the Free #
|
||||
# Software Foundation; version 2 of the License. #
|
||||
# #
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT #
|
||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
|
||||
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
|
||||
# more details. #
|
||||
# #
|
||||
# You should have received a copy of the GNU General Public License along #
|
||||
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
|
||||
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||
###############################################################################
|
||||
"""
|
||||
Package to test the openlp.core.ui.renderer package.
|
||||
"""
|
||||
from unittest import TestCase
|
||||
|
||||
from PyQt4 import QtCore
|
||||
|
||||
from openlp.core.common import Registry
|
||||
from openlp.core.lib import Renderer, ScreenList
|
||||
|
||||
from tests.interfaces import MagicMock
|
||||
|
||||
SCREEN = {
|
||||
'primary': False,
|
||||
'number': 1,
|
||||
'size': QtCore.QRect(0, 0, 1024, 768)
|
||||
}
|
||||
|
||||
|
||||
class TestRenderer(TestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""
|
||||
Set up the components need for all tests.
|
||||
"""
|
||||
# Mocked out desktop object
|
||||
self.desktop = MagicMock()
|
||||
self.desktop.primaryScreen.return_value = SCREEN['primary']
|
||||
self.desktop.screenCount.return_value = SCREEN['number']
|
||||
self.desktop.screenGeometry.return_value = SCREEN['size']
|
||||
self.screens = ScreenList.create(self.desktop)
|
||||
Registry.create()
|
||||
|
||||
def tearDown(self):
|
||||
"""
|
||||
Delete QApplication.
|
||||
"""
|
||||
del self.screens
|
||||
|
||||
def initial_renderer_test(self):
|
||||
"""
|
||||
Test the initial renderer state .
|
||||
"""
|
||||
# GIVEN: A new renderer instance.
|
||||
renderer = Renderer()
|
||||
# WHEN: the default renderer is built.
|
||||
# THEN: The renderer should be a live controller.
|
||||
self.assertEqual(renderer.is_live, True, 'The base renderer should be a live controller')
|
||||
|
||||
def default_screen_layout_test(self):
|
||||
"""
|
||||
Test the default layout calculations.
|
||||
"""
|
||||
# GIVEN: A new renderer instance.
|
||||
renderer = Renderer()
|
||||
# WHEN: given the default screen size has been created.
|
||||
# THEN: The renderer have created a default screen.
|
||||
self.assertEqual(renderer.width, 1024, 'The base renderer should be a live controller')
|
||||
self.assertEqual(renderer.height, 768, 'The base renderer should be a live controller')
|
||||
self.assertEqual(renderer.screen_ratio, 0.75, 'The base renderer should be a live controller')
|
||||
self.assertEqual(renderer.footer_start, 691, 'The base renderer should be a live controller')
|
|
@ -84,4 +84,4 @@ class TestScreenList(TestCase):
|
|||
new_screen_count = len(self.screens.screen_list)
|
||||
self.assertEqual(old_screen_count + 1, new_screen_count, 'The new_screens list should be bigger')
|
||||
self.assertEqual(SCREEN, self.screens.screen_list.pop(),
|
||||
'The 2nd screen should be identical to the first screen')
|
||||
'The 2nd screen should be identical to the first screen')
|
||||
|
|
|
@ -99,9 +99,9 @@ class TestServiceItem(TestCase):
|
|||
# THEN: The frames should also be valid
|
||||
self.assertEqual('Test Custom', service_item.get_display_title(), 'The title should be "Test Custom"')
|
||||
self.assertEqual(VERSE[:-1], service_item.get_frames()[0]['text'],
|
||||
'The returned text matches the input, except the last line feed')
|
||||
'The returned text matches the input, except the last line feed')
|
||||
self.assertEqual(VERSE.split('\n', 1)[0], service_item.get_rendered_frame(1),
|
||||
'The first line has been returned')
|
||||
'The first line has been returned')
|
||||
self.assertEqual('Slide 1', service_item.get_frame_title(0), '"Slide 1" has been returned as the title')
|
||||
self.assertEqual('Slide 2', service_item.get_frame_title(1), '"Slide 2" has been returned as the title')
|
||||
self.assertEqual('', service_item.get_frame_title(2), 'Blank has been returned as the title of slide 3')
|
||||
|
@ -127,24 +127,24 @@ class TestServiceItem(TestCase):
|
|||
# THEN: We should get back a valid service item
|
||||
self.assertTrue(service_item.is_valid, 'The new service item should be valid')
|
||||
self.assertEqual(test_file, service_item.get_rendered_frame(0),
|
||||
'The first frame should match the path to the image')
|
||||
'The first frame should match the path to the image')
|
||||
self.assertEqual(frame_array, service_item.get_frames()[0],
|
||||
'The return should match frame array1')
|
||||
'The return should match frame array1')
|
||||
self.assertEqual(test_file, service_item.get_frame_path(0),
|
||||
'The frame path should match the full path to the image')
|
||||
'The frame path should match the full path to the image')
|
||||
self.assertEqual(image_name, service_item.get_frame_title(0),
|
||||
'The frame title should match the image name')
|
||||
'The frame title should match the image name')
|
||||
self.assertEqual(image_name, service_item.get_display_title(),
|
||||
'The display title should match the first image name')
|
||||
'The display title should match the first image name')
|
||||
self.assertTrue(service_item.is_image(), 'This service item should be of an "image" type')
|
||||
self.assertTrue(service_item.is_capable(ItemCapabilities.CanMaintain),
|
||||
'This service item should be able to be Maintained')
|
||||
'This service item should be able to be Maintained')
|
||||
self.assertTrue(service_item.is_capable(ItemCapabilities.CanPreview),
|
||||
'This service item should be able to be be Previewed')
|
||||
'This service item should be able to be be Previewed')
|
||||
self.assertTrue(service_item.is_capable(ItemCapabilities.CanLoop),
|
||||
'This service item should be able to be run in a can be made to Loop')
|
||||
'This service item should be able to be run in a can be made to Loop')
|
||||
self.assertTrue(service_item.is_capable(ItemCapabilities.CanAppend),
|
||||
'This service item should be able to have new items added to it')
|
||||
'This service item should be able to have new items added to it')
|
||||
|
||||
def service_item_load_image_from_local_service_test(self):
|
||||
"""
|
||||
|
@ -180,27 +180,27 @@ class TestServiceItem(TestCase):
|
|||
self.assertTrue(service_item.is_valid, 'The first service item should be valid')
|
||||
self.assertTrue(service_item2.is_valid, 'The second service item should be valid')
|
||||
self.assertEqual(test_file1, service_item.get_rendered_frame(0),
|
||||
'The first frame should match the path to the image')
|
||||
'The first frame should match the path to the image')
|
||||
self.assertEqual(test_file2, service_item2.get_rendered_frame(0),
|
||||
'The Second frame should match the path to the image')
|
||||
'The Second frame should match the path to the image')
|
||||
self.assertEqual(frame_array1, service_item.get_frames()[0], 'The return should match the frame array1')
|
||||
self.assertEqual(frame_array2, service_item2.get_frames()[0], 'The return should match the frame array2')
|
||||
self.assertEqual(test_file1, service_item.get_frame_path(0),
|
||||
'The frame path should match the full path to the image')
|
||||
'The frame path should match the full path to the image')
|
||||
self.assertEqual(test_file2, service_item2.get_frame_path(0),
|
||||
'The frame path should match the full path to the image')
|
||||
'The frame path should match the full path to the image')
|
||||
self.assertEqual(image_name1, service_item.get_frame_title(0),
|
||||
'The 1st frame title should match the image name')
|
||||
'The 1st frame title should match the image name')
|
||||
self.assertEqual(image_name2, service_item2.get_frame_title(0),
|
||||
'The 2nd frame title should match the image name')
|
||||
'The 2nd frame title should match the image name')
|
||||
self.assertEqual(service_item.name, service_item.title.lower(),
|
||||
'The plugin name should match the display title, as there are > 1 Images')
|
||||
'The plugin name should match the display title, as there are > 1 Images')
|
||||
self.assertTrue(service_item.is_image(), 'This service item should be of an "image" type')
|
||||
self.assertTrue(service_item.is_capable(ItemCapabilities.CanMaintain),
|
||||
'This service item should be able to be Maintained')
|
||||
'This service item should be able to be Maintained')
|
||||
self.assertTrue(service_item.is_capable(ItemCapabilities.CanPreview),
|
||||
'This service item should be able to be be Previewed')
|
||||
'This service item should be able to be be Previewed')
|
||||
self.assertTrue(service_item.is_capable(ItemCapabilities.CanLoop),
|
||||
'This service item should be able to be run in a can be made to Loop')
|
||||
'This service item should be able to be run in a can be made to Loop')
|
||||
self.assertTrue(service_item.is_capable(ItemCapabilities.CanAppend),
|
||||
'This service item should be able to have new items added to it')
|
||||
'This service item should be able to have new items added to it')
|
||||
|
|
|
@ -27,24 +27,55 @@
|
|||
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||
###############################################################################
|
||||
"""
|
||||
The Theme Controller helps manages adding, deleteing and modifying of themes.
|
||||
Package to test the openlp.core.ui.slidecontroller package.
|
||||
"""
|
||||
import os
|
||||
from unittest import TestCase
|
||||
|
||||
from openlp.core.common import AppLocation, check_directory_exists
|
||||
from PyQt4 import QtCore
|
||||
|
||||
from openlp.core.common import Registry
|
||||
from openlp.core.lib import ScreenList
|
||||
from openlp.core.ui import MainDisplay
|
||||
|
||||
from tests.interfaces import MagicMock, patch
|
||||
|
||||
SCREEN = {
|
||||
'primary': False,
|
||||
'number': 1,
|
||||
'size': QtCore.QRect(0, 0, 1024, 768)
|
||||
}
|
||||
|
||||
|
||||
class ThemeManagerHelper(object):
|
||||
"""
|
||||
Manages the non ui theme functions.
|
||||
"""
|
||||
def build_theme_path(self):
|
||||
class TestMainDisplay(TestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""
|
||||
Set up the theme path variables
|
||||
Set up the components need for all tests.
|
||||
"""
|
||||
self.log_debug('build theme path called')
|
||||
self.path = AppLocation.get_section_data_path(self.settings_section)
|
||||
check_directory_exists(self.path)
|
||||
self.thumb_path = os.path.join(self.path, 'thumbnails')
|
||||
check_directory_exists(self.thumb_path)
|
||||
self.theme_form.path = self.path
|
||||
# Mocked out desktop object
|
||||
self.desktop = MagicMock()
|
||||
self.desktop.primaryScreen.return_value = SCREEN['primary']
|
||||
self.desktop.screenCount.return_value = SCREEN['number']
|
||||
self.desktop.screenGeometry.return_value = SCREEN['size']
|
||||
self.screens = ScreenList.create(self.desktop)
|
||||
Registry.create()
|
||||
|
||||
def tearDown(self):
|
||||
"""
|
||||
Delete QApplication.
|
||||
"""
|
||||
del self.screens
|
||||
|
||||
def initial_main_display_test(self):
|
||||
"""
|
||||
Test the initial Main Display state .
|
||||
"""
|
||||
# GIVEN: A new slideController instance.
|
||||
display = MagicMock()
|
||||
display.is_live = True
|
||||
|
||||
# WHEN: the default controller is built.
|
||||
main_display = MainDisplay(display)
|
||||
|
||||
# THEN: The controller should not be a live controller.
|
||||
self.assertEqual(main_display.is_live, True, 'The main display should be a live controller')
|
|
@ -0,0 +1,77 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
|
||||
|
||||
###############################################################################
|
||||
# OpenLP - Open Source Lyrics Projection #
|
||||
# --------------------------------------------------------------------------- #
|
||||
# Copyright (c) 2008-2014 Raoul Snyman #
|
||||
# Portions copyright (c) 2008-2014 Tim Bentley, Gerald Britton, Jonathan #
|
||||
# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, #
|
||||
# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. #
|
||||
# Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru, #
|
||||
# Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, #
|
||||
# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Dave Warnock, #
|
||||
# Frode Woldsund, Martin Zibricky, Patrick Zimmermann #
|
||||
# --------------------------------------------------------------------------- #
|
||||
# This program is free software; you can redistribute it and/or modify it #
|
||||
# under the terms of the GNU General Public License as published by the Free #
|
||||
# Software Foundation; version 2 of the License. #
|
||||
# #
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT #
|
||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
|
||||
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
|
||||
# more details. #
|
||||
# #
|
||||
# You should have received a copy of the GNU General Public License along #
|
||||
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
|
||||
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||
###############################################################################
|
||||
"""
|
||||
Package to test the openlp.core.ui.slidecontroller package.
|
||||
"""
|
||||
from unittest import TestCase
|
||||
|
||||
from openlp.core.common import Registry
|
||||
from openlp.core.ui import ServiceManager
|
||||
|
||||
from tests.interfaces import MagicMock, patch
|
||||
|
||||
|
||||
class TestServiceManager(TestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""
|
||||
Create the UI
|
||||
"""
|
||||
Registry.create()
|
||||
|
||||
def tearDown(self):
|
||||
"""
|
||||
Delete all the C++ objects at the end so that we don't have a segfault
|
||||
"""
|
||||
pass
|
||||
|
||||
def initial_service_manager_test(self):
|
||||
"""
|
||||
Test the initial of service manager.
|
||||
"""
|
||||
# GIVEN: A new service manager instance.
|
||||
ServiceManager(None)
|
||||
# WHEN: the default service manager is built.
|
||||
# THEN: The the controller should be registered in the registry.
|
||||
self.assertNotEqual(Registry().get('service_manager'), None, 'The base service manager should be registered')
|
||||
|
||||
def create_basic_service_test(self):
|
||||
"""
|
||||
Test the create basic service array
|
||||
"""
|
||||
# GIVEN: A new service manager instance.
|
||||
service_manager = ServiceManager(None)
|
||||
# WHEN: when the basic service array is created.
|
||||
service_manager._save_lite = False
|
||||
service_manager.service_theme = 'test_theme'
|
||||
service = service_manager.create_basic_service()[0]
|
||||
# THEN: The the controller should be registered in the registry.
|
||||
self.assertNotEqual(service, None, 'The base service should be created')
|
||||
self.assertEqual(service['openlp_core']['service-theme'], 'test_theme', 'The test theme should be saved')
|
||||
self.assertEqual(service['openlp_core']['lite-service'], False, 'The lite service should be saved')
|
|
@ -0,0 +1,78 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
|
||||
|
||||
###############################################################################
|
||||
# OpenLP - Open Source Lyrics Projection #
|
||||
# --------------------------------------------------------------------------- #
|
||||
# Copyright (c) 2008-2014 Raoul Snyman #
|
||||
# Portions copyright (c) 2008-2014 Tim Bentley, Gerald Britton, Jonathan #
|
||||
# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, #
|
||||
# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. #
|
||||
# Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru, #
|
||||
# Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, #
|
||||
# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Dave Warnock, #
|
||||
# Frode Woldsund, Martin Zibricky, Patrick Zimmermann #
|
||||
# --------------------------------------------------------------------------- #
|
||||
# This program is free software; you can redistribute it and/or modify it #
|
||||
# under the terms of the GNU General Public License as published by the Free #
|
||||
# Software Foundation; version 2 of the License. #
|
||||
# #
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT #
|
||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
|
||||
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
|
||||
# more details. #
|
||||
# #
|
||||
# You should have received a copy of the GNU General Public License along #
|
||||
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
|
||||
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||
###############################################################################
|
||||
"""
|
||||
Package to test the openlp.core.ui.slidecontroller package.
|
||||
"""
|
||||
from unittest import TestCase
|
||||
|
||||
from openlp.core.ui import SlideController
|
||||
|
||||
from tests.interfaces import MagicMock, patch
|
||||
|
||||
|
||||
class TestSlideController(TestCase):
|
||||
|
||||
def initial_slide_controller_test(self):
|
||||
"""
|
||||
Test the initial slide controller state .
|
||||
"""
|
||||
# GIVEN: A new slideController instance.
|
||||
slide_controller = SlideController(None)
|
||||
# WHEN: the default controller is built.
|
||||
# THEN: The controller should not be a live controller.
|
||||
self.assertEqual(slide_controller.is_live, False, 'The base slide controller should not be a live controller')
|
||||
|
||||
def toggle_blank_test(self):
|
||||
"""
|
||||
Test the setting of the display blank icons by display type.
|
||||
"""
|
||||
# GIVEN: A new slideController instance.
|
||||
slide_controller = SlideController(None)
|
||||
service_item = MagicMock()
|
||||
toolbar = MagicMock()
|
||||
toolbar.set_widget_visible = self.dummy_widget_visible
|
||||
slide_controller.toolbar = toolbar
|
||||
slide_controller.service_item = service_item
|
||||
|
||||
# WHEN a text based service item is used
|
||||
slide_controller.service_item.is_text = MagicMock(return_value=True)
|
||||
slide_controller.set_blank_menu()
|
||||
|
||||
# THEN: then call set up the toolbar to blank the display screen.
|
||||
self.assertEqual(len(self.test_widget), 3, 'There should be three icons to display on the screen')
|
||||
|
||||
# WHEN a non text based service item is used
|
||||
slide_controller.service_item.is_text = MagicMock(return_value=False)
|
||||
slide_controller.set_blank_menu()
|
||||
|
||||
# THEN: then call set up the toolbar to blank the display screen.
|
||||
self.assertEqual(len(self.test_widget), 2, 'There should be only two icons to display on the screen')
|
||||
|
||||
def dummy_widget_visible(self, widget, visible=True):
|
||||
self.test_widget = widget
|
|
@ -0,0 +1,143 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
|
||||
|
||||
###############################################################################
|
||||
# OpenLP - Open Source Lyrics Projection #
|
||||
# --------------------------------------------------------------------------- #
|
||||
# Copyright (c) 2008-2013 Raoul Snyman #
|
||||
# Portions copyright (c) 2008-2013 Tim Bentley, Gerald Britton, Jonathan #
|
||||
# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, #
|
||||
# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. #
|
||||
# Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru, #
|
||||
# Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, #
|
||||
# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Dave Warnock, #
|
||||
# Frode Woldsund, Martin Zibricky, Patrick Zimmermann #
|
||||
# --------------------------------------------------------------------------- #
|
||||
# This program is free software; you can redistribute it and/or modify it #
|
||||
# under the terms of the GNU General Public License as published by the Free #
|
||||
# Software Foundation; version 2 of the License. #
|
||||
# #
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT #
|
||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
|
||||
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
|
||||
# more details. #
|
||||
# #
|
||||
# You should have received a copy of the GNU General Public License along #
|
||||
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
|
||||
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||
###############################################################################
|
||||
"""
|
||||
This module contains tests for the pptviewcontroller module of the Presentations plugin.
|
||||
"""
|
||||
|
||||
from unittest import TestCase
|
||||
|
||||
from tests.functional import MagicMock, patch
|
||||
|
||||
from openlp.plugins.presentations.lib.pptviewcontroller import PptviewDocument
|
||||
|
||||
#TODO: Items left to test
|
||||
# PptviewController
|
||||
# __init__
|
||||
# check_availablecheck_installed
|
||||
# start_process(self)
|
||||
# kill
|
||||
|
||||
class TestPptviewDocument(TestCase):
|
||||
"""
|
||||
Test the PptviewDocument Class
|
||||
"""
|
||||
#TODO: Items left to test
|
||||
# PptviewDocument
|
||||
# __init__
|
||||
# create_thumbnails
|
||||
# close_presentation
|
||||
# is_loaded
|
||||
# is_active
|
||||
# blank_screen
|
||||
# unblank_screen
|
||||
# is_blank
|
||||
# stop_presentation
|
||||
# start_presentation
|
||||
# get_slide_number
|
||||
# get_slide_count
|
||||
# goto_slide
|
||||
# next_step
|
||||
# previous_step
|
||||
|
||||
def setUp(self):
|
||||
"""
|
||||
Set up the patches and mocks need for all tests.
|
||||
"""
|
||||
self.os_patcher = patch('openlp.plugins.presentations.lib.pptviewcontroller.os')
|
||||
self.pptview_document_create_thumbnails_patcher = patch(
|
||||
'openlp.plugins.presentations.lib.pptviewcontroller.PptviewDocument.create_thumbnails')
|
||||
self.pptview_document_stop_presentation_patcher = patch(
|
||||
'openlp.plugins.presentations.lib.pptviewcontroller.PptviewDocument.stop_presentation')
|
||||
self.presentation_document_get_temp_folder_patcher = patch(
|
||||
'openlp.plugins.presentations.lib.pptviewcontroller.PresentationDocument.get_temp_folder')
|
||||
self.presentation_document_setup_patcher = patch(
|
||||
'openlp.plugins.presentations.lib.pptviewcontroller.PresentationDocument._setup')
|
||||
self.rect_patcher = patch('openlp.plugins.presentations.lib.pptviewcontroller.RECT')
|
||||
self.screen_list_patcher = patch('openlp.plugins.presentations.lib.pptviewcontroller.ScreenList')
|
||||
|
||||
self.mock_os = self.os_patcher.start()
|
||||
self.mock_pptview_document_create_thumbnails = self.pptview_document_create_thumbnails_patcher.start()
|
||||
self.mock_pptview_document_stop_presentation = self.pptview_document_stop_presentation_patcher.start()
|
||||
self.mock_presentation_document_get_temp_folder = self.presentation_document_get_temp_folder_patcher.start()
|
||||
self.mock_presentation_document_setup = self.presentation_document_setup_patcher.start()
|
||||
self.mock_rect = self.rect_patcher.start()
|
||||
self.mock_screen_list = self.screen_list_patcher.start()
|
||||
|
||||
self.mock_controller = MagicMock()
|
||||
self.mock_presentation = MagicMock()
|
||||
|
||||
self.mock_presentation_document_get_temp_folder.return_value = 'temp folder'
|
||||
|
||||
def tearDown(self):
|
||||
"""
|
||||
Stop the patches
|
||||
"""
|
||||
self.os_patcher.stop()
|
||||
self.pptview_document_create_thumbnails_patcher.stop()
|
||||
self.pptview_document_stop_presentation_patcher.stop()
|
||||
self.presentation_document_get_temp_folder_patcher.stop()
|
||||
self.presentation_document_setup_patcher.stop()
|
||||
self.rect_patcher.stop()
|
||||
self.screen_list_patcher.stop()
|
||||
|
||||
def load_presentation_succesfull_test(self):
|
||||
"""
|
||||
Test the PptviewDocument.load_presentation() method when the PPT is successfully opened
|
||||
"""
|
||||
# GIVEN: A reset mocked_os
|
||||
self.mock_os.reset()
|
||||
|
||||
# WHEN: The temporary directory exists and OpenPPT returns successfully (not -1)
|
||||
self.mock_os.path.isdir.return_value = True
|
||||
self.mock_controller.process.OpenPPT.return_value = 0
|
||||
instance = PptviewDocument(self.mock_controller, self.mock_presentation)
|
||||
instance.filepath = 'test\path.ppt'
|
||||
result = instance.load_presentation()
|
||||
|
||||
# THEN: PptviewDocument.load_presentation should return True
|
||||
self.assertTrue(result)
|
||||
|
||||
def load_presentation_unsuccesfull_test(self):
|
||||
"""
|
||||
Test the PptviewDocument.load_presentation() method when the temporary directory does not exist and the PPT is
|
||||
not successfully opened
|
||||
"""
|
||||
# GIVEN: A reset mocked_os
|
||||
self.mock_os.reset()
|
||||
|
||||
# WHEN: The temporary directory does not exist and OpenPPT returns unsuccessfully (-1)
|
||||
self.mock_os.path.isdir.return_value = False
|
||||
self.mock_controller.process.OpenPPT.return_value = -1
|
||||
instance = PptviewDocument(self.mock_controller, self.mock_presentation)
|
||||
instance.filepath = 'test\path.ppt'
|
||||
result = instance.load_presentation()
|
||||
|
||||
# THEN: The temporary directory should be created and PptviewDocument.load_presentation should return False
|
||||
self.mock_os.makedirs.assert_called_once_with('temp folder')
|
||||
self.assertFalse(result)
|
|
@ -1,53 +1,154 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
|
||||
|
||||
###############################################################################
|
||||
# OpenLP - Open Source Lyrics Projection #
|
||||
# --------------------------------------------------------------------------- #
|
||||
# Copyright (c) 2008-2014 Raoul Snyman #
|
||||
# Portions copyright (c) 2008-2014 Tim Bentley, Gerald Britton, Jonathan #
|
||||
# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, #
|
||||
# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. #
|
||||
# Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru, #
|
||||
# Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, #
|
||||
# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Dave Warnock, #
|
||||
# Frode Woldsund, Martin Zibricky, Patrick Zimmermann #
|
||||
# --------------------------------------------------------------------------- #
|
||||
# This program is free software; you can redistribute it and/or modify it #
|
||||
# under the terms of the GNU General Public License as published by the Free #
|
||||
# Software Foundation; version 2 of the License. #
|
||||
# #
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT #
|
||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
|
||||
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
|
||||
# more details. #
|
||||
# #
|
||||
# You should have received a copy of the GNU General Public License along #
|
||||
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
|
||||
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||
###############################################################################
|
||||
"""
|
||||
This module contains tests for the Presentation Controller.
|
||||
"""
|
||||
from unittest import TestCase
|
||||
|
||||
from openlp.plugins.presentations.lib.presentationcontroller import PresentationController
|
||||
from tests.functional import MagicMock
|
||||
|
||||
class TestPresentationController(TestCase):
|
||||
"""
|
||||
Test the PresentationController.
|
||||
"""
|
||||
def constructor_test(self):
|
||||
"""
|
||||
Test the Constructor
|
||||
"""
|
||||
# GIVEN: No presentation controller
|
||||
controller = None
|
||||
|
||||
# WHEN: The presentation controller object is created
|
||||
controller = PresentationController(plugin=MagicMock())
|
||||
|
||||
# THEN: The name of the presentation controller should be correct
|
||||
self.assertEqual('PresentationController', controller.name,
|
||||
'The name of the presentation controller should be correct')
|
||||
# -*- coding: utf-8 -*-
|
||||
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
|
||||
|
||||
###############################################################################
|
||||
# OpenLP - Open Source Lyrics Projection #
|
||||
# --------------------------------------------------------------------------- #
|
||||
# Copyright (c) 2008-2014 Raoul Snyman #
|
||||
# Portions copyright (c) 2008-2014 Tim Bentley, Gerald Britton, Jonathan #
|
||||
# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, #
|
||||
# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. #
|
||||
# Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru, #
|
||||
# Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, #
|
||||
# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Dave Warnock, #
|
||||
# Frode Woldsund, Martin Zibricky, Patrick Zimmermann #
|
||||
# --------------------------------------------------------------------------- #
|
||||
# This program is free software; you can redistribute it and/or modify it #
|
||||
# under the terms of the GNU General Public License as published by the Free #
|
||||
# Software Foundation; version 2 of the License. #
|
||||
# #
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT #
|
||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
|
||||
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
|
||||
# more details. #
|
||||
# #
|
||||
# You should have received a copy of the GNU General Public License along #
|
||||
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
|
||||
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||
###############################################################################
|
||||
"""
|
||||
This module contains tests for the Presentation Controller.
|
||||
"""
|
||||
from unittest import TestCase
|
||||
|
||||
from openlp.plugins.presentations.lib.presentationcontroller import PresentationController, PresentationDocument
|
||||
from tests.functional import MagicMock, patch
|
||||
|
||||
class TestPresentationController(TestCase):
|
||||
"""
|
||||
Test the PresentationController.
|
||||
"""
|
||||
# TODO: Items left to test
|
||||
# PresentationController
|
||||
# __init__
|
||||
# enabled
|
||||
# is_available
|
||||
# check_available
|
||||
# start_process
|
||||
# kill
|
||||
# add_document
|
||||
# remove_doc
|
||||
# close_presentation
|
||||
# _get_plugin_manager
|
||||
|
||||
def constructor_test(self):
|
||||
"""
|
||||
Test the Constructor
|
||||
"""
|
||||
# GIVEN: No presentation controller
|
||||
controller = None
|
||||
|
||||
# WHEN: The presentation controller object is created
|
||||
controller = PresentationController(plugin=MagicMock())
|
||||
|
||||
# THEN: The name of the presentation controller should be correct
|
||||
self.assertEqual('PresentationController', controller.name,
|
||||
'The name of the presentation controller should be correct')
|
||||
|
||||
class TestPresentationDocument(TestCase):
|
||||
"""
|
||||
Test the PresentationDocument Class
|
||||
"""
|
||||
# TODO: Items left to test
|
||||
# PresentationDocument
|
||||
# __init__
|
||||
# load_presentation
|
||||
# presentation_deleted
|
||||
# get_file_name
|
||||
# get_thumbnail_folder
|
||||
# get_temp_folder
|
||||
# check_thumbnails
|
||||
# close_presentation
|
||||
# is_active
|
||||
# is_loaded
|
||||
# blank_screen
|
||||
# unblank_screen
|
||||
# is_blank
|
||||
# stop_presentation
|
||||
# start_presentation
|
||||
# get_slide_number
|
||||
# get_slide_count
|
||||
# goto_slide
|
||||
# next_step
|
||||
# previous_step
|
||||
# convert_thumbnail
|
||||
# get_thumbnail_path
|
||||
# poll_slidenumber
|
||||
# get_slide_text
|
||||
# get_slide_notes
|
||||
|
||||
def setUp(self):
|
||||
"""
|
||||
Set up the patches and mocks need for all tests.
|
||||
"""
|
||||
self.check_directory_exists_patcher = patch(
|
||||
'openlp.plugins.presentations.lib.presentationcontroller.check_directory_exists')
|
||||
self.get_thumbnail_folder_patcher = patch(
|
||||
'openlp.plugins.presentations.lib.presentationcontroller.PresentationDocument.get_thumbnail_folder')
|
||||
self._setup_patcher = patch(
|
||||
'openlp.plugins.presentations.lib.presentationcontroller.PresentationDocument._setup')
|
||||
|
||||
self.mock_check_directory_exists = self.check_directory_exists_patcher.start()
|
||||
self.mock_get_thumbnail_folder = self.get_thumbnail_folder_patcher.start()
|
||||
self.mock_setup = self._setup_patcher.start()
|
||||
|
||||
self.mock_controller = MagicMock()
|
||||
|
||||
self.mock_get_thumbnail_folder.return_value = 'returned/path/'
|
||||
|
||||
def tearDown(self):
|
||||
"""
|
||||
Stop the patches
|
||||
"""
|
||||
self.check_directory_exists_patcher.stop()
|
||||
self.get_thumbnail_folder_patcher.stop()
|
||||
self._setup_patcher.stop()
|
||||
|
||||
def initalise_presentation_document_test(self):
|
||||
"""
|
||||
Test the PresentationDocument __init__ method when initalising the PresentationDocument Class
|
||||
"""
|
||||
# GIVEN: A reset mock_setup and mocked controller
|
||||
self.mock_setup.reset()
|
||||
|
||||
# WHEN: Creating an instance of PresentationDocument
|
||||
instance = PresentationDocument(self.mock_controller, 'Name')
|
||||
|
||||
# THEN: PresentationDocument.__init__ should have been called with the correct arguments
|
||||
self.mock_setup.assert_called_once_with('Name')
|
||||
|
||||
def presentation_document_setup_test(self):
|
||||
"""
|
||||
Test the PresentationDocument _setup method when initalising the PresentationDocument Class
|
||||
"""
|
||||
self._setup_patcher.stop()
|
||||
|
||||
# GIVEN: A mocked controller, patched check_directory_exists_patcher and patched get_thumbnail_folder method
|
||||
|
||||
# WHEN: Creating an instance of PresentationDocument
|
||||
instance = PresentationDocument(self.mock_controller, 'Name')
|
||||
|
||||
# THEN: check_directory_exists should have been called with the correct arguments
|
||||
self.mock_check_directory_exists.assert_called_once_with('returned/path/')
|
||||
|
||||
self._setup_patcher.start()
|
||||
|
|
|
@ -87,7 +87,6 @@ class TestRouter(TestCase):
|
|||
router = HttpRouter()
|
||||
router.initialise()
|
||||
test_value = 'b3BlbmxwOnBhc3N3b3Jk'
|
||||
print(router.auth)
|
||||
|
||||
# THEN: the function should return the correct password
|
||||
self.assertEqual(router.auth, test_value,
|
||||
|
|
|
@ -38,7 +38,7 @@ class TestListPreviewWidget(TestCase):
|
|||
|
||||
def initial_slide_count_test(self):
|
||||
"""
|
||||
Test the inital slide count.
|
||||
Test the initial slide count .
|
||||
"""
|
||||
# GIVEN: A new ListPreviewWidget instance.
|
||||
# WHEN: No SlideItem has been added yet.
|
||||
|
@ -47,7 +47,7 @@ class TestListPreviewWidget(TestCase):
|
|||
|
||||
def initial_slide_number_test(self):
|
||||
"""
|
||||
Test the inital slide number.
|
||||
Test the initial current slide number.
|
||||
"""
|
||||
# GIVEN: A new ListPreviewWidget instance.
|
||||
# WHEN: No SlideItem has been added yet.
|
||||
|
|
|
@ -22,12 +22,13 @@ class TestMainWindow(TestCase):
|
|||
# Mock cursor busy/normal methods.
|
||||
self.app.set_busy_cursor = MagicMock()
|
||||
self.app.set_normal_cursor = MagicMock()
|
||||
self.app.args =[]
|
||||
self.app.args = []
|
||||
Registry().register('application', self.app)
|
||||
# Mock classes and methods used by mainwindow.
|
||||
with patch('openlp.core.ui.mainwindow.SettingsForm') as mocked_settings_form, \
|
||||
patch('openlp.core.ui.mainwindow.ImageManager') as mocked_image_manager, \
|
||||
patch('openlp.core.ui.mainwindow.SlideController') as mocked_slide_controller, \
|
||||
patch('openlp.core.ui.mainwindow.LiveController') as mocked_live_controller, \
|
||||
patch('openlp.core.ui.mainwindow.PreviewController') as mocked_preview_controller, \
|
||||
patch('openlp.core.ui.mainwindow.OpenLPDockWidget') as mocked_dock_widget, \
|
||||
patch('openlp.core.ui.mainwindow.QtGui.QToolBox') as mocked_q_tool_box_class, \
|
||||
patch('openlp.core.ui.mainwindow.QtGui.QMainWindow.addDockWidget') as mocked_add_dock_method, \
|
||||
|
@ -53,7 +54,7 @@ class TestMainWindow(TestCase):
|
|||
mocked_value.side_effect = [True, 2]
|
||||
|
||||
# WHEN: Call the restore method.
|
||||
Registry().execute('bootstrap_post_set_up')
|
||||
self.main_window.restore_current_media_manager_item()
|
||||
|
||||
# THEN: The current widget should have been set.
|
||||
self.main_window.media_tool_box.setCurrentIndex.assert_called_with(2)
|
||||
|
|
|
@ -4,10 +4,10 @@
|
|||
|
||||
from unittest import TestCase
|
||||
|
||||
from PyQt4 import QtGui
|
||||
from PyQt4 import QtGui, QtTest, QtCore
|
||||
|
||||
from openlp.core.common import Registry
|
||||
from openlp.core.lib import ScreenList, ServiceItem
|
||||
from openlp.core.lib import ScreenList, ServiceItem, ItemCapabilities
|
||||
from openlp.core.ui.mainwindow import MainWindow
|
||||
from tests.interfaces import MagicMock, patch
|
||||
|
||||
|
@ -35,20 +35,22 @@ class TestServiceManager(TestCase):
|
|||
|
||||
def basic_service_manager_test(self):
|
||||
"""
|
||||
Test the Service Manager display functionality
|
||||
Test the Service Manager UI Functionality
|
||||
"""
|
||||
# GIVEN: A New Service Manager instance
|
||||
|
||||
# WHEN I have an empty display
|
||||
# WHEN I have set up the display
|
||||
self.service_manager.setup_ui(self.service_manager)
|
||||
# THEN the count of items should be zero
|
||||
self.assertEqual(self.service_manager.service_manager_list.topLevelItemCount(), 0,
|
||||
'The service manager list should be empty ')
|
||||
|
||||
def context_menu_test(self):
|
||||
def default_context_menu_test(self):
|
||||
"""
|
||||
Test the context_menu() method.
|
||||
Test the context_menu() method with a default service item
|
||||
"""
|
||||
# GIVEN: A service item added
|
||||
self.service_manager.setup_ui(self.service_manager)
|
||||
with patch('PyQt4.QtGui.QTreeWidget.itemAt') as mocked_item_at_method, \
|
||||
patch('PyQt4.QtGui.QWidget.mapToGlobal'), \
|
||||
patch('PyQt4.QtGui.QMenu.exec_'):
|
||||
|
@ -84,3 +86,236 @@ class TestServiceManager(TestCase):
|
|||
'The action should be set invisible.'
|
||||
self.service_manager.auto_start_action.setVisible.assert_called_once_with(False), \
|
||||
'The action should be set invisible.'
|
||||
|
||||
def edit_context_menu_test(self):
|
||||
"""
|
||||
Test the context_menu() method with a edit service item
|
||||
"""
|
||||
# GIVEN: A service item added
|
||||
self.service_manager.setup_ui(self.service_manager)
|
||||
with patch('PyQt4.QtGui.QTreeWidget.itemAt') as mocked_item_at_method, \
|
||||
patch('PyQt4.QtGui.QWidget.mapToGlobal'), \
|
||||
patch('PyQt4.QtGui.QMenu.exec_'):
|
||||
mocked_item = MagicMock()
|
||||
mocked_item.parent.return_value = None
|
||||
mocked_item_at_method.return_value = mocked_item
|
||||
# We want 1 to be returned for the position
|
||||
mocked_item.data.return_value = 1
|
||||
# A service item without capabilities.
|
||||
service_item = ServiceItem()
|
||||
service_item.add_capability(ItemCapabilities.CanEdit)
|
||||
service_item.edit_id = 1
|
||||
self.service_manager.service_items = [{'service_item': service_item}]
|
||||
q_point = None
|
||||
# Mocked actions.
|
||||
self.service_manager.edit_action.setVisible = MagicMock()
|
||||
self.service_manager.create_custom_action.setVisible = MagicMock()
|
||||
self.service_manager.maintain_action.setVisible = MagicMock()
|
||||
self.service_manager.notes_action.setVisible = MagicMock()
|
||||
self.service_manager.time_action.setVisible = MagicMock()
|
||||
self.service_manager.auto_start_action.setVisible = MagicMock()
|
||||
|
||||
# WHEN: Show the context menu.
|
||||
self.service_manager.context_menu(q_point)
|
||||
|
||||
# THEN: The following actions should be not visible.
|
||||
self.service_manager.edit_action.setVisible.assert_called_with(True), \
|
||||
'The action should be set visible.'
|
||||
self.service_manager.create_custom_action.setVisible.assert_called_once_with(False), \
|
||||
'The action should be set invisible.'
|
||||
self.service_manager.maintain_action.setVisible.assert_called_once_with(False), \
|
||||
'The action should be set invisible.'
|
||||
self.service_manager.notes_action.setVisible.assert_called_with(True), 'The action should be set visible.'
|
||||
self.service_manager.time_action.setVisible.assert_called_once_with(False), \
|
||||
'The action should be set invisible.'
|
||||
self.service_manager.auto_start_action.setVisible.assert_called_once_with(False), \
|
||||
'The action should be set invisible.'
|
||||
|
||||
def maintain_context_menu_test(self):
|
||||
"""
|
||||
Test the context_menu() method with a maintain
|
||||
"""
|
||||
# GIVEN: A service item added
|
||||
self.service_manager.setup_ui(self.service_manager)
|
||||
with patch('PyQt4.QtGui.QTreeWidget.itemAt') as mocked_item_at_method, \
|
||||
patch('PyQt4.QtGui.QWidget.mapToGlobal'), \
|
||||
patch('PyQt4.QtGui.QMenu.exec_'):
|
||||
mocked_item = MagicMock()
|
||||
mocked_item.parent.return_value = None
|
||||
mocked_item_at_method.return_value = mocked_item
|
||||
# We want 1 to be returned for the position
|
||||
mocked_item.data.return_value = 1
|
||||
# A service item without capabilities.
|
||||
service_item = ServiceItem()
|
||||
service_item.add_capability(ItemCapabilities.CanMaintain)
|
||||
self.service_manager.service_items = [{'service_item': service_item}]
|
||||
q_point = None
|
||||
# Mocked actions.
|
||||
self.service_manager.edit_action.setVisible = MagicMock()
|
||||
self.service_manager.create_custom_action.setVisible = MagicMock()
|
||||
self.service_manager.maintain_action.setVisible = MagicMock()
|
||||
self.service_manager.notes_action.setVisible = MagicMock()
|
||||
self.service_manager.time_action.setVisible = MagicMock()
|
||||
self.service_manager.auto_start_action.setVisible = MagicMock()
|
||||
|
||||
# WHEN: Show the context menu.
|
||||
self.service_manager.context_menu(q_point)
|
||||
|
||||
# THEN: The following actions should be not visible.
|
||||
self.service_manager.edit_action.setVisible.assert_called_once_with(False), \
|
||||
'The action should be set invisible.'
|
||||
self.service_manager.create_custom_action.setVisible.assert_called_once_with(False), \
|
||||
'The action should be set invisible.'
|
||||
self.service_manager.maintain_action.setVisible.assert_called_with(True), \
|
||||
'The action should be set visible.'
|
||||
self.service_manager.notes_action.setVisible.assert_called_with(True), 'The action should be set visible.'
|
||||
self.service_manager.time_action.setVisible.assert_called_once_with(False), \
|
||||
'The action should be set invisible.'
|
||||
self.service_manager.auto_start_action.setVisible.assert_called_once_with(False), \
|
||||
'The action should be set invisible.'
|
||||
|
||||
def loopy_context_menu_test(self):
|
||||
"""
|
||||
Test the context_menu() method with a loop
|
||||
"""
|
||||
# GIVEN: A service item added
|
||||
self.service_manager.setup_ui(self.service_manager)
|
||||
with patch('PyQt4.QtGui.QTreeWidget.itemAt') as mocked_item_at_method, \
|
||||
patch('PyQt4.QtGui.QWidget.mapToGlobal'), \
|
||||
patch('PyQt4.QtGui.QMenu.exec_'):
|
||||
mocked_item = MagicMock()
|
||||
mocked_item.parent.return_value = None
|
||||
mocked_item_at_method.return_value = mocked_item
|
||||
# We want 1 to be returned for the position
|
||||
mocked_item.data.return_value = 1
|
||||
# A service item without capabilities.
|
||||
service_item = ServiceItem()
|
||||
service_item.add_capability(ItemCapabilities.CanLoop)
|
||||
service_item._raw_frames.append("One")
|
||||
service_item._raw_frames.append("Two")
|
||||
self.service_manager.service_items = [{'service_item': service_item}]
|
||||
q_point = None
|
||||
# Mocked actions.
|
||||
self.service_manager.edit_action.setVisible = MagicMock()
|
||||
self.service_manager.create_custom_action.setVisible = MagicMock()
|
||||
self.service_manager.maintain_action.setVisible = MagicMock()
|
||||
self.service_manager.notes_action.setVisible = MagicMock()
|
||||
self.service_manager.time_action.setVisible = MagicMock()
|
||||
self.service_manager.auto_start_action.setVisible = MagicMock()
|
||||
|
||||
# WHEN: Show the context menu.
|
||||
self.service_manager.context_menu(q_point)
|
||||
|
||||
# THEN: The following actions should be not visible.
|
||||
self.service_manager.edit_action.setVisible.assert_called_once_with(False), \
|
||||
'The action should be set invisible.'
|
||||
self.service_manager.create_custom_action.setVisible.assert_called_once_with(False), \
|
||||
'The action should be set invisible.'
|
||||
self.service_manager.maintain_action.setVisible.assert_called_once_with(False), \
|
||||
'The action should be set invisible.'
|
||||
self.service_manager.notes_action.setVisible.assert_called_with(True), 'The action should be set visible.'
|
||||
self.service_manager.time_action.setVisible.assert_called_once_with(False), \
|
||||
'The action should be set invisible.'
|
||||
self.service_manager.auto_start_action.setVisible.assert_called_once_with(False), \
|
||||
'The action should be set invisible.'
|
||||
|
||||
def start_time_context_menu_test(self):
|
||||
"""
|
||||
Test the context_menu() method with a start time
|
||||
"""
|
||||
# GIVEN: A service item added
|
||||
self.service_manager.setup_ui(self.service_manager)
|
||||
with patch('PyQt4.QtGui.QTreeWidget.itemAt') as mocked_item_at_method, \
|
||||
patch('PyQt4.QtGui.QWidget.mapToGlobal'), \
|
||||
patch('PyQt4.QtGui.QMenu.exec_'):
|
||||
mocked_item = MagicMock()
|
||||
mocked_item.parent.return_value = None
|
||||
mocked_item_at_method.return_value = mocked_item
|
||||
# We want 1 to be returned for the position
|
||||
mocked_item.data.return_value = 1
|
||||
# A service item without capabilities.
|
||||
service_item = ServiceItem()
|
||||
service_item.add_capability(ItemCapabilities.HasVariableStartTime)
|
||||
self.service_manager.service_items = [{'service_item': service_item}]
|
||||
q_point = None
|
||||
# Mocked actions.
|
||||
self.service_manager.edit_action.setVisible = MagicMock()
|
||||
self.service_manager.create_custom_action.setVisible = MagicMock()
|
||||
self.service_manager.maintain_action.setVisible = MagicMock()
|
||||
self.service_manager.notes_action.setVisible = MagicMock()
|
||||
self.service_manager.time_action.setVisible = MagicMock()
|
||||
self.service_manager.auto_start_action.setVisible = MagicMock()
|
||||
|
||||
# WHEN: Show the context menu.
|
||||
self.service_manager.context_menu(q_point)
|
||||
|
||||
# THEN: The following actions should be not visible.
|
||||
self.service_manager.edit_action.setVisible.assert_called_once_with(False), \
|
||||
'The action should be set invisible.'
|
||||
self.service_manager.create_custom_action.setVisible.assert_called_once_with(False), \
|
||||
'The action should be set invisible.'
|
||||
self.service_manager.maintain_action.setVisible.assert_called_once_with(False), \
|
||||
'The action should be set invisible.'
|
||||
self.service_manager.notes_action.setVisible.assert_called_with(True), 'The action should be set visible.'
|
||||
self.service_manager.time_action.setVisible.assert_called_with(True), \
|
||||
'The action should be set visible.'
|
||||
self.service_manager.auto_start_action.setVisible.assert_called_once_with(False), \
|
||||
'The action should be set invisible.'
|
||||
|
||||
def auto_start_context_menu_test(self):
|
||||
"""
|
||||
Test the context_menu() method with can auto start
|
||||
"""
|
||||
# GIVEN: A service item added
|
||||
self.service_manager.setup_ui(self.service_manager)
|
||||
with patch('PyQt4.QtGui.QTreeWidget.itemAt') as mocked_item_at_method, \
|
||||
patch('PyQt4.QtGui.QWidget.mapToGlobal'), \
|
||||
patch('PyQt4.QtGui.QMenu.exec_'):
|
||||
mocked_item = MagicMock()
|
||||
mocked_item.parent.return_value = None
|
||||
mocked_item_at_method.return_value = mocked_item
|
||||
# We want 1 to be returned for the position
|
||||
mocked_item.data.return_value = 1
|
||||
# A service item without capabilities.
|
||||
service_item = ServiceItem()
|
||||
service_item.add_capability(ItemCapabilities.CanAutoStartForLive)
|
||||
self.service_manager.service_items = [{'service_item': service_item}]
|
||||
q_point = None
|
||||
# Mocked actions.
|
||||
self.service_manager.edit_action.setVisible = MagicMock()
|
||||
self.service_manager.create_custom_action.setVisible = MagicMock()
|
||||
self.service_manager.maintain_action.setVisible = MagicMock()
|
||||
self.service_manager.notes_action.setVisible = MagicMock()
|
||||
self.service_manager.time_action.setVisible = MagicMock()
|
||||
self.service_manager.auto_start_action.setVisible = MagicMock()
|
||||
|
||||
# WHEN: Show the context menu.
|
||||
self.service_manager.context_menu(q_point)
|
||||
|
||||
# THEN: The following actions should be not visible.
|
||||
self.service_manager.edit_action.setVisible.assert_called_once_with(False), \
|
||||
'The action should be set invisible.'
|
||||
self.service_manager.create_custom_action.setVisible.assert_called_once_with(False), \
|
||||
'The action should be set invisible.'
|
||||
self.service_manager.maintain_action.setVisible.assert_called_once_with(False), \
|
||||
'The action should be set invisible.'
|
||||
self.service_manager.notes_action.setVisible.assert_called_with(True), 'The action should be set visible.'
|
||||
self.service_manager.time_action.setVisible.assert_called_once_with(False), \
|
||||
'The action should be set invisible.'
|
||||
self.service_manager.auto_start_action.setVisible.assert_called_with(True), \
|
||||
'The action should be set visible.'
|
||||
|
||||
def click_on_new_service_test(self):
|
||||
"""
|
||||
Test the on_new_service event handler is called by the UI
|
||||
"""
|
||||
# GIVEN: An initial form
|
||||
mocked_event = MagicMock()
|
||||
self.service_manager.on_new_service_clicked = mocked_event
|
||||
self.service_manager.setup_ui(self.service_manager)
|
||||
|
||||
# WHEN displaying the UI and pressing cancel
|
||||
new_service = self.service_manager.toolbar.actions['newService']
|
||||
new_service.trigger()
|
||||
|
||||
assert mocked_event.call_count == 1, 'The on_new_service_clicked method should have been called once'
|
||||
|
|
|
@ -34,28 +34,28 @@ class TestStartTimeDialog(TestCase):
|
|||
"""
|
||||
Test StartTimeDialog are defaults correct
|
||||
"""
|
||||
self.assertEqual(self.form.hourSpinBox.minimum(), 0, 'The minimum hour should stay the same as the dialog')
|
||||
self.assertEqual(self.form.hourSpinBox.maximum(), 4, 'The maximum hour should stay the same as the dialog')
|
||||
self.assertEqual(self.form.minuteSpinBox.minimum(), 0,
|
||||
'The minimum minute should stay the same as the dialog')
|
||||
self.assertEqual(self.form.minuteSpinBox.maximum(), 59,
|
||||
'The maximum minute should stay the same as the dialog')
|
||||
self.assertEqual(self.form.secondSpinBox.minimum(), 0,
|
||||
'The minimum second should stay the same as the dialog')
|
||||
self.assertEqual(self.form.secondSpinBox.maximum(), 59,
|
||||
'The maximum second should stay the same as the dialog')
|
||||
self.assertEqual(self.form.hourFinishSpinBox.minimum(), 0,
|
||||
'The minimum finish hour should stay the same as the dialog')
|
||||
self.assertEqual(self.form.hourFinishSpinBox.maximum(), 4,
|
||||
'The maximum finish hour should stay the same as the dialog')
|
||||
self.assertEqual(self.form.minuteFinishSpinBox.minimum(), 0,
|
||||
'The minimum finish minute should stay the same as the dialog')
|
||||
self.assertEqual(self.form.minuteFinishSpinBox.maximum(), 59,
|
||||
'The maximum finish minute should stay the same as the dialog')
|
||||
self.assertEqual(self.form.secondFinishSpinBox.minimum(), 0,
|
||||
'The minimum finish second should stay the same as the dialog')
|
||||
self.assertEqual(self.form.secondFinishSpinBox.maximum(), 59,
|
||||
'The maximum finish second should stay the same as the dialog')
|
||||
self.assertEqual(self.form.hour_spin_box.minimum(), 0, 'The minimum hour should stay the same as the dialog')
|
||||
self.assertEqual(self.form.hour_spin_box.maximum(), 4, 'The maximum hour should stay the same as the dialog')
|
||||
self.assertEqual(self.form.minute_spin_box.minimum(), 0,
|
||||
'The minimum minute should stay the same as the dialog')
|
||||
self.assertEqual(self.form.minute_spin_box.maximum(), 59,
|
||||
'The maximum minute should stay the same as the dialog')
|
||||
self.assertEqual(self.form.second_spin_box.minimum(), 0,
|
||||
'The minimum second should stay the same as the dialog')
|
||||
self.assertEqual(self.form.second_spin_box.maximum(), 59,
|
||||
'The maximum second should stay the same as the dialog')
|
||||
self.assertEqual(self.form.hour_finish_spin_box.minimum(), 0,
|
||||
'The minimum finish hour should stay the same as the dialog')
|
||||
self.assertEqual(self.form.hour_finish_spin_box.maximum(), 4,
|
||||
'The maximum finish hour should stay the same as the dialog')
|
||||
self.assertEqual(self.form.minute_finish_spin_box.minimum(), 0,
|
||||
'The minimum finish minute should stay the same as the dialog')
|
||||
self.assertEqual(self.form.minute_finish_spin_box.maximum(), 59,
|
||||
'The maximum finish minute should stay the same as the dialog')
|
||||
self.assertEqual(self.form.second_finish_spin_box.minimum(), 0,
|
||||
'The minimum finish second should stay the same as the dialog')
|
||||
self.assertEqual(self.form.second_finish_spin_box.maximum(), 59,
|
||||
'The maximum finish second should stay the same as the dialog')
|
||||
|
||||
def time_display_test(self):
|
||||
"""
|
||||
|
@ -75,22 +75,22 @@ class TestStartTimeDialog(TestCase):
|
|||
QtTest.QTest.mouseClick(ok_widget, QtCore.Qt.LeftButton)
|
||||
|
||||
# THEN the following input values are returned
|
||||
self.assertEqual(self.form.hourSpinBox.value(), 0)
|
||||
self.assertEqual(self.form.minuteSpinBox.value(), 1)
|
||||
self.assertEqual(self.form.secondSpinBox.value(), 1)
|
||||
self.assertEqual(self.form.hour_spin_box.value(), 0)
|
||||
self.assertEqual(self.form.minute_spin_box.value(), 1)
|
||||
self.assertEqual(self.form.second_spin_box.value(), 1)
|
||||
self.assertEqual(self.form.item['service_item'].start_time, 61, 'The start time should stay the same')
|
||||
|
||||
# WHEN displaying the UI, changing the time to 2min 3secs and pressing enter
|
||||
self.form.item = {'service_item': mocked_serviceitem}
|
||||
with patch('PyQt4.QtGui.QDialog.exec_'):
|
||||
self.form.exec_()
|
||||
self.form.minuteSpinBox.setValue(2)
|
||||
self.form.secondSpinBox.setValue(3)
|
||||
self.form.minute_spin_box.setValue(2)
|
||||
self.form.second_spin_box.setValue(3)
|
||||
ok_widget = self.form.button_box.button(self.form.button_box.Ok)
|
||||
QtTest.QTest.mouseClick(ok_widget, QtCore.Qt.LeftButton)
|
||||
|
||||
# THEN the following values are returned
|
||||
self.assertEqual(self.form.hourSpinBox.value(), 0)
|
||||
self.assertEqual(self.form.minuteSpinBox.value(), 2)
|
||||
self.assertEqual(self.form.secondSpinBox.value(), 3)
|
||||
self.assertEqual(self.form.hour_spin_box.value(), 0)
|
||||
self.assertEqual(self.form.minute_spin_box.value(), 2)
|
||||
self.assertEqual(self.form.second_spin_box.value(), 3)
|
||||
self.assertEqual(self.form.item['service_item'].start_time, 123, 'The start time should have changed')
|
||||
|
|
|
@ -27,20 +27,22 @@
|
|||
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||
###############################################################################
|
||||
"""
|
||||
Interface tests to test the thememanagerhelper class and related methods.
|
||||
Interface tests to test the themeManager class and related methods.
|
||||
"""
|
||||
import os
|
||||
from unittest import TestCase
|
||||
from tempfile import mkstemp
|
||||
|
||||
from openlp.core.common import Settings
|
||||
from openlp.core.ui import ThemeManagerHelper
|
||||
from PyQt4 import QtGui
|
||||
|
||||
from openlp.core.common import Registry, Settings
|
||||
from openlp.core.ui import ThemeManager
|
||||
from tests.functional import patch, MagicMock
|
||||
|
||||
|
||||
class TestThemeManagerHelper(TestCase):
|
||||
class TestThemeManager(TestCase):
|
||||
"""
|
||||
Test the functions in the ThemeManagerHelp[er module
|
||||
Test the functions in the ThemeManager module
|
||||
"""
|
||||
def setUp(self):
|
||||
"""
|
||||
|
@ -48,8 +50,9 @@ class TestThemeManagerHelper(TestCase):
|
|||
"""
|
||||
fd, self.ini_file = mkstemp('.ini')
|
||||
Settings().set_filename(self.ini_file)
|
||||
self.helper = ThemeManagerHelper()
|
||||
self.helper.settings_section = "themes"
|
||||
self.app = QtGui.QApplication([])
|
||||
Registry.create()
|
||||
self.theme_manager = ThemeManager()
|
||||
|
||||
def tearDown(self):
|
||||
"""
|
||||
|
@ -57,30 +60,31 @@ class TestThemeManagerHelper(TestCase):
|
|||
"""
|
||||
os.unlink(self.ini_file)
|
||||
os.unlink(Settings().fileName())
|
||||
del self.app
|
||||
|
||||
def test_initialise(self):
|
||||
def initialise_test(self):
|
||||
"""
|
||||
Test the thememanagerhelper initialise - basic test
|
||||
Test the thememanager initialise - basic test
|
||||
"""
|
||||
# GIVEN: A new a call to initialise
|
||||
self.theme_manager.build_theme_path = MagicMock()
|
||||
self.theme_manager.load_first_time_themes = MagicMock()
|
||||
Settings().setValue('themes/global theme', 'my_theme')
|
||||
self.helper.build_theme_path = MagicMock()
|
||||
self.helper.load_first_time_themes = MagicMock()
|
||||
|
||||
# WHEN: the initialistion is run
|
||||
self.helper.initialise()
|
||||
self.theme_manager.bootstrap_initialise()
|
||||
|
||||
# THEN:
|
||||
self.assertEqual(1, self.helper.build_theme_path.call_count,
|
||||
self.assertEqual(1, self.theme_manager.build_theme_path.call_count,
|
||||
'The function build_theme_path should have been called')
|
||||
self.assertEqual(1, self.helper.load_first_time_themes.call_count,
|
||||
self.assertEqual(1, self.theme_manager.load_first_time_themes.call_count,
|
||||
'The function load_first_time_themes should have been called only once')
|
||||
self.assertEqual(self.helper.global_theme, 'my_theme',
|
||||
self.assertEqual(self.theme_manager.global_theme, 'my_theme',
|
||||
'The global theme should have been set to my_theme')
|
||||
|
||||
def test_build_theme_path(self):
|
||||
def build_theme_path_test(self):
|
||||
"""
|
||||
Test the thememanagerhelper build_theme_path - basic test
|
||||
Test the thememanager build_theme_path - basic test
|
||||
"""
|
||||
# GIVEN: A new a call to initialise
|
||||
with patch('openlp.core.common.applocation.check_directory_exists') as mocked_check_directory_exists:
|
||||
|
@ -88,12 +92,28 @@ class TestThemeManagerHelper(TestCase):
|
|||
mocked_check_directory_exists.return_value = True
|
||||
Settings().setValue('themes/global theme', 'my_theme')
|
||||
|
||||
self.helper.theme_form = MagicMock()
|
||||
#self.helper.load_first_time_themes = MagicMock()
|
||||
self.theme_manager.theme_form = MagicMock()
|
||||
self.theme_manager.load_first_time_themes = MagicMock()
|
||||
|
||||
# WHEN: the build_theme_path is run
|
||||
self.helper.build_theme_path()
|
||||
self.theme_manager.build_theme_path()
|
||||
|
||||
# THEN:
|
||||
self.assertEqual(self.helper.path, self.helper.theme_form.path,
|
||||
'The theme path and the main path should be the same value')
|
||||
# THEN:
|
||||
assert self.theme_manager.thumb_path.startswith(self.theme_manager.path) is True, \
|
||||
'The thumb path and the main path should start with the same value'
|
||||
|
||||
def click_on_new_theme_test(self):
|
||||
"""
|
||||
Test the on_add_theme event handler is called by the UI
|
||||
"""
|
||||
# GIVEN: An initial form
|
||||
Settings().setValue('themes/global theme', 'my_theme')
|
||||
mocked_event = MagicMock()
|
||||
self.theme_manager.on_add_theme = mocked_event
|
||||
self.theme_manager.setup_ui(self.theme_manager)
|
||||
|
||||
# WHEN displaying the UI and pressing cancel
|
||||
new_theme = self.theme_manager.toolbar.actions['newTheme']
|
||||
new_theme.trigger()
|
||||
|
||||
assert mocked_event.call_count == 1, 'The on_add_theme method should have been called once'
|
Loading…
Reference in New Issue