diff --git a/openlp.pyw b/openlp.pyw index fffed8726..1f819481f 100755 --- a/openlp.pyw +++ b/openlp.pyw @@ -74,15 +74,21 @@ class OpenLP(QtGui.QApplication): #Load and store current Application Version filepath = os.path.split(os.path.abspath(__file__))[0] filepath = os.path.abspath(os.path.join(filepath, u'version.txt')) + fversion = None try: fversion = open(filepath, 'r') for line in fversion: bits = unicode(line).split(u'-') applicationVersion = {u'Full':unicode(line).rstrip(), u'version':bits[0], u'build':bits[1]} + log.info(u'Openlp version %s build %s' % + (applicationVersion[u'version'],applicationVersion[u'build'] )) except: applicationVersion = {u'Full':u'1.9.0-000', u'version':u'1.9.0', u'build':u'000'} + finally: + if fversion: + fversion.close() #set the default string encoding try: sys.setappdefaultencoding(u'utf-8') @@ -127,24 +133,40 @@ def main(): # Set up command line options. usage = u'Usage: %prog [options] [qt-options]' parser = OptionParser(usage=usage) - parser.add_option("-d", "--debug", dest="debug", - action="store_true", help="set logging to DEBUG level") + parser.add_option("-l", "--log-level", dest="loglevel", + default="info", metavar="LEVEL", + help="Set logging to LEVEL level. Valid values are " + "\"debug\", \"info\", \"warning\".") + parser.add_option("-p", "--portable", dest="portable", + action="store_true", + help="Specify if this should be run as a portable app, " + "off a USB flash drive.") + parser.add_option("-s", "--style", dest="style", + help="Set the Qt4 style (passed directly to Qt4).") # Set up logging filename = u'openlp.log' logfile = RotatingFileHandler(filename, maxBytes=200000, backupCount=5) logfile.setFormatter(logging.Formatter( u'%(asctime)s %(name)-15s %(levelname)-8s %(message)s')) log.addHandler(logfile) + logging.addLevelName(15, u'Timer') # Parse command line options and deal with them. (options, args) = parser.parse_args() - if options.debug: + qt_args = [] + if options.loglevel.lower() in ['d', 'debug']: log.setLevel(logging.DEBUG) + elif options.loglevel.lower() in ['w', 'warning']: + log.setLevel(logging.WARNING) else: log.setLevel(logging.INFO) + if options.style: + qt_args.extend(['-style', options.style]) + # Throw the rest of the arguments at Qt, just in case. + qt_args.extend(args) # Initialise the resources qInitResources() # Now create and actually run the application. - app = OpenLP(sys.argv) + app = OpenLP(qt_args) sys.exit(app.run()) if __name__ == u'__main__': diff --git a/openlp/core/lib/__init__.py b/openlp/core/lib/__init__.py index a7223e938..1f3fcd4b8 100644 --- a/openlp/core/lib/__init__.py +++ b/openlp/core/lib/__init__.py @@ -26,11 +26,14 @@ The :mod:`lib` module contains most of the components and libraries that make OpenLP work. """ - +import logging +import os.path import types from PyQt4 import QtCore, QtGui +log = logging.getLogger(__name__) + def translate(context, text): """ A special shortcut method to wrap around the Qt4 translation functions. @@ -47,24 +50,28 @@ def translate(context, text): return QtGui.QApplication.translate( context, text, None, QtGui.QApplication.UnicodeUTF8) -def file_to_xml(xmlfile): +def get_text_file_string(text_file): """ - Open a file and return the contents of the file. + Open a file and return the contents of the file. If the supplied file name + is not a file then the function returns False. If there is an error + loading the file then the function will return None. - ``xmlfile`` + ``textfile`` The name of the file. """ - file = None - xml = None + if not os.path.isfile(text_file): + return False + file_handle = None + content_string = None try: - file = open(xmlfile, u'r') - xml = file.read() + file_handle = open(text_file, u'r') + content_string = file_handle.read() except IOError: - log.exception(u'Failed to open XML file') + log.error(u'Failed to open text file %s' % text_file) finally: - if file: - file.close() - return xml + if file_handle: + file_handle.close() + return content_string def str_to_bool(stringvalue): """ @@ -148,5 +155,5 @@ from rendermanager import RenderManager from mediamanageritem import MediaManagerItem from baselistwithdnd import BaseListWithDnD -__all__ = [ 'translate', 'file_to_xml', 'str_to_bool', - 'contextMenuAction', 'contextMenuSeparator','ServiceItem'] +__all__ = [ 'translate', 'get_text_file_string', 'str_to_bool', + 'contextMenuAction', 'contextMenuSeparator', 'ServiceItem'] diff --git a/openlp/core/lib/eventreceiver.py b/openlp/core/lib/eventreceiver.py index 4072672f4..96b4832e2 100644 --- a/openlp/core/lib/eventreceiver.py +++ b/openlp/core/lib/eventreceiver.py @@ -129,16 +129,16 @@ class EventReceiver(QtCore.QObject): class Receiver(): """ - Class to allow events to be passed from different parts of the - system. This is a static wrapper around the ``EventReceiver`` - class. As there is only one instance of it in the system the QT - signal/slot architecture can send messages across the system. + Class to allow events to be passed from different parts of the system. This + is a static wrapper around the ``EventReceiver`` class. As there is only + one instance of it in the system the Qt4 signal/slot architecture can send + messages across the system. To send a message: - ``Receiver().send_message(u'<>', data)`` + ``Receiver.send_message(u'<>', data)`` To receive a Message - ``QtCore.QObject.connect(Receiver().get_receiver(), QtCore.SIGNAL(u'<>'), <>)`` + ``QtCore.QObject.connect(Receiver.get_receiver(), QtCore.SIGNAL(u'<>'), <>)`` """ eventreceiver = EventReceiver() diff --git a/openlp/core/lib/plugin.py b/openlp/core/lib/plugin.py index dba8666e3..38302cea1 100644 --- a/openlp/core/lib/plugin.py +++ b/openlp/core/lib/plugin.py @@ -139,14 +139,6 @@ class Plugin(QtCore.QObject): """ return True - def can_be_disabled(self): - """ - Indicates whether the plugin can be disabled by the plugin list. - - Returns True or False. - """ - return False - def set_status(self): """ Sets the status of the plugin diff --git a/openlp/core/lib/pluginmanager.py b/openlp/core/lib/pluginmanager.py index 4ac23e6a2..60643a588 100644 --- a/openlp/core/lib/pluginmanager.py +++ b/openlp/core/lib/pluginmanager.py @@ -105,10 +105,7 @@ class PluginManager(object): for plugin in plugins_list: if plugin.check_pre_conditions(): log.debug(u'Plugin %s active', unicode(plugin.name)) - if plugin.can_be_disabled(): - plugin.set_status() - else: - plugin.status = PluginStatus.Active + plugin.set_status() else: plugin.status = PluginStatus.Disabled self.plugins.append(plugin) diff --git a/openlp/core/lib/renderer.py b/openlp/core/lib/renderer.py index 4ca653303..b8d9b7172 100644 --- a/openlp/core/lib/renderer.py +++ b/openlp/core/lib/renderer.py @@ -168,35 +168,45 @@ class Renderer(object): line_width = self._rect.width() - self._right_margin #number of lines on a page - adjust for rounding up. page_length = int(self._rect.height() / metrics.height() - 2 ) - 1 + #Average number of characters in line ave_line_width = line_width / metrics.averageCharWidth() - ave_line_width = int(ave_line_width + (ave_line_width * 1)) + #Maximum size of a character + max_char_width = metrics.maxWidth() + #Min size of a character + min_char_width = metrics.width(u'i') + char_per_line = line_width / min_char_width log.debug(u'Page Length area height %s , metrics %s , lines %s' % (int(self._rect.height()), metrics.height(), page_length )) split_pages = [] page = [] split_lines = [] + count = 0 for line in text: #Must be a blank line so keep it. if len(line) == 0: line = u' ' while len(line) > 0: - if len(line) > ave_line_width: - pos = line.find(u' ', ave_line_width) - split_text = line[:pos] - else: - pos = len(line) - split_text = line - while metrics.width(split_text, -1) > line_width: - #Find the next space to the left - pos = line[:pos].rfind(u' ') - #no more spaces found - if pos == 0: - split_text = line + pos = char_per_line + split_text = line[:pos] + #line needs splitting + if metrics.width(split_text, -1) > line_width: + #We have no spaces + if split_text.find(u' ') == -1: + #Move back 1 char at a time till it fits while metrics.width(split_text, -1) > line_width: split_text = split_text[:-1] - pos = len(split_text) + pos = len(split_text) else: - split_text = line[:pos] + #We have spaces so split at previous one + while metrics.width(split_text, -1) > line_width: + pos = split_text.rfind(u' ') + #no more spaces and we are still too long + if pos == -1: + while metrics.width(split_text, -1) > line_width: + split_text = split_text[:-1] + pos = len(split_text) + else: + split_text = line[:pos] split_lines.append(split_text) line = line[pos:].lstrip() #if we have more text add up to 10 spaces on the front. @@ -450,32 +460,32 @@ class Renderer(object): draw=True, color = self._theme.display_shadow_color) if self._theme.display_outline: self._get_extent_and_render(line, footer, - (x+self._outline_offset, y), draw=True, + (x + self._outline_offset, y), draw=True, color = self._theme.display_outline_color) self._get_extent_and_render(line, footer, - (x, y+self._outline_offset), draw=True, + (x, y + self._outline_offset), draw=True, color = self._theme.display_outline_color) self._get_extent_and_render(line, footer, - (x, y-self._outline_offset), draw=True, + (x, y - self._outline_offset), draw=True, color = self._theme.display_outline_color) self._get_extent_and_render(line, footer, - (x-self._outline_offset, y), draw=True, + (x - self._outline_offset, y), draw=True, color = self._theme.display_outline_color) if self._outline_offset > 1: self._get_extent_and_render(line, footer, - (x+self._outline_offset, y+self._outline_offset), + (x + self._outline_offset, y + self._outline_offset), draw=True, color = self._theme.display_outline_color) self._get_extent_and_render(line, footer, - (x-self._outline_offset, y+self._outline_offset), + (x - self._outline_offset, y + self._outline_offset), draw=True, color = self._theme.display_outline_color) self._get_extent_and_render(line, footer, - (x+self._outline_offset, y-self._outline_offset), + (x + self._outline_offset, y - self._outline_offset), draw=True, color = self._theme.display_outline_color) self._get_extent_and_render(line, footer, - (x-self._outline_offset, y-self._outline_offset), + (x - self._outline_offset, y - self._outline_offset), draw=True, color = self._theme.display_outline_color) self._get_extent_and_render(line, footer,tlcorner=(x, y), diff --git a/openlp/core/lib/rendermanager.py b/openlp/core/lib/rendermanager.py index d4463ea21..034abff49 100644 --- a/openlp/core/lib/rendermanager.py +++ b/openlp/core/lib/rendermanager.py @@ -193,17 +193,17 @@ class RenderManager(object): self.renderer.set_theme(themedata) self.build_text_rectangle(themedata) self.renderer.set_frame_dest(self.width, self.height, True) - verse = [] - verse.append(u'Amazing Grace!') - verse.append(u'How sweet the sound') - verse.append(u'To save a wretch like me;') - verse.append(u'I once was lost but now am found,') - verse.append(u'Was blind, but now I see.') + verse = u'Amazing Grace!\n'\ + 'How sweet the sound\n'\ + 'To save a wretch like me;\n'\ + 'I once was lost but now am found,\n'\ + 'Was blind, but now I see.' footer = [] footer.append(u'Amazing Grace (John Newton)' ) footer.append(u'Public Domain') footer.append(u'CCLI xxx') - return self.renderer.generate_frame_from_lines(verse, footer) + formatted = self.renderer.format_slide(verse, False) + return self.renderer.generate_frame_from_lines(formatted[0], footer) def format_slide(self, words): """ @@ -253,7 +253,7 @@ class RenderManager(object): newImage = QtGui.QImage(w, h, QtGui.QImage.Format_ARGB32_Premultiplied) newImage.fill(QtCore.Qt.black) painter = QtGui.QPainter(newImage) - painter.drawImage((w-realw) / 2, (h-realh) / 2, preview) + painter.drawImage((w - realw) / 2, (h - realh) / 2, preview) return newImage def calculate_default(self, screen): diff --git a/openlp/core/lib/serviceitem.py b/openlp/core/lib/serviceitem.py index db147b786..27e16e95f 100644 --- a/openlp/core/lib/serviceitem.py +++ b/openlp/core/lib/serviceitem.py @@ -29,7 +29,7 @@ import uuid from PyQt4 import QtGui -from openlp.core.lib import buildIcon +from openlp.core.lib import buildIcon, Receiver class ServiceItemType(object): """ @@ -38,7 +38,6 @@ class ServiceItemType(object): Text = 1 Image = 2 Command = 3 - Video = 4 class ServiceItem(object): """ @@ -50,31 +49,28 @@ class ServiceItem(object): log = logging.getLogger(u'ServiceItem') log.info(u'Service Item created') - def __init__(self, hostplugin=None): + def __init__(self, plugin=None): """ Set up the service item. - ``hostplugin`` + ``plugin`` The plugin that this service item belongs to. """ - self.plugin = hostplugin - if hostplugin: - self.RenderManager = self.plugin.render_manager - self.shortname = hostplugin.name - self.name = self.plugin.name + if plugin: + self.RenderManager = plugin.render_manager + self.name = plugin.name self.title = u'' self.audit = u'' self.items = [] self.iconic_representation = None - self.raw_slides = None - self.frames = [] self.raw_footer = None self.theme = None self.service_item_path = None self.service_item_type = None - self.editEnabled = False - self.service_frames = [] - self.uuid = unicode(uuid.uuid1()) + self.edit_enabled = False + self._raw_frames = [] + self._display_frames = [] + self._uuid = unicode(uuid.uuid1()) def addIcon(self, icon): """ @@ -93,32 +89,27 @@ class ServiceItem(object): The render method is what generates the frames for the screen. """ log.debug(u'Render called') - self.frames = [] + self._display_frames = [] if self.service_item_type == ServiceItemType.Text: log.debug(u'Formatting slides') if self.theme is None: self.RenderManager.set_override_theme(None) else: self.RenderManager.set_override_theme(self.theme) - for slide in self.service_frames: + for slide in self._raw_frames: before = time.time() formated = self.RenderManager.format_slide(slide[u'raw_slide']) for format in formated: - frame = None lines = u'' for line in format: lines += line + u'\n' title = lines.split(u'\n')[0] - self.frames.append({u'title': title, u'text': lines, - u'image': frame}) - log.info(u'Formatting took %4s' % (time.time() - before)) - elif self.service_item_type == ServiceItemType.Command: - self.frames = self.service_frames + self._display_frames.append({u'title': title, u'text': lines}) + log.log(15, u'Formatting took %4s' % (time.time() - before)) elif self.service_item_type == ServiceItemType.Image: - for slide in self.service_frames: + for slide in self._raw_frames: slide[u'image'] = \ self.RenderManager.resize_image(slide[u'image']) - self.frames = self.service_frames else: log.error(u'Invalid value renderer :%s' % self.service_item_type) @@ -133,19 +124,19 @@ class ServiceItem(object): self.RenderManager.set_override_theme(None) else: self.RenderManager.set_override_theme(self.theme) - format = self.frames[row][u'text'].split(u'\n') + format = self._display_frames[row][u'text'].split(u'\n') frame = self.RenderManager.generate_slide(format, self.raw_footer) return frame - def add_from_image(self, path, frame_title, image): + def add_from_image(self, path, title, image): """ Add an image slide to the service item. ``path`` The directory in which the image file is located. - ``frame_title`` + ``title`` A title for the slide in the service item. ``image`` @@ -153,16 +144,10 @@ class ServiceItem(object): """ self.service_item_type = ServiceItemType.Image self.service_item_path = path - self.service_frames.append( - {u'title': frame_title, u'text': None, u'image': image}) + self._raw_frames.append( + {u'title': title, u'image': image}) - def add_from_media(self, path, frame_title, image): - self.service_item_type = ServiceItemType.Video - self.service_item_path = path - self.service_frames.append( - {u'title': frame_title, u'text': None, u'image': image}) - - def add_from_text(self, frame_title, raw_slide): + def add_from_text(self, title, raw_slide): """ Add a text slide to the service item. @@ -173,24 +158,27 @@ class ServiceItem(object): The raw text of the slide. """ self.service_item_type = ServiceItemType.Text - frame_title = frame_title.split(u'\n')[0] - self.service_frames.append( - {u'title': frame_title, u'raw_slide': raw_slide}) + title = title.split(u'\n')[0] + self._raw_frames.append( + {u'title': title, u'raw_slide': raw_slide}) - def add_from_command(self, path, frame_title, image): + def add_from_command(self, path, file_name, image): """ Add a slide from a command. - ``frame_title`` + ``path`` The title of the slide in the service item. - ``command`` + ``file_name`` + The title of the slide in the service item. + + ``image`` The command of/for the slide. """ self.service_item_type = ServiceItemType.Command self.service_item_path = path - self.service_frames.append( - {u'title': frame_title, u'command': None, u'text':None, u'image': image}) + self._raw_frames.append( + {u'title': file_name, u'image': image}) def get_service_repr(self): """ @@ -199,7 +187,7 @@ class ServiceItem(object): """ service_header = { u'name': self.name.lower(), - u'plugin': self.shortname, + u'plugin': self.name, u'theme':self.theme, u'title':self.title, u'icon':self.icon, @@ -209,17 +197,14 @@ class ServiceItem(object): } service_data = [] if self.service_item_type == ServiceItemType.Text: - for slide in self.service_frames: + for slide in self._raw_frames: service_data.append(slide) elif self.service_item_type == ServiceItemType.Image: - for slide in self.service_frames: + for slide in self._raw_frames: service_data.append(slide[u'title']) elif self.service_item_type == ServiceItemType.Command: - for slide in self.service_frames: - service_data.append(slide[u'title']) - elif self.service_item_type == ServiceItemType.Video: - for slide in self.service_frames: - service_data.append(slide[u'title']) + for slide in self._raw_frames: + service_data.append({u'title':slide[u'title'], u'image':slide[u'image']}) return {u'header': service_header, u'data': service_data} def set_from_service(self, serviceitem, path=None): @@ -244,7 +229,7 @@ class ServiceItem(object): self.audit = header[u'audit'] if self.service_item_type == ServiceItemType.Text: for slide in serviceitem[u'serviceitem'][u'data']: - self.service_frames.append(slide) + self._raw_frames.append(slide) elif self.service_item_type == ServiceItemType.Image: for text_image in serviceitem[u'serviceitem'][u'data']: filename = os.path.join(path, text_image) @@ -252,7 +237,68 @@ class ServiceItem(object): self.add_from_image(path, text_image, real_image) elif self.service_item_type == ServiceItemType.Command: for text_image in serviceitem[u'serviceitem'][u'data']: - filename = os.path.join(path, text_image) - self.add_from_command(path, text_image) - elif self.service_item_type == ServiceItemType.Video: - pass + filename = os.path.join(path, text_image[u'title']) + self.add_from_command(path, text_image[u'title'], text_image[u'image'] ) + + def merge(self, other): + """ + Updates the _uuid with the value from the original one + The _uuid is unique for a give service item but this allows one to + replace an original version. + """ + self._uuid = other._uuid + + def __eq__(self, other): + """ + Confirms the service items are for the same instance + """ + if not other: + return False + return self._uuid == other._uuid + + def __ne__(self, other): + """ + Confirms the service items are not for the same instance + """ + return self._uuid != other._uuid + + def is_song(self): + return self.name == u'Songs' + + def is_media(self): + return self.name.lower() == u'media' + + def is_command(self): + return self.service_item_type == ServiceItemType.Command + + def is_image(self): + return self.service_item_type == ServiceItemType.Image + + def is_text(self): + return self.service_item_type == ServiceItemType.Text + + def get_frames(self): + if self.service_item_type == ServiceItemType.Text: + return self._display_frames + else: + return self._raw_frames + + def get_rendered_frame(self, row): + """ + Returns the correct frame for a given list and + renders it if required. + """ + if self.service_item_type == ServiceItemType.Text: + return self.render_individual(row) + else: + return self._raw_frames[row][u'image'] + + def get_frame_title(self, row=0): + """ + Returns the title of the raw frame + """ + return self._raw_frames[row][u'title'] + + def request_audit(self): + if self.audit: + Receiver.send_message(u'songusage_live', self.audit) diff --git a/openlp/core/lib/settingsmanager.py b/openlp/core/lib/settingsmanager.py index 43f148ffd..944f4161c 100644 --- a/openlp/core/lib/settingsmanager.py +++ b/openlp/core/lib/settingsmanager.py @@ -45,7 +45,6 @@ class SettingsManager(object): else: self.mainwindow_left = mainwindow_docbars self.mainwindow_right = mainwindow_docbars - self.slidecontroller = (self.width - ( self.mainwindow_left + self.mainwindow_right) - 100 ) / 2 self.slidecontroller_image = self.slidecontroller - 50 @@ -60,7 +59,7 @@ class SettingsManager(object): u'user interface', u'display previewpanel', True)) def setUIItemVisibility(self, item=u'', isVisible=True): - if item != u'': + if item: if item == u'ThemeManagerDock': ConfigHelper.set_config(u'user interface', u'display thememanager', isVisible) diff --git a/openlp/core/lib/songxmlhandler.py b/openlp/core/lib/songxmlhandler.py index 4543464bb..37bbe25e4 100644 --- a/openlp/core/lib/songxmlhandler.py +++ b/openlp/core/lib/songxmlhandler.py @@ -134,7 +134,8 @@ class SongXMLParser(object): The XML of the song to be parsed. """ try: - self.song_xml = ElementTree(element=XML(xml)) + self.song_xml = ElementTree( + element=XML(unicode(xml).encode('unicode-escape'))) except: log.exception(u'Invalid xml %s', xml) @@ -147,7 +148,8 @@ class SongXMLParser(object): verse_list = [] for element in iter: if element.tag == u'verse': - verse_list.append([element.attrib, element.text]) + verse_list.append([element.attrib, + unicode(element.text).decode('unicode-escape')]) return verse_list def dump_xml(self): diff --git a/openlp/core/lib/themexmlhandler.py b/openlp/core/lib/themexmlhandler.py index 0501fb06f..1ffece78b 100644 --- a/openlp/core/lib/themexmlhandler.py +++ b/openlp/core/lib/themexmlhandler.py @@ -30,7 +30,7 @@ from xml.etree.ElementTree import ElementTree, XML from openlp.core.lib import str_to_bool blankthemexml=\ -''' +''' BlankStyle @@ -348,6 +348,7 @@ class ThemeXML(object): iter = theme_xml.getiterator() master = u'' for element in iter: + element.text = unicode(element.text).decode('unicode-escape') if len(element.getchildren()) > 0: master = element.tag + u'_' else: diff --git a/openlp/core/lib/toolbar.py b/openlp/core/lib/toolbar.py index 8ab0e1961..564a30db7 100644 --- a/openlp/core/lib/toolbar.py +++ b/openlp/core/lib/toolbar.py @@ -46,7 +46,7 @@ class OpenLPToolbar(QtGui.QToolBar): self.log.debug(u'Init done') def addToolbarButton(self, title, icon, tooltip=None, slot=None, - objectname=None): + checkable=False): """ A method to help developers easily add a button to the toolbar. @@ -69,14 +69,19 @@ class OpenLPToolbar(QtGui.QToolBar): """ ButtonIcon = buildIcon(icon) if ButtonIcon: - if slot: + if slot and not checkable: ToolbarButton = self.addAction(ButtonIcon, title, slot) else: ToolbarButton = self.addAction(ButtonIcon, title) if tooltip: ToolbarButton.setToolTip(tooltip) + if checkable: + ToolbarButton.setCheckable(True) + QtCore.QObject.connect(ToolbarButton, + QtCore.SIGNAL(u'toggled(bool)'), slot) self.icons[title] = ButtonIcon self.actions[title] = ToolbarButton + return ToolbarButton def addToolbarSeparator(self, handle): """ diff --git a/openlp/core/ui/amendthemeform.py b/openlp/core/ui/amendthemeform.py index 533f49691..2ba1872ad 100644 --- a/openlp/core/ui/amendthemeform.py +++ b/openlp/core/ui/amendthemeform.py @@ -28,7 +28,7 @@ import os.path from PyQt4 import QtCore, QtGui -from openlp.core.lib import ThemeXML, file_to_xml +from openlp.core.lib import ThemeXML from amendthemedialog import Ui_AmendThemeDialog log = logging.getLogger(u'AmendThemeForm') @@ -126,7 +126,7 @@ class AmendThemeForm(QtGui.QDialog, Ui_AmendThemeDialog): def accept(self): new_theme = ThemeXML() theme_name = unicode(self.ThemeNameEdit.displayText()) - new_theme.new_document(theme_name) + new_theme.new_document(theme_name.encode('unicode-escape')) save_from = None save_to = None if self.theme.background_mode == u'transparent': @@ -144,7 +144,7 @@ class AmendThemeForm(QtGui.QDialog, Ui_AmendThemeDialog): (path, filename) = \ os.path.split(unicode(self.theme.background_filename)) new_theme.add_background_image(filename) - save_to= os.path.join(self.path, theme_name, filename ) + save_to = os.path.join(self.path, theme_name, filename) save_from = self.theme.background_filename new_theme.add_font(unicode(self.theme.font_main_name), @@ -184,33 +184,26 @@ class AmendThemeForm(QtGui.QDialog, Ui_AmendThemeDialog): def loadTheme(self, theme): log.debug(u'LoadTheme %s', theme) - if theme is None: - self.theme.parse(self.thememanager.baseTheme()) - else: - xml_file = os.path.join(self.path, theme, theme + u'.xml') - xml = file_to_xml(xml_file) - self.theme.parse(xml) - self.theme.extend_image_filename(self.path) - self.thememanager.cleanTheme(self.theme) + self.theme = theme # Stop the initial screen setup generating 1 preview per field! self.allowPreview = False self.paintUi(self.theme) self.allowPreview = True - self.previewTheme(self.theme) + self.previewTheme() def onImageToolButtonClicked(self): filename = QtGui.QFileDialog.getOpenFileName( self, self.trUtf8(u'Open file')) - if filename != u'': + if filename: self.ImageLineEdit.setText(filename) self.theme.background_filename = filename - self.previewTheme(self.theme) + self.previewTheme() # #Main Font Tab # def onFontMainComboBoxSelected(self): self.theme.font_main_name = self.FontMainComboBox.currentFont().family() - self.previewTheme(self.theme) + self.previewTheme() def onFontMainWeightComboBoxSelected(self, value): if value == 0: @@ -225,7 +218,7 @@ class AmendThemeForm(QtGui.QDialog, Ui_AmendThemeDialog): else: self.theme.font_main_weight = u'Bold' self.theme.font_main_italics = True - self.previewTheme(self.theme) + self.previewTheme() def onFontMainColorPushButtonClicked(self): self.theme.font_main_color = QtGui.QColorDialog.getColor( @@ -233,12 +226,12 @@ class AmendThemeForm(QtGui.QDialog, Ui_AmendThemeDialog): self.FontMainColorPushButton.setStyleSheet( u'background-color: %s' % unicode(self.theme.font_main_color)) - self.previewTheme(self.theme) + self.previewTheme() def onFontMainSizeSpinBoxChanged(self): if self.theme.font_main_proportion != self.FontMainSizeSpinBox.value(): self.theme.font_main_proportion = self.FontMainSizeSpinBox.value() - self.previewTheme(self.theme) + self.previewTheme() def onFontMainDefaultCheckBoxChanged(self, value): if value == 2: # checked @@ -259,41 +252,41 @@ class AmendThemeForm(QtGui.QDialog, Ui_AmendThemeDialog): self.FontMainLineSpacingSpinBox.setValue( self.theme.font_main_indentation) self.stateChanging(self.theme) - self.previewTheme(self.theme) + self.previewTheme() def onFontMainXSpinBoxChanged(self): if self.theme.font_main_x != self.FontMainXSpinBox.value(): self.theme.font_main_x = self.FontMainXSpinBox.value() - self.previewTheme(self.theme) + self.previewTheme() def onFontMainYSpinBoxChanged(self): if self.theme.font_main_y != self.FontMainYSpinBox.value(): self.theme.font_main_y = self.FontMainYSpinBox.value() - self.previewTheme(self.theme) + self.previewTheme() def onFontMainWidthSpinBoxChanged(self): if self.theme.font_main_width != self.FontMainWidthSpinBox.value(): self.theme.font_main_width = self.FontMainWidthSpinBox.value() - self.previewTheme(self.theme) + self.previewTheme() def onFontMainLineSpacingSpinBoxChanged(self): if self.theme.font_main_indentation != \ self.FontMainLineSpacingSpinBox.value(): self.theme.font_main_indentation = \ self.FontMainLineSpacingSpinBox.value() - self.previewTheme(self.theme) + self.previewTheme() def onFontMainHeightSpinBoxChanged(self): if self.theme.font_main_height != self.FontMainHeightSpinBox.value(): self.theme.font_main_height = self.FontMainHeightSpinBox.value() - self.previewTheme(self.theme) + self.previewTheme() # #Footer Font Tab # def onFontFooterComboBoxSelected(self): self.theme.font_footer_name = \ self.FontFooterComboBox.currentFont().family() - self.previewTheme(self.theme) + self.previewTheme() def onFontFooterWeightComboBoxSelected(self, value): if value == 0: @@ -308,22 +301,21 @@ class AmendThemeForm(QtGui.QDialog, Ui_AmendThemeDialog): else: self.theme.font_footer_weight = u'Bold' self.theme.font_footer_italics = True - self.previewTheme(self.theme) + self.previewTheme() def onFontFooterColorPushButtonClicked(self): self.theme.font_footer_color = QtGui.QColorDialog.getColor( QtGui.QColor(self.theme.font_footer_color), self).name() - self.FontFooterColorPushButton.setStyleSheet( 'background-color: %s' % unicode(self.theme.font_footer_color)) - self.previewTheme(self.theme) + self.previewTheme() def onFontFooterSizeSpinBoxChanged(self): if self.theme.font_footer_proportion != \ self.FontFooterSizeSpinBox.value(): self.theme.font_footer_proportion = \ self.FontFooterSizeSpinBox.value() - self.previewTheme(self.theme) + self.previewTheme() def onFontFooterDefaultCheckBoxChanged(self, value): if value == 2: # checked @@ -343,29 +335,29 @@ class AmendThemeForm(QtGui.QDialog, Ui_AmendThemeDialog): self.FontFooterHeightSpinBox.setValue( self.theme.font_footer_height) self.stateChanging(self.theme) - self.previewTheme(self.theme) + self.previewTheme() def onFontFooterXSpinBoxChanged(self): if self.theme.font_footer_x != self.FontFooterXSpinBox.value(): self.theme.font_footer_x = self.FontFooterXSpinBox.value() - self.previewTheme(self.theme) + self.previewTheme() def onFontFooterYSpinBoxChanged(self): if self.theme.font_footer_y != self.FontFooterYSpinBox.value(): self.theme.font_footer_y = self.FontFooterYSpinBox.value() - self.previewTheme(self.theme) + self.previewTheme() def onFontFooterWidthSpinBoxChanged(self): if self.theme.font_footer_width != self.FontFooterWidthSpinBox.value(): self.theme.font_footer_width = self.FontFooterWidthSpinBox.value() - self.previewTheme(self.theme) + self.previewTheme() def onFontFooterHeightSpinBoxChanged(self): if self.theme.font_footer_height != \ self.FontFooterHeightSpinBox.value(): self.theme.font_footer_height = \ self.FontFooterHeightSpinBox.value() - self.previewTheme(self.theme) + self.previewTheme() # #Background Tab # @@ -379,7 +371,7 @@ class AmendThemeForm(QtGui.QDialog, Ui_AmendThemeDialog): else: self.theme.background_mode = u'transparent' self.stateChanging(self.theme) - self.previewTheme(self.theme) + self.previewTheme() def onBackgroundTypeComboBoxSelected(self, currentIndex): self.setBackground(currentIndex, self.GradientComboBox.currentIndex()) @@ -404,7 +396,7 @@ class AmendThemeForm(QtGui.QDialog, Ui_AmendThemeDialog): else: self.theme.background_type = u'image' self.stateChanging(self.theme) - self.previewTheme(self.theme) + self.previewTheme() def onColor1PushButtonClicked(self): if self.theme.background_type == u'solid': @@ -419,14 +411,14 @@ class AmendThemeForm(QtGui.QDialog, Ui_AmendThemeDialog): u'background-color: %s' % \ unicode(self.theme.background_startColor)) - self.previewTheme(self.theme) + self.previewTheme() def onColor2PushButtonClicked(self): self.theme.background_endColor = QtGui.QColorDialog.getColor( QtGui.QColor(self.theme.background_endColor), self).name() self.Color2PushButton.setStyleSheet( u'background-color: %s' % unicode(self.theme.background_endColor)) - self.previewTheme(self.theme) + self.previewTheme() # #Other Tab # @@ -436,14 +428,14 @@ class AmendThemeForm(QtGui.QDialog, Ui_AmendThemeDialog): else: self.theme.display_outline = False self.stateChanging(self.theme) - self.previewTheme(self.theme) + self.previewTheme() def onOutlineColorPushButtonClicked(self): self.theme.display_outline_color = QtGui.QColorDialog.getColor( QtGui.QColor(self.theme.display_outline_color), self).name() self.OutlineColorPushButton.setStyleSheet( u'background-color: %s' % unicode(self.theme.display_outline_color)) - self.previewTheme(self.theme) + self.previewTheme() def onShadowCheckBoxChanged(self, value): if value == 2: # checked @@ -451,24 +443,24 @@ class AmendThemeForm(QtGui.QDialog, Ui_AmendThemeDialog): else: self.theme.display_shadow = False self.stateChanging(self.theme) - self.previewTheme(self.theme) + self.previewTheme() def onShadowColorPushButtonClicked(self): self.theme.display_shadow_color = QtGui.QColorDialog.getColor( QtGui.QColor(self.theme.display_shadow_color), self).name() self.ShadowColorPushButton.setStyleSheet( u'background-color: %s' % unicode(self.theme.display_shadow_color)) - self.previewTheme(self.theme) + self.previewTheme() def onHorizontalComboBoxSelected(self, currentIndex): self.theme.display_horizontalAlign = currentIndex self.stateChanging(self.theme) - self.previewTheme(self.theme) + self.previewTheme() def onVerticalComboBoxSelected(self, currentIndex): self.theme.display_verticalAlign = currentIndex self.stateChanging(self.theme) - self.previewTheme(self.theme) + self.previewTheme() # #Local Methods # @@ -661,18 +653,10 @@ class AmendThemeForm(QtGui.QDialog, Ui_AmendThemeDialog): else: self.ShadowColorPushButton.setEnabled(False) - def previewTheme(self, theme): + def previewTheme(self): if self.allowPreview: #calculate main number of rows - main_weight = 50 - if self.theme.font_main_weight == u'Bold': - main_weight = 75 - mainFont = QtGui.QFont(self.theme.font_main_name, - self.theme.font_main_proportion, # size - main_weight, # weight - self.theme.font_main_italics)# italic - mainFont.setPixelSize(self.theme.font_main_proportion) - metrics = QtGui.QFontMetrics(mainFont) + metrics = self._getThemeMetrics() page_length = \ (self.FontMainHeightSpinBox.value() / metrics.height() - 2) - 1 log.debug(u'Page Length area height %s, metrics %s, lines %s' % @@ -680,6 +664,22 @@ class AmendThemeForm(QtGui.QDialog, Ui_AmendThemeDialog): page_length)) page_length_text = unicode(self.trUtf8(u'Slide Height is %s rows')) self.FontMainLinesPageLabel.setText(page_length_text % page_length) - frame = self.thememanager.generateImage(theme) + #a=c + frame = self.thememanager.generateImage(self.theme) self.ThemePreview.setPixmap(QtGui.QPixmap.fromImage(frame)) + def _getThemeMetrics(self): + main_weight = 50 + if self.theme.font_main_weight == u'Bold': + main_weight = 75 + mainFont = QtGui.QFont(self.theme.font_main_name, + self.theme.font_main_proportion, # size + main_weight, # weight + self.theme.font_main_italics)# italic + mainFont.setPixelSize(self.theme.font_main_proportion) + metrics = QtGui.QFontMetrics(mainFont) + #Validate that the screen width is big enough to display the text + if self.theme.font_main_width < metrics.maxWidth() * 2 + 64: + self.theme.font_main_width = metrics.maxWidth() * 2 + 64 + self.FontMainWidthSpinBox.setValue(self.theme.font_main_width) + return metrics diff --git a/openlp/core/ui/maindisplay.py b/openlp/core/ui/maindisplay.py index 705bc75ae..a387deaef 100644 --- a/openlp/core/ui/maindisplay.py +++ b/openlp/core/ui/maindisplay.py @@ -28,9 +28,9 @@ import os from PyQt4 import QtCore, QtGui from PyQt4.phonon import Phonon -from openlp.core.lib import Receiver, str_to_bool +from openlp.core.lib import Receiver -class DisplayLabel(QtGui.QWidget): +class DisplayWidget(QtGui.QWidget): """ Customised version of QTableWidget which can respond to keyboard events. @@ -40,23 +40,23 @@ class DisplayLabel(QtGui.QWidget): log.info(u'MainDisplay loaded') def __init__(self, parent=None, name=None): - QQtGui.QWidget.__init__(self, parent) + QtGui.QWidget.__init__(self, parent) self.parent = parent def keyPressEvent(self, event): if type(event) == QtGui.QKeyEvent: #here accept the event and do something if event.key() == QtCore.Qt.Key_Up: - Receiver().send_message(u'slidecontroller_previous') + Receiver.send_message(u'live_slidecontroller_previous') event.accept() elif event.key() == QtCore.Qt.Key_Down: - Receiver().send_message(u'slidecontroller_next') + Receiver.send_message(u'live_slidecontroller_next') event.accept() elif event.key() == QtCore.Qt.Key_PageUp: - Receiver().send_message(u'slidecontroller_first') + Receiver.send_message(u'live_slidecontroller_first') event.accept() elif event.key() == QtCore.Qt.Key_PageDown: - Receiver().send_message(u'slidecontroller_last') + Receiver.send_message(u'live_slidecontroller_last') event.accept() elif event.key() == QtCore.Qt.Key_Escape: self.resetDisplay() @@ -65,7 +65,7 @@ class DisplayLabel(QtGui.QWidget): else: event.ignore() -class MainDisplay(DisplayLabel): +class MainDisplay(DisplayWidget): """ This is the form that is used to display things on the projector. """ @@ -84,7 +84,7 @@ class MainDisplay(DisplayLabel): The list of screens. """ log.debug(u'Initilisation started') - QtGui.QWidget.__init__(self, None) + DisplayWidget.__init__(self, None) self.parent = parent self.setWindowTitle(u'OpenLP Display') self.screens = screens @@ -107,18 +107,15 @@ class MainDisplay(DisplayLabel): self.blankFrame = None self.frame = None self.alertactive = False - self.alertTab = None self.timer_id = 0 self.firstTime = True self.mediaLoaded = False - QtCore.QObject.connect(Receiver.get_receiver(), - QtCore.SIGNAL(u'live_slide_blank'), self.blankDisplay) QtCore.QObject.connect(Receiver.get_receiver(), QtCore.SIGNAL(u'alert_text'), self.displayAlert) QtCore.QObject.connect(Receiver.get_receiver(), - QtCore.SIGNAL(u'presentations_start'), self.hideDisplay) + QtCore.SIGNAL(u'live_slide_hide'), self.hideDisplay) QtCore.QObject.connect(Receiver.get_receiver(), - QtCore.SIGNAL(u'presentations_stop'), self.showDisplay) + QtCore.SIGNAL(u'live_slide_show'), self.showDisplay) QtCore.QObject.connect(self.mediaObject, QtCore.SIGNAL(u'finished()'), self.onMediaFinish) QtCore.QObject.connect(Receiver.get_receiver(), @@ -130,13 +127,13 @@ class MainDisplay(DisplayLabel): QtCore.QObject.connect(Receiver.get_receiver(), QtCore.SIGNAL(u'media_stop'), self.onMediaStop) - def setup(self, screenNumber): """ Sets up the screen on a particular screen. @param (integer) screen This is the screen number. """ - log.debug(u'Setup %s for %s ' %(self.screens, screenNumber) ) + log.debug(u'Setup %s for %s ' %(self.screens, screenNumber)) + self.setVisible(False) screen = self.screens[screenNumber] if screen[u'number'] != screenNumber: # We will most probably never actually hit this bit, but just in @@ -147,26 +144,19 @@ class MainDisplay(DisplayLabel): screen = scrn break self.setGeometry(screen[u'size']) - if not screen[u'primary']: - self.showFullScreen() - self.primary = False - else: - self.setVisible(False) - self.primary = True #Build a custom splash screen - if str_to_bool(self.parent.generalConfig.get_config(u'show splash', u'True')): - self.InitialFrame = QtGui.QImage( - screen[u'size'].width(), screen[u'size'].height(), - QtGui.QImage.Format_ARGB32_Premultiplied) - splash_image = QtGui.QImage(u':/graphics/openlp-splash-screen.png') - painter_image = QtGui.QPainter() - painter_image.begin(self.InitialFrame) - painter_image.fillRect(self.InitialFrame.rect(), QtCore.Qt.white) - painter_image.drawImage( - (screen[u'size'].width() - splash_image.width()) / 2, - (screen[u'size'].height() - splash_image.height()) / 2, - splash_image) - self.frameView(self.InitialFrame) + self.InitialFrame = QtGui.QImage( + screen[u'size'].width(), screen[u'size'].height(), + QtGui.QImage.Format_ARGB32_Premultiplied) + splash_image = QtGui.QImage(u':/graphics/openlp-splash-screen.png') + painter_image = QtGui.QPainter() + painter_image.begin(self.InitialFrame) + painter_image.fillRect(self.InitialFrame.rect(), QtCore.Qt.white) + painter_image.drawImage( + (screen[u'size'].width() - splash_image.width()) / 2, + (screen[u'size'].height() - splash_image.height()) / 2, + splash_image) + self.frameView(self.InitialFrame) #Build a Black screen painter = QtGui.QPainter() self.blankFrame = QtGui.QImage( @@ -174,6 +164,13 @@ class MainDisplay(DisplayLabel): QtGui.QImage.Format_ARGB32_Premultiplied) painter.begin(self.blankFrame) painter.fillRect(self.blankFrame.rect(), QtCore.Qt.black) + # To display or not to display? + if not screen[u'primary']: + self.showFullScreen() + self.primary = False + else: + self.setVisible(False) + self.primary = True def resetDisplay(self): if self.primary: @@ -197,24 +194,28 @@ class MainDisplay(DisplayLabel): if self.timer_id != 0 : self.displayAlert() elif not self.displayBlank: +# self.setWindowOpacity(0.5) +# self.show() self.display.setPixmap(QtGui.QPixmap.fromImage(frame)) +# QtCore.QTimer.singleShot(500, self.aa ) if not self.isVisible(): self.setVisible(True) self.showFullScreen() +# +# def aa(self): +# self.setWindowOpacity(1) - def blankDisplay(self): - if not self.displayBlank: + def blankDisplay(self, blanked=True): + if blanked: self.displayBlank = True self.display.setPixmap(QtGui.QPixmap.fromImage(self.blankFrame)) else: self.displayBlank = False if self.frame: self.frameView(self.frame) - if self.parent.LiveController.blackPushButton.isChecked() != \ - self.displayBlank: - self.parent.LiveController.blackPushButton.setChecked( - self.displayBlank) - self.parent.generalConfig.set_config(u'Screen Blank',self.displayBlank) + if blanked != self.parent.LiveController.blankButton.isChecked(): + self.parent.LiveController.blankButton.setChecked(self.displayBlank) + self.parent.generalConfig.set_config(u'Screen Blank', self.displayBlank) def displayAlert(self, text=u''): """ @@ -266,13 +267,14 @@ class MainDisplay(DisplayLabel): self.onMediaPlay() def onMediaPlay(self): - log.debug(u'Play the new media') + log.debug(u'Play the new media, Live ') if not self.mediaLoaded and not self.displayBlank: self.blankDisplay() self.firstTime = True self.mediaLoaded = True self.display.hide() self.video.setFullScreen(True) + self.video.setVisible(True) self.mediaObject.play() if self.primary: self.setVisible(True) @@ -284,7 +286,6 @@ class MainDisplay(DisplayLabel): def onMediaStop(self): log.debug(u'Media stopped by user') self.mediaObject.stop() - self.display.show() def onMediaFinish(self): log.debug(u'Reached end of media playlist') diff --git a/openlp/core/ui/mainwindow.py b/openlp/core/ui/mainwindow.py index abb5062da..901bfe985 100644 --- a/openlp/core/ui/mainwindow.py +++ b/openlp/core/ui/mainwindow.py @@ -84,6 +84,7 @@ class Ui_MainWindow(object): MainWindow.setCentralWidget(self.MainContent) self.ControlSplitter = QtGui.QSplitter(self.MainContent) self.ControlSplitter.setOrientation(QtCore.Qt.Horizontal) + self.ControlSplitter.setOpaqueResize(False) self.ControlSplitter.setObjectName(u'ControlSplitter') self.MainContentLayout.addWidget(self.ControlSplitter) # Create slide controllers @@ -300,8 +301,17 @@ class Ui_MainWindow(object): # Connect up some signals and slots QtCore.QObject.connect(self.FileExitItem, QtCore.SIGNAL(u'triggered()'), MainWindow.close) + QtCore.QObject.connect(self.ControlSplitter, + QtCore.SIGNAL(u'splitterMoved(int, int)'), self.trackSplitter) QtCore.QMetaObject.connectSlotsByName(MainWindow) + def trackSplitter(self, tab, pos): + """ + Splitter between the Preview and Live Controllers. + """ + self.LiveController.widthChanged() + self.PreviewController.widthChanged() + def retranslateUi(self, MainWindow): """ Set up the translation system @@ -526,6 +536,9 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow): self.settingsForm.postSetUp() def versionCheck(self): + """ + Checks the version of the Application called from openlp.pyw + """ applicationVersion = self.applicationVersion[u'Full'] version = check_latest_version(self.generalConfig, applicationVersion) if applicationVersion != version: @@ -544,7 +557,6 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow): monitor number does not exist. """ screen_number = int(self.generalConfig.get_config(u'Monitor', 0)) - monitor_exists = False for screen in self.screenList: if screen[u'number'] == screen_number: @@ -560,7 +572,9 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow): self.showMaximized() screen_number = self.getMonitorNumber() self.mainDisplay.setup(screen_number) - self.setFocus() + if self.mainDisplay.isVisible(): + self.mainDisplay.setFocus() + self.activateWindow() if str_to_bool(self.generalConfig.get_config(u'Auto Open', False)): self.ServiceManagerContents.onLoadService(True) if str_to_bool(self.generalConfig.get_config(u'Screen Blank', False)) \ @@ -570,7 +584,7 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow): self.trUtf8(u'The Main Display has been blanked out'), QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Ok), QtGui.QMessageBox.Ok) - self.LiveController.blackPushButton.setChecked(True) + #self.LiveController.blackPushButton.setChecked(True) def onHelpAboutItemClicked(self): """ @@ -601,6 +615,7 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow): if updated_display != self.RenderManager.current_display: self.RenderManager.update_display(updated_display) self.mainDisplay.setup(updated_display) + self.activateWindow() def closeEvent(self, event): """ @@ -664,7 +679,8 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow): self.setWindowTitle(title) def defaultThemeChanged(self, theme): - self.DefaultThemeLabel.setText(self.defaultThemeText + theme) + self.DefaultThemeLabel.setText( + u'%s %s' % (self.defaultThemeText, theme)) def toggleMediaManager(self, visible): if self.MediaManagerDock.isVisible() != visible: diff --git a/openlp/core/ui/pluginform.py b/openlp/core/ui/pluginform.py index 2cbb4fef5..a6b57e7c5 100644 --- a/openlp/core/ui/pluginform.py +++ b/openlp/core/ui/pluginform.py @@ -79,17 +79,16 @@ class PluginForm(QtGui.QDialog, Ui_PluginViewDialog): self.StatusComboBox.setCurrentIndex(-1) self.VersionNumberLabel.setText(u'') self.AboutTextBrowser.setHtml(u'') + self.StatusComboBox.setEnabled(False) def _setDetails(self): log.debug('PluginStatus: %s', str(self.activePlugin.status)) self.VersionNumberLabel.setText(self.activePlugin.version) self.AboutTextBrowser.setHtml(self.activePlugin.about()) - if self.activePlugin.can_be_disabled(): - self.programaticChange = True - self.StatusComboBox.setCurrentIndex(int(self.activePlugin.status)) - self.StatusComboBox.setEnabled(True) - else: - self.StatusComboBox.setEnabled(False) + self.programaticChange = True + self.StatusComboBox.setCurrentIndex(int(self.activePlugin.status)) + self.StatusComboBox.setEnabled(True) + self.programaticChange = False def onPluginListWidgetSelectionChanged(self): if self.PluginListWidget.currentItem() is None: @@ -108,7 +107,6 @@ class PluginForm(QtGui.QDialog, Ui_PluginViewDialog): def onStatusComboBoxChanged(self, status): if self.programaticChange: - self.programaticChange = False return self.activePlugin.toggle_status(status) if status == PluginStatus.Active: diff --git a/openlp/core/ui/servicemanager.py b/openlp/core/ui/servicemanager.py index b6b6a8575..8a927fc68 100644 --- a/openlp/core/ui/servicemanager.py +++ b/openlp/core/ui/servicemanager.py @@ -23,7 +23,6 @@ ############################################################################### import os -import string import logging import cPickle import zipfile @@ -35,7 +34,7 @@ from openlp.core.lib import PluginConfig, OpenLPToolbar, ServiceItem, \ class ServiceManagerList(QtGui.QTreeWidget): - def __init__(self,parent=None,name=None): + def __init__(self, parent=None, name=None): QtGui.QTreeWidget.__init__(self,parent) self.parent = parent @@ -48,7 +47,7 @@ class ServiceManagerList(QtGui.QTreeWidget): # else: # pos = parentitem.data(0, QtCore.Qt.UserRole).toInt()[0] # serviceItem = self.parent.serviceItems[pos - 1] -# if serviceItem[u'data'].editEnabled: +# if serviceItem[u'data'].edit_enabled: # self.parent.editAction.setVisible(True) # else: # self.parent.editAction.setVisible(False) @@ -56,7 +55,6 @@ class ServiceManagerList(QtGui.QTreeWidget): # else: # event.ignore() - def keyPressEvent(self, event): if type(event) == QtGui.QKeyEvent: #here accept the event and do something @@ -99,7 +97,6 @@ class ServiceManagerList(QtGui.QTreeWidget): mimeData.setText(u'ServiceManager') dropAction = drag.start(QtCore.Qt.CopyAction) - class ServiceManager(QtGui.QWidget): """ Manages the services. This involves taking text strings from plugins and @@ -390,14 +387,14 @@ class ServiceManager(QtGui.QWidget): #Repaint the screen self.ServiceManagerList.clear() for itemcount, item in enumerate(self.serviceItems): - serviceitem = item[u'data'] + serviceitem = item[u'service_item'] treewidgetitem = QtGui.QTreeWidgetItem(self.ServiceManagerList) treewidgetitem.setText(0,serviceitem.title) treewidgetitem.setIcon(0,serviceitem.iconic_representation) treewidgetitem.setData(0, QtCore.Qt.UserRole, QtCore.QVariant(item[u'order'])) treewidgetitem.setExpanded(item[u'expanded']) - for count, frame in enumerate(serviceitem.frames): + for count, frame in enumerate(serviceitem.get_frames()): treewidgetitem1 = QtGui.QTreeWidgetItem(treewidgetitem) text = frame[u'title'] treewidgetitem1.setText(0,text[:40]) @@ -418,7 +415,7 @@ class ServiceManager(QtGui.QWidget): u'Save Service', self.config.get_last_dir()) else: filename = self.config.get_last_dir() - if filename != u'': + if filename: splittedFile = filename.split(u'.') if splittedFile[-1] != u'osz': filename = filename + u'.osz' @@ -427,21 +424,30 @@ class ServiceManager(QtGui.QWidget): self.config.set_last_dir(filename) service = [] servicefile = filename + u'.osd' - zip = zipfile.ZipFile(unicode(filename), 'w') - for item in self.serviceItems: - service.append( - {u'serviceitem':item[u'data'].get_service_repr()}) - if item[u'data'].service_item_type == ServiceItemType.Image or \ - item[u'data'].service_item_type == ServiceItemType.Command: - for frame in item[u'data'].frames: - path_from = unicode(os.path.join( - item[u'data'].service_item_path, frame[u'title'])) - zip.write(path_from) - file = open(servicefile, u'wb') - cPickle.dump(service, file) - file.close() - zip.write(servicefile) - zip.close() + zip = None + file = None + try: + zip = zipfile.ZipFile(unicode(filename), 'w') + for item in self.serviceItems: + service.append( + {u'serviceitem':item[u'service_item'].get_service_repr()}) + if item[u'service_item'].service_item_type == ServiceItemType.Image or \ + item[u'service_item'].service_item_type == ServiceItemType.Command: + for frame in item[u'service_item'].frames: + path_from = unicode(os.path.join( + item[u'service_item'].service_item_path, frame[u'title'])) + zip.write(path_from) + file = open(servicefile, u'wb') + cPickle.dump(service, file) + file.close() + zip.write(servicefile) + except: + log.exception(u'Failed to save service to disk') + finally: + if file: + file.close() + if zip: + zip.close() try: os.remove(servicefile) except: @@ -453,7 +459,7 @@ class ServiceManager(QtGui.QWidget): def onQuickSaveService(self): self.onSaveService(True) - def onLoadService(self, lastService = False): + def onLoadService(self, lastService=False): """ Load an existing service from disk and rebuild the serviceitems. All files retrieved from the zip file are placed in a temporary directory @@ -467,16 +473,15 @@ class ServiceManager(QtGui.QWidget): self.config.get_last_dir(), u'Services (*.osz)') filename = unicode(filename) name = filename.split(os.path.sep) - if filename != u'': + if filename: self.config.set_last_dir(filename) + zip = None + f = None try: zip = zipfile.ZipFile(unicode(filename)) for file in zip.namelist(): - if os.name == u'nt': - winfile = string.replace(file, '/', os.path.sep) - names = winfile.split(os.path.sep) - else: - names = file.split(os.path.sep) + osfile = unicode(QtCore.QDir.toNativeSeparators(file)) + names = osfile.split(os.path.sep) file_to = os.path.join(self.servicePath, names[len(names) - 1]) f = open(file_to, u'wb') @@ -492,7 +497,7 @@ class ServiceManager(QtGui.QWidget): for item in items: serviceitem = ServiceItem() serviceitem.RenderManager = self.parent.RenderManager - serviceitem.set_from_service(item, self.servicePath ) + serviceitem.set_from_service(item, self.servicePath) self.addServiceItem(serviceitem) try: if os.path.isfile(p_file): @@ -501,6 +506,11 @@ class ServiceManager(QtGui.QWidget): log.exception(u'Failed to remove osd file') except: log.exception(u'Problem loading a service file') + finally: + if f: + f.close() + if zip: + zip.close() self.isNew = False self.serviceName = name[len(name) - 1] self.parent.serviceChanged(True, self.serviceName) @@ -531,7 +541,7 @@ class ServiceManager(QtGui.QWidget): tempServiceItems = self.serviceItems self.onNewService() for item in tempServiceItems: - self.addServiceItem(item[u'data']) + self.addServiceItem(item[u'service_item']) def addServiceItem(self, item): """ @@ -544,19 +554,19 @@ class ServiceManager(QtGui.QWidget): sitem, count = self.findServiceItem() item.render() if self.remoteEditTriggered: - item.uuid = self.serviceItems[sitem][u'data'].uuid - self.serviceItems[sitem][u'data'] = item + item.merge(self.serviceItems[sitem][u'service_item']) + self.serviceItems[sitem][u'service_item'] = item self.remoteEditTriggered = False self.repaintServiceList(sitem + 1, 0) self.parent.LiveController.replaceServiceManagerItem(item) else: if sitem == -1: - self.serviceItems.append({u'data': item, + self.serviceItems.append({u'service_item': item, u'order': len(self.serviceItems) + 1, u'expanded':True}) self.repaintServiceList(len(self.serviceItems) + 1, 0) else: - self.serviceItems.insert(sitem + 1, {u'data': item, + self.serviceItems.insert(sitem + 1, {u'service_item': item, u'order': len(self.serviceItems)+1, u'expanded':True}) self.repaintServiceList(sitem + 1, 0) @@ -568,7 +578,7 @@ class ServiceManager(QtGui.QWidget): """ item, count = self.findServiceItem() self.parent.PreviewController.addServiceManagerItem( - self.serviceItems[item][u'data'], count) + self.serviceItems[item][u'service_item'], count) def makeLive(self): """ @@ -576,17 +586,18 @@ class ServiceManager(QtGui.QWidget): """ item, count = self.findServiceItem() self.parent.LiveController.addServiceManagerItem( - self.serviceItems[item][u'data'], count) + self.serviceItems[item][u'service_item'], count) def remoteEdit(self): """ Posts a remote edit message to a plugin to allow item to be edited. """ item, count = self.findServiceItem() - if self.serviceItems[item][u'data'].editEnabled: + if self.serviceItems[item][u'service_item'].edit_enabled: self.remoteEditTriggered = True - Receiver().send_message(u'%s_edit' % self.serviceItems[item][u'data'].name, u'L:%s' % - self.serviceItems[item][u'data'].editId ) + Receiver.send_message(u'%s_edit' % + self.serviceItems[item][u'service_item'].name, u'L:%s' % + self.serviceItems[item][u'service_item'].editId ) def onRemoteEditClear(self): self.remoteEditTriggered = False @@ -652,7 +663,7 @@ class ServiceManager(QtGui.QWidget): self.serviceItems.insert(newpos, serviceItem) self.repaintServiceList(endpos, startCount) else: - Receiver().send_message(u'%s_add_service_item' % plugin) + Receiver.send_message(u'%s_add_service_item' % plugin) def updateThemeList(self, theme_list): """ @@ -684,5 +695,5 @@ class ServiceManager(QtGui.QWidget): def onThemeChangeAction(self): theme = unicode(self.sender().text()) item, count = self.findServiceItem() - self.serviceItems[item][u'data'].theme = theme + self.serviceItems[item][u'service_item'].theme = theme self.regenerateServiceItems() diff --git a/openlp/core/ui/settingsform.py b/openlp/core/ui/settingsform.py index a91e1aecb..f14c64ebd 100644 --- a/openlp/core/ui/settingsform.py +++ b/openlp/core/ui/settingsform.py @@ -67,7 +67,7 @@ class SettingsForm(QtGui.QDialog, Ui_SettingsDialog): def accept(self): for tab_index in range(0, self.SettingsTabWidget.count()): self.SettingsTabWidget.widget(tab_index).save() - Receiver().send_message(u'config_updated') + Receiver.send_message(u'config_updated') return QtGui.QDialog.accept(self) def postSetUp(self): diff --git a/openlp/core/ui/slidecontroller.py b/openlp/core/ui/slidecontroller.py index efd523c82..9edf906b3 100644 --- a/openlp/core/ui/slidecontroller.py +++ b/openlp/core/ui/slidecontroller.py @@ -1,6 +1,5 @@ # -*- coding: utf-8 -*- # vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4 - ############################################################################### # OpenLP - Open Source Lyrics Projection # # --------------------------------------------------------------------------- # @@ -24,10 +23,12 @@ import logging import time +import os from PyQt4 import QtCore, QtGui -from openlp.core.lib import OpenLPToolbar, Receiver, ServiceItemType, \ - str_to_bool, PluginConfig +from PyQt4.phonon import Phonon + +from openlp.core.lib import OpenLPToolbar, Receiver, str_to_bool, PluginConfig class SlideList(QtGui.QTableWidget): """ @@ -80,18 +81,13 @@ class SlideController(QtGui.QWidget): u'Loop Separator', u'Image SpinBox' ] - self.media_list = [ - u'Media Start', - u'Media Stop', - u'Media Pause' - ] - self.song_list = [ + self.song_edit_list = [ u'Edit Song', ] self.timer_id = 0 - self.commandItem = None self.songEdit = False - self.row = 0 + self.selectedRow = 0 + self.serviceItem = None self.Panel = QtGui.QWidget(parent.ControlSplitter) # Layout for holding panel self.PanelLayout = QtGui.QVBoxLayout(self.Panel) @@ -102,14 +98,19 @@ class SlideController(QtGui.QWidget): if self.isLive: self.TypeLabel.setText(u'%s' % self.trUtf8(u'Live')) + self.split = 1 + prefix = u'live_slidecontroller' else: self.TypeLabel.setText(u'%s' % self.trUtf8(u'Preview')) + self.split = 0 + prefix = u'preview_slidecontroller' self.TypeLabel.setAlignment(QtCore.Qt.AlignCenter) self.PanelLayout.addWidget(self.TypeLabel) # Splitter self.Splitter = QtGui.QSplitter(self.Panel) self.Splitter.setOrientation(QtCore.Qt.Vertical) + self.Splitter.setOpaqueResize(False) self.PanelLayout.addWidget(self.Splitter) # Actual controller section self.Controller = QtGui.QWidget(self.Splitter) @@ -130,6 +131,7 @@ class SlideController(QtGui.QWidget): self.PreviewListWidget.setObjectName(u'PreviewListWidget') self.PreviewListWidget.setEditTriggers( QtGui.QAbstractItemView.NoEditTriggers) + self.PreviewListWidget.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) self.ControllerLayout.addWidget(self.PreviewListWidget) # Build the full toolbar self.Toolbar = OpenLPToolbar(self) @@ -156,8 +158,11 @@ class SlideController(QtGui.QWidget): self.trUtf8(u'Move to last'), self.onSlideSelectedLast) if self.isLive: self.Toolbar.addToolbarSeparator(u'Close Separator') - self.blackPushButton = self.Toolbar.addPushButton( - u':/slides/slide_close.png') + self.blankButton = self.Toolbar.addToolbarButton( + u'Blank Screen', u':/slides/slide_close.png', + self.trUtf8(u'Blank Screen'), self.onBlankScreen, True) + QtCore.QObject.connect(Receiver.get_receiver(), + QtCore.SIGNAL(u'live_slide_blank'), self.onBlankDisplay) if not self.isLive: self.Toolbar.addToolbarSeparator(u'Close Separator') self.Toolbar.addToolbarButton( @@ -180,31 +185,38 @@ class SlideController(QtGui.QWidget): u'Image SpinBox', self.DelaySpinBox) self.DelaySpinBox.setSuffix(self.trUtf8(u's')) self.DelaySpinBox.setToolTip(self.trUtf8(u'Delay between slides in seconds')) - self.Toolbar.addToolbarButton( - u'Media Start', u':/slides/media_playback_start.png', - self.trUtf8(u'Start playing media'), self.onMediaPlay) - self.Toolbar.addToolbarButton( - u'Media Pause', u':/slides/media_playback_pause.png', - self.trUtf8(u'Start playing media'), self.onMediaPause) - self.Toolbar.addToolbarButton( - u'Media Stop', u':/slides/media_playback_stop.png', - self.trUtf8(u'Start playing media'), self.onMediaStop) - self.ControllerLayout.addWidget(self.Toolbar) + #Build a Media ToolBar + self.Mediabar = OpenLPToolbar(self) + self.Mediabar.addToolbarButton( + u'Media Start', u':/slides/media_playback_start.png', + self.trUtf8(u'Start playing media'), self.onMediaPlay) + self.Mediabar.addToolbarButton( + u'Media Pause', u':/slides/media_playback_pause.png', + self.trUtf8(u'Start playing media'), self.onMediaPause) + self.Mediabar.addToolbarButton( + u'Media Stop', u':/slides/media_playback_stop.png', + self.trUtf8(u'Start playing media'), self.onMediaStop) + self.volumeSlider = Phonon.VolumeSlider() + self.volumeSlider.setGeometry(QtCore.QRect(90, 260, 221, 24)) + self.volumeSlider.setObjectName(u'volumeSlider') + self.Mediabar.addToolbarWidget( + u'Audio Volume', self.volumeSlider) + self.ControllerLayout.addWidget(self.Mediabar) # Build the Song Toolbar if isLive: self.Songbar = OpenLPToolbar(self) self.Songbar.addToolbarButton( - u'Bridge', u':/slides/slide_close.png', + u'Bridge', u':/pages/page_bridge.png', self.trUtf8(u'Bridge'), self.onSongBarHandler) self.Songbar.addToolbarButton( - u'Chorus', u':/slides/slide_close.png', + u'Chorus', u':/pages/page_chorus.png', self.trUtf8(u'Chorus'), self.onSongBarHandler) - for verse in range(1, 20): + for verse in range(1, 12): self.Songbar.addToolbarButton( - unicode(verse), u':/slides/slide_close.png', + unicode(verse), u':/pages/page_%s.png' % verse, unicode(self.trUtf8(u'Verse %s'))%verse, self.onSongBarHandler) self.ControllerLayout.addWidget(self.Songbar) @@ -213,15 +225,26 @@ class SlideController(QtGui.QWidget): self.PreviewFrame = QtGui.QFrame(self.Splitter) self.PreviewFrame.setGeometry(QtCore.QRect(0, 0, 300, 225)) self.PreviewFrame.setSizePolicy(QtGui.QSizePolicy( - QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Minimum)) + QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Label)) self.PreviewFrame.setFrameShape(QtGui.QFrame.StyledPanel) self.PreviewFrame.setFrameShadow(QtGui.QFrame.Sunken) self.PreviewFrame.setObjectName(u'PreviewFrame') self.grid = QtGui.QGridLayout(self.PreviewFrame) self.grid.setMargin(8) self.grid.setObjectName(u'grid') + self.SlideLayout = QtGui.QVBoxLayout() + self.SlideLayout.setSpacing(0) + self.SlideLayout.setMargin(0) + self.SlideLayout.setObjectName(u'SlideLayout') + self.mediaObject = Phonon.MediaObject(self) + self.video = Phonon.VideoWidget() + self.video.setVisible(False) + self.audio = Phonon.AudioOutput(Phonon.VideoCategory, self.mediaObject) + Phonon.createPath(self.mediaObject, self.video) + Phonon.createPath(self.mediaObject, self.audio) + self.SlideLayout.insertWidget(0, self.video) # Actual preview screen - self.SlidePreview = QtGui.QLabel(self.parent) + self.SlidePreview = QtGui.QLabel(self) sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) @@ -236,33 +259,54 @@ class SlideController(QtGui.QWidget): self.SlidePreview.setLineWidth(1) self.SlidePreview.setScaledContents(True) self.SlidePreview.setObjectName(u'SlidePreview') - self.grid.addWidget(self.SlidePreview, 0, 0, 1, 1) + self.SlideLayout.insertWidget(0, self.SlidePreview) + self.grid.addLayout(self.SlideLayout, 0, 0, 1, 1) # Signals QtCore.QObject.connect(self.PreviewListWidget, QtCore.SIGNAL(u'clicked(QModelIndex)'), self.onSlideSelected) QtCore.QObject.connect(self.PreviewListWidget, QtCore.SIGNAL(u'activated(QModelIndex)'), self.onSlideSelected) if isLive: - QtCore.QObject.connect(self.blackPushButton, - QtCore.SIGNAL(u'clicked(bool)'), self.onBlankScreen) + #QtCore.QObject.connect(self.blackPushButton, + # QtCore.SIGNAL(u'clicked(bool)'), self.onBlankScreen) QtCore.QObject.connect(Receiver.get_receiver(), QtCore.SIGNAL(u'update_spin_delay'), self.receiveSpinDelay) - Receiver().send_message(u'request_spin_delay') + Receiver.send_message(u'request_spin_delay') if isLive: self.Toolbar.makeWidgetsInvisible(self.image_list) - self.Toolbar.makeWidgetsInvisible(self.media_list) else: - self.Toolbar.makeWidgetsInvisible(self.song_list) + self.Toolbar.makeWidgetsInvisible(self.song_edit_list) + self.Mediabar.setVisible(False) QtCore.QObject.connect(Receiver.get_receiver(), - QtCore.SIGNAL(u'slidecontroller_first'), self.onSlideSelectedFirst) + QtCore.SIGNAL(u'%s_first' % prefix), self.onSlideSelectedFirst) QtCore.QObject.connect(Receiver.get_receiver(), - QtCore.SIGNAL(u'slidecontroller_next'), self.onSlideSelectedNext) + QtCore.SIGNAL(u'%s_next' % prefix), self.onSlideSelectedNext) QtCore.QObject.connect(Receiver.get_receiver(), - QtCore.SIGNAL(u'slidecontroller_previous'), self.onSlideSelectedPrevious) + QtCore.SIGNAL(u'%s_previous' % prefix), self.onSlideSelectedPrevious) QtCore.QObject.connect(Receiver.get_receiver(), - QtCore.SIGNAL(u'slidecontroller_last'), self.onSlideSelectedLast) + QtCore.SIGNAL(u'%s_last' % prefix), self.onSlideSelectedLast) QtCore.QObject.connect(Receiver.get_receiver(), - QtCore.SIGNAL(u'slidecontroller_change'), self.onSlideChange) + QtCore.SIGNAL(u'%s_change' % prefix), self.onSlideChange) + QtCore.QObject.connect(self.Splitter, + QtCore.SIGNAL(u'splitterMoved(int, int)'), self.trackSplitter) + + def widthChanged(self): + """ + Handle changes of width from the splitter between the live and preview + controller. Event only issues when changes have finished + """ + width = self.parent.ControlSplitter.sizes()[self.split] + height = width * self.parent.RenderManager.screen_ratio + self.PreviewListWidget.setColumnWidth(0, width) + if self.serviceItem and not self.serviceItem.is_text(): + for framenumber, frame in enumerate(self.serviceItem.get_frames()): + self.PreviewListWidget.setRowHeight(framenumber, height) + + def trackSplitter(self, tab, pos): + """ + Splitter between the slide list and the preview panel + """ + pass def onSongBarHandler(self): request = self.sender().text() @@ -296,92 +340,91 @@ class SlideController(QtGui.QWidget): """ Allows the live toolbar to be customised """ + self.Toolbar.setVisible(True) self.Songbar.setVisible(False) + self.Mediabar.setVisible(False) self.Toolbar.makeWidgetsInvisible(self.image_list) - self.Toolbar.makeWidgetsInvisible(self.media_list) - if item.service_item_type == ServiceItemType.Text: + if item.is_text(): self.Toolbar.makeWidgetsInvisible(self.image_list) - if item.name == u'Songs' and \ + if item.is_song() and \ str_to_bool(self.songsconfig.get_config(u'display songbar', True)): for action in self.Songbar.actions: self.Songbar.actions[action].setVisible(False) if item.verse_order: verses = item.verse_order.split(u' ') for verse in verses: + if not verse or int(verse) > 12: + break try: self.Songbar.actions[verse].setVisible(True) except: #More than 20 verses hard luck pass self.Songbar.setVisible(True) - elif item.service_item_type == ServiceItemType.Image: + elif item.is_image(): #Not sensible to allow loops with 1 frame - if len(item.frames) > 1: + if len(item.get_frames()) > 1: self.Toolbar.makeWidgetsVisible(self.image_list) - elif item.service_item_type == ServiceItemType.Command and \ - item.name == u'Media': - self.Toolbar.makeWidgetsVisible(self.media_list) + elif item.is_media(): + self.Toolbar.setVisible(False) + self.Mediabar.setVisible(True) + self.volumeSlider.setAudioOutput(self.parent.mainDisplay.audio) def enablePreviewToolBar(self, item): """ Allows the Preview toolbar to be customised """ - if (item.name == u'Songs' or item.name == u'Custom') and item.fromPlugin: - self.Toolbar.makeWidgetsVisible(self.song_list) - else: - self.Toolbar.makeWidgetsInvisible(self.song_list) + self.Toolbar.setVisible(True) + self.Mediabar.setVisible(False) + self.Toolbar.makeWidgetsInvisible(self.song_edit_list) + if item.edit_enabled and item.fromPlugin: + self.Toolbar.makeWidgetsVisible(self.song_edit_list) + elif item.is_media(): + self.Toolbar.setVisible(False) + self.Mediabar.setVisible(True) + self.volumeSlider.setAudioOutput(self.audio) def addServiceItem(self, item): """ - Method to install the service item into the controller and - request the correct the toolbar of the plugin + Method to install the service item into the controller Called by plugins """ log.debug(u'addServiceItem') - #If old item was a command tell it to stop - if self.commandItem and \ - self.commandItem.service_item_type == ServiceItemType.Command: - Receiver().send_message(u'%s_stop'% self.commandItem.name.lower()) - self.commandItem = item before = time.time() item.render() - log.info(u'Rendering took %4s' % (time.time() - before)) - self.enableToolBar(item) - if item.service_item_type == ServiceItemType.Command: - Receiver().send_message(u'%s_start' % item.name.lower(), \ - [item.shortname, item.service_item_path, - item.service_frames[0][u'title']]) + log.log(15, u'Rendering took %4s' % (time.time() - before)) slideno = 0 if self.songEdit: - slideno = self.row + slideno = self.selectedRow self.songEdit = False - self.displayServiceManagerItems(item, slideno) + self.addServiceManagerItem(item, slideno) def replaceServiceManagerItem(self, item): """ Replacement item following a remote edit """ - if self.commandItem and \ - item.uuid == self.commandItem.uuid: + if item.__eq__(self.serviceItem): self.addServiceManagerItem(item, self.PreviewListWidget.currentRow()) def addServiceManagerItem(self, item, slideno): """ Method to install the service item into the controller and - request the correct the toolbar of the plugin + request the correct toolbar for the plugin. Called by ServiceManager """ log.debug(u'addServiceManagerItem') #If old item was a command tell it to stop - if self.commandItem and \ - self.commandItem.service_item_type == ServiceItemType.Command: - Receiver().send_message(u'%s_stop'% self.commandItem.name.lower()) - self.commandItem = item + if self.serviceItem and self.serviceItem.is_command(): + self.onMediaStop() self.enableToolBar(item) - if item.service_item_type == ServiceItemType.Command: - Receiver().send_message(u'%s_start' % item.name.lower(), \ - [item.shortname, item.service_item_path, - item.service_frames[0][u'title'], slideno]) + if item.is_command(): + if self.isLive: + Receiver.send_message(u'%s_start' % item.name.lower(), \ + [item.title, item.service_item_path, + item.get_frame_title(), slideno, self.isLive]) + else: + if item.is_media(): + self.onMediaStart(item) self.displayServiceManagerItems(item, slideno) def displayServiceManagerItems(self, serviceitem, slideno): @@ -390,33 +433,34 @@ class SlideController(QtGui.QWidget): Display the slide number passed """ log.debug(u'displayServiceManagerItems Start') + width = self.parent.ControlSplitter.sizes()[self.split] + #Set pointing cursor when we have somthing to point at + self.PreviewListWidget.setCursor(QtCore.Qt.PointingHandCursor) before = time.time() - self.serviceitem = serviceitem + self.serviceItem = serviceitem self.PreviewListWidget.clear() self.PreviewListWidget.setRowCount(0) - self.PreviewListWidget.setColumnWidth( - 0, self.settingsmanager.slidecontroller_image) - for framenumber, frame in enumerate(self.serviceitem.frames): + self.PreviewListWidget.setColumnWidth(0, width) + for framenumber, frame in enumerate(self.serviceItem.get_frames()): self.PreviewListWidget.setRowCount( self.PreviewListWidget.rowCount() + 1) item = QtGui.QTableWidgetItem() slide_height = 0 #It is a Image - if frame[u'text'] is None: + if not self.serviceItem.is_text(): label = QtGui.QLabel() label.setMargin(4) pixmap = self.parent.RenderManager.resize_image(frame[u'image']) label.setScaledContents(True) label.setPixmap(QtGui.QPixmap.fromImage(pixmap)) self.PreviewListWidget.setCellWidget(framenumber, 0, label) - slide_height = self.settingsmanager.slidecontroller_image * \ - self.parent.RenderManager.screen_ratio + slide_height = width * self.parent.RenderManager.screen_ratio else: item.setText(frame[u'text']) self.PreviewListWidget.setItem(framenumber, 0, item) if slide_height != 0: self.PreviewListWidget.setRowHeight(framenumber, slide_height) - if self.serviceitem.frames[0][u'text']: + if self.serviceItem.is_text(): self.PreviewListWidget.resizeRowsToContents() self.PreviewListWidget.setColumnWidth( 0, self.PreviewListWidget.viewport().size().width()) @@ -426,9 +470,9 @@ class SlideController(QtGui.QWidget): self.PreviewListWidget.selectRow(slideno) self.onSlideSelected() self.PreviewListWidget.setFocus() - log.info(u'Display Rendering took %4s' % (time.time() - before)) - if self.serviceitem.audit != u'' and self.isLive: - Receiver().send_message(u'songusage_live', self.serviceitem.audit) + log.log(15, u'Display Rendering took %4s' % (time.time() - before)) + if self.isLive: + self.serviceItem.request_audit() log.debug(u'displayServiceManagerItems End') #Screen event methods @@ -436,26 +480,27 @@ class SlideController(QtGui.QWidget): """ Go to the first slide. """ - if self.commandItem and \ - self.commandItem.service_item_type == ServiceItemType.Command: - Receiver().send_message(u'%s_first'% self.commandItem.name.lower()) - QtCore.QTimer.singleShot(0.5, self.grabMainDisplay) + if self.serviceItem.is_command(): + Receiver.send_message(u'%s_first'% self.serviceItem.name.lower()) + self.updatePreview() else: self.PreviewListWidget.selectRow(0) self.onSlideSelected() + def onBlankDisplay(self): + self.blankButton.setChecked(self.parent.mainDisplay.displayBlank) + def onBlankScreen(self, blanked): """ Blank the screen. """ - if self.commandItem and \ - self.commandItem.service_item_type == ServiceItemType.Command: + if self.serviceItem and self.serviceItem.is_command(): if blanked: - Receiver().send_message(u'%s_blank'% self.commandItem.name.lower()) + Receiver.send_message(u'%s_blank'% self.serviceItem.name.lower()) else: - Receiver().send_message(u'%s_unblank'% self.commandItem.name.lower()) + Receiver.send_message(u'%s_unblank'% self.serviceItem.name.lower()) else: - self.parent.mainDisplay.blankDisplay() + self.parent.mainDisplay.blankDisplay(blanked) def onSlideSelected(self): """ @@ -463,49 +508,54 @@ class SlideController(QtGui.QWidget): if this is the Live Controller also display on the screen """ row = self.PreviewListWidget.currentRow() - self.row = 0 + self.selectedRow = 0 if row > -1 and row < self.PreviewListWidget.rowCount(): - if self.commandItem.service_item_type == ServiceItemType.Command: - Receiver().send_message(u'%s_slide'% self.commandItem.name.lower(), [row]) + if self.serviceItem.is_command(): + Receiver.send_message(u'%s_slide'% self.serviceItem.name.lower(), [row]) if self.isLive: - QtCore.QTimer.singleShot(0.5, self.grabMainDisplay) + self.updatePreview() else: - frame = self.serviceitem.frames[row][u'image'] before = time.time() - if frame is None: - frame = self.serviceitem.render_individual(row) + frame = self.serviceItem.get_rendered_frame(row) self.SlidePreview.setPixmap(QtGui.QPixmap.fromImage(frame)) - log.info(u'Slide Rendering took %4s' % (time.time() - before)) + log.log(15, u'Slide Rendering took %4s' % (time.time() - before)) if self.isLive: self.parent.mainDisplay.frameView(frame) - self.row = row + self.selectedRow = row def onSlideChange(self, row): """ The slide has been changed. Update the slidecontroller accordingly """ self.PreviewListWidget.selectRow(row) - QtCore.QTimer.singleShot(0.5, self.grabMainDisplay) + self.updatePreview() + + def updatePreview(self): + rm = self.parent.RenderManager + if not rm.screen_list[rm.current_display][u'primary']: + # Grab now, but try again in a couple of seconds if slide change is slow + QtCore.QTimer.singleShot(0.5, self.grabMainDisplay) + QtCore.QTimer.singleShot(2.5, self.grabMainDisplay) + else: + label = self.PreviewListWidget.cellWidget( + self.PreviewListWidget.currentRow(), 0) + self.SlidePreview.setPixmap(label.pixmap()) def grabMainDisplay(self): rm = self.parent.RenderManager - if not rm.screen_list[rm.current_display][u'primary']: - winid = QtGui.QApplication.desktop().winId() - rect = rm.screen_list[rm.current_display][u'size'] - winimg = QtGui.QPixmap.grabWindow(winid, rect.x(), rect.y(), rect.width(), rect.height()) - self.SlidePreview.setPixmap(winimg) - else: - label = self.PreviewListWidget.cellWidget(self.PreviewListWidget.currentRow(), 0) - self.SlidePreview.setPixmap(label.pixmap()) + winid = QtGui.QApplication.desktop().winId() + rect = rm.screen_list[rm.current_display][u'size'] + winimg = QtGui.QPixmap.grabWindow(winid, rect.x(), + rect.y(), rect.width(), rect.height()) + self.SlidePreview.setPixmap(winimg) def onSlideSelectedNext(self): """ Go to the next slide. """ - if self.commandItem and \ - self.commandItem.service_item_type == ServiceItemType.Command: - Receiver().send_message(u'%s_next'% self.commandItem.name.lower()) - QtCore.QTimer.singleShot(0.5, self.grabMainDisplay) + if self.serviceItem.is_command(): + Receiver.send_message(u'%s_next'% self.serviceItem.name.lower()) + self.updatePreview() else: row = self.PreviewListWidget.currentRow() + 1 if row == self.PreviewListWidget.rowCount(): @@ -517,11 +567,10 @@ class SlideController(QtGui.QWidget): """ Go to the previous slide. """ - if self.commandItem and \ - self.commandItem.service_item_type == ServiceItemType.Command: - Receiver().send_message( - u'%s_previous'% self.commandItem.name.lower()) - QtCore.QTimer.singleShot(0.5, self.grabMainDisplay) + if self.serviceItem.is_command(): + Receiver.send_message( + u'%s_previous'% self.serviceItem.name.lower()) + self.updatePreview() else: row = self.PreviewListWidget.currentRow() - 1 if row == -1: @@ -533,10 +582,9 @@ class SlideController(QtGui.QWidget): """ Go to the last slide. """ - if self.commandItem and \ - self.commandItem.service_item_type == ServiceItemType.Command: - Receiver().send_message(u'%s_last'% self.commandItem.name.lower()) - QtCore.QTimer.singleShot(0.5, self.grabMainDisplay) + if self.serviceItem.is_command(): + Receiver.send_message(u'%s_last'% self.serviceItem.name.lower()) + self.updatePreview() else: self.PreviewListWidget.selectRow(self.PreviewListWidget.rowCount() - 1) self.onSlideSelected() @@ -564,8 +612,8 @@ class SlideController(QtGui.QWidget): def onEditSong(self): self.songEdit = True - Receiver().send_message(u'%s_edit' % self.commandItem.name, u'P:%s' % - self.commandItem.editId ) + Receiver.send_message(u'%s_edit' % self.serviceItem.name, u'P:%s' % + self.serviceItem.editId ) def onGoLive(self): """ @@ -574,13 +622,34 @@ class SlideController(QtGui.QWidget): row = self.PreviewListWidget.currentRow() if row > -1 and row < self.PreviewListWidget.rowCount(): self.parent.LiveController.addServiceManagerItem( - self.commandItem, row) + self.serviceItem, row) + + def onMediaStart(self, item): + self.mediaObject.stop() + self.mediaObject.clearQueue() + file = os.path.join(item.service_item_path, item.get_frame_title()) + self.mediaObject.setCurrentSource(Phonon.MediaSource(file)) + self.onMediaPlay() def onMediaPause(self): - Receiver().send_message(u'%s_pause'% self.commandItem.name.lower()) + if self.isLive: + Receiver.send_message(u'%s_pause'% self.serviceItem.name.lower()) + else: + self.mediaObject.pause() def onMediaPlay(self): - Receiver().send_message(u'%s_play'% self.commandItem.name.lower()) + if self.isLive: + Receiver.send_message(u'%s_play'% self.serviceItem.name.lower(), self.isLive) + else: + self.SlidePreview.hide() + self.video.show() + self.mediaObject.play() def onMediaStop(self): - Receiver().send_message(u'%s_stop'% self.commandItem.name.lower()) + if self.isLive: + Receiver.send_message(u'%s_stop'% self.serviceItem.name.lower()) + else: + self.mediaObject.stop() + self.video.hide() + self.SlidePreview.clear() + self.SlidePreview.show() diff --git a/openlp/core/ui/thememanager.py b/openlp/core/ui/thememanager.py index 44ac83043..6a207fb9c 100644 --- a/openlp/core/ui/thememanager.py +++ b/openlp/core/ui/thememanager.py @@ -33,7 +33,7 @@ from PyQt4 import QtCore, QtGui from openlp.core.ui import AmendThemeForm from openlp.core.theme import Theme from openlp.core.lib import PluginConfig, OpenLPToolbar, ThemeXML, \ - str_to_bool, file_to_xml, buildIcon, Receiver, contextMenuAction, \ + str_to_bool, get_text_file_string, buildIcon, Receiver, contextMenuAction, \ contextMenuSeparator from openlp.core.utils import ConfigHelper @@ -145,20 +145,22 @@ class ThemeManager(QtGui.QWidget): name = u'%s (%s)' % (self.global_theme, self.trUtf8(u'default')) self.ThemeListWidget.item(count).setText(name) self.config.set_config(u'theme global theme', self.global_theme) - Receiver().send_message( + Receiver.send_message( u'update_global_theme', self.global_theme) self.pushThemes() def onAddTheme(self): - self.amendThemeForm.loadTheme(None) + theme = self.createThemeFromXml(self.baseTheme(), self.path) + self.amendThemeForm.loadTheme(theme) self.saveThemeName = u'' self.amendThemeForm.exec_() def onEditTheme(self): item = self.ThemeListWidget.currentItem() if item: - self.amendThemeForm.loadTheme( + theme = self.getThemeData( unicode(item.data(QtCore.Qt.UserRole).toString())) + self.amendThemeForm.loadTheme(theme) self.saveThemeName = unicode( item.data(QtCore.Qt.UserRole).toString()) self.amendThemeForm.exec_() @@ -182,10 +184,6 @@ class ThemeManager(QtGui.QWidget): self.ThemeListWidget.takeItem(row) try: os.remove(os.path.join(self.path, th)) - except: - #if not present do not worry - pass - try: shutil.rmtree(os.path.join(self.path, theme)) except: #if not present do not worry @@ -210,16 +208,22 @@ class ThemeManager(QtGui.QWidget): unicode(self.trUtf8(u'Save Theme - (%s)')) % theme, self.config.get_last_dir(1) ) path = unicode(path) - if path != u'': + if path: self.config.set_last_dir(path, 1) themePath = os.path.join(path, theme + u'.theme') - zip = zipfile.ZipFile(themePath, u'w') - source = os.path.join(self.path, theme) - for root, dirs, files in os.walk(source): - for name in files: - zip.write( - os.path.join(source, name), os.path.join(theme, name)) - zip.close() + zip = None + try: + zip = zipfile.ZipFile(themePath, u'w') + source = os.path.join(self.path, theme) + for root, dirs, files in os.walk(source): + for name in files: + zip.write( + os.path.join(source, name), os.path.join(theme, name)) + except: + log.exception(u'Export Theme Failed') + finally: + if zip: + zip.close() def onImportTheme(self): files = QtGui.QFileDialog.getOpenFileNames( @@ -263,7 +267,7 @@ class ThemeManager(QtGui.QWidget): self.pushThemes() def pushThemes(self): - Receiver().send_message(u'update_themes', self.getThemes() ) + Receiver.send_message(u'update_themes', self.getThemes() ) def getThemes(self): return self.themelist @@ -272,15 +276,10 @@ class ThemeManager(QtGui.QWidget): log.debug(u'getthemedata for theme %s', themename) xml_file = os.path.join(self.path, unicode(themename), unicode(themename) + u'.xml') - try: - xml = file_to_xml(xml_file) - except: + xml = get_text_file_string(xml_file) + if not xml: xml = self.baseTheme() - theme = ThemeXML() - theme.parse(xml) - self.cleanTheme(theme) - theme.extend_image_filename(self.path) - return theme + return self.createThemeFromXml(xml, self.path) def checkThemesExists(self, dir): log.debug(u'check themes') @@ -295,44 +294,49 @@ class ThemeManager(QtGui.QWidget): """ log.debug(u'Unzipping theme %s', filename) filename = unicode(filename) + zip = None + outfile = None try: zip = zipfile.ZipFile(filename) + filexml = None + themename = None + for file in zip.namelist(): + if file.endswith(os.path.sep): + theme_dir = os.path.join(dir, file) + if not os.path.exists(theme_dir): + os.mkdir(os.path.join(dir, file)) + else: + fullpath = os.path.join(dir, file) + names = file.split(os.path.sep) + if len(names) > 1: + # not preview file + if themename is None: + themename = names[0] + xml_data = zip.read(file) + if os.path.splitext(file)[1].lower() in [u'.xml']: + if self.checkVersion1(xml_data): + # upgrade theme xml + filexml = self.migrateVersion122(filename, + fullpath, xml_data) + else: + filexml = xml_data + outfile = open(fullpath, u'w') + outfile.write(filexml) + else: + outfile = open(fullpath, u'w') + outfile.write(zip.read(file)) + self.generateAndSaveImage(dir, themename, filexml) except: QtGui.QMessageBox.critical( self, self.trUtf8(u'Error'), self.trUtf8(u'File is not a valid theme!'), QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Ok)) - return - filexml = None - themename = None - for file in zip.namelist(): - if file.endswith(os.path.sep): - theme_dir = os.path.join(dir, file) - if not os.path.exists(theme_dir): - os.mkdir(os.path.join(dir, file)) - else: - fullpath = os.path.join(dir, file) - names = file.split(os.path.sep) - if len(names) > 1: - # not preview file - if themename is None: - themename = names[0] - xml_data = zip.read(file) - if os.path.splitext(file)[1].lower() in [u'.xml']: - if self.checkVersion1(xml_data): - # upgrade theme xml - filexml = self.migrateVersion122(filename, - fullpath, xml_data) - else: - filexml = xml_data - outfile = open(fullpath, u'w') - outfile.write(filexml) - outfile.close() - else: - outfile = open(fullpath, u'w') - outfile.write(zip.read(file)) - outfile.close() - self.generateAndSaveImage(dir, themename, filexml) + log.exception(u'Importing theme from zip file failed') + finally: + if zip: + zip.close() + if outfile: + outfile.close() def checkVersion1(self, xmlfile): """ @@ -412,13 +416,22 @@ class ThemeManager(QtGui.QWidget): result == QtGui.QMessageBox.Yes if result == QtGui.QMessageBox.Yes: # Save the theme, overwriting the existing theme if necessary. - outfile = open(theme_file, u'w') - outfile.write(theme_pretty_xml) - outfile.close() + outfile = None + try: + outfile = open(theme_file, u'w') + outfile.write(theme_pretty_xml) + except: + log.exception(u'Saving theme to file failed') + finally: + if outfile: + outfile.close() if image_from and image_from != image_to: print "if", image_from print "it", image_to - shutil.copyfile(image_from, image_to) + try: + shutil.copyfile(image_from, image_to) + except: + log.exception(u'Failed to save theme image') self.generateAndSaveImage(self.path, name, theme_xml) self.loadThemes() else: @@ -428,10 +441,7 @@ class ThemeManager(QtGui.QWidget): def generateAndSaveImage(self, dir, name, theme_xml): log.debug(u'generateAndSaveImage %s %s %s', dir, name, theme_xml) - theme = ThemeXML() - theme.parse(theme_xml) - self.cleanTheme(theme) - theme.extend_image_filename(dir) + theme = self.createThemeFromXml(theme_xml, dir) frame = self.generateImage(theme) samplepathname = os.path.join(self.path, name + u'.png') if os.path.exists(samplepathname): @@ -465,6 +475,13 @@ class ThemeManager(QtGui.QWidget): unicode(u'#FFFFFF'), unicode(0), unicode(0), unicode(0)) return newtheme.extract_xml() + def createThemeFromXml(self, theme_xml, path): + theme = ThemeXML() + theme.parse(theme_xml) + self.cleanTheme(theme) + theme.extend_image_filename(path) + return theme + def cleanTheme(self, theme): theme.background_color = theme.background_color.strip() theme.background_direction = theme.background_direction.strip() @@ -486,6 +503,8 @@ class ThemeManager(QtGui.QWidget): theme.display_wrapStyle = theme.display_wrapStyle.strip() theme.font_footer_color = theme.font_footer_color.strip() theme.font_footer_height = int(theme.font_footer_height.strip()) + theme.font_footer_indentation = \ + int(theme.font_footer_indentation.strip()) theme.font_footer_italics = str_to_bool(theme.font_footer_italics) theme.font_footer_name = theme.font_footer_name.strip() #theme.font_footer_override diff --git a/openlp/core/ui/themestab.py b/openlp/core/ui/themestab.py index 2afbf297c..d5c91c5e3 100644 --- a/openlp/core/ui/themestab.py +++ b/openlp/core/ui/themestab.py @@ -135,10 +135,10 @@ class ThemesTab(SettingsTab): def save(self): self.config.set_config(u'theme global style', self.global_style ) self.config.set_config(u'theme global theme',self.global_theme) - Receiver().send_message(u'update_global_theme', self.global_theme ) + Receiver.send_message(u'update_global_theme', self.global_theme ) def postSetUp(self): - Receiver().send_message(u'update_global_theme', self.global_theme ) + Receiver.send_message(u'update_global_theme', self.global_theme ) def onSongLevelButtonPressed(self): self.global_style = u'Song' diff --git a/openlp/core/utils/registry.py b/openlp/core/utils/registry.py index 3a1ceb520..e56d939ef 100644 --- a/openlp/core/utils/registry.py +++ b/openlp/core/utils/registry.py @@ -101,23 +101,29 @@ class Registry(object): return False def _load(self): + if not os.path.isfile(self.file_name): + return False + file_handle = None try: - if not os.path.isfile(self.file_name): - return False file_handle = open(self.file_name, u'r') self.config.readfp(file_handle) - file_handle.close() return True except: return False + finally: + if file_handle: + file_handle.close() def _save(self): + file_handle = None try: if not os.path.exists(os.path.dirname(self.file_name)): os.makedirs(os.path.dirname(self.file_name)) file_handle = open(self.file_name, u'w') self.config.write(file_handle) - file_handle.close() return self._load() except: return False + finally: + if file_handle: + file_handle.close() diff --git a/openlp/plugins/bibles/bibleplugin.py b/openlp/plugins/bibles/bibleplugin.py index ef0308925..dfc3595f7 100644 --- a/openlp/plugins/bibles/bibleplugin.py +++ b/openlp/plugins/bibles/bibleplugin.py @@ -41,9 +41,6 @@ class BiblePlugin(Plugin): #Register the bible Manager self.biblemanager = None - def can_be_disabled(self): - return True - def initialise(self): log.info(u'bibles Initialising') if self.biblemanager is None: diff --git a/openlp/plugins/bibles/forms/bibleimportform.py b/openlp/plugins/bibles/forms/bibleimportform.py index de7397355..633855ceb 100644 --- a/openlp/plugins/bibles/forms/bibleimportform.py +++ b/openlp/plugins/bibles/forms/bibleimportform.py @@ -59,6 +59,7 @@ class BibleImportForm(QtGui.QDialog, Ui_BibleImportDialog): filepath = os.path.split(os.path.abspath(__file__))[0] filepath = os.path.abspath(os.path.join(filepath, u'..', u'resources', u'crosswalkbooks.csv')) + fbibles = None try: fbibles = open(filepath, 'r') for line in fbibles: @@ -66,6 +67,9 @@ class BibleImportForm(QtGui.QDialog, Ui_BibleImportDialog): self.cwBibleVersions[p[0]] = p[1].replace(u'\n', u'') except: log.exception(u'Crosswalk resources missing') + finally: + if fbibles: + fbibles.close() #Load and store BibleGateway Bibles filepath = os.path.split(os.path.abspath(__file__))[0] filepath = os.path.abspath(os.path.join(filepath, u'..', @@ -77,6 +81,9 @@ class BibleImportForm(QtGui.QDialog, Ui_BibleImportDialog): self.bgBibleVersions[p[0]] = p[1].replace(u'\n', u'') except: log.exception(u'Biblegateway resources missing') + finally: + if fbibles: + fbibles.close() self.loadBibleCombo(self.cwBibleVersions) self.cwActive = True @@ -125,7 +132,7 @@ class BibleImportForm(QtGui.QDialog, Ui_BibleImportDialog): filename = QtGui.QFileDialog.getOpenFileName( self, self.trUtf8(u'Open Bible Verses file'), self.config.get_last_dir(1)) - if filename != u'': + if filename: self.VerseLocationEdit.setText(filename) self.config.set_last_dir(filename, 1) self.setCsv() @@ -134,7 +141,7 @@ class BibleImportForm(QtGui.QDialog, Ui_BibleImportDialog): filename = QtGui.QFileDialog.getOpenFileName( self, self.trUtf8(u'Open Bible Books file'), self.config.get_last_dir(2)) - if filename != u'': + if filename: self.BooksLocationEdit.setText(filename) self.config.set_last_dir(filename, 2) self.setCsv() @@ -143,7 +150,7 @@ class BibleImportForm(QtGui.QDialog, Ui_BibleImportDialog): filename = QtGui.QFileDialog.getOpenFileName( self, self.trUtf8(u'Open OSIS import file'), self.config.get_last_dir(3)) - if filename != u'': + if filename: self.OSISLocationEdit.setText(filename) self.config.set_last_dir(filename, 3) self.setOsis() @@ -192,9 +199,9 @@ class BibleImportForm(QtGui.QDialog, Ui_BibleImportDialog): def onCancelButtonClicked(self): # tell import to stop self.message = self.trUtf8(u'Bible import stopped') - Receiver().send_message(u'stop_import') + Receiver.send_message(u'stop_import') # tell bibleplugin to reload the bibles - Receiver().send_message(u'pre_load_bibles') + Receiver.send_message(u'pre_load_bibles') self.close() def onImportButtonClicked(self): @@ -213,7 +220,7 @@ class BibleImportForm(QtGui.QDialog, Ui_BibleImportDialog): self.MessageLabel.setText(message) self.ProgressBar.setValue(self.barmax) # tell bibleplugin to reload the bibles - Receiver().send_message(u'pre_load_bibles') + Receiver.send_message(u'pre_load_bibles') QtGui.QMessageBox.information(self, self.trUtf8(u'Information'), self.trUtf8(message)) diff --git a/openlp/plugins/bibles/lib/bibleCSVimpl.py b/openlp/plugins/bibles/lib/bibleCSVimpl.py index a2fed1ce5..3410478fb 100644 --- a/openlp/plugins/bibles/lib/bibleCSVimpl.py +++ b/openlp/plugins/bibles/lib/bibleCSVimpl.py @@ -40,7 +40,7 @@ class BibleCSVImpl(BibleCommon): """ self.bibledb = bibledb self.loadbible = True - QtCore.QObject.connect(Receiver().get_receiver(), + QtCore.QObject.connect(Receiver.get_receiver(), QtCore.SIGNAL(u'openlpstopimport'), self.stop_import) def stop_import(self): @@ -48,46 +48,59 @@ class BibleCSVImpl(BibleCommon): def load_data(self, booksfile, versesfile, dialogobject): #Populate the Tables - fbooks = open(booksfile, 'r') - fverse = open(versesfile, 'r') + fbooks = None + try: + fbooks = open(booksfile, 'r') + count = 0 + for line in fbooks: + # cancel pressed + if not self.loadbible: + break + details = chardet.detect(line) + line = unicode(line, details['encoding']) + p = line.split(u',') + p1 = p[1].replace(u'"', u'') + p2 = p[2].replace(u'"', u'') + p3 = p[3].replace(u'"', u'') + self.bibledb.create_book(p2, p3, int(p1)) + count += 1 + #Flush the screen events + if count % 3 == 0: + Receiver.send_message(u'process_events') + count = 0 + except: + log.exception(u'Loading books from file failed') + finally: + if fbooks: + fbooks.close() - count = 0 - for line in fbooks: - # cancel pressed - if not self.loadbible: - break - details = chardet.detect(line) - line = unicode(line, details['encoding']) - p = line.split(u',') - p1 = p[1].replace(u'"', u'') - p2 = p[2].replace(u'"', u'') - p3 = p[3].replace(u'"', u'') - self.bibledb.create_book(p2, p3, int(p1)) - count += 1 - #Flush the screen events - if count % 3 == 0: - Receiver().send_message(u'process_events') - count = 0 - - count = 0 - book_ptr = None - for line in fverse: - if not self.loadbible: # cancel pressed - break - details = chardet.detect(line) - line = unicode(line, details['encoding']) - # split into 3 units and leave the rest as a single field - p = line.split(u',', 3) - p0 = p[0].replace(u'"', u'') - p3 = p[3].replace(u'"',u'') - if book_ptr is not p0: - book = self.bibledb.get_bible_book(p0) - book_ptr = book.name - # increament the progress bar - dialogobject.incrementProgressBar(book.name) - self.bibledb.add_verse(book.id, p[1], p[2], p3) - count += 1 - #Every x verses repaint the screen - if count % 3 == 0: - Receiver().send_message(u'process_events') - count = 0 + fverse = None + try: + fverse = open(versesfile, 'r') + count = 0 + book_ptr = None + for line in fverse: + if not self.loadbible: # cancel pressed + break + details = chardet.detect(line) + line = unicode(line, details['encoding']) + # split into 3 units and leave the rest as a single field + p = line.split(u',', 3) + p0 = p[0].replace(u'"', u'') + p3 = p[3].replace(u'"',u'') + if book_ptr is not p0: + book = self.bibledb.get_bible_book(p0) + book_ptr = book.name + # increament the progress bar + dialogobject.incrementProgressBar(book.name) + self.bibledb.add_verse(book.id, p[1], p[2], p3) + count += 1 + #Every x verses repaint the screen + if count % 3 == 0: + Receiver.send_message(u'process_events') + count = 0 + except: + log.exception(u'Loading verses from file failed') + finally: + if fverse: + fverse.close() diff --git a/openlp/plugins/bibles/lib/bibleOSISimpl.py b/openlp/plugins/bibles/lib/bibleOSISimpl.py index 04609e262..0e16c0256 100644 --- a/openlp/plugins/bibles/lib/bibleOSISimpl.py +++ b/openlp/plugins/bibles/lib/bibleOSISimpl.py @@ -60,13 +60,21 @@ class BibleOSISImpl(): filepath = os.path.split(os.path.abspath(__file__))[0] filepath = os.path.abspath(os.path.join( filepath, u'..', u'resources',u'osisbooks.csv')) - fbibles = open(filepath, u'r') - for line in fbibles: - p = line.split(u',') - self.booksOfBible[p[0]] = p[1].replace(u'\n', u'') - self.abbrevOfBible[p[0]] = p[2].replace(u'\n', u'') - self.loadbible = True - QtCore.QObject.connect(Receiver().get_receiver(), + fbibles = None + try: + fbibles = open(filepath, u'r') + for line in fbibles: + p = line.split(u',') + self.booksOfBible[p[0]] = p[1].replace(u'\n', u'') + self.abbrevOfBible[p[0]] = p[2].replace(u'\n', u'') + self.loadbible = True + except: + log.exception(u'OSIS bible import failed') + finally: + self.loadbible = False + if fbibles: + fbibles.close() + QtCore.QObject.connect(Receiver.get_receiver(), QtCore.SIGNAL(u'openlpstopimport'), self.stop_import) def stop_import(self): @@ -86,82 +94,98 @@ class BibleOSISImpl(): The Import dialog, so that we can increase the counter on the progress bar. """ - detect_file = open(osisfile_record, u'r') - details = chardet.detect(detect_file.read(2048)) - detect_file.close() - osis = codecs.open(osisfile_record, u'r', details['encoding']) - book_ptr = None - count = 0 - verseText = u'', pos) - # Book Reference - ref = file_record[pos+15:epos-1] - #lets find the bible text only - # find start of text - pos = epos + 1 - # end of text - epos = file_record.find(u'', pos) - text = file_record[pos : epos] - #remove tags of extra information - text = self.remove_block(u'', text) - text = self.remove_block(u'', text) - text = self.remove_block(u'', text) - text = self.remove_tag(u'') - while pos > -1: - epos = text.find(u'', pos) - if epos == -1: # TODO - pos = -1 - else: - text = text[:pos] + text[epos + 4: ] - pos = text.find(u'') - pos = text.find(u'') - while pos > -1: - epos = text.find(u'', pos) - text = text[:pos] + text[epos + 4: ] - pos = text.find(u'') - # split up the reference - p = ref.split(u'.', 3) - if book_ptr != p[0]: - # first time through - if book_ptr is None: - # set the max book size depending on the first book read - if p[0] == u'Gen': - dialogobject.setMax(65) + detect_file = None + try: + detect_file = open(osisfile_record, u'r') + details = chardet.detect(detect_file.read(2048)) + except: + log.exception(u'Failed to detect OSIS file encoding') + return + finally: + if detect_file: + detect_file.close() + osis = None + try: + osis = codecs.open(osisfile_record, u'r', details['encoding']) + book_ptr = None + count = 0 + verseText = u'', pos) + # Book Reference + ref = file_record[pos+15:epos-1] + #lets find the bible text only + # find start of text + pos = epos + 1 + # end of text + epos = file_record.find(u'', pos) + text = file_record[pos : epos] + #remove tags of extra information + text = self.remove_block(u'', text) + text = self.remove_block(u'', text) + text = self.remove_block( + u'', text) + text = self.remove_tag(u'') + while pos > -1: + epos = text.find(u'', pos) + if epos == -1: # TODO + pos = -1 else: - dialogobject.setMax(27) - # First book of NT - if p[0] == u'Matt': - testament += 1 - book_ptr = p[0] - book = self.bibledb.create_book( - unicode(self.booksOfBible[p[0]]), - unicode(self.abbrevOfBible[p[0]]), - testament) - dialogobject.incrementProgressBar( - self.booksOfBible[p[0]]) - Receiver().send_message(u'process_events') - count = 0 - self.bibledb.add_verse(book.id, p[1], p[2], text) - count += 1 - #Every 3 verses repaint the screen - if count % 3 == 0: - Receiver().send_message(u'process_events') - count = 0 + text = text[:pos] + text[epos + 4: ] + pos = text.find(u'') + pos = text.find(u'') + while pos > -1: + epos = text.find(u'', pos) + text = text[:pos] + text[epos + 4: ] + pos = text.find(u'') + # split up the reference + p = ref.split(u'.', 3) + if book_ptr != p[0]: + # first time through + if book_ptr is None: + # set the max book size depending + # on the first book read + if p[0] == u'Gen': + dialogobject.setMax(65) + else: + dialogobject.setMax(27) + # First book of NT + if p[0] == u'Matt': + testament += 1 + book_ptr = p[0] + book = self.bibledb.create_book( + unicode(self.booksOfBible[p[0]]), + unicode(self.abbrevOfBible[p[0]]), + testament) + dialogobject.incrementProgressBar( + self.booksOfBible[p[0]]) + Receiver.send_message(u'process_events') + count = 0 + self.bibledb.add_verse(book.id, p[1], p[2], text) + count += 1 + #Every 3 verses repaint the screen + if count % 3 == 0: + Receiver.send_message(u'process_events') + count = 0 + except: + log.exception(u'Loading bible from OSIS file failed') + finally: + if osis: + osis.close() def remove_block(self, start_tag, end_tag, text): """ diff --git a/openlp/plugins/bibles/lib/common.py b/openlp/plugins/bibles/lib/common.py index 8cc4e461e..5ef9f5126 100644 --- a/openlp/plugins/bibles/lib/common.py +++ b/openlp/plugins/bibles/lib/common.py @@ -105,7 +105,8 @@ class BibleCommon(object): xml_string = u'' req = urllib2.Request(urlstring) #Make us look like an IE Browser on XP to stop blocking by web site - req.add_header(u'User-Agent', u'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)') + req.add_header(u'User-Agent', + u'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)') try: handle = urllib2.urlopen(req) html = handle.read() @@ -164,4 +165,3 @@ class BibleCommon(object): start_tag = text.find(u'<') text = text.replace(u'>', u'') return text.rstrip().lstrip() - diff --git a/openlp/plugins/bibles/lib/manager.py b/openlp/plugins/bibles/lib/manager.py index 1b308571e..c540ae60d 100644 --- a/openlp/plugins/bibles/lib/manager.py +++ b/openlp/plugins/bibles/lib/manager.py @@ -71,7 +71,7 @@ class BibleManager(object): def reload_bibles(self): log.debug(u'Reload bibles') files = self.config.get_files(self.bibleSuffix) - log.debug(u'Bible Files %s', files ) + log.debug(u'Bible Files %s', files) self.bible_db_cache = {} self.bible_http_cache = {} # books of the bible with testaments @@ -116,12 +116,19 @@ class BibleManager(object): filepath = os.path.split(os.path.abspath(__file__))[0] filepath = os.path.abspath(os.path.join( filepath, u'..', u'resources',u'httpbooks.csv')) - fbibles = open(filepath, u'r') - for line in fbibles: - p = line.split(u',') - self.book_abbreviations[p[0]] = p[1].replace(u'\n', '') - self.book_testaments[p[0]] = p[2].replace(u'\n', '') - self.book_chapters.append({u'book':p[0], u'total':p[3].replace(u'\n', '')}) + fbibles = None + try: + fbibles = open(filepath, u'r') + for line in fbibles: + p = line.split(u',') + self.book_abbreviations[p[0]] = p[1].replace(u'\n', '') + self.book_testaments[p[0]] = p[2].replace(u'\n', '') + self.book_chapters.append({u'book':p[0], u'total':p[3].replace(u'\n', '')}) + except: + log.exception(u'Failed to load bible') + finally: + if fbibles: + fbibles.close() log.debug(u'Bible Initialised') def process_dialog(self, dialogobject): diff --git a/openlp/plugins/bibles/lib/mediaitem.py b/openlp/plugins/bibles/lib/mediaitem.py index a8106d4f6..d2f469242 100644 --- a/openlp/plugins/bibles/lib/mediaitem.py +++ b/openlp/plugins/bibles/lib/mediaitem.py @@ -54,7 +54,7 @@ class BibleMediaItem(MediaManagerItem): MediaManagerItem.__init__(self, parent, icon, title) # place to store the search results self.search_results = {} - QtCore.QObject.connect(Receiver().get_receiver(), + QtCore.QObject.connect(Receiver.get_receiver(), QtCore.SIGNAL(u'openlpreloadbibles'), self.reloadBibles) def initPluginNameVisible(self): @@ -284,7 +284,7 @@ class BibleMediaItem(MediaManagerItem): def setQuickMessage(self, text): self.QuickMessage.setText(text) self.AdvancedMessage.setText(text) - Receiver().send_message(u'process_events') + Receiver.send_message(u'process_events') #minor delay to get the events processed time.sleep(0.1) diff --git a/openlp/plugins/custom/customplugin.py b/openlp/plugins/custom/customplugin.py index bcd61f5bc..3ae693e1e 100644 --- a/openlp/plugins/custom/customplugin.py +++ b/openlp/plugins/custom/customplugin.py @@ -54,9 +54,6 @@ class CustomPlugin(Plugin): # Create the CustomManagerItem object return CustomMediaItem(self, self.icon, self.name) - def can_be_disabled(self): - return True - def initialise(self): log.info(u'Plugin Initialising') Plugin.initialise(self) diff --git a/openlp/plugins/custom/forms/editcustomform.py b/openlp/plugins/custom/forms/editcustomform.py index a4fc7a44a..1fef1df65 100644 --- a/openlp/plugins/custom/forms/editcustomform.py +++ b/openlp/plugins/custom/forms/editcustomform.py @@ -21,6 +21,7 @@ # with this program; if not, write to the Free Software Foundation, Inc., 59 # # Temple Place, Suite 330, Boston, MA 02111-1307 USA # ############################################################################### +import logging from PyQt4 import QtCore, QtGui @@ -32,6 +33,9 @@ class EditCustomForm(QtGui.QDialog, Ui_customEditDialog): """ Class documentation goes here. """ + global log + log = logging.getLogger(u'EditCustomForm') + log.info(u'Custom Editor loaded') def __init__(self, custommanager, parent = None): """ Constructor @@ -40,6 +44,12 @@ class EditCustomForm(QtGui.QDialog, Ui_customEditDialog): #self.parent = parent self.setupUi(self) # Connecting signals and slots + self.previewButton = QtGui.QPushButton() + self.previewButton.setText(self.trUtf8(u'Save && Preview')) + self.buttonBox.addButton( + self.previewButton, QtGui.QDialogButtonBox.ActionRole) + QtCore.QObject.connect(self.buttonBox, + QtCore.SIGNAL(u'clicked(QAbstractButton*)'), self.onPreview) QtCore.QObject.connect(self.AddButton, QtCore.SIGNAL(u'pressed()'), self.onAddButtonPressed) QtCore.QObject.connect(self.EditButton, @@ -68,6 +78,12 @@ class EditCustomForm(QtGui.QDialog, Ui_customEditDialog): self.custommanager = custommanager self.initialise() + def onPreview(self, button): + log.debug(u'onPreview') + if button.text() == unicode(self.trUtf8(u'Save && Preview')) \ + and self.saveCustom(): + Receiver.send_message(u'preview_custom') + def initialise(self): self.editAll = False self.DeleteButton.setEnabled(False) @@ -89,7 +105,7 @@ class EditCustomForm(QtGui.QDialog, Ui_customEditDialog): for themename in themelist: self.ThemeComboBox.addItem(themename) - def loadCustom(self, id): + def loadCustom(self, id, preview): self.customSlide = CustomSlide() self.initialise() if id != 0: @@ -108,17 +124,27 @@ class EditCustomForm(QtGui.QDialog, Ui_customEditDialog): self.ThemeComboBox.setCurrentIndex(id) else: self.ThemeComboBox.setCurrentIndex(0) + #if not preview hide the preview button + self.previewButton.setVisible(False) + if preview: + self.previewButton.setVisible(True) def closePressed(self): - Receiver().send_message(u'remote_edit_clear') + Receiver.send_message(u'remote_edit_clear') self.close() def accept(self): + log.debug(u'accept') + if self.saveCustom(): + Receiver.send_message(u'load_custom_list') + self.close() + + def saveCustom(self): valid, message = self._validate() if not valid: QtGui.QMessageBox.critical(self, self.trUtf8(u'Error'), message, QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Ok)) - return + return False sxml = SongXMLBuilder() sxml.new_document() sxml.add_lyrics_to_song() @@ -132,8 +158,7 @@ class EditCustomForm(QtGui.QDialog, Ui_customEditDialog): self.customSlide.credits = unicode(self.CreditEdit.displayText()) self.customSlide.theme_name = unicode(self.ThemeComboBox.currentText()) self.custommanager.save_slide(self.customSlide) - Receiver().send_message(u'load_custom_list') - self.close() + return True def onUpButtonPressed(self): selectedRow = self.VerseListView.currentRow() diff --git a/openlp/plugins/custom/lib/mediaitem.py b/openlp/plugins/custom/lib/mediaitem.py index 2f64ac0a7..dbd7db643 100644 --- a/openlp/plugins/custom/lib/mediaitem.py +++ b/openlp/plugins/custom/lib/mediaitem.py @@ -50,7 +50,9 @@ class CustomMediaItem(MediaManagerItem): self.ListViewWithDnD_class = CustomListView self.servicePath = None MediaManagerItem.__init__(self, parent, icon, title) - self.fromServiceManager = -1 + # Holds information about whether the edit is remotly triggered and + # which Custom is required. + self.remoteCustom = -1 def addEndHeaderBar(self): QtCore.QObject.connect(Receiver.get_receiver(), @@ -59,6 +61,8 @@ class CustomMediaItem(MediaManagerItem): QtCore.SIGNAL(u'remote_edit_clear' ), self.onRemoteEditClear) QtCore.QObject.connect(Receiver.get_receiver(), QtCore.SIGNAL(u'load_custom_list'), self.initialise) + QtCore.QObject.connect(Receiver.get_receiver(), + QtCore.SIGNAL(u'preview_custom'), self.onPreviewClick) def initPluginNameVisible(self): self.PluginNameVisible = self.trUtf8(u'Custom') @@ -69,6 +73,14 @@ class CustomMediaItem(MediaManagerItem): def initialise(self): self.loadCustomListView(self.parent.custommanager.get_all_slides()) + #Called to redisplay the song list screen edith from a search + #or from the exit of the Song edit dialog. If remote editing is active + #Trigger it and clean up so it will not update again. + if self.remoteTriggered == u'L': + self.onAddClick() + if self.remoteTriggered == u'P': + self.onPreviewClick() + self.onRemoteEditClear() def loadCustomListView(self, list): self.ListView.clear() @@ -77,8 +89,6 @@ class CustomMediaItem(MediaManagerItem): custom_name.setData( QtCore.Qt.UserRole, QtCore.QVariant(CustomSlide.id)) self.ListView.addItem(custom_name) - if CustomSlide.id == self.fromServiceManager: - self.onAddClick() def onNewClick(self): self.parent.edit_custom_form.loadCustom(0) @@ -86,20 +96,29 @@ class CustomMediaItem(MediaManagerItem): self.initialise() def onRemoteEditClear(self): - self.fromServiceManager = -1 + self.remoteTriggered = None + self.remoteCustom = -1 - def onRemoteEdit(self, item_id): - valid = self.parent.custommanager.get_custom(item_id) + def onRemoteEdit(self, customid): + """ + Called by ServiceManager or SlideController by event passing + the Song Id in the payload along with an indicator to say which + type of display is required. + """ + fields = customid.split(u':') + valid = self.parent.custommanager.get_custom(fields[1]) if valid: - self.fromServiceManager = item_id - self.parent.edit_custom_form.loadCustom(item_id) + self.remoteCustom = fields[1] + self.remoteTriggered = fields[0] + self.parent.edit_custom_form.loadCustom(fields[1], + (fields[0] == u'P')) self.parent.edit_custom_form.exec_() def onEditClick(self): item = self.ListView.currentItem() if item: item_id = (item.data(QtCore.Qt.UserRole)).toInt()[0] - self.parent.edit_custom_form.loadCustom(item_id) + self.parent.edit_custom_form.loadCustom(item_id, False) self.parent.edit_custom_form.exec_() self.initialise() @@ -116,18 +135,17 @@ class CustomMediaItem(MediaManagerItem): raw_footer = [] slide = None theme = None - if self.fromServiceManager == -1: + if self.remoteTriggered is None: item = self.ListView.currentItem() if item is None: return False item_id = (item.data(QtCore.Qt.UserRole)).toInt()[0] else: - item_id = self.fromServiceManager - self.fromServiceManager = -1 + item_id = self.remoteCustom customSlide = self.parent.custommanager.get_custom(item_id) title = customSlide.title credit = customSlide.credits - service_item.editEnabled = True + service_item.edit_enabled = True service_item.editId = item_id theme = customSlide.theme_name if len(theme) is not 0 : @@ -137,9 +155,8 @@ class CustomMediaItem(MediaManagerItem): for verse in verseList: raw_slides.append(verse[1]) raw_footer.append(title + u' '+ credit) - if theme: - service_item.title = title - for slide in raw_slides: - service_item.add_from_text(slide[:30], slide) - service_item.raw_footer = raw_footer - return True + service_item.title = title + for slide in raw_slides: + service_item.add_from_text(slide[:30], slide) + service_item.raw_footer = raw_footer + return True \ No newline at end of file diff --git a/openlp/plugins/images/imageplugin.py b/openlp/plugins/images/imageplugin.py index 229b57f95..aba19f47f 100644 --- a/openlp/plugins/images/imageplugin.py +++ b/openlp/plugins/images/imageplugin.py @@ -37,9 +37,6 @@ class ImagePlugin(Plugin): self.weight = -7 self.icon = buildIcon(u':/media/media_image.png') - def can_be_disabled(self): - return True - def initialise(self): log.info(u'Plugin Initialising') Plugin.initialise(self) @@ -60,7 +57,7 @@ class ImagePlugin(Plugin): about_text = self.trUtf8(u'Image Plugin
Allows images of ' u'all types to be displayed. If a number of images are selected ' u'together and presented on the live controller it is possible ' - u'to turn them into a timed loop.
From the plugin if the ' + u'to turn them into a timed loop.From the plugin if the ' u'Override background is chosen and an image is selected ' u'any somgs which are rendered will use the selected image from ' u'the background instead of the one provied by the theme.
') diff --git a/openlp/plugins/images/lib/imagetab.py b/openlp/plugins/images/lib/imagetab.py index 84da616d0..40653706f 100644 --- a/openlp/plugins/images/lib/imagetab.py +++ b/openlp/plugins/images/lib/imagetab.py @@ -74,7 +74,7 @@ class ImageTab(SettingsTab): def save(self): self.config.set_config(u'loop delay', self.loop_delay) - Receiver().send_message(u'update_spin_delay', self.loop_delay) + Receiver.send_message(u'update_spin_delay', self.loop_delay) def postSetUp(self): - Receiver().send_message(u'update_spin_delay', self.loop_delay) + Receiver.send_message(u'update_spin_delay', self.loop_delay) diff --git a/openlp/plugins/media/lib/mediaitem.py b/openlp/plugins/media/lib/mediaitem.py index 708307868..bd9739e4a 100644 --- a/openlp/plugins/media/lib/mediaitem.py +++ b/openlp/plugins/media/lib/mediaitem.py @@ -79,7 +79,7 @@ class MediaMediaItem(MediaManagerItem): items = self.ListView.selectedIndexes() if len(items) > 1: return False - service_item.title = self.trUtf8(u'Media') + service_item.title = unicode(self.trUtf8(u'Media')) for item in items: bitem = self.ListView.item(item.row()) filename = unicode((bitem.data(QtCore.Qt.UserRole)).toString()) diff --git a/openlp/plugins/media/mediaplugin.py b/openlp/plugins/media/mediaplugin.py index acfd170d6..3295dabc2 100644 --- a/openlp/plugins/media/mediaplugin.py +++ b/openlp/plugins/media/mediaplugin.py @@ -42,9 +42,6 @@ class MediaPlugin(Plugin): def get_settings_tab(self): return MediaTab(self.name) - def can_be_disabled(self): - return True - def initialise(self): log.info(u'Plugin Initialising') Plugin.initialise(self) diff --git a/openlp/plugins/presentations/lib/impresscontroller.py b/openlp/plugins/presentations/lib/impresscontroller.py index 992b4c1ad..a1db9358a 100644 --- a/openlp/plugins/presentations/lib/impresscontroller.py +++ b/openlp/plugins/presentations/lib/impresscontroller.py @@ -272,7 +272,7 @@ class ImpressController(PresentationController): self.goto_slide(1) def get_slide_number(self): - return self.controller.getCurrentSlideIndex() + return self.controller.getCurrentSlideIndex() + 1 def get_slide_count(self): return self.document.getDrawPages().getCount() diff --git a/openlp/plugins/presentations/lib/mediaitem.py b/openlp/plugins/presentations/lib/mediaitem.py index cb4bfd320..f0b4785ae 100644 --- a/openlp/plugins/presentations/lib/mediaitem.py +++ b/openlp/plugins/presentations/lib/mediaitem.py @@ -136,18 +136,18 @@ class PresentationMediaItem(MediaManagerItem): return False service_item.title = unicode(self.DisplayTypeComboBox.currentText()) service_item.shortname = unicode(self.DisplayTypeComboBox.currentText()) - cont = self.controllers[service_item.shortname] + controller = self.controllers[service_item.shortname] for item in items: bitem = self.ListView.item(item.row()) filename = unicode((bitem.data(QtCore.Qt.UserRole)).toString()) (path, name) = os.path.split(filename) - cont.store_filename(filename) - if cont.get_slide_preview_file(1) is None: - cont.load_presentation(filename) + controller.store_filename(filename) + if controller.get_slide_preview_file(1) is None: + controller.load_presentation(filename) i = 1 - img = cont.get_slide_preview_file(i) + img = controller.get_slide_preview_file(i) while img: service_item.add_from_command(path, name, img) i = i + 1 - img = cont.get_slide_preview_file(i) + img = controller.get_slide_preview_file(i) return True diff --git a/openlp/plugins/presentations/lib/messagelistener.py b/openlp/plugins/presentations/lib/messagelistener.py index 5008c1e17..022b2cd3f 100644 --- a/openlp/plugins/presentations/lib/messagelistener.py +++ b/openlp/plugins/presentations/lib/messagelistener.py @@ -41,6 +41,7 @@ class MessageListener(object): def __init__(self, controllers): self.controllers = controllers self.handler = None + self.is_live = None # messages are sent from core.ui.slidecontroller QtCore.QObject.connect(Receiver.get_receiver(), QtCore.SIGNAL(u'presentations_start'), self.startup) @@ -60,20 +61,25 @@ class MessageListener(object): QtCore.SIGNAL(u'presentations_blank'), self.blank) QtCore.QObject.connect(Receiver.get_receiver(), QtCore.SIGNAL(u'presentations_unblank'), self.unblank) + self.timer = QtCore.QTimer() + self.timer.setInterval(500) + QtCore.QObject.connect(self.timer, QtCore.SIGNAL("timeout()"), self.timeout) def startup(self, message): """ Start of new presentation Save the handler as any new presentations start here """ - self.handler, file = self.decodeMessage(message) + self.handler, file, self.is_live = self.decodeMessage(message) self.controller = self.controllers[self.handler] if self.controller.is_loaded(): self.shutdown(None) self.controller.load_presentation(file) - self.controller.start_presentation() + if self.is_live: + self.controller.start_presentation() + Receiver.send_message(u'live_slide_hide') self.controller.slidenumber = 0 - self.controller.timer.start() + self.timer.start() def activate(self): if self.controller.is_active(): @@ -83,63 +89,79 @@ class MessageListener(object): self.controller.start_presentation() if self.controller.slidenumber > 1: self.controller.goto_slide(self.controller.slidenumber) - + def slide(self, message): + if not self.is_live: + return self.activate() if message: self.controller.goto_slide(message[0]+1) - self.controller.poll_slidenumber() + self.controller.poll_slidenumber(self.is_live) def first(self, message): """ Based on the handler passed at startup triggers the first slide """ + if not self.is_live: + return self.activate() self.controller.start_presentation() - self.controller.poll_slidenumber() + self.controller.poll_slidenumber(self.is_live) def last(self, message): """ Based on the handler passed at startup triggers the first slide """ + if not self.is_live: + return self.activate() self.controller.goto_slide(self.controller.get_slide_count()) - self.controller.poll_slidenumber() + self.controller.poll_slidenumber(self.is_live) def next(self, message): """ Based on the handler passed at startup triggers the next slide event """ + if not self.is_live: + return self.activate() self.controller.next_step() - self.controller.poll_slidenumber() + self.controller.poll_slidenumber(self.is_live) def previous(self, message): """ Based on the handler passed at startup triggers the previous slide event """ + if not self.is_live: + return self.activate() self.controller.previous_step() - self.controller.poll_slidenumber() + self.controller.poll_slidenumber(self.is_live) def shutdown(self, message): """ Based on the handler passed at startup triggers slide show to shut down """ + if self.is_live: + Receiver.send_message(u'live_slide_show') self.controller.close_presentation() self.controller.slidenumber = 0 - self.controller.timer.shutdown() + self.timer.stop() def blank(self): + if not self.is_live: + return if not self.controller.is_loaded(): return if not self.controller.is_active(): return - self.controller.blank_screen() + self.controller.blank_screen() def unblank(self): + if not self.is_live: + return self.activate() - self.controller.unblank_screen() + self.controller.unblank_screen() def decodeMessage(self, message): """ @@ -149,4 +171,7 @@ class MessageListener(object): Message containing Presentaion handler name and file to be presented. """ file = os.path.join(message[1], message[2]) - return message[0], file + return message[0], file, message[3] + + def timeout(self): + self.controller.poll_slidenumber(self.is_live) diff --git a/openlp/plugins/presentations/lib/presentationcontroller.py b/openlp/plugins/presentations/lib/presentationcontroller.py index 00f6f8495..1e33ceab4 100644 --- a/openlp/plugins/presentations/lib/presentationcontroller.py +++ b/openlp/plugins/presentations/lib/presentationcontroller.py @@ -50,25 +50,25 @@ class PresentationController(object): ``name`` The name that appears in the options and the media manager - + ``enabled`` The controller is enabled ``available`` The controller is available on this machine. Set by init via call to check_available - + ``plugin`` The presentationplugin object **Hook Functions** - + ``kill()`` Called at system exit to clean up any running presentations ``check_available()`` Returns True if presentation application is installed/can run on this machine - + ``presentation_deleted()`` Deletes presentation specific files, e.g. thumbnails @@ -83,7 +83,7 @@ class PresentationController(object): ``is_active()`` Returns True if a presentation is currently running - + ``blank_screen()`` Blanks the screen, making it black. @@ -118,7 +118,7 @@ class PresentationController(object): global log log = logging.getLogger(u'PresentationController') log.info(u'loaded') - + def __init__(self, plugin=None, name=u'PresentationController'): """ This is the constructor for the presentationcontroller object. @@ -149,9 +149,6 @@ class PresentationController(object): self.thumbnailprefix = u'slide' if not os.path.isdir(self.thumbnailroot): os.makedirs(self.thumbnailroot) - self.timer = QtCore.QTimer() - self.timer.setInterval(500) - QtCore.QObject.connect(self.timer, QtCore.SIGNAL("timeout()"), self.poll_slidenumber) def check_available(self): """ @@ -166,7 +163,7 @@ class PresentationController(object): """ self.store_filename(presentation) shutil.rmtree(self.thumbnailpath) - + def start_process(self): """ Loads a running version of the presentation application in the background. @@ -188,6 +185,7 @@ class PresentationController(object): ``presentation`` The file name of the presentations to the run. + """ pass @@ -207,9 +205,7 @@ class PresentationController(object): recent than the powerpoint """ lastimage = self.get_slide_preview_file(self.get_slide_count()) - if lastimage is None: - return False - if not os.path.isfile(lastimage): + if not (lastimage and os.path.isfile(lastimage)): return False imgdate = os.stat(lastimage).st_mtime pptdate = os.stat(self.filepath).st_mtime @@ -233,7 +229,7 @@ class PresentationController(object): Returns true if a presentation is loaded """ return False - + def blank_screen(self): """ Blanks the screen, making it black. @@ -301,7 +297,7 @@ class PresentationController(object): """ return None - def poll_slidenumber(self): + def poll_slidenumber(self, is_live): """ Check the current slide number """ @@ -311,5 +307,9 @@ class PresentationController(object): if current == self.slidenumber: return self.slidenumber = current - Receiver().send_message(u'slidecontroller_change', self.slidenumber - 1) - \ No newline at end of file + if is_live: + prefix = u'live' + else: + prefix = u'preview' + Receiver.send_message(u'%s_slidecontroller_change' % prefix, + self.slidenumber - 1) diff --git a/openlp/plugins/presentations/presentationplugin.py b/openlp/plugins/presentations/presentationplugin.py index e54220e08..11d582925 100644 --- a/openlp/plugins/presentations/presentationplugin.py +++ b/openlp/plugins/presentations/presentationplugin.py @@ -46,9 +46,6 @@ class PresentationPlugin(Plugin): """ return PresentationTab(self.name, self.controllers) - def can_be_disabled(self): - return True - def initialise(self): log.info(u'Presentations Initialising') Plugin.initialise(self) diff --git a/openlp/plugins/remotes/remoteplugin.py b/openlp/plugins/remotes/remoteplugin.py index 3a350cd0c..13664da1e 100644 --- a/openlp/plugins/remotes/remoteplugin.py +++ b/openlp/plugins/remotes/remoteplugin.py @@ -40,9 +40,6 @@ class RemotesPlugin(Plugin): self.weight = -1 self.server = None - def can_be_disabled(self): - return True - def initialise(self): log.debug(u'initialise') Plugin.initialise(self) @@ -76,9 +73,9 @@ class RemotesPlugin(Plugin): pos = datagram.find(u':') event = unicode(datagram[:pos].lower()) if event == u'alert': - Receiver().send_message(u'alert_text', unicode(datagram[pos + 1:])) + Receiver.send_message(u'alert_text', unicode(datagram[pos + 1:])) if event == u'next_slide': - Receiver().send_message(u'live_slide_next') + Receiver.send_message(u'live_slide_next') def about(self): about_text = self.trUtf8(u'Remote Plugin
This plugin ' diff --git a/openlp/plugins/songs/forms/editsongform.py b/openlp/plugins/songs/forms/editsongform.py index 96a5021be..714442de1 100644 --- a/openlp/plugins/songs/forms/editsongform.py +++ b/openlp/plugins/songs/forms/editsongform.py @@ -404,16 +404,16 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog): log.debug(u'onPreview') if button.text() == unicode(self.trUtf8(u'Save && Preview')) \ and self.saveSong(): - Receiver().send_message(u'preview_song') + Receiver.send_message(u'preview_song') def closePressed(self): - Receiver().send_message(u'remote_edit_clear') + Receiver.send_message(u'remote_edit_clear') self.close() def accept(self): log.debug(u'accept') if self.saveSong(): - Receiver().send_message(u'load_song_list') + Receiver.send_message(u'load_song_list') self.close() def saveSong(self): @@ -462,7 +462,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog): text = text.replace(u'}', u'') text = text.replace(u'?', u'') self.song.search_lyrics = unicode(text) - self.song.lyrics = unicode(sxml.extract_xml()) + self.song.lyrics = unicode(sxml.extract_xml(), u'utf-8') except: log.exception(u'Problem processing song Lyrics \n%s', sxml.dump_xml()) diff --git a/openlp/plugins/songs/lib/mediaitem.py b/openlp/plugins/songs/lib/mediaitem.py index 33ee028e2..000f752af 100644 --- a/openlp/plugins/songs/lib/mediaitem.py +++ b/openlp/plugins/songs/lib/mediaitem.py @@ -287,11 +287,11 @@ class SongMediaItem(MediaManagerItem): item_id = self.remoteSong song = self.parent.songmanager.get_song(item_id) service_item.theme = song.theme_name - service_item.editEnabled = True + service_item.edit_enabled = True service_item.editId = item_id service_item.verse_order = song.verse_order if song.lyrics.startswith(u'theme_export.png theme_import.png + + page_chorus.png + page_bridge.png + page_1.png + page_2.png + page_3.png + page_4.png + page_5.png + page_6.png + page_7.png + page_8.png + page_9.png + page_10.png + page_11.png + page_12.png + diff --git a/resources/images/page_1.png b/resources/images/page_1.png new file mode 100644 index 000000000..091ab47f0 Binary files /dev/null and b/resources/images/page_1.png differ diff --git a/resources/images/page_10.png b/resources/images/page_10.png new file mode 100644 index 000000000..a4b2f7491 Binary files /dev/null and b/resources/images/page_10.png differ diff --git a/resources/images/page_11.png b/resources/images/page_11.png new file mode 100644 index 000000000..a4b2f7491 Binary files /dev/null and b/resources/images/page_11.png differ diff --git a/resources/images/page_12.png b/resources/images/page_12.png new file mode 100644 index 000000000..a4b2f7491 Binary files /dev/null and b/resources/images/page_12.png differ diff --git a/resources/images/page_2.png b/resources/images/page_2.png new file mode 100644 index 000000000..c4661947f Binary files /dev/null and b/resources/images/page_2.png differ diff --git a/resources/images/page_3.png b/resources/images/page_3.png new file mode 100644 index 000000000..30f65b2d9 Binary files /dev/null and b/resources/images/page_3.png differ diff --git a/resources/images/page_4.png b/resources/images/page_4.png new file mode 100644 index 000000000..7749bc3c3 Binary files /dev/null and b/resources/images/page_4.png differ diff --git a/resources/images/page_5.png b/resources/images/page_5.png new file mode 100644 index 000000000..065dfa293 Binary files /dev/null and b/resources/images/page_5.png differ diff --git a/resources/images/page_6.png b/resources/images/page_6.png new file mode 100644 index 000000000..a4b2f7491 Binary files /dev/null and b/resources/images/page_6.png differ diff --git a/resources/images/page_7.png b/resources/images/page_7.png new file mode 100644 index 000000000..a4b2f7491 Binary files /dev/null and b/resources/images/page_7.png differ diff --git a/resources/images/page_8.png b/resources/images/page_8.png new file mode 100644 index 000000000..a4b2f7491 Binary files /dev/null and b/resources/images/page_8.png differ diff --git a/resources/images/page_9.png b/resources/images/page_9.png new file mode 100644 index 000000000..a4b2f7491 Binary files /dev/null and b/resources/images/page_9.png differ diff --git a/resources/images/page_bridge.png b/resources/images/page_bridge.png new file mode 100644 index 000000000..7d81a4a71 Binary files /dev/null and b/resources/images/page_bridge.png differ diff --git a/resources/images/page_chorus.png b/resources/images/page_chorus.png new file mode 100644 index 000000000..80c3a6d53 Binary files /dev/null and b/resources/images/page_chorus.png differ diff --git a/version.txt b/version.txt index 3774bd35d..9ad850423 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.9.0-656 +1.9.0-675