forked from openlp/openlp
bugfixing and Head
This commit is contained in:
commit
d7f4298fbb
@ -137,7 +137,7 @@ def image_to_byte(image):
|
||||
# convert to base64 encoding so does not get missed!
|
||||
return byte_array.toBase64()
|
||||
|
||||
def resize_image(image_path, width, height, background=QtCore.Qt.black):
|
||||
def resize_image(image_path, width, height, background):
|
||||
"""
|
||||
Resize an image to fit on the current screen.
|
||||
|
||||
|
@ -31,11 +31,13 @@ import logging
|
||||
import os
|
||||
|
||||
from PyQt4 import QtCore
|
||||
from sqlalchemy import create_engine, MetaData
|
||||
from sqlalchemy.exc import InvalidRequestError
|
||||
from sqlalchemy.orm import scoped_session, sessionmaker
|
||||
from sqlalchemy import Table, MetaData, Column, types, create_engine
|
||||
from sqlalchemy.exc import SQLAlchemyError, InvalidRequestError, DBAPIError
|
||||
from sqlalchemy.orm import scoped_session, sessionmaker, mapper
|
||||
from sqlalchemy.pool import NullPool
|
||||
|
||||
from openlp.core.lib import translate
|
||||
from openlp.core.lib.ui import critical_error_message_box
|
||||
from openlp.core.utils import AppLocation, delete_file
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
@ -59,6 +61,64 @@ def init_db(url, auto_flush=True, auto_commit=False):
|
||||
autocommit=auto_commit, bind=engine))
|
||||
return session, metadata
|
||||
|
||||
|
||||
def upgrade_db(url, upgrade):
|
||||
"""
|
||||
Upgrade a database.
|
||||
|
||||
``url``
|
||||
The url of the database to upgrade.
|
||||
|
||||
``upgrade``
|
||||
The python module that contains the upgrade instructions.
|
||||
"""
|
||||
session, metadata = init_db(url)
|
||||
|
||||
class Metadata(BaseModel):
|
||||
"""
|
||||
Provides a class for the metadata table.
|
||||
"""
|
||||
pass
|
||||
load_changes = True
|
||||
try:
|
||||
tables = upgrade.upgrade_setup(metadata)
|
||||
except SQLAlchemyError, DBAPIError:
|
||||
load_changes = False
|
||||
metadata_table = Table(u'metadata', metadata,
|
||||
Column(u'key', types.Unicode(64), primary_key=True),
|
||||
Column(u'value', types.UnicodeText(), default=None)
|
||||
)
|
||||
metadata_table.create(checkfirst=True)
|
||||
mapper(Metadata, metadata_table)
|
||||
version_meta = session.query(Metadata).get(u'version')
|
||||
if version_meta is None:
|
||||
version_meta = Metadata.populate(key=u'version', value=u'0')
|
||||
version = 0
|
||||
else:
|
||||
version = int(version_meta.value)
|
||||
if version > upgrade.__version__:
|
||||
return version, upgrade.__version__
|
||||
version += 1
|
||||
if load_changes:
|
||||
while hasattr(upgrade, u'upgrade_%d' % version):
|
||||
log.debug(u'Running upgrade_%d', version)
|
||||
try:
|
||||
getattr(upgrade, u'upgrade_%d' % version) \
|
||||
(session, metadata, tables)
|
||||
version_meta.value = unicode(version)
|
||||
except SQLAlchemyError, DBAPIError:
|
||||
log.exception(u'Could not run database upgrade script '
|
||||
'"upgrade_%s", upgrade process has been halted.', version)
|
||||
break
|
||||
version += 1
|
||||
else:
|
||||
version_meta = Metadata.populate(key=u'version',
|
||||
value=int(upgrade.__version__))
|
||||
session.add(version_meta)
|
||||
session.commit()
|
||||
return int(version_meta.value), upgrade.__version__
|
||||
|
||||
|
||||
def delete_database(plugin_name, db_file_name=None):
|
||||
"""
|
||||
Remove a database file from the system.
|
||||
@ -79,6 +139,7 @@ def delete_database(plugin_name, db_file_name=None):
|
||||
AppLocation.get_section_data_path(plugin_name), plugin_name)
|
||||
return delete_file(db_file_path)
|
||||
|
||||
|
||||
class BaseModel(object):
|
||||
"""
|
||||
BaseModel provides a base object with a set of generic functions
|
||||
@ -93,12 +154,12 @@ class BaseModel(object):
|
||||
instance.__setattr__(key, value)
|
||||
return instance
|
||||
|
||||
|
||||
class Manager(object):
|
||||
"""
|
||||
Provide generic object persistence management
|
||||
"""
|
||||
def __init__(self, plugin_name, init_schema, db_file_name=None):
|
||||
def __init__(self, plugin_name, init_schema, db_file_name=None,
|
||||
upgrade_mod=None):
|
||||
"""
|
||||
Runs the initialisation process that includes creating the connection
|
||||
to the database and the tables if they don't exist.
|
||||
@ -109,6 +170,9 @@ class Manager(object):
|
||||
``init_schema``
|
||||
The init_schema function for this database
|
||||
|
||||
``upgrade_schema``
|
||||
The upgrade_schema function for this database
|
||||
|
||||
``db_file_name``
|
||||
The file name to use for this database. Defaults to None resulting
|
||||
in the plugin_name being used.
|
||||
@ -134,7 +198,27 @@ class Manager(object):
|
||||
unicode(settings.value(u'db hostname').toString()),
|
||||
unicode(settings.value(u'db database').toString()))
|
||||
settings.endGroup()
|
||||
if upgrade_mod:
|
||||
db_ver, up_ver = upgrade_db(self.db_url, upgrade_mod)
|
||||
if db_ver > up_ver:
|
||||
critical_error_message_box(
|
||||
translate('OpenLP.Manager', 'Database Error'),
|
||||
unicode(translate('OpenLP.Manager', 'The database being '
|
||||
'loaded was created in a more recent version of '
|
||||
'OpenLP. The database is version %d, while OpenLP '
|
||||
'expects version %d. The database will not be loaded.'
|
||||
'\n\nDatabase: %s')) % \
|
||||
(db_ver, up_ver, self.db_url)
|
||||
)
|
||||
return
|
||||
try:
|
||||
self.session = init_schema(self.db_url)
|
||||
except:
|
||||
critical_error_message_box(
|
||||
translate('OpenLP.Manager', 'Database Error'),
|
||||
unicode(translate('OpenLP.Manager', 'OpenLP cannot load your '
|
||||
'database.\n\nDatabase: %s')) % self.db_url
|
||||
)
|
||||
|
||||
def save_object(self, object_instance, commit=True):
|
||||
"""
|
||||
|
@ -36,6 +36,7 @@ log = logging.getLogger(__name__)
|
||||
|
||||
# FIXME: Add html5 doctype. However, do not break theme gradients.
|
||||
HTMLSRC = u"""
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>OpenLP Display</title>
|
||||
@ -276,25 +277,25 @@ def build_html(item, screen, alert, islive, background, plugins=None, \
|
||||
image_src = u'src="data:image/png;base64,%s"' % image
|
||||
else:
|
||||
image_src = u'style="display:none;"'
|
||||
plugin_css = u''
|
||||
plugin_js = u''
|
||||
plugin_html = u''
|
||||
css_additions = u''
|
||||
js_additions = u''
|
||||
html_additions = u''
|
||||
if plugins:
|
||||
for plugin in plugins:
|
||||
plugin_css += plugin.getDisplayCss()
|
||||
plugin_js += plugin.getDisplayJavascript()
|
||||
plugin_html += plugin.getDisplayHtml()
|
||||
css_additions += plugin.getDisplayCss()
|
||||
js_additions += plugin.getDisplayJavascript()
|
||||
html_additions += plugin.getDisplayHtml()
|
||||
html = HTMLSRC % (build_background_css(item, width, height),
|
||||
width, height,
|
||||
plugin_css,
|
||||
css_additions,
|
||||
build_alert_css(alert, width),
|
||||
build_footer_css(item, height),
|
||||
build_lyrics_css(item, webkitvers),
|
||||
u'true' if theme and theme.display_slide_transition and islive \
|
||||
else u'false',
|
||||
plugin_js,
|
||||
js_additions,
|
||||
bgimage_src, image_src,
|
||||
plugin_html,
|
||||
html_additions,
|
||||
build_lyrics_html(item, webkitvers))
|
||||
return html
|
||||
|
||||
|
@ -36,7 +36,7 @@ import Queue
|
||||
|
||||
from PyQt4 import QtCore
|
||||
|
||||
from openlp.core.lib import resize_image, image_to_byte
|
||||
from openlp.core.lib import resize_image, image_to_byte, Receiver
|
||||
from openlp.core.ui import ScreenList
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
@ -100,12 +100,14 @@ class Image(object):
|
||||
variables ``image`` and ``image_bytes`` to ``None`` and add the image object
|
||||
to the queue of images to process.
|
||||
"""
|
||||
def __init__(self, name='', path=''):
|
||||
def __init__(self, name, path, source, background):
|
||||
self.name = name
|
||||
self.path = path
|
||||
self.image = None
|
||||
self.image_bytes = None
|
||||
self.priority = Priority.Normal
|
||||
self.source = source
|
||||
self.background = background
|
||||
|
||||
|
||||
class PriorityQueue(Queue.PriorityQueue):
|
||||
@ -151,6 +153,8 @@ class ImageManager(QtCore.QObject):
|
||||
self._cache = {}
|
||||
self._imageThread = ImageThread(self)
|
||||
self._conversion_queue = PriorityQueue()
|
||||
QtCore.QObject.connect(Receiver.get_receiver(),
|
||||
QtCore.SIGNAL(u'config_updated'), self.process_updates)
|
||||
|
||||
def update_display(self):
|
||||
"""
|
||||
@ -162,12 +166,42 @@ class ImageManager(QtCore.QObject):
|
||||
self.height = current_screen[u'size'].height()
|
||||
# Mark the images as dirty for a rebuild by setting the image and byte
|
||||
# stream to None.
|
||||
self._conversion_queue = PriorityQueue()
|
||||
for key, image in self._cache.iteritems():
|
||||
image.priority = Priority.Normal
|
||||
self._reset_image(image)
|
||||
|
||||
def update_images(self, image_type, background):
|
||||
"""
|
||||
Border has changed so update all the images affected.
|
||||
"""
|
||||
log.debug(u'update_images')
|
||||
# Mark the images as dirty for a rebuild by setting the image and byte
|
||||
# stream to None.
|
||||
for key, image in self._cache.iteritems():
|
||||
if image.source == image_type:
|
||||
image.background = background
|
||||
self._reset_image(image)
|
||||
|
||||
def update_image(self, name, image_type, background):
|
||||
"""
|
||||
Border has changed so update the image affected.
|
||||
"""
|
||||
log.debug(u'update_images')
|
||||
# Mark the images as dirty for a rebuild by setting the image and byte
|
||||
# stream to None.
|
||||
for key, image in self._cache.iteritems():
|
||||
if image.source == image_type and image.name == name:
|
||||
image.background = background
|
||||
self._reset_image(image)
|
||||
|
||||
def _reset_image(self, image):
|
||||
image.image = None
|
||||
image.image_bytes = None
|
||||
self._conversion_queue.put((image.priority, image))
|
||||
self._conversion_queue.modify_priority(image, Priority.Normal)
|
||||
|
||||
def process_updates(self):
|
||||
"""
|
||||
Flush the queue to updated any data to update
|
||||
"""
|
||||
# We want only one thread.
|
||||
if not self._imageThread.isRunning():
|
||||
self._imageThread.start()
|
||||
@ -215,13 +249,13 @@ class ImageManager(QtCore.QObject):
|
||||
self._conversion_queue.remove(self._cache[name])
|
||||
del self._cache[name]
|
||||
|
||||
def add_image(self, name, path):
|
||||
def add_image(self, name, path, source, background):
|
||||
"""
|
||||
Add image to cache if it is not already there.
|
||||
"""
|
||||
log.debug(u'add_image %s:%s' % (name, path))
|
||||
if not name in self._cache:
|
||||
image = Image(name, path)
|
||||
image = Image(name, path, source, background)
|
||||
self._cache[name] = image
|
||||
self._conversion_queue.put((image.priority, image))
|
||||
else:
|
||||
@ -247,7 +281,8 @@ class ImageManager(QtCore.QObject):
|
||||
image = self._conversion_queue.get()[1]
|
||||
# Generate the QImage for the image.
|
||||
if image.image is None:
|
||||
image.image = resize_image(image.path, self.width, self.height)
|
||||
image.image = resize_image(image.path, self.width, self.height,
|
||||
image.background)
|
||||
# Set the priority to Lowest and stop here as we need to process
|
||||
# more important images first.
|
||||
if image.priority == Priority.Normal:
|
||||
|
@ -27,7 +27,7 @@
|
||||
|
||||
import logging
|
||||
|
||||
from PyQt4 import QtCore, QtWebKit
|
||||
from PyQt4 import QtGui, QtCore, QtWebKit
|
||||
|
||||
from openlp.core.lib import ServiceItem, expand_tags, \
|
||||
build_lyrics_format_css, build_lyrics_outline_css, Receiver, \
|
||||
@ -169,7 +169,8 @@ class Renderer(object):
|
||||
# if No file do not update cache
|
||||
if self.theme_data.background_filename:
|
||||
self.imageManager.add_image(self.theme_data.theme_name,
|
||||
self.theme_data.background_filename)
|
||||
self.theme_data.background_filename, u'theme',
|
||||
QtGui.QColor(self.theme_data.background_border_color))
|
||||
return self._rect, self._rect_footer
|
||||
|
||||
def generate_preview(self, theme_data, force_page=False):
|
||||
@ -230,15 +231,56 @@ class Renderer(object):
|
||||
# Clean up line endings.
|
||||
lines = self._lines_split(text)
|
||||
pages = self._paginate_slide(lines, line_end)
|
||||
if len(pages) > 1:
|
||||
# Songs and Custom
|
||||
if item.is_capable(ItemCapabilities.AllowsVirtualSplit):
|
||||
# Do not forget the line breaks!
|
||||
slides = text.split(u'[---]')
|
||||
if item.is_capable(ItemCapabilities.AllowsVirtualSplit) and \
|
||||
len(pages) > 1 and u'[---]' in text:
|
||||
pages = []
|
||||
for slide in slides:
|
||||
lines = slide.strip(u'\n').split(u'\n')
|
||||
while True:
|
||||
# Check if the first two potential virtual slides will fit
|
||||
# (as a whole) on one slide.
|
||||
html_text = expand_tags(
|
||||
u'\n'.join(text.split(u'\n[---]\n', 2)[:-1]))
|
||||
html_text = html_text.replace(u'\n', u'<br>')
|
||||
if self._text_fits_on_slide(html_text):
|
||||
# The first two virtual slides fit (as a whole) on one
|
||||
# slide. Replace the occurrences of [---].
|
||||
text = text.replace(u'\n[---]', u'', 2)
|
||||
else:
|
||||
# The first two virtual slides did not fit as a whole.
|
||||
# Check if the first virtual slide will fit.
|
||||
html_text = expand_tags(text.split(u'\n[---]\n', 1)[1])
|
||||
html_text = html_text.replace(u'\n', u'<br>')
|
||||
if self._text_fits_on_slide(html_text):
|
||||
# The first virtual slide fits, so remove it.
|
||||
text = text.replace(u'\n[---]', u'', 1)
|
||||
else:
|
||||
# The first virtual slide does not fit, which means
|
||||
# we have to render the first virtual slide.
|
||||
text_contains_break = u'[---]' in text
|
||||
if text_contains_break:
|
||||
html_text, text = text.split(u'\n[---]\n', 1)
|
||||
else:
|
||||
html_text = text
|
||||
text = u''
|
||||
lines = expand_tags(html_text)
|
||||
lines = lines.strip(u'\n').split(u'\n')
|
||||
slides = self._paginate_slide(lines, line_end)
|
||||
if len(slides) > 1 and text:
|
||||
# Add all slides apart from the last one the
|
||||
# list.
|
||||
pages.extend(slides[:-1])
|
||||
if text_contains_break:
|
||||
text = slides[-1] + u'\n[---]\n' + text
|
||||
else:
|
||||
text = slides[-1] + u'\n'+ text
|
||||
text = text.replace(u'<br>', u'\n')
|
||||
else:
|
||||
pages.extend(slides)
|
||||
if u'[---]' not in text:
|
||||
lines = expand_tags(text)
|
||||
lines = lines.strip(u'\n').split(u'\n')
|
||||
pages.extend(self._paginate_slide(lines, line_end))
|
||||
break
|
||||
new_pages = []
|
||||
for page in pages:
|
||||
while page.endswith(u'<br>'):
|
||||
@ -344,7 +386,7 @@ class Renderer(object):
|
||||
separator = u'<br>'
|
||||
html_lines = map(expand_tags, lines)
|
||||
# Text too long so go to next page.
|
||||
if self._text_fits_on_slide(separator.join(html_lines)):
|
||||
if not self._text_fits_on_slide(separator.join(html_lines)):
|
||||
html_text, previous_raw = self._binary_chop(formatted,
|
||||
previous_html, previous_raw, html_lines, lines, separator, u'')
|
||||
else:
|
||||
@ -377,18 +419,18 @@ class Renderer(object):
|
||||
line = line.strip()
|
||||
html_line = expand_tags(line)
|
||||
# Text too long so go to next page.
|
||||
if self._text_fits_on_slide(previous_html + html_line):
|
||||
if not self._text_fits_on_slide(previous_html + html_line):
|
||||
# Check if there was a verse before the current one and append
|
||||
# it, when it fits on the page.
|
||||
if previous_html:
|
||||
if not self._text_fits_on_slide(previous_html):
|
||||
if self._text_fits_on_slide(previous_html):
|
||||
formatted.append(previous_raw)
|
||||
previous_html = u''
|
||||
previous_raw = u''
|
||||
# Now check if the current verse will fit, if it does
|
||||
# not we have to start to process the verse word by
|
||||
# word.
|
||||
if not self._text_fits_on_slide(html_line):
|
||||
if self._text_fits_on_slide(html_line):
|
||||
previous_html = html_line + line_end
|
||||
previous_raw = line + line_end
|
||||
continue
|
||||
@ -445,7 +487,7 @@ class Renderer(object):
|
||||
highest_index = len(html_list) - 1
|
||||
index = int(highest_index / 2)
|
||||
while True:
|
||||
if self._text_fits_on_slide(
|
||||
if not self._text_fits_on_slide(
|
||||
previous_html + separator.join(html_list[:index + 1]).strip()):
|
||||
# We know that it does not fit, so change/calculate the
|
||||
# new index and highest_index accordingly.
|
||||
@ -468,7 +510,7 @@ class Renderer(object):
|
||||
else:
|
||||
continue
|
||||
# Check if the remaining elements fit on the slide.
|
||||
if not self._text_fits_on_slide(
|
||||
if self._text_fits_on_slide(
|
||||
separator.join(html_list[index + 1:]).strip()):
|
||||
previous_html = separator.join(
|
||||
html_list[index + 1:]).strip() + line_end
|
||||
@ -491,11 +533,11 @@ class Renderer(object):
|
||||
returned, otherwise ``False``.
|
||||
|
||||
``text``
|
||||
The text to check. It can contain HTML tags.
|
||||
The text to check. It may contain HTML tags.
|
||||
"""
|
||||
self.web_frame.evaluateJavaScript(u'show_text("%s")' %
|
||||
text.replace(u'\\', u'\\\\').replace(u'\"', u'\\\"'))
|
||||
return self.web_frame.contentsSize().height() > self.page_height
|
||||
return self.web_frame.contentsSize().height() <= self.page_height
|
||||
|
||||
def _words_split(self, line):
|
||||
"""
|
||||
|
@ -115,6 +115,7 @@ class ServiceItem(object):
|
||||
self.end_time = 0
|
||||
self.media_length = 0
|
||||
self.from_service = False
|
||||
self.image_border = u'#000000'
|
||||
self._new_item()
|
||||
|
||||
def _new_item(self):
|
||||
@ -195,7 +196,7 @@ class ServiceItem(object):
|
||||
self.foot_text = \
|
||||
u'<br>'.join([footer for footer in self.raw_footer if footer])
|
||||
|
||||
def add_from_image(self, path, title):
|
||||
def add_from_image(self, path, title, background=None):
|
||||
"""
|
||||
Add an image slide to the service item.
|
||||
|
||||
@ -205,9 +206,12 @@ class ServiceItem(object):
|
||||
``title``
|
||||
A title for the slide in the service item.
|
||||
"""
|
||||
if background:
|
||||
self.image_border = background
|
||||
self.service_item_type = ServiceItemType.Image
|
||||
self._raw_frames.append({u'title': title, u'path': path})
|
||||
self.renderer.imageManager.add_image(title, path)
|
||||
self.renderer.imageManager.add_image(title, path, u'image',
|
||||
self.image_border)
|
||||
self._new_item()
|
||||
|
||||
def add_from_text(self, title, raw_slide, verse_tag=None):
|
||||
|
@ -44,6 +44,7 @@ BLANK_THEME_XML = \
|
||||
<name> </name>
|
||||
<background type="image">
|
||||
<filename></filename>
|
||||
<borderColor>#000000</borderColor>
|
||||
</background>
|
||||
<background type="gradient">
|
||||
<startColor>#000000</startColor>
|
||||
@ -282,7 +283,7 @@ class ThemeXML(object):
|
||||
# Create direction element
|
||||
self.child_element(background, u'direction', unicode(direction))
|
||||
|
||||
def add_background_image(self, filename):
|
||||
def add_background_image(self, filename, borderColor):
|
||||
"""
|
||||
Add a image background.
|
||||
|
||||
@ -294,6 +295,8 @@ class ThemeXML(object):
|
||||
self.theme.appendChild(background)
|
||||
# Create Filename element
|
||||
self.child_element(background, u'filename', filename)
|
||||
# Create endColor element
|
||||
self.child_element(background, u'borderColor', unicode(borderColor))
|
||||
|
||||
def add_font(self, name, color, size, override, fonttype=u'main',
|
||||
bold=u'False', italics=u'False', line_adjustment=0,
|
||||
@ -597,7 +600,7 @@ class ThemeXML(object):
|
||||
self.background_direction)
|
||||
else:
|
||||
filename = os.path.split(self.background_filename)[1]
|
||||
self.add_background_image(filename)
|
||||
self.add_background_image(filename, self.background_border_color)
|
||||
self.add_font(self.font_main_name,
|
||||
self.font_main_color,
|
||||
self.font_main_size,
|
||||
|
@ -116,7 +116,7 @@ class Ui_AboutDialog(object):
|
||||
u'Scott "sguerrieri" Guerrieri',
|
||||
u'Matthias "matthub" Hub', u'Meinert "m2j" Jordan',
|
||||
u'Armin "orangeshirt" K\xf6hler', u'Joshua "milleja46" Miller',
|
||||
u'Stevan "StevanP" Pettit', u'Mattias "mahfiaz" P\xf5ldaru',
|
||||
u'Stevan "ElderP" Pettit', u'Mattias "mahfiaz" P\xf5ldaru',
|
||||
u'Christian "crichter" Richter', u'Philip "Phill" Ridout',
|
||||
u'Simon "samscudder" Scudder', u'Jeffrey "whydoubt" Smith',
|
||||
u'Maikel Stuivenberg', u'Frode "frodus" Woldsund']
|
||||
@ -125,7 +125,7 @@ class Ui_AboutDialog(object):
|
||||
packagers = ['Thomas "tabthorpe" Abthorpe (FreeBSD)',
|
||||
u'Tim "TRB143" Bentley (Fedora)',
|
||||
u'Matthias "matthub" Hub (Mac OS X)',
|
||||
u'Stevan "StevanP" Pettit (Windows)',
|
||||
u'Stevan "ElderP" Pettit (Windows)',
|
||||
u'Raoul "superfly" Snyman (Ubuntu)']
|
||||
translators = {
|
||||
u'af': [u'Johan "nuvolari" Mynhardt'],
|
||||
|
@ -67,6 +67,7 @@ class Display(QtGui.QGraphicsView):
|
||||
self.width(), self.height())
|
||||
self.webView.settings().setAttribute(
|
||||
QtWebKit.QWebSettings.PluginsEnabled, True)
|
||||
self.webView.settings().setAttribute(7, True)
|
||||
self.page = self.webView.page()
|
||||
self.frame = self.page.mainFrame()
|
||||
self.frame.setScrollBarPolicy(QtCore.Qt.Vertical,
|
||||
@ -129,6 +130,7 @@ class MainDisplay(Display):
|
||||
self.screen[u'size'].width(), self.screen[u'size'].height())
|
||||
self.webView.settings().setAttribute( \
|
||||
QtWebKit.QWebSettings.PluginsEnabled, True)
|
||||
self.webView.settings().setAttribute(7, True)
|
||||
self.page = self.webView.page()
|
||||
self.frame = self.page.mainFrame()
|
||||
QtCore.QObject.connect(self.webView,
|
||||
@ -231,11 +233,11 @@ class MainDisplay(Display):
|
||||
shrinkItem.setVisible(False)
|
||||
self.setGeometry(self.screen[u'size'])
|
||||
|
||||
def directImage(self, name, path):
|
||||
def directImage(self, name, path, background):
|
||||
"""
|
||||
API for replacement backgrounds so Images are added directly to cache
|
||||
"""
|
||||
self.imageManager.add_image(name, path)
|
||||
self.imageManager.add_image(name, path, u'image', background)
|
||||
if hasattr(self, u'serviceItem'):
|
||||
self.override[u'image'] = name
|
||||
self.override[u'theme'] = self.serviceItem.themedata.theme_name
|
||||
@ -253,7 +255,7 @@ class MainDisplay(Display):
|
||||
"""
|
||||
log.debug(u'image to display')
|
||||
image = self.imageManager.get_image_bytes(name)
|
||||
self.resetVideo()
|
||||
self.controller.mediaManager.video_reset(self.controller)
|
||||
self.displayImage(image)
|
||||
return self.preview()
|
||||
|
||||
|
@ -30,11 +30,13 @@ import os
|
||||
import sys
|
||||
import shutil
|
||||
from tempfile import gettempdir
|
||||
from datetime import datetime
|
||||
|
||||
from PyQt4 import QtCore, QtGui
|
||||
|
||||
from openlp.core.lib import Renderer, build_icon, OpenLPDockWidget, \
|
||||
PluginManager, Receiver, translate, ImageManager, PluginStatus
|
||||
PluginManager, Receiver, translate, ImageManager, PluginStatus, \
|
||||
SettingsManager
|
||||
from openlp.core.lib.ui import UiStrings, base_action, checkable_action, \
|
||||
icon_action, shortcut_action
|
||||
from openlp.core.ui import AboutForm, SettingsForm, ServiceManager, \
|
||||
@ -285,6 +287,10 @@ class Ui_MainWindow(object):
|
||||
self.settingsConfigureItem = icon_action(mainWindow,
|
||||
u'settingsConfigureItem', u':/system/system_settings.png',
|
||||
category=UiStrings().Settings)
|
||||
self.settingsImportItem = base_action(mainWindow,
|
||||
u'settingsImportItem', category=UiStrings().Settings)
|
||||
self.settingsExportItem = base_action(mainWindow,
|
||||
u'settingsExportItem', category=UiStrings().Settings)
|
||||
action_list.add_category(UiStrings().Help, CategoryOrder.standardMenu)
|
||||
self.aboutItem = shortcut_action(mainWindow, u'aboutItem',
|
||||
[QtGui.QKeySequence(u'Ctrl+F1')], self.onAboutItemClicked,
|
||||
@ -302,10 +308,10 @@ class Ui_MainWindow(object):
|
||||
u':/system/system_online_help.png', category=UiStrings().Help)
|
||||
self.webSiteItem = base_action(
|
||||
mainWindow, u'webSiteItem', category=UiStrings().Help)
|
||||
add_actions(self.fileImportMenu,
|
||||
(self.importThemeItem, self.importLanguageItem))
|
||||
add_actions(self.fileExportMenu,
|
||||
(self.exportThemeItem, self.exportLanguageItem))
|
||||
add_actions(self.fileImportMenu, (self.settingsImportItem, None,
|
||||
self.importThemeItem, self.importLanguageItem))
|
||||
add_actions(self.fileExportMenu, (self.settingsExportItem, None,
|
||||
self.exportThemeItem, self.exportLanguageItem))
|
||||
add_actions(self.fileMenu, (self.fileNewItem, self.fileOpenItem,
|
||||
self.fileSaveItem, self.fileSaveAsItem,
|
||||
self.recentFilesMenu.menuAction(), None,
|
||||
@ -358,6 +364,7 @@ class Ui_MainWindow(object):
|
||||
self.importLanguageItem.setVisible(False)
|
||||
self.exportLanguageItem.setVisible(False)
|
||||
self.setLockPanel(panelLocked)
|
||||
self.settingsImported = False
|
||||
|
||||
def retranslateUi(self, mainWindow):
|
||||
"""
|
||||
@ -421,6 +428,15 @@ class Ui_MainWindow(object):
|
||||
translate('OpenLP.MainWindow', 'Configure &Formatting Tags...'))
|
||||
self.settingsConfigureItem.setText(
|
||||
translate('OpenLP.MainWindow', '&Configure OpenLP...'))
|
||||
self.settingsExportItem.setStatusTip(translate('OpenLP.MainWindow',
|
||||
'Export OpenLP settings to a specified *.config file'))
|
||||
self.settingsExportItem.setText(
|
||||
translate('OpenLP.MainWindow', 'Settings'))
|
||||
self.settingsImportItem.setStatusTip(translate('OpenLP.MainWindow',
|
||||
'Import OpenLP settings from a specified *.config file previously '
|
||||
'exported on this or another machine'))
|
||||
self.settingsImportItem.setText(
|
||||
translate('OpenLP.MainWindow', 'Settings'))
|
||||
self.viewMediaManagerItem.setText(
|
||||
translate('OpenLP.MainWindow', '&Media Manager'))
|
||||
self.viewMediaManagerItem.setToolTip(
|
||||
@ -524,8 +540,12 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
|
||||
# (not for use by plugins)
|
||||
self.uiSettingsSection = u'user interface'
|
||||
self.generalSettingsSection = u'general'
|
||||
self.serviceSettingsSection = u'servicemanager'
|
||||
self.advancedlSettingsSection = u'advanced'
|
||||
self.servicemanagerSettingsSection = u'servicemanager'
|
||||
self.songsSettingsSection = u'songs'
|
||||
self.themesSettingsSection = u'themes'
|
||||
self.displayTagsSection = u'displayTags'
|
||||
self.headerSection = u'SettingsImport'
|
||||
self.serviceNotSaved = False
|
||||
self.aboutForm = AboutForm(self)
|
||||
self.settingsForm = SettingsForm(self, self)
|
||||
@ -575,6 +595,10 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
|
||||
QtCore.SIGNAL(u'triggered()'), self.onSettingsConfigureItemClicked)
|
||||
QtCore.QObject.connect(self.settingsShortcutsItem,
|
||||
QtCore.SIGNAL(u'triggered()'), self.onSettingsShortcutsItemClicked)
|
||||
QtCore.QObject.connect(self.settingsImportItem,
|
||||
QtCore.SIGNAL(u'triggered()'), self.onSettingsImportItemClicked)
|
||||
QtCore.QObject.connect(self.settingsExportItem,
|
||||
QtCore.SIGNAL(u'triggered()'), self.onSettingsExportItemClicked)
|
||||
# i18n set signals for languages
|
||||
self.languageGroup.triggered.connect(LanguageManager.set_language)
|
||||
QtCore.QObject.connect(self.modeDefaultItem,
|
||||
@ -771,6 +795,9 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
|
||||
self.themeManagerContents.loadThemes(True)
|
||||
Receiver.send_message(u'theme_update_global',
|
||||
self.themeManagerContents.global_theme)
|
||||
# Check if any Bibles downloaded. If there are, they will be
|
||||
# processed.
|
||||
Receiver.send_message(u'bibles_load_list', True)
|
||||
|
||||
def blankCheck(self):
|
||||
"""
|
||||
@ -872,6 +899,172 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
|
||||
if self.shortcutForm.exec_():
|
||||
self.shortcutForm.save()
|
||||
|
||||
def onSettingsImportItemClicked(self):
|
||||
"""
|
||||
Import settings from an export INI file
|
||||
"""
|
||||
answer = QtGui.QMessageBox.critical(self,
|
||||
translate('OpenLP.MainWindow', 'Import settings?'),
|
||||
translate('OpenLP.MainWindow',
|
||||
'Are you sure you want to import settings?\n\n'
|
||||
'Importing settings will make permanent changes to your current '
|
||||
'OpenLP configuration.\n\n'
|
||||
'Importing incorrect settings may cause erratic behaviour or '
|
||||
'OpenLP to terminate abnormally.'),
|
||||
QtGui.QMessageBox.StandardButtons(
|
||||
QtGui.QMessageBox.Yes |
|
||||
QtGui.QMessageBox.No),
|
||||
QtGui.QMessageBox.No)
|
||||
if answer == QtGui.QMessageBox.No:
|
||||
return
|
||||
importFileName = unicode(QtGui.QFileDialog.getOpenFileName(self,
|
||||
translate('OpenLP.MainWindow', 'Open File'),
|
||||
'',
|
||||
translate('OpenLP.MainWindow',
|
||||
'OpenLP Export Settings Files (*.conf)')))
|
||||
if not importFileName:
|
||||
return
|
||||
settingSections = []
|
||||
# Add main sections.
|
||||
settingSections.extend([self.generalSettingsSection])
|
||||
settingSections.extend([self.advancedlSettingsSection])
|
||||
settingSections.extend([self.uiSettingsSection])
|
||||
settingSections.extend([self.servicemanagerSettingsSection])
|
||||
settingSections.extend([self.themesSettingsSection])
|
||||
settingSections.extend([self.displayTagsSection])
|
||||
settingSections.extend([self.headerSection])
|
||||
# Add plugin sections.
|
||||
for plugin in self.pluginManager.plugins:
|
||||
settingSections.extend([plugin.name])
|
||||
settings = QtCore.QSettings()
|
||||
importSettings = QtCore.QSettings(importFileName,
|
||||
QtCore.QSettings.IniFormat)
|
||||
importKeys = importSettings.allKeys()
|
||||
for sectionKey in importKeys:
|
||||
# We need to handle the really bad files.
|
||||
try:
|
||||
section, key = sectionKey.split(u'/')
|
||||
except ValueError:
|
||||
section = u'unknown'
|
||||
key = u''
|
||||
# Switch General back to lowercase.
|
||||
if section == u'General':
|
||||
section = u'general'
|
||||
sectionKey = section + "/" + key
|
||||
# Make sure it's a valid section for us.
|
||||
if not section in settingSections:
|
||||
QtGui.QMessageBox.critical(self,
|
||||
translate('OpenLP.MainWindow', 'Import settings'),
|
||||
translate('OpenLP.MainWindow',
|
||||
'The file you selected does appear to be a valid OpenLP '
|
||||
'settings file.\n\n'
|
||||
'Section [%s] is not valid \n\n'
|
||||
'Processing has terminated and no changed have been made.'
|
||||
% section),
|
||||
QtGui.QMessageBox.StandardButtons(
|
||||
QtGui.QMessageBox.Ok))
|
||||
return
|
||||
# We have a good file, import it.
|
||||
for sectionKey in importKeys:
|
||||
value = importSettings.value(sectionKey)
|
||||
settings.setValue(u'%s' % (sectionKey) ,
|
||||
QtCore.QVariant(value))
|
||||
now = datetime.now()
|
||||
settings.beginGroup(self.headerSection)
|
||||
settings.setValue( u'file_imported' , QtCore.QVariant(importFileName))
|
||||
settings.setValue(u'file_date_imported',
|
||||
now.strftime("%Y-%m-%d %H:%M"))
|
||||
settings.endGroup()
|
||||
settings.sync()
|
||||
# We must do an immediate restart or current configuration will
|
||||
# overwrite what was just imported when application terminates
|
||||
# normally. We need to exit without saving configuration.
|
||||
QtGui.QMessageBox.information(self,
|
||||
translate('OpenLP.MainWindow', 'Import settings'),
|
||||
translate('OpenLP.MainWindow',
|
||||
'OpenLP will now close. Imported settings will '
|
||||
'be applied the next time you start OpenLP.'),
|
||||
QtGui.QMessageBox.StandardButtons(
|
||||
QtGui.QMessageBox.Ok))
|
||||
self.settingsImported = True
|
||||
self.cleanUp()
|
||||
QtCore.QCoreApplication.exit()
|
||||
|
||||
def onSettingsExportItemClicked(self, exportFileName=None):
|
||||
"""
|
||||
Export settings to an INI file
|
||||
"""
|
||||
if not exportFileName:
|
||||
exportFileName = unicode(QtGui.QFileDialog.getSaveFileName(self,
|
||||
translate('OpenLP.MainWindow', 'Export Settings File'), '',
|
||||
translate('OpenLP.MainWindow',
|
||||
'OpenLP Export Settings File (*.conf)')))
|
||||
if not exportFileName:
|
||||
return
|
||||
# Make sure it's an .ini file.
|
||||
if not exportFileName.endswith(u'conf'):
|
||||
exportFileName = exportFileName + u'.conf'
|
||||
temp_file = os.path.join(unicode(gettempdir()),
|
||||
u'openlp', u'exportIni.tmp')
|
||||
self.saveSettings()
|
||||
settingSections = []
|
||||
# Add main sections.
|
||||
settingSections.extend([self.generalSettingsSection])
|
||||
settingSections.extend([self.advancedlSettingsSection])
|
||||
settingSections.extend([self.uiSettingsSection])
|
||||
settingSections.extend([self.servicemanagerSettingsSection])
|
||||
settingSections.extend([self.themesSettingsSection])
|
||||
settingSections.extend([self.displayTagsSection])
|
||||
# Add plugin sections.
|
||||
for plugin in self.pluginManager.plugins:
|
||||
settingSections.extend([plugin.name])
|
||||
# Delete old files if found.
|
||||
if os.path.exists(temp_file):
|
||||
os.remove(temp_file)
|
||||
if os.path.exists(exportFileName):
|
||||
os.remove(exportFileName)
|
||||
settings = QtCore.QSettings()
|
||||
settings.remove(self.headerSection)
|
||||
# Get the settings.
|
||||
keys = settings.allKeys()
|
||||
exportSettings = QtCore.QSettings(temp_file,
|
||||
QtCore.QSettings.IniFormat)
|
||||
# Add a header section.
|
||||
# This is to insure it's our ini file for import.
|
||||
now = datetime.now()
|
||||
applicationVersion = get_application_version()
|
||||
# Write INI format using Qsettings.
|
||||
# Write our header.
|
||||
exportSettings.beginGroup(self.headerSection)
|
||||
exportSettings.setValue(u'Make_Changes', u'At_Own_RISK')
|
||||
exportSettings.setValue(u'type', u'OpenLP_settings_export')
|
||||
exportSettings.setValue(u'file_date_created',
|
||||
now.strftime("%Y-%m-%d %H:%M"))
|
||||
exportSettings.setValue(u'version', applicationVersion[u'full'])
|
||||
exportSettings.endGroup()
|
||||
# Write all the sections and keys.
|
||||
for sectionKey in keys:
|
||||
section, key = sectionKey.split(u'/')
|
||||
keyValue = settings.value(sectionKey)
|
||||
sectionKey = section + u"/" + key
|
||||
# Change the service section to servicemanager.
|
||||
if section == u'service':
|
||||
sectionKey = u'servicemanager/' + key
|
||||
exportSettings.setValue(sectionKey, keyValue)
|
||||
exportSettings.sync()
|
||||
# Temp INI file has been written. Blanks in keys are now '%20'.
|
||||
# Read the temp file and output the user's INI file with blanks to
|
||||
# make it more readable.
|
||||
tempIni = open(temp_file, u'r')
|
||||
exportIni = open(exportFileName, u'w')
|
||||
for fileRecord in tempIni:
|
||||
fileRecord = fileRecord.replace(u'%20', u' ')
|
||||
exportIni.write(fileRecord)
|
||||
tempIni.close()
|
||||
exportIni.close()
|
||||
os.remove(temp_file)
|
||||
return
|
||||
|
||||
def onModeDefaultItemClicked(self):
|
||||
"""
|
||||
Put OpenLP into "Default" view mode.
|
||||
@ -924,6 +1117,9 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
|
||||
"""
|
||||
Hook to close the main window and display windows on exit
|
||||
"""
|
||||
# If we just did a settings import, close without saving changes.
|
||||
if self.settingsImported:
|
||||
event.accept()
|
||||
if self.serviceManagerContents.isModified():
|
||||
ret = self.serviceManagerContents.saveModifiedService()
|
||||
if ret == QtGui.QMessageBox.Save:
|
||||
@ -1121,6 +1317,9 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
|
||||
"""
|
||||
Save the main window settings.
|
||||
"""
|
||||
# Exit if we just did a settings import.
|
||||
if self.settingsImported:
|
||||
return
|
||||
log.debug(u'Saving QSettings')
|
||||
settings = QtCore.QSettings()
|
||||
settings.beginGroup(self.generalSettingsSection)
|
||||
|
@ -39,10 +39,12 @@ class MediaState(object):
|
||||
class MediaType(object):
|
||||
"""
|
||||
"""
|
||||
Audio = 0
|
||||
Video = 1
|
||||
Unused = 0
|
||||
Audio = 1
|
||||
Video = 2
|
||||
Cd = 3
|
||||
Dvd = 4
|
||||
Folder = 5
|
||||
|
||||
class MediaInfo(object):
|
||||
"""
|
||||
@ -50,7 +52,7 @@ class MediaInfo(object):
|
||||
"""
|
||||
file_info = None
|
||||
volume = 100
|
||||
isFlash = False
|
||||
is_flash = False
|
||||
is_background = False
|
||||
length = 0
|
||||
start_time = 0
|
||||
|
@ -49,7 +49,6 @@ class MediaAPI(object):
|
||||
"""
|
||||
return False
|
||||
|
||||
|
||||
def setup(self, display):
|
||||
"""
|
||||
Create the related widgets for the current display
|
||||
@ -118,21 +117,19 @@ class MediaAPI(object):
|
||||
"""
|
||||
pass
|
||||
|
||||
def getDisplayCss(self):
|
||||
def get_media_display_css(self):
|
||||
"""
|
||||
Add css style sheets to htmlbuilder
|
||||
"""
|
||||
return u''
|
||||
|
||||
|
||||
def getDisplayJavascript(self):
|
||||
def get_media_display_javascript(self):
|
||||
"""
|
||||
Add javascript functions to htmlbuilder
|
||||
"""
|
||||
return u''
|
||||
|
||||
|
||||
def getDisplayHtml(self):
|
||||
def get_media_display_html(self):
|
||||
"""
|
||||
Add html code to htmlbuilder
|
||||
"""
|
||||
|
@ -32,7 +32,7 @@ from PyQt4 import QtCore, QtGui, QtWebKit
|
||||
|
||||
from openlp.core.lib import OpenLPToolbar, Receiver, translate
|
||||
from openlp.core.lib.ui import UiStrings, critical_error_message_box
|
||||
from openlp.core.ui.media import MediaAPI, MediaState, MediaInfo
|
||||
from openlp.core.ui.media import MediaAPI, MediaState, MediaInfo, MediaType
|
||||
from openlp.core.utils import AppLocation
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
@ -61,12 +61,12 @@ class MediaManager(object):
|
||||
self.APIs = {}
|
||||
self.controller = []
|
||||
self.curDisplayMediaAPI = {}
|
||||
#Timer for video state
|
||||
# Timer for video state
|
||||
self.Timer = QtCore.QTimer()
|
||||
self.Timer.setInterval(200)
|
||||
self.withLivePreview = False
|
||||
self.checkPreConditions()
|
||||
#Signals
|
||||
self.check_available_media_apis()
|
||||
# Signals
|
||||
QtCore.QObject.connect(self.Timer,
|
||||
QtCore.SIGNAL("timeout()"), self.video_state)
|
||||
QtCore.QObject.connect(Receiver.get_receiver(),
|
||||
@ -86,7 +86,7 @@ class MediaManager(object):
|
||||
QtCore.QObject.connect(Receiver.get_receiver(),
|
||||
QtCore.SIGNAL(u'media_unblank'), self.video_unblank)
|
||||
|
||||
def registerControllers(self, controller):
|
||||
def register_controllers(self, controller):
|
||||
"""
|
||||
Register each media API controller (Webkit, Phonon, etc) and
|
||||
store for later use
|
||||
@ -94,12 +94,12 @@ class MediaManager(object):
|
||||
if controller.check_available():
|
||||
self.APIs[controller.name] = controller
|
||||
|
||||
def checkPreConditions(self):
|
||||
def check_available_media_apis(self):
|
||||
"""
|
||||
Check to see if we have any media API's available
|
||||
If Not do not install the plugin.
|
||||
"""
|
||||
log.debug(u'checkPreConditions')
|
||||
log.debug(u'check_available_media_apis')
|
||||
controller_dir = os.path.join(
|
||||
AppLocation.get_directory(AppLocation.AppDir),
|
||||
u'core', u'ui', u'media')
|
||||
@ -119,7 +119,7 @@ class MediaManager(object):
|
||||
controller_classes = MediaAPI.__subclasses__()
|
||||
for controller_class in controller_classes:
|
||||
controller = controller_class(self)
|
||||
self.registerControllers(controller)
|
||||
self.register_controllers(controller)
|
||||
if self.APIs:
|
||||
return True
|
||||
else:
|
||||
@ -143,34 +143,34 @@ class MediaManager(object):
|
||||
if not isAnyonePlaying:
|
||||
self.Timer.stop()
|
||||
|
||||
def getDisplayCss(self):
|
||||
def get_media_display_css(self):
|
||||
"""
|
||||
Add css style sheets to htmlbuilder
|
||||
"""
|
||||
css = u'';
|
||||
for api in self.APIs.values():
|
||||
css += api.getDisplayCss()
|
||||
css += api.get_media_display_css()
|
||||
return css
|
||||
|
||||
def getDisplayJavascript(self):
|
||||
def get_media_display_javascript(self):
|
||||
"""
|
||||
Add javascript functions to htmlbuilder
|
||||
"""
|
||||
js = u''
|
||||
for api in self.APIs.values():
|
||||
js += api.getDisplayJavascript()
|
||||
js += api.get_media_display_javascript()
|
||||
return js
|
||||
|
||||
def getDisplayHtml(self):
|
||||
def get_media_display_html(self):
|
||||
"""
|
||||
Add html code to htmlbuilder
|
||||
"""
|
||||
html = u''
|
||||
for api in self.APIs.values():
|
||||
html += api.getDisplayHtml()
|
||||
html += api.get_media_display_html()
|
||||
return html
|
||||
|
||||
def addControllerItems(self, controller, control_panel):
|
||||
def add_controller_items(self, controller, control_panel):
|
||||
self.controller.append(controller)
|
||||
self.setup_generic_controls(controller, control_panel)
|
||||
for api in self.APIs.values():
|
||||
@ -210,7 +210,7 @@ class MediaManager(object):
|
||||
controller.mediabar.addToolbarWidget(u'Audio Volume', controller.volumeSlider)
|
||||
control_panel.addWidget(controller.mediabar)
|
||||
controller.mediabar.setVisible(False)
|
||||
#Signals
|
||||
# Signals
|
||||
QtCore.QObject.connect(controller.seekSlider,
|
||||
QtCore.SIGNAL(u'sliderMoved(int)'), controller.sendToPlugins)
|
||||
QtCore.QObject.connect(controller.volumeSlider,
|
||||
@ -277,20 +277,26 @@ class MediaManager(object):
|
||||
display = controller.previewDisplay
|
||||
isValid = self.check_file_type(controller, display)
|
||||
if not isValid:
|
||||
#Media could not be loaded correctly
|
||||
# Media could not be loaded correctly
|
||||
critical_error_message_box(
|
||||
translate('MediaPlugin.MediaItem', 'Unsupported File'),
|
||||
unicode(translate('MediaPlugin.MediaItem',
|
||||
'Unsupported File')))
|
||||
return False
|
||||
#now start playing
|
||||
self.video_play([controller])
|
||||
# now start playing
|
||||
if self.video_play([controller]):
|
||||
self.video_pause([controller])
|
||||
self.video_seek([controller, [0]])
|
||||
self.video_play([controller])
|
||||
if self.video_play([controller]):
|
||||
self.set_controls_visible(controller, True)
|
||||
log.debug(u'use %s controller' % self.curDisplayMediaAPI[display])
|
||||
return True
|
||||
else:
|
||||
critical_error_message_box(
|
||||
translate('MediaPlugin.MediaItem', 'Unsupported File'),
|
||||
unicode(translate('MediaPlugin.MediaItem',
|
||||
'Unsupported File')))
|
||||
return False
|
||||
|
||||
def check_file_type(self, controller, display):
|
||||
"""
|
||||
@ -310,6 +316,12 @@ class MediaManager(object):
|
||||
self.resize(controller, display, api)
|
||||
if api.load(display):
|
||||
self.curDisplayMediaAPI[display] = api
|
||||
controller.media_info.media_type = MediaType.Video
|
||||
return True
|
||||
if suffix in api.audio_extensions_list:
|
||||
if api.load(display):
|
||||
self.curDisplayMediaAPI[display] = api
|
||||
controller.media_info.media_type = MediaType.Audio
|
||||
return True
|
||||
# no valid api found
|
||||
return False
|
||||
@ -325,17 +337,19 @@ class MediaManager(object):
|
||||
if controller.isLive:
|
||||
if controller.hideMenu.defaultAction().isChecked():
|
||||
controller.hideMenu.defaultAction().trigger()
|
||||
#Receiver.send_message(u'maindisplay_show')
|
||||
self.curDisplayMediaAPI[display].play(display)
|
||||
if not self.curDisplayMediaAPI[display].play(display):
|
||||
return False
|
||||
self.curDisplayMediaAPI[display].set_visible(display, True)
|
||||
# Start Timer for ui updates
|
||||
if not self.Timer.isActive():
|
||||
self.Timer.start()
|
||||
return True
|
||||
|
||||
def video_pause(self, msg):
|
||||
"""
|
||||
Responds to the request to pause a loaded video
|
||||
"""
|
||||
log.debug(u'videoPause')
|
||||
log.debug(u'video_pause')
|
||||
controller = msg[0]
|
||||
for display in self.curDisplayMediaAPI.keys():
|
||||
if display.controller == controller:
|
||||
@ -383,6 +397,7 @@ class MediaManager(object):
|
||||
if display.controller == controller:
|
||||
display.override = {}
|
||||
self.curDisplayMediaAPI[display].reset(display)
|
||||
self.curDisplayMediaAPI[display].set_visible(display, False)
|
||||
del self.curDisplayMediaAPI[display]
|
||||
self.set_controls_visible(controller, False)
|
||||
|
||||
@ -428,7 +443,7 @@ class MediaManager(object):
|
||||
if display.controller == controller:
|
||||
if self.curDisplayMediaAPI[display] \
|
||||
.state == MediaState.Paused:
|
||||
self.curDisplayMediaAPI[display].play(display)
|
||||
if self.curDisplayMediaAPI[display].play(display):
|
||||
self.curDisplayMediaAPI[display] \
|
||||
.set_visible(display, True)
|
||||
|
||||
|
@ -76,7 +76,6 @@ class PhononAPI(MediaAPI):
|
||||
ext = u'*%s' % extension
|
||||
if ext not in list:
|
||||
list.append(ext)
|
||||
#self.parent.parent.serviceManagerContents.supportedSuffixes(extension[1:])
|
||||
log.info(u'MediaPlugin: %s extensions: %s' % (mimetype,
|
||||
u' '.join(extensions)))
|
||||
# Add extensions for this mimetype from self.additional_extensions.
|
||||
@ -87,7 +86,6 @@ class PhononAPI(MediaAPI):
|
||||
ext = u'*%s' % extension
|
||||
if ext not in list:
|
||||
list.append(ext)
|
||||
#self.parent.parent.serviceManagerContents.supportedSuffixes(extension[1:])
|
||||
log.info(u'MediaPlugin: %s additional extensions: %s' % (mimetype,
|
||||
u' '.join(self.additional_extensions[mimetype])))
|
||||
|
||||
@ -133,19 +131,21 @@ class PhononAPI(MediaAPI):
|
||||
volume = controller.media_info.volume
|
||||
path = controller.media_info.file_info.absoluteFilePath()
|
||||
display.mediaObject.setCurrentSource(Phonon.MediaSource(path))
|
||||
if not self.mediaStateWait(display, Phonon.StoppedState):
|
||||
if not self.media_state_wait(display, Phonon.StoppedState):
|
||||
return False
|
||||
self.volume(display, volume)
|
||||
return True
|
||||
|
||||
def mediaStateWait(self, display, mediaState):
|
||||
def media_state_wait(self, display, mediaState):
|
||||
"""
|
||||
Wait for the video to change its state
|
||||
Wait no longer than 5 seconds.
|
||||
"""
|
||||
start = datetime.now()
|
||||
while display.mediaObject.state() != mediaState:
|
||||
if display.mediaObject.state() == Phonon.ErrorState:
|
||||
current_state = display.mediaObject.state()
|
||||
while current_state != mediaState:
|
||||
current_state = display.mediaObject.state()
|
||||
if current_state == Phonon.ErrorState:
|
||||
return False
|
||||
Receiver.send_message(u'openlp_process_events')
|
||||
if (datetime.now() - start).seconds > 5:
|
||||
@ -162,7 +162,7 @@ class PhononAPI(MediaAPI):
|
||||
controller.media_info.start_time > 0:
|
||||
start_time = controller.media_info.start_time
|
||||
display.mediaObject.play()
|
||||
if self.mediaStateWait(display, Phonon.PlayingState):
|
||||
if self.media_state_wait(display, Phonon.PlayingState):
|
||||
if start_time > 0:
|
||||
self.seek(display, controller.media_info.start_time*1000)
|
||||
self.volume(display, controller.media_info.volume)
|
||||
@ -170,10 +170,14 @@ class PhononAPI(MediaAPI):
|
||||
int(display.mediaObject.totalTime()/1000)
|
||||
controller.seekSlider.setMaximum(controller.media_info.length*1000)
|
||||
self.state = MediaState.Playing
|
||||
self.set_visible(display, True)
|
||||
#self.set_visible(display, True)
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def pause(self, display):
|
||||
display.mediaObject.pause()
|
||||
if self.media_state_wait(display, Phonon.PausedState):
|
||||
self.state = MediaState.Paused
|
||||
|
||||
def stop(self, display):
|
||||
|
@ -44,6 +44,10 @@ class WebkitAPI(MediaAPI):
|
||||
MediaAPI.__init__(self, parent, u'Webkit')
|
||||
self.parent = parent
|
||||
self.canBackground = True
|
||||
self.audio_extensions_list = [
|
||||
u'*.mp3'
|
||||
, u'*.ogg'
|
||||
]
|
||||
self.video_extensions_list = [
|
||||
u'*.3gp'
|
||||
, u'*.3gpp'
|
||||
@ -62,8 +66,6 @@ class WebkitAPI(MediaAPI):
|
||||
, u'*.m4v'
|
||||
, u'*.mkv'
|
||||
, u'*.mp4'
|
||||
, u'*.mp3'
|
||||
, u'*.ogg'
|
||||
, u'*.ogv'
|
||||
, u'*.webm'
|
||||
, u'*.swf', u'*.mpg', u'*.wmv', u'*.mpeg', u'*.avi'
|
||||
@ -73,7 +75,7 @@ class WebkitAPI(MediaAPI):
|
||||
# no special controls
|
||||
pass
|
||||
|
||||
def getDisplayCss(self):
|
||||
def get_media_display_css(self):
|
||||
"""
|
||||
Add css style sheets to htmlbuilder
|
||||
"""
|
||||
@ -91,7 +93,7 @@ class WebkitAPI(MediaAPI):
|
||||
return css
|
||||
|
||||
|
||||
def getDisplayJavascript(self):
|
||||
def get_media_display_javascript(self):
|
||||
"""
|
||||
Add javascript functions to htmlbuilder
|
||||
"""
|
||||
@ -99,7 +101,7 @@ class WebkitAPI(MediaAPI):
|
||||
var video_timer = null;
|
||||
var current_video = '1';
|
||||
|
||||
function show_video(state, path, volume, loop, seekVal){
|
||||
function show_video(state, path, volume, loop, varVal){
|
||||
// Note, the preferred method for looping would be to use the
|
||||
// video tag loop attribute.
|
||||
// But QtWebKit doesn't support this. Neither does it support the
|
||||
@ -139,7 +141,7 @@ class WebkitAPI(MediaAPI):
|
||||
break;
|
||||
case 'play':
|
||||
vid.play();
|
||||
vid.style.visibility = 'visible';
|
||||
//vid.style.visibility = 'visible';
|
||||
if(vid.looping){
|
||||
video_timer = setInterval(
|
||||
function() {
|
||||
@ -156,7 +158,6 @@ class WebkitAPI(MediaAPI):
|
||||
break;
|
||||
case 'stop':
|
||||
show_video('pause');
|
||||
vid.style.visibility = 'hidden';
|
||||
break;
|
||||
case 'poll':
|
||||
if(vid.ended||vid.currentTime+0.2>vid.duration)
|
||||
@ -182,7 +183,10 @@ class WebkitAPI(MediaAPI):
|
||||
return vid.currentTime;
|
||||
case 'seek':
|
||||
// doesnt work currently
|
||||
//vid.currentTime = seekVal;
|
||||
//vid.currentTime = varVal;
|
||||
break;
|
||||
case 'setVisible':
|
||||
vid.style.visibility = varVal;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -197,8 +201,7 @@ class WebkitAPI(MediaAPI):
|
||||
return document.embeds[movieName];
|
||||
}
|
||||
|
||||
// http://www.adobe.com/support/flash/publishexport/scriptingwithflash/scriptingwithflash_03.html
|
||||
function show_flash(state, path, volume, seekVal){
|
||||
function show_flash(state, path, volume, varVal){
|
||||
var text = document.getElementById('flash');
|
||||
var flashMovie = getFlashMovieObject("OpenLPFlashMovie");
|
||||
var src = "src = 'file:///" + path + "'";
|
||||
@ -214,27 +217,22 @@ class WebkitAPI(MediaAPI):
|
||||
case 'load':
|
||||
text.innerHTML = "<embed " + src + view_parm + swf_parm + "/>";
|
||||
flashMovie = getFlashMovieObject("OpenLPFlashMovie");
|
||||
text.style.visibility = 'visible';
|
||||
flashMovie.Play();
|
||||
break;
|
||||
case 'play':
|
||||
text.style.visibility = 'visible';
|
||||
flashMovie.Play();
|
||||
break;
|
||||
case 'pause':
|
||||
flashMovie.StopPlay();
|
||||
text.style.visibility = 'hidden';
|
||||
break;
|
||||
case 'stop':
|
||||
flashMovie.StopPlay();
|
||||
text.style.visibility = 'hidden';
|
||||
tempHtml = text.innerHTML;
|
||||
text.innerHTML = '';
|
||||
text.innerHTML = tempHtml;
|
||||
break;
|
||||
case 'close':
|
||||
flashMovie.StopPlay();
|
||||
text.style.visibility = 'hidden';
|
||||
text.innerHTML = '';
|
||||
break;
|
||||
case 'length':
|
||||
@ -242,7 +240,10 @@ class WebkitAPI(MediaAPI):
|
||||
case 'currentTime':
|
||||
return flashMovie.CurrentFrame();
|
||||
case 'seek':
|
||||
// flashMovie.GotoFrame(seekVal);
|
||||
// flashMovie.GotoFrame(varVal);
|
||||
break;
|
||||
case 'setVisible':
|
||||
text.style.visibility = varVal;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -250,7 +251,7 @@ class WebkitAPI(MediaAPI):
|
||||
return js
|
||||
|
||||
|
||||
def getDisplayHtml(self):
|
||||
def get_media_display_html(self):
|
||||
"""
|
||||
Add html code to htmlbuilder
|
||||
"""
|
||||
@ -267,7 +268,6 @@ class WebkitAPI(MediaAPI):
|
||||
display.webView.resize(display.size())
|
||||
display.webView.raise_()
|
||||
self.hasOwnWidget = False
|
||||
#display.webView.hide()
|
||||
|
||||
def check_available(self):
|
||||
return True
|
||||
@ -287,7 +287,7 @@ class WebkitAPI(MediaAPI):
|
||||
loop = u'false'
|
||||
display.webView.setVisible(True)
|
||||
if controller.media_info.file_info.suffix() == u'swf':
|
||||
controller.media_info.isFlash = True
|
||||
controller.media_info.is_flash = True
|
||||
js = u'show_flash("load","%s");' % \
|
||||
(path.replace(u'\\', u'\\\\'))
|
||||
else:
|
||||
@ -305,17 +305,18 @@ class WebkitAPI(MediaAPI):
|
||||
display.webLoaded = True
|
||||
length = 0
|
||||
self.set_visible(display, True)
|
||||
if controller.media_info.isFlash:
|
||||
if controller.media_info.is_flash:
|
||||
display.frame.evaluateJavaScript(u'show_flash("play");')
|
||||
else:
|
||||
display.frame.evaluateJavaScript(u'show_video("play");')
|
||||
#TODO add playing check and get the correct media length
|
||||
# TODO add playing check and get the correct media length
|
||||
controller.media_info.length = length
|
||||
self.state = MediaState.Playing
|
||||
return True
|
||||
|
||||
def pause(self, display):
|
||||
controller = display.controller
|
||||
if controller.media_info.isFlash:
|
||||
if controller.media_info.is_flash:
|
||||
display.frame.evaluateJavaScript(u'show_flash("pause");')
|
||||
else:
|
||||
display.frame.evaluateJavaScript(u'show_video("pause");')
|
||||
@ -323,7 +324,7 @@ class WebkitAPI(MediaAPI):
|
||||
|
||||
def stop(self, display):
|
||||
controller = display.controller
|
||||
if controller.media_info.isFlash:
|
||||
if controller.media_info.is_flash:
|
||||
display.frame.evaluateJavaScript(u'show_flash("stop");')
|
||||
else:
|
||||
display.frame.evaluateJavaScript(u'show_video("stop");')
|
||||
@ -334,13 +335,13 @@ class WebkitAPI(MediaAPI):
|
||||
# 1.0 is the highest value
|
||||
if display.hasAudio:
|
||||
vol = float(vol) / float(100)
|
||||
if not controller.media_info.isFlash:
|
||||
if not controller.media_info.is_flash:
|
||||
display.frame.evaluateJavaScript(
|
||||
u'show_video(null, null, %s);' % str(vol))
|
||||
|
||||
def seek(self, display, seekVal):
|
||||
controller = display.controller
|
||||
if controller.media_info.isFlash:
|
||||
if controller.media_info.is_flash:
|
||||
seek = seekVal
|
||||
display.frame.evaluateJavaScript( \
|
||||
u'show_flash("seek", null, null, "%s");' % (seek))
|
||||
@ -351,19 +352,28 @@ class WebkitAPI(MediaAPI):
|
||||
|
||||
def reset(self, display):
|
||||
controller = display.controller
|
||||
if controller.media_info.isFlash:
|
||||
if controller.media_info.is_flash:
|
||||
display.frame.evaluateJavaScript(u'show_flash("close");')
|
||||
else:
|
||||
display.frame.evaluateJavaScript(u'show_video("close");')
|
||||
self.state = MediaState.Off
|
||||
|
||||
def set_visible(self, display, status):
|
||||
if self.hasOwnWidget:
|
||||
display.webView.setVisible(status)
|
||||
controller = display.controller
|
||||
if status:
|
||||
is_visible = "visible"
|
||||
else:
|
||||
is_visible = "hidden"
|
||||
if controller.media_info.is_flash:
|
||||
display.frame.evaluateJavaScript(u'show_flash( \
|
||||
"setVisible", null, null, "%s");' % (is_visible))
|
||||
else:
|
||||
display.frame.evaluateJavaScript(u'show_video( \
|
||||
"setVisible", null, null, null, "%s");' % (is_visible))
|
||||
|
||||
def update_ui(self, display):
|
||||
controller = display.controller
|
||||
if controller.media_info.isFlash:
|
||||
if controller.media_info.is_flash:
|
||||
currentTime = display.frame.evaluateJavaScript( \
|
||||
u'show_flash("currentTime");').toInt()[0]
|
||||
length = display.frame.evaluateJavaScript( \
|
||||
@ -372,12 +382,12 @@ class WebkitAPI(MediaAPI):
|
||||
(currentTime, ok) = display.frame.evaluateJavaScript( \
|
||||
u'show_video("currentTime");').toFloat()
|
||||
# check if conversion was ok and value is not 'NaN'
|
||||
if ok and currentTime == currentTime:
|
||||
if ok and currentTime != float('inf'):
|
||||
currentTime = int(currentTime*1000)
|
||||
(length, ok) = display.frame.evaluateJavaScript( \
|
||||
u'show_video("length");').toFloat()
|
||||
# check if conversion was ok and value is not 'NaN'
|
||||
if ok and length == length:
|
||||
if ok and length != float('inf'):
|
||||
length = int(length*1000)
|
||||
if currentTime > 0:
|
||||
controller.media_info.length = length
|
||||
|
@ -31,7 +31,7 @@ import os
|
||||
from PyQt4 import QtCore, QtGui
|
||||
from lxml import html
|
||||
|
||||
from openlp.core.lib import translate, get_text_file_string
|
||||
from openlp.core.lib import translate, get_text_file_string, Receiver
|
||||
from openlp.core.lib.ui import UiStrings
|
||||
from openlp.core.ui.printservicedialog import Ui_PrintServiceDialog, ZoomSize
|
||||
from openlp.core.utils import AppLocation
|
||||
@ -327,12 +327,14 @@ class PrintServiceForm(QtGui.QDialog, Ui_PrintServiceDialog):
|
||||
"""
|
||||
Copies the display text to the clipboard as plain text
|
||||
"""
|
||||
self.update_song_usage()
|
||||
self.mainWindow.clipboard.setText(self.document.toPlainText())
|
||||
|
||||
def copyHtmlText(self):
|
||||
"""
|
||||
Copies the display text to the clipboard as Html
|
||||
"""
|
||||
self.update_song_usage()
|
||||
self.mainWindow.clipboard.setText(self.document.toHtml())
|
||||
|
||||
def printServiceOrder(self):
|
||||
@ -341,6 +343,7 @@ class PrintServiceForm(QtGui.QDialog, Ui_PrintServiceDialog):
|
||||
"""
|
||||
if not self.printDialog.exec_():
|
||||
return
|
||||
self.update_song_usage()
|
||||
# Print the document.
|
||||
self.document.print_(self.printer)
|
||||
|
||||
@ -397,3 +400,9 @@ class PrintServiceForm(QtGui.QDialog, Ui_PrintServiceDialog):
|
||||
settings.setValue(u'print notes',
|
||||
QtCore.QVariant(self.notesCheckBox.isChecked()))
|
||||
settings.endGroup()
|
||||
|
||||
def update_song_usage(self):
|
||||
for index, item in enumerate(self.serviceManager.serviceItems):
|
||||
# Trigger Audit requests
|
||||
Receiver.send_message(u'print_service_started',
|
||||
[item[u'service_item']])
|
||||
|
@ -290,7 +290,7 @@ class ServiceManager(QtGui.QWidget):
|
||||
QtCore.SIGNAL(u'service_item_update'), self.serviceItemUpdate)
|
||||
# Last little bits of setting up
|
||||
self.service_theme = unicode(QtCore.QSettings().value(
|
||||
self.mainwindow.serviceSettingsSection + u'/service theme',
|
||||
self.mainwindow.servicemanagerSettingsSection + u'/service theme',
|
||||
QtCore.QVariant(u'')).toString())
|
||||
self.servicePath = AppLocation.get_section_data_path(u'servicemanager')
|
||||
# build the drag and drop context menu
|
||||
@ -371,7 +371,7 @@ class ServiceManager(QtGui.QWidget):
|
||||
self.mainwindow.setServiceModified(self.isModified(),
|
||||
self.shortFileName())
|
||||
QtCore.QSettings(). \
|
||||
setValue(u'service/last file',QtCore.QVariant(fileName))
|
||||
setValue(u'servicemanager/last file',QtCore.QVariant(fileName))
|
||||
|
||||
def fileName(self):
|
||||
"""
|
||||
@ -429,14 +429,15 @@ class ServiceManager(QtGui.QWidget):
|
||||
self.mainwindow,
|
||||
translate('OpenLP.ServiceManager', 'Open File'),
|
||||
SettingsManager.get_last_dir(
|
||||
self.mainwindow.serviceSettingsSection),
|
||||
self.mainwindow.servicemanagerSettingsSection),
|
||||
translate('OpenLP.ServiceManager',
|
||||
'OpenLP Service Files (*.osz)')))
|
||||
if not fileName:
|
||||
return False
|
||||
else:
|
||||
fileName = loadFile
|
||||
SettingsManager.set_last_dir(self.mainwindow.serviceSettingsSection,
|
||||
SettingsManager.set_last_dir(
|
||||
self.mainwindow.servicemanagerSettingsSection,
|
||||
split_filename(fileName)[0])
|
||||
self.loadFile(fileName)
|
||||
|
||||
@ -461,7 +462,7 @@ class ServiceManager(QtGui.QWidget):
|
||||
self.setFileName(u'')
|
||||
self.setModified(False)
|
||||
QtCore.QSettings(). \
|
||||
setValue(u'service/last file',QtCore.QVariant(u''))
|
||||
setValue(u'servicemanager/last file',QtCore.QVariant(u''))
|
||||
|
||||
def saveFile(self):
|
||||
"""
|
||||
@ -474,7 +475,8 @@ class ServiceManager(QtGui.QWidget):
|
||||
(basename, extension) = os.path.splitext(file_name)
|
||||
service_file_name = basename + '.osd'
|
||||
log.debug(u'ServiceManager.saveFile - %s' % path_file_name)
|
||||
SettingsManager.set_last_dir(self.mainwindow.serviceSettingsSection,
|
||||
SettingsManager.set_last_dir(
|
||||
self.mainwindow.servicemanagerSettingsSection,
|
||||
path)
|
||||
service = []
|
||||
write_list = []
|
||||
@ -562,7 +564,7 @@ class ServiceManager(QtGui.QWidget):
|
||||
fileName = unicode(QtGui.QFileDialog.getSaveFileName(self.mainwindow,
|
||||
UiStrings().SaveService,
|
||||
SettingsManager.get_last_dir(
|
||||
self.mainwindow.serviceSettingsSection),
|
||||
self.mainwindow.servicemanagerSettingsSection),
|
||||
translate('OpenLP.ServiceManager', 'OpenLP Service Files (*.osz)')))
|
||||
if not fileName:
|
||||
return False
|
||||
@ -624,7 +626,7 @@ class ServiceManager(QtGui.QWidget):
|
||||
self.mainwindow.addRecentFile(fileName)
|
||||
self.setModified(False)
|
||||
QtCore.QSettings().setValue(
|
||||
'service/last file', QtCore.QVariant(fileName))
|
||||
'servicemanager/last file', QtCore.QVariant(fileName))
|
||||
else:
|
||||
critical_error_message_box(
|
||||
message=translate('OpenLP.ServiceManager',
|
||||
@ -666,7 +668,7 @@ class ServiceManager(QtGui.QWidget):
|
||||
present.
|
||||
"""
|
||||
fileName = QtCore.QSettings(). \
|
||||
value(u'service/last file',QtCore.QVariant(u'')).toString()
|
||||
value(u'servicemanager/last file',QtCore.QVariant(u'')).toString()
|
||||
if fileName:
|
||||
self.loadFile(fileName)
|
||||
|
||||
@ -1008,7 +1010,8 @@ class ServiceManager(QtGui.QWidget):
|
||||
self.service_theme = unicode(self.themeComboBox.currentText())
|
||||
self.mainwindow.renderer.set_service_theme(self.service_theme)
|
||||
QtCore.QSettings().setValue(
|
||||
self.mainwindow.serviceSettingsSection + u'/service theme',
|
||||
self.mainwindow.servicemanagerSettingsSection +
|
||||
u'/service theme',
|
||||
QtCore.QVariant(self.service_theme))
|
||||
self.regenerateServiceItems()
|
||||
|
||||
|
@ -253,7 +253,7 @@ class SlideController(Controller):
|
||||
self.onEditSong)
|
||||
self.controllerLayout.addWidget(self.toolbar)
|
||||
# Build the Media Toolbar
|
||||
self.mediaManager.addControllerItems(self, self.controllerLayout)
|
||||
self.mediaManager.add_controller_items(self, self.controllerLayout)
|
||||
if self.isLive:
|
||||
# Build the Song Toolbar
|
||||
self.songMenu = QtGui.QToolButton(self.toolbar)
|
||||
@ -512,9 +512,13 @@ class SlideController(Controller):
|
||||
self.toolbar.makeWidgetsVisible(self.loopList)
|
||||
if item.is_media():
|
||||
self.mediabar.setVisible(True)
|
||||
self.previousItem.setVisible(False)
|
||||
self.nextItem.setVisible(False)
|
||||
else:
|
||||
# Work-around for OS X, hide and then show the toolbar
|
||||
# See bug #791050
|
||||
self.previousItem.setVisible(True)
|
||||
self.nextItem.setVisible(True)
|
||||
self.toolbar.show()
|
||||
self.toolbar.show()
|
||||
|
||||
|
@ -66,6 +66,8 @@ class ThemeForm(QtGui.QWizard, Ui_ThemeWizard):
|
||||
self.onGradientComboBoxCurrentIndexChanged)
|
||||
QtCore.QObject.connect(self.colorButton,
|
||||
QtCore.SIGNAL(u'clicked()'), self.onColorButtonClicked)
|
||||
QtCore.QObject.connect(self.imageColorButton,
|
||||
QtCore.SIGNAL(u'clicked()'), self.onImageColorButtonClicked)
|
||||
QtCore.QObject.connect(self.gradientStartButton,
|
||||
QtCore.SIGNAL(u'clicked()'), self.onGradientStartButtonClicked)
|
||||
QtCore.QObject.connect(self.gradientEndButton,
|
||||
@ -330,6 +332,8 @@ class ThemeForm(QtGui.QWizard, Ui_ThemeWizard):
|
||||
self.theme.background_end_color)
|
||||
self.setField(u'background_type', QtCore.QVariant(1))
|
||||
else:
|
||||
self.imageColorButton.setStyleSheet(u'background-color: %s' %
|
||||
self.theme.background_border_color)
|
||||
self.imageFileEdit.setText(self.theme.background_filename)
|
||||
self.setField(u'background_type', QtCore.QVariant(2))
|
||||
if self.theme.background_direction == \
|
||||
@ -464,6 +468,14 @@ class ThemeForm(QtGui.QWizard, Ui_ThemeWizard):
|
||||
self._colorButton(self.theme.background_color)
|
||||
self.setBackgroundPageValues()
|
||||
|
||||
def onImageColorButtonClicked(self):
|
||||
"""
|
||||
Background / Gradient 1 Color button pushed.
|
||||
"""
|
||||
self.theme.background_border_color = \
|
||||
self._colorButton(self.theme.background_border_color)
|
||||
self.setBackgroundPageValues()
|
||||
|
||||
def onGradientStartButtonClicked(self):
|
||||
"""
|
||||
Gradient 2 Color button pushed.
|
||||
@ -564,7 +576,7 @@ class ThemeForm(QtGui.QWizard, Ui_ThemeWizard):
|
||||
|
||||
def accept(self):
|
||||
"""
|
||||
Lets save the them as Finish has been pressed
|
||||
Lets save the theme as Finish has been pressed
|
||||
"""
|
||||
# Save the theme name
|
||||
self.theme.theme_name = unicode(self.field(u'name').toString())
|
||||
|
@ -610,6 +610,11 @@ class ThemeManager(QtGui.QWidget):
|
||||
and to trigger the reload of the theme list
|
||||
"""
|
||||
self._writeTheme(theme, imageFrom, imageTo)
|
||||
if theme.background_type == \
|
||||
BackgroundType.to_string(BackgroundType.Image):
|
||||
self.mainwindow.imageManager.update_image(theme.theme_name,
|
||||
u'theme', QtGui.QColor(theme.background_border_color))
|
||||
self.mainwindow.imageManager.process_updates()
|
||||
self.loadThemes()
|
||||
|
||||
def _writeTheme(self, theme, imageFrom, imageTo):
|
||||
|
@ -105,6 +105,11 @@ class Ui_ThemeWizard(object):
|
||||
self.imageLayout = QtGui.QFormLayout(self.imageWidget)
|
||||
self.imageLayout.setMargin(0)
|
||||
self.imageLayout.setObjectName(u'ImageLayout')
|
||||
self.imageColorLabel = QtGui.QLabel(self.colorWidget)
|
||||
self.imageColorLabel.setObjectName(u'ImageColorLabel')
|
||||
self.imageColorButton = QtGui.QPushButton(self.colorWidget)
|
||||
self.imageColorButton.setObjectName(u'ImageColorButton')
|
||||
self.imageLayout.addRow(self.imageColorLabel, self.imageColorButton)
|
||||
self.imageLabel = QtGui.QLabel(self.imageWidget)
|
||||
self.imageLabel.setObjectName(u'ImageLabel')
|
||||
self.imageFileLayout = QtGui.QHBoxLayout()
|
||||
@ -118,7 +123,7 @@ class Ui_ThemeWizard(object):
|
||||
build_icon(u':/general/general_open.png'))
|
||||
self.imageFileLayout.addWidget(self.imageBrowseButton)
|
||||
self.imageLayout.addRow(self.imageLabel, self.imageFileLayout)
|
||||
self.imageLayout.setItem(1, QtGui.QFormLayout.LabelRole, self.spacer)
|
||||
self.imageLayout.setItem(2, QtGui.QFormLayout.LabelRole, self.spacer)
|
||||
self.backgroundStack.addWidget(self.imageWidget)
|
||||
self.backgroundLayout.addLayout(self.backgroundStack)
|
||||
themeWizard.addPage(self.backgroundPage)
|
||||
@ -443,6 +448,8 @@ class Ui_ThemeWizard(object):
|
||||
translate('OpenLP.ThemeWizard', 'Top Left - Bottom Right'))
|
||||
self.gradientComboBox.setItemText(BackgroundGradientType.LeftBottom,
|
||||
translate('OpenLP.ThemeWizard', 'Bottom Left - Top Right'))
|
||||
self.imageColorLabel.setText(
|
||||
translate(u'OpenLP.ThemeWizard', 'Background color:'))
|
||||
self.imageLabel.setText(u'%s:' % UiStrings().Image)
|
||||
self.mainAreaPage.setTitle(
|
||||
translate('OpenLP.ThemeWizard', 'Main Area Font Details'))
|
||||
|
@ -391,10 +391,13 @@ class BibleMediaItem(MediaManagerItem):
|
||||
elif len(bibles):
|
||||
self.initialiseAdvancedBible(bibles[0])
|
||||
|
||||
def reloadBibles(self):
|
||||
def reloadBibles(self, process=False):
|
||||
log.debug(u'Reloading Bibles')
|
||||
self.plugin.manager.reload_bibles()
|
||||
self.loadBibles()
|
||||
# If called from first time wizard re-run, process any new bibles.
|
||||
if process:
|
||||
self.plugin.appStartup()
|
||||
self.updateAutoCompleter()
|
||||
|
||||
def initialiseAdvancedBible(self, bible):
|
||||
|
@ -217,8 +217,7 @@ class CustomMediaItem(MediaManagerItem):
|
||||
for item in self.listView.selectedIndexes()]
|
||||
for id in id_list:
|
||||
self.plugin.manager.delete_object(CustomSlide, id)
|
||||
for row in row_list:
|
||||
self.listView.takeItem(row)
|
||||
self.onSearchTextButtonClick()
|
||||
|
||||
def onFocus(self):
|
||||
self.searchTextEdit.setFocus()
|
||||
|
@ -25,10 +25,13 @@
|
||||
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||
###############################################################################
|
||||
|
||||
from PyQt4 import QtCore, QtGui
|
||||
|
||||
import logging
|
||||
|
||||
from openlp.core.lib import Plugin, StringContent, build_icon, translate
|
||||
from openlp.plugins.images.lib import ImageMediaItem
|
||||
from openlp.core.lib import Plugin, StringContent, build_icon, translate, \
|
||||
Receiver
|
||||
from openlp.plugins.images.lib import ImageMediaItem, ImageTab
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
@ -36,10 +39,13 @@ class ImagePlugin(Plugin):
|
||||
log.info(u'Image Plugin loaded')
|
||||
|
||||
def __init__(self, plugin_helpers):
|
||||
Plugin.__init__(self, u'images', plugin_helpers, ImageMediaItem)
|
||||
Plugin.__init__(self, u'images', plugin_helpers, ImageMediaItem,
|
||||
ImageTab)
|
||||
self.weight = -7
|
||||
self.icon_path = u':/plugins/plugin_images.png'
|
||||
self.icon = build_icon(self.icon_path)
|
||||
QtCore.QObject.connect(Receiver.get_receiver(),
|
||||
QtCore.SIGNAL(u'image_updated'), self.image_updated)
|
||||
|
||||
def about(self):
|
||||
about_text = translate('ImagePlugin', '<strong>Image Plugin</strong>'
|
||||
@ -81,3 +87,13 @@ class ImagePlugin(Plugin):
|
||||
'Add the selected image to the service.')
|
||||
}
|
||||
self.setPluginUiTextStrings(tooltips)
|
||||
|
||||
def image_updated(self):
|
||||
"""
|
||||
Triggered by saving and changing the image border. Sets the images in
|
||||
image manager to require updates. Actual update is triggered by the
|
||||
last part of saving the config.
|
||||
"""
|
||||
background = QtGui.QColor(QtCore.QSettings().value(self.settingsSection
|
||||
+ u'/background color', QtCore.QVariant(u'#000000')))
|
||||
self.liveController.imageManager.update_images(u'image', background)
|
||||
|
@ -26,3 +26,4 @@
|
||||
###############################################################################
|
||||
|
||||
from mediaitem import ImageMediaItem
|
||||
from imagetab import ImageTab
|
||||
|
101
openlp/plugins/images/lib/imagetab.py
Normal file
101
openlp/plugins/images/lib/imagetab.py
Normal file
@ -0,0 +1,101 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
|
||||
|
||||
###############################################################################
|
||||
# OpenLP - Open Source Lyrics Projection #
|
||||
# --------------------------------------------------------------------------- #
|
||||
# Copyright (c) 2008-2011 Raoul Snyman #
|
||||
# Portions copyright (c) 2008-2011 Tim Bentley, Gerald Britton, Jonathan #
|
||||
# Corwin, Michael Gorven, Scott Guerrieri, Matthias Hub, Meinert Jordan, #
|
||||
# Armin Köhler, Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias #
|
||||
# Põldaru, Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, #
|
||||
# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Frode Woldsund #
|
||||
# --------------------------------------------------------------------------- #
|
||||
# This program is free software; you can redistribute it and/or modify it #
|
||||
# under the terms of the GNU General Public License as published by the Free #
|
||||
# Software Foundation; version 2 of the License. #
|
||||
# #
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT #
|
||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
|
||||
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
|
||||
# more details. #
|
||||
# #
|
||||
# You should have received a copy of the GNU General Public License along #
|
||||
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
|
||||
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||
###############################################################################
|
||||
|
||||
from PyQt4 import QtCore, QtGui
|
||||
|
||||
from openlp.core.lib import SettingsTab, translate, Receiver
|
||||
from openlp.core.lib.ui import UiStrings, create_valign_combo
|
||||
|
||||
class ImageTab(SettingsTab):
|
||||
"""
|
||||
ImageTab is the images settings tab in the settings dialog.
|
||||
"""
|
||||
def __init__(self, parent, name, visible_title, icon_path):
|
||||
SettingsTab.__init__(self, parent, name, visible_title, icon_path)
|
||||
|
||||
def setupUi(self):
|
||||
self.setObjectName(u'ImagesTab')
|
||||
SettingsTab.setupUi(self)
|
||||
self.bgColorGroupBox = QtGui.QGroupBox(self.leftColumn)
|
||||
self.bgColorGroupBox.setObjectName(u'FontGroupBox')
|
||||
self.formLayout = QtGui.QFormLayout(self.bgColorGroupBox)
|
||||
self.formLayout.setObjectName(u'FormLayout')
|
||||
self.colorLayout = QtGui.QHBoxLayout()
|
||||
self.backgroundColorLabel = QtGui.QLabel(self.bgColorGroupBox)
|
||||
self.backgroundColorLabel.setObjectName(u'BackgroundColorLabel')
|
||||
self.colorLayout.addWidget(self.backgroundColorLabel)
|
||||
self.backgroundColorButton = QtGui.QPushButton(self.bgColorGroupBox)
|
||||
self.backgroundColorButton.setObjectName(u'BackgroundColorButton')
|
||||
self.colorLayout.addWidget(self.backgroundColorButton)
|
||||
self.formLayout.addRow(self.colorLayout)
|
||||
self.informationLabel = QtGui.QLabel(self.bgColorGroupBox)
|
||||
self.informationLabel.setObjectName(u'InformationLabel')
|
||||
self.formLayout.addRow(self.informationLabel)
|
||||
self.leftLayout.addWidget(self.bgColorGroupBox)
|
||||
self.leftLayout.addStretch()
|
||||
self.rightColumn.setSizePolicy(
|
||||
QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Preferred)
|
||||
self.rightLayout.addStretch()
|
||||
# Signals and slots
|
||||
QtCore.QObject.connect(self.backgroundColorButton,
|
||||
QtCore.SIGNAL(u'pressed()'), self.onbackgroundColorButtonClicked)
|
||||
|
||||
def retranslateUi(self):
|
||||
self.bgColorGroupBox.setTitle(
|
||||
translate('ImagesPlugin.ImageTab', 'Background Color'))
|
||||
self.backgroundColorLabel.setText(
|
||||
translate('ImagesPlugin.ImageTab', 'Default Color:'))
|
||||
self.informationLabel.setText(
|
||||
translate('ImagesPlugin.ImageTab', 'Provides border where image '
|
||||
'is not the correct dimensions for the screen when resized.'))
|
||||
|
||||
def onbackgroundColorButtonClicked(self):
|
||||
new_color = QtGui.QColorDialog.getColor(
|
||||
QtGui.QColor(self.bg_color), self)
|
||||
if new_color.isValid():
|
||||
self.bg_color = new_color.name()
|
||||
self.backgroundColorButton.setStyleSheet(
|
||||
u'background-color: %s' % self.bg_color)
|
||||
|
||||
def load(self):
|
||||
settings = QtCore.QSettings()
|
||||
settings.beginGroup(self.settingsSection)
|
||||
self.bg_color = unicode(settings.value(
|
||||
u'background color', QtCore.QVariant(u'#000000')).toString())
|
||||
self.initial_color = self.bg_color
|
||||
settings.endGroup()
|
||||
self.backgroundColorButton.setStyleSheet(
|
||||
u'background-color: %s' % self.bg_color)
|
||||
|
||||
def save(self):
|
||||
settings = QtCore.QSettings()
|
||||
settings.beginGroup(self.settingsSection)
|
||||
settings.setValue(u'background color', QtCore.QVariant(self.bg_color))
|
||||
settings.endGroup()
|
||||
if self.initial_color != self.bg_color:
|
||||
Receiver.send_message(u'image_updated')
|
||||
|
@ -140,6 +140,8 @@ class ImageMediaItem(MediaManagerItem):
|
||||
self.plugin.formparent.finishedProgressBar()
|
||||
|
||||
def generateSlideData(self, service_item, item=None, xmlVersion=False):
|
||||
background = QtGui.QColor(QtCore.QSettings().value(self.settingsSection
|
||||
+ u'/background color', QtCore.QVariant(u'#000000')))
|
||||
if item:
|
||||
items = [item]
|
||||
else:
|
||||
@ -183,7 +185,7 @@ class ImageMediaItem(MediaManagerItem):
|
||||
for bitem in items:
|
||||
filename = unicode(bitem.data(QtCore.Qt.UserRole).toString())
|
||||
(path, name) = os.path.split(filename)
|
||||
service_item.add_from_image(filename, name)
|
||||
service_item.add_from_image(filename, name, background)
|
||||
return True
|
||||
|
||||
def onResetClick(self):
|
||||
@ -206,13 +208,16 @@ class ImageMediaItem(MediaManagerItem):
|
||||
if check_item_selected(self.listView,
|
||||
translate('ImagePlugin.MediaItem',
|
||||
'You must select an image to replace the background with.')):
|
||||
background = QtGui.QColor(QtCore.QSettings().value(
|
||||
self.settingsSection + u'/background color',
|
||||
QtCore.QVariant(u'#000000')))
|
||||
item = self.listView.selectedIndexes()[0]
|
||||
bitem = self.listView.item(item.row())
|
||||
filename = unicode(bitem.data(QtCore.Qt.UserRole).toString())
|
||||
if os.path.exists(filename):
|
||||
(path, name) = os.path.split(filename)
|
||||
if self.plugin.liveController.display.directImage(name,
|
||||
filename):
|
||||
filename, background):
|
||||
self.resetAction.setVisible(True)
|
||||
else:
|
||||
critical_error_message_box(UiStrings().LiveBGError,
|
||||
|
@ -56,11 +56,15 @@ class MediaMediaItem(MediaManagerItem):
|
||||
self.mediaObject = None
|
||||
self.mediaController = Controller(parent)
|
||||
self.mediaController.controllerLayout = QtGui.QVBoxLayout()
|
||||
self.plugin.mediaManager.addControllerItems(self.mediaController, self.mediaController.controllerLayout)
|
||||
self.plugin.mediaManager.set_controls_visible(self.mediaController, False)
|
||||
self.mediaController.previewDisplay = Display(self.mediaController, False, self.mediaController, self.plugin.pluginManager.plugins)
|
||||
self.plugin.mediaManager.add_controller_items(self.mediaController, \
|
||||
self.mediaController.controllerLayout)
|
||||
self.plugin.mediaManager.set_controls_visible(self.mediaController, \
|
||||
False)
|
||||
self.mediaController.previewDisplay = Display(self.mediaController, \
|
||||
False, self.mediaController, self.plugin.pluginManager.plugins)
|
||||
self.mediaController.previewDisplay.setup()
|
||||
self.plugin.mediaManager.setup_display(self.mediaController.previewDisplay)
|
||||
self.plugin.mediaManager.setup_display( \
|
||||
self.mediaController.previewDisplay)
|
||||
self.mediaController.previewDisplay.hide()
|
||||
|
||||
QtCore.QObject.connect(Receiver.get_receiver(),
|
||||
@ -100,7 +104,7 @@ class MediaMediaItem(MediaManagerItem):
|
||||
|
||||
def onResetClick(self):
|
||||
"""
|
||||
Called to reset the Live backgound with the media selected,
|
||||
Called to reset the Live background with the media selected,
|
||||
"""
|
||||
self.plugin.liveController.mediaManager.video_reset( \
|
||||
self.plugin.liveController)
|
||||
@ -114,7 +118,7 @@ class MediaMediaItem(MediaManagerItem):
|
||||
|
||||
def onReplaceClick(self):
|
||||
"""
|
||||
Called to replace Live backgound with the media selected.
|
||||
Called to replace Live background with the media selected.
|
||||
"""
|
||||
if check_item_selected(self.listView,
|
||||
translate('MediaPlugin.MediaItem',
|
||||
@ -122,7 +126,7 @@ class MediaMediaItem(MediaManagerItem):
|
||||
item = self.listView.currentItem()
|
||||
filename = unicode(item.data(QtCore.Qt.UserRole).toString())
|
||||
if os.path.exists(filename):
|
||||
(path, name) = os.path.split(filename)
|
||||
#(path, name) = os.path.split(filename)
|
||||
if self.plugin.liveController.mediaManager.video( \
|
||||
self.plugin.liveController, filename, True, True):
|
||||
self.resetAction.setVisible(True)
|
||||
@ -158,6 +162,8 @@ class MediaMediaItem(MediaManagerItem):
|
||||
if self.mediaLength > 0:
|
||||
service_item.add_capability(
|
||||
ItemCapabilities.AllowsVariableStartTime)
|
||||
else:
|
||||
return False
|
||||
service_item.media_length = self.mediaLength
|
||||
service_item.title = unicode(self.plugin.nameStrings[u'singular'])
|
||||
service_item.add_capability(ItemCapabilities.RequiresMedia)
|
||||
|
@ -53,7 +53,6 @@ class MediaTab(SettingsTab):
|
||||
self.ApiCheckBoxes[api.name] = checkbox
|
||||
self.mediaApiLayout.addWidget(checkbox)
|
||||
self.leftLayout.addWidget(self.mediaAPIGroupBox)
|
||||
|
||||
self.apiOrderGroupBox = QtGui.QGroupBox(self.leftColumn)
|
||||
self.apiOrderGroupBox.setObjectName(u'apiOrderGroupBox')
|
||||
self.apiOrderLayout = QtGui.QVBoxLayout(self.apiOrderGroupBox)
|
||||
@ -67,7 +66,6 @@ class MediaTab(SettingsTab):
|
||||
sizePolicy.setHeightForWidth(self.apiOrderlistWidget. \
|
||||
sizePolicy().hasHeightForWidth())
|
||||
self.apiOrderlistWidget.setSizePolicy(sizePolicy)
|
||||
|
||||
self.apiOrderlistWidget.setVerticalScrollBarPolicy( \
|
||||
QtCore.Qt.ScrollBarAsNeeded)
|
||||
self.apiOrderlistWidget.setHorizontalScrollBarPolicy( \
|
||||
|
@ -99,21 +99,22 @@ class MediaPlugin(Plugin):
|
||||
"""
|
||||
log.info(u'Media Finalising')
|
||||
self.mediaManager.finalise()
|
||||
Plugin.finalise(self)
|
||||
|
||||
def getDisplayCss(self):
|
||||
"""
|
||||
Add css style sheets to htmlbuilder
|
||||
"""
|
||||
return self.mediaManager.getDisplayCss()
|
||||
return self.mediaManager.get_media_display_css()
|
||||
|
||||
def getDisplayJavascript(self):
|
||||
"""
|
||||
Add javascript functions to htmlbuilder
|
||||
"""
|
||||
return self.mediaManager.getDisplayJavascript()
|
||||
return self.mediaManager.get_media_display_javascript()
|
||||
|
||||
def getDisplayHtml(self):
|
||||
"""
|
||||
Add html code to htmlbuilder
|
||||
"""
|
||||
return self.mediaManager.getDisplayHtml()
|
||||
return self.mediaManager.get_media_display_html()
|
||||
|
@ -170,8 +170,8 @@ class SongExportForm(OpenLPWizard):
|
||||
translate('OpenLP.Ui', 'Welcome to the Song Export Wizard'))
|
||||
self.informationLabel.setText(
|
||||
translate('SongsPlugin.ExportWizardForm', 'This wizard will help to'
|
||||
' export your songs to the open and free OpenLyrics worship song '
|
||||
'format.'))
|
||||
' export your songs to the open and free <strong>OpenLyrics'
|
||||
'</strong> worship song format.'))
|
||||
self.availableSongsPage.setTitle(
|
||||
translate('SongsPlugin.ExportWizardForm', 'Select Songs'))
|
||||
self.availableSongsPage.setSubTitle(
|
||||
@ -285,7 +285,9 @@ class SongExportForm(OpenLPWizard):
|
||||
self, songs, unicode(self.directoryLineEdit.text()))
|
||||
if exporter.do_export():
|
||||
self.progressLabel.setText(
|
||||
translate('SongsPlugin.SongExportForm', 'Finished export.'))
|
||||
translate('SongsPlugin.SongExportForm', 'Finished export. To '
|
||||
'import these files use the <strong>OpenLyrics</strong> '
|
||||
'importer.'))
|
||||
else:
|
||||
self.progressLabel.setText(
|
||||
translate('SongsPlugin.SongExportForm',
|
||||
|
@ -31,6 +31,7 @@ the Songs plugin
|
||||
|
||||
from sqlalchemy import Column, ForeignKey, Table, types
|
||||
from sqlalchemy.orm import mapper, relation
|
||||
from sqlalchemy.sql.expression import func
|
||||
|
||||
from openlp.core.lib.db import BaseModel, init_db
|
||||
|
||||
@ -70,7 +71,6 @@ class Topic(BaseModel):
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
def init_schema(url):
|
||||
"""
|
||||
Setup the songs database connection and initialise the database schema.
|
||||
@ -111,10 +111,6 @@ def init_schema(url):
|
||||
* file_name
|
||||
* type
|
||||
|
||||
**media_files_songs Table**
|
||||
* media_file_id
|
||||
* song_id
|
||||
|
||||
**song_books Table**
|
||||
The *song_books* table holds a list of books that a congregation gets
|
||||
their songs from, or old hymnals now no longer used. This table has the
|
||||
@ -162,7 +158,7 @@ def init_schema(url):
|
||||
|
||||
# Definition of the "authors" table
|
||||
authors_table = Table(u'authors', metadata,
|
||||
Column(u'id', types.Integer, primary_key=True),
|
||||
Column(u'id', types.Integer(), primary_key=True),
|
||||
Column(u'first_name', types.Unicode(128)),
|
||||
Column(u'last_name', types.Unicode(128)),
|
||||
Column(u'display_name', types.Unicode(255), index=True, nullable=False)
|
||||
@ -170,22 +166,25 @@ def init_schema(url):
|
||||
|
||||
# Definition of the "media_files" table
|
||||
media_files_table = Table(u'media_files', metadata,
|
||||
Column(u'id', types.Integer, primary_key=True),
|
||||
Column(u'id', types.Integer(), primary_key=True),
|
||||
Column(u'song_id', types.Integer(), ForeignKey(u'songs.id'),
|
||||
default=None),
|
||||
Column(u'file_name', types.Unicode(255), nullable=False),
|
||||
Column(u'type', types.Unicode(64), nullable=False, default=u'audio')
|
||||
Column(u'type', types.Unicode(64), nullable=False, default=u'audio'),
|
||||
Column(u'weight', types.Integer(), default=0)
|
||||
)
|
||||
|
||||
# Definition of the "song_books" table
|
||||
song_books_table = Table(u'song_books', metadata,
|
||||
Column(u'id', types.Integer, primary_key=True),
|
||||
Column(u'id', types.Integer(), primary_key=True),
|
||||
Column(u'name', types.Unicode(128), nullable=False),
|
||||
Column(u'publisher', types.Unicode(128))
|
||||
)
|
||||
|
||||
# Definition of the "songs" table
|
||||
songs_table = Table(u'songs', metadata,
|
||||
Column(u'id', types.Integer, primary_key=True),
|
||||
Column(u'song_book_id', types.Integer,
|
||||
Column(u'id', types.Integer(), primary_key=True),
|
||||
Column(u'song_book_id', types.Integer(),
|
||||
ForeignKey(u'song_books.id'), default=None),
|
||||
Column(u'title', types.Unicode(255), nullable=False),
|
||||
Column(u'alternate_title', types.Unicode(255)),
|
||||
@ -197,36 +196,31 @@ def init_schema(url):
|
||||
Column(u'song_number', types.Unicode(64)),
|
||||
Column(u'theme_name', types.Unicode(128)),
|
||||
Column(u'search_title', types.Unicode(255), index=True, nullable=False),
|
||||
Column(u'search_lyrics', types.UnicodeText, nullable=False)
|
||||
Column(u'search_lyrics', types.UnicodeText, nullable=False),
|
||||
Column(u'create_date', types.DateTime(), default=func.now()),
|
||||
Column(u'last_modified', types.DateTime(), default=func.now(),
|
||||
onupdate=func.now())
|
||||
)
|
||||
|
||||
# Definition of the "topics" table
|
||||
topics_table = Table(u'topics', metadata,
|
||||
Column(u'id', types.Integer, primary_key=True),
|
||||
Column(u'id', types.Integer(), primary_key=True),
|
||||
Column(u'name', types.Unicode(128), index=True, nullable=False)
|
||||
)
|
||||
|
||||
# Definition of the "authors_songs" table
|
||||
authors_songs_table = Table(u'authors_songs', metadata,
|
||||
Column(u'author_id', types.Integer,
|
||||
Column(u'author_id', types.Integer(),
|
||||
ForeignKey(u'authors.id'), primary_key=True),
|
||||
Column(u'song_id', types.Integer,
|
||||
ForeignKey(u'songs.id'), primary_key=True)
|
||||
)
|
||||
|
||||
# Definition of the "media_files_songs" table
|
||||
media_files_songs_table = Table(u'media_files_songs', metadata,
|
||||
Column(u'media_file_id', types.Integer,
|
||||
ForeignKey(u'media_files.id'), primary_key=True),
|
||||
Column(u'song_id', types.Integer,
|
||||
Column(u'song_id', types.Integer(),
|
||||
ForeignKey(u'songs.id'), primary_key=True)
|
||||
)
|
||||
|
||||
# Definition of the "songs_topics" table
|
||||
songs_topics_table = Table(u'songs_topics', metadata,
|
||||
Column(u'song_id', types.Integer,
|
||||
Column(u'song_id', types.Integer(),
|
||||
ForeignKey(u'songs.id'), primary_key=True),
|
||||
Column(u'topic_id', types.Integer,
|
||||
Column(u'topic_id', types.Integer(),
|
||||
ForeignKey(u'topics.id'), primary_key=True)
|
||||
)
|
||||
|
||||
@ -238,8 +232,7 @@ def init_schema(url):
|
||||
'authors': relation(Author, backref='songs',
|
||||
secondary=authors_songs_table, lazy=False),
|
||||
'book': relation(Book, backref='songs'),
|
||||
'media_files': relation(MediaFile, backref='songs',
|
||||
secondary=media_files_songs_table),
|
||||
'media_files': relation(MediaFile, backref='songs'),
|
||||
'topics': relation(Topic, backref='songs',
|
||||
secondary=songs_topics_table)
|
||||
})
|
||||
|
88
openlp/plugins/songs/lib/upgrade.py
Normal file
88
openlp/plugins/songs/lib/upgrade.py
Normal file
@ -0,0 +1,88 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
|
||||
|
||||
###############################################################################
|
||||
# OpenLP - Open Source Lyrics Projection #
|
||||
# --------------------------------------------------------------------------- #
|
||||
# Copyright (c) 2008-2011 Raoul Snyman #
|
||||
# Portions copyright (c) 2008-2011 Tim Bentley, Gerald Britton, Jonathan #
|
||||
# Corwin, Michael Gorven, Scott Guerrieri, Matthias Hub, Meinert Jordan, #
|
||||
# Armin Köhler, Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias #
|
||||
# Põldaru, Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, #
|
||||
# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Frode Woldsund #
|
||||
# --------------------------------------------------------------------------- #
|
||||
# This program is free software; you can redistribute it and/or modify it #
|
||||
# under the terms of the GNU General Public License as published by the Free #
|
||||
# Software Foundation; version 2 of the License. #
|
||||
# #
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT #
|
||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
|
||||
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
|
||||
# more details. #
|
||||
# #
|
||||
# You should have received a copy of the GNU General Public License along #
|
||||
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
|
||||
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||
###############################################################################
|
||||
"""
|
||||
The :mod:`upgrade` module provides a way for the database and schema that is the
|
||||
backend for the Songs plugin
|
||||
"""
|
||||
|
||||
from sqlalchemy import Column, ForeignKey, Table, types
|
||||
from sqlalchemy.sql.expression import func
|
||||
from migrate import changeset
|
||||
from migrate.changeset.constraint import ForeignKeyConstraint
|
||||
|
||||
__version__ = 2
|
||||
|
||||
def upgrade_setup(metadata):
|
||||
"""
|
||||
Set up the latest revision all tables, with reflection, needed for the
|
||||
upgrade process. If you want to drop a table, you need to remove it from
|
||||
here, and add it to your upgrade function.
|
||||
"""
|
||||
tables = {
|
||||
u'authors': Table(u'authors', metadata, autoload=True),
|
||||
u'media_files': Table(u'media_files', metadata, autoload=True),
|
||||
u'song_books': Table(u'song_books', metadata, autoload=True),
|
||||
u'songs': Table(u'songs', metadata, autoload=True),
|
||||
u'topics': Table(u'topics', metadata, autoload=True),
|
||||
u'authors_songs': Table(u'authors_songs', metadata, autoload=True),
|
||||
u'songs_topics': Table(u'songs_topics', metadata, autoload=True)
|
||||
}
|
||||
return tables
|
||||
|
||||
|
||||
def upgrade_1(session, metadata, tables):
|
||||
"""
|
||||
Version 1 upgrade.
|
||||
|
||||
This upgrade removes the many-to-many relationship between songs and
|
||||
media_files and replaces it with a one-to-many, which is far more
|
||||
representative of the real relationship between the two entities.
|
||||
|
||||
In order to facilitate this one-to-many relationship, a song_id column is
|
||||
added to the media_files table, and a weight column so that the media
|
||||
files can be ordered.
|
||||
"""
|
||||
Table(u'media_files_songs', metadata, autoload=True).drop(checkfirst=True)
|
||||
Column(u'song_id', types.Integer(), default=None)\
|
||||
.create(table=tables[u'media_files'], populate_default=True)
|
||||
Column(u'weight', types.Integer(), default=0)\
|
||||
.create(table=tables[u'media_files'], populate_default=True)
|
||||
if metadata.bind.url.get_dialect().name != 'sqlite':
|
||||
# SQLite doesn't support ALTER TABLE ADD CONSTRAINT
|
||||
ForeignKeyConstraint([u'song_id'], [u'songs.id'],
|
||||
table=tables[u'media_files']).create()
|
||||
|
||||
def upgrade_2(session, metadata, tables):
|
||||
"""
|
||||
Version 2 upgrade.
|
||||
|
||||
This upgrade adds a create_date and last_modified date to the songs table
|
||||
"""
|
||||
Column(u'create_date', types.DateTime(), default=func.now())\
|
||||
.create(table=tables[u'songs'], populate_default=True)
|
||||
Column(u'last_modified', types.DateTime(), default=func.now())\
|
||||
.create(table=tables[u'songs'], populate_default=True)
|
@ -36,7 +36,8 @@ from openlp.core.lib import Plugin, StringContent, build_icon, translate, \
|
||||
from openlp.core.lib.db import Manager
|
||||
from openlp.core.lib.ui import UiStrings, base_action, icon_action
|
||||
from openlp.core.utils.actions import ActionList
|
||||
from openlp.plugins.songs.lib import clean_song, SongMediaItem, SongsTab
|
||||
from openlp.plugins.songs.lib import clean_song, upgrade, SongMediaItem, \
|
||||
SongsTab
|
||||
from openlp.plugins.songs.lib.db import init_schema, Song
|
||||
from openlp.plugins.songs.lib.importer import SongFormat
|
||||
from openlp.plugins.songs.lib.olpimport import OpenLPSongImport
|
||||
@ -58,8 +59,8 @@ class SongsPlugin(Plugin):
|
||||
Create and set up the Songs plugin.
|
||||
"""
|
||||
Plugin.__init__(self, u'songs', plugin_helpers, SongMediaItem, SongsTab)
|
||||
self.manager = Manager(u'songs', init_schema, upgrade_mod=upgrade)
|
||||
self.weight = -10
|
||||
self.manager = Manager(u'songs', init_schema)
|
||||
self.icon_path = u':/plugins/plugin_songs.png'
|
||||
self.icon = build_icon(self.icon_path)
|
||||
|
||||
|
@ -117,9 +117,11 @@ class SongUsageDetailForm(QtGui.QDialog, Ui_SongUsageDetailDialog):
|
||||
try:
|
||||
fileHandle = open(outname, u'w')
|
||||
for instance in usage:
|
||||
record = u'\"%s\",\"%s\",\"%s\",\"%s\",\"%s\",\"%s\"\n' % (
|
||||
instance.usagedate, instance.usagetime, instance.title,
|
||||
instance.copyright, instance.ccl_number, instance.authors)
|
||||
record = u'\"%s\",\"%s\",\"%s\",\"%s\",\"%s\",\"%s\",' \
|
||||
u'\"%s\",\"%s\"\n' % ( instance.usagedate,
|
||||
instance.usagetime, instance.title, instance.copyright,
|
||||
instance.ccl_number, instance.authors,
|
||||
instance.plugin_name, instance.source)
|
||||
fileHandle.write(record.encode(u'utf-8'))
|
||||
Receiver.send_message(u'openlp_information_message', {
|
||||
u'title': translate('SongUsagePlugin.SongUsageDetailForm',
|
||||
|
@ -56,7 +56,9 @@ def init_schema(url):
|
||||
Column(u'title', types.Unicode(255), nullable=False),
|
||||
Column(u'authors', types.Unicode(255), nullable=False),
|
||||
Column(u'copyright', types.Unicode(255)),
|
||||
Column(u'ccl_number', types.Unicode(65))
|
||||
Column(u'ccl_number', types.Unicode(65)),
|
||||
Column(u'plugin_name', types.Unicode(20)),
|
||||
Column(u'source', types.Unicode(10))
|
||||
)
|
||||
|
||||
mapper(SongUsageItem, songusage_table)
|
||||
|
58
openlp/plugins/songusage/lib/upgrade.py
Normal file
58
openlp/plugins/songusage/lib/upgrade.py
Normal file
@ -0,0 +1,58 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
|
||||
|
||||
###############################################################################
|
||||
# OpenLP - Open Source Lyrics Projection #
|
||||
# --------------------------------------------------------------------------- #
|
||||
# Copyright (c) 2008-2011 Raoul Snyman #
|
||||
# Portions copyright (c) 2008-2011 Tim Bentley, Gerald Britton, Jonathan #
|
||||
# Corwin, Michael Gorven, Scott Guerrieri, Matthias Hub, Meinert Jordan, #
|
||||
# Armin Köhler, Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias #
|
||||
# Põldaru, Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, #
|
||||
# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Frode Woldsund #
|
||||
# --------------------------------------------------------------------------- #
|
||||
# This program is free software; you can redistribute it and/or modify it #
|
||||
# under the terms of the GNU General Public License as published by the Free #
|
||||
# Software Foundation; version 2 of the License. #
|
||||
# #
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT #
|
||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
|
||||
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
|
||||
# more details. #
|
||||
# #
|
||||
# You should have received a copy of the GNU General Public License along #
|
||||
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
|
||||
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||
###############################################################################
|
||||
"""
|
||||
The :mod:`upgrade` module provides a way for the database and schema that is the
|
||||
backend for the SongsUsage plugin
|
||||
"""
|
||||
|
||||
from sqlalchemy import Column, Table, types
|
||||
from migrate import changeset
|
||||
|
||||
__version__ = 1
|
||||
|
||||
def upgrade_setup(metadata):
|
||||
"""
|
||||
Set up the latest revision all tables, with reflection, needed for the
|
||||
upgrade process. If you want to drop a table, you need to remove it from
|
||||
here, and add it to your upgrade function.
|
||||
"""
|
||||
tables = {
|
||||
u'songusage_data': Table(u'songusage_data', metadata, autoload=True)
|
||||
}
|
||||
return tables
|
||||
|
||||
|
||||
def upgrade_1(session, metadata, tables):
|
||||
"""
|
||||
Version 1 upgrade.
|
||||
|
||||
This upgrade adds two new fields to the songusage database
|
||||
"""
|
||||
Column(u'plugin_name', types.Unicode(20), default=u'') \
|
||||
.create(table=tables[u'songusage_data'], populate_default=True)
|
||||
Column(u'source', types.Unicode(10), default=u'') \
|
||||
.create(table=tables[u'songusage_data'], populate_default=True)
|
@ -37,6 +37,7 @@ from openlp.core.lib.ui import base_action, shortcut_action
|
||||
from openlp.core.utils.actions import ActionList
|
||||
from openlp.plugins.songusage.forms import SongUsageDetailForm, \
|
||||
SongUsageDeleteForm
|
||||
from openlp.plugins.songusage.lib import upgrade
|
||||
from openlp.plugins.songusage.lib.db import init_schema, SongUsageItem
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
@ -46,11 +47,11 @@ class SongUsagePlugin(Plugin):
|
||||
|
||||
def __init__(self, plugin_helpers):
|
||||
Plugin.__init__(self, u'songusage', plugin_helpers)
|
||||
self.manager = Manager(u'songusage', init_schema, upgrade_mod=upgrade)
|
||||
self.weight = -4
|
||||
self.icon = build_icon(u':/plugins/plugin_songusage.png')
|
||||
self.activeIcon = build_icon(u':/songusage/song_usage_active.png')
|
||||
self.inactiveIcon = build_icon(u':/songusage/song_usage_inactive.png')
|
||||
self.manager = None
|
||||
self.songUsageActive = False
|
||||
|
||||
def addToolsMenuItem(self, tools_menu):
|
||||
@ -121,7 +122,10 @@ class SongUsagePlugin(Plugin):
|
||||
Plugin.initialise(self)
|
||||
QtCore.QObject.connect(Receiver.get_receiver(),
|
||||
QtCore.SIGNAL(u'slidecontroller_live_started'),
|
||||
self.onReceiveSongUsage)
|
||||
self.displaySongUsage)
|
||||
QtCore.QObject.connect(Receiver.get_receiver(),
|
||||
QtCore.SIGNAL(u'print_service_started'),
|
||||
self.printSongUsage)
|
||||
self.songUsageActive = QtCore.QSettings().value(
|
||||
self.settingsSection + u'/active',
|
||||
QtCore.QVariant(False)).toBool()
|
||||
@ -134,8 +138,6 @@ class SongUsagePlugin(Plugin):
|
||||
translate('SongUsagePlugin', 'Song Usage'))
|
||||
action_list.add_action(self.songUsageReport,
|
||||
translate('SongUsagePlugin', 'Song Usage'))
|
||||
if self.manager is None:
|
||||
self.manager = Manager(u'songusage', init_schema)
|
||||
self.songUsageDeleteForm = SongUsageDeleteForm(self.manager,
|
||||
self.formparent)
|
||||
self.songUsageDetailForm = SongUsageDetailForm(self, self.formparent)
|
||||
@ -194,10 +196,21 @@ class SongUsagePlugin(Plugin):
|
||||
self.songUsageStatus.blockSignals(False)
|
||||
|
||||
|
||||
def onReceiveSongUsage(self, item):
|
||||
def displaySongUsage(self, item):
|
||||
"""
|
||||
Song Usage for live song from SlideController
|
||||
Song Usage for which has been displayed
|
||||
"""
|
||||
self._add_song_usage(unicode(translate('SongUsagePlugin',
|
||||
'display')), item)
|
||||
|
||||
def printSongUsage(self, item):
|
||||
"""
|
||||
Song Usage for which has been printed
|
||||
"""
|
||||
self._add_song_usage(unicode(translate('SongUsagePlugin',
|
||||
'printed')), item)
|
||||
|
||||
def _add_song_usage(self, source, item):
|
||||
audit = item[0].audit
|
||||
if self.songUsageActive and audit:
|
||||
song_usage_item = SongUsageItem()
|
||||
@ -207,6 +220,8 @@ class SongUsagePlugin(Plugin):
|
||||
song_usage_item.copyright = audit[2]
|
||||
song_usage_item.ccl_number = audit[3]
|
||||
song_usage_item.authors = u' '.join(audit[1])
|
||||
song_usage_item.plugin_name = item[0].name
|
||||
song_usage_item.source = source
|
||||
self.manager.save_object(song_usage_item)
|
||||
|
||||
def onSongUsageDelete(self):
|
||||
|
@ -11,7 +11,7 @@ Package: openlp
|
||||
Architecture: all
|
||||
Depends: ${shlibs:Depends}, ${misc:Depends}, ${python:Depends}, python-qt4,
|
||||
python-qt4-phonon, python-sqlalchemy, python-chardet, python-beautifulsoup,
|
||||
python-lxml, python-sqlite, python-enchant
|
||||
python-lxml, python-sqlite, python-enchant, python-mako, python-migrate
|
||||
Conflicts: python-openlp
|
||||
Description: Church lyrics projection application
|
||||
OpenLP is free church presentation software, or lyrics projection software,
|
||||
|
@ -46,14 +46,14 @@ VERS = {
|
||||
'sqlalchemy': '0.5',
|
||||
# pyenchant 1.6 required on Windows
|
||||
'enchant': '1.6' if is_win else '1.3'
|
||||
}
|
||||
}
|
||||
|
||||
# pywin32
|
||||
WIN32_MODULES = [
|
||||
'win32com',
|
||||
'win32ui',
|
||||
'pywintypes',
|
||||
]
|
||||
]
|
||||
|
||||
MODULES = [
|
||||
'PyQt4',
|
||||
@ -72,7 +72,8 @@ MODULES = [
|
||||
'enchant',
|
||||
'BeautifulSoup',
|
||||
'mako',
|
||||
]
|
||||
'migrate',
|
||||
]
|
||||
|
||||
|
||||
OPTIONAL_MODULES = [
|
||||
|
@ -186,25 +186,6 @@ def update_export_at_pootle(source_filename):
|
||||
page = urllib.urlopen(REVIEW_URL)
|
||||
page.close()
|
||||
|
||||
|
||||
def download_file(source_filename, dest_filename):
|
||||
"""
|
||||
Download a file and save it to disk.
|
||||
|
||||
``source_filename``
|
||||
The file to download.
|
||||
|
||||
``dest_filename``
|
||||
The new local file name.
|
||||
"""
|
||||
print_verbose(u'Downloading from: %s' % (SERVER_URL + source_filename))
|
||||
page = urllib.urlopen(SERVER_URL + source_filename)
|
||||
content = page.read().decode('utf8')
|
||||
page.close()
|
||||
file = open(dest_filename, u'w')
|
||||
file.write(content.encode('utf8'))
|
||||
file.close()
|
||||
|
||||
def download_translations():
|
||||
"""
|
||||
This method downloads the translation files from the Pootle server.
|
||||
@ -219,7 +200,7 @@ def download_translations():
|
||||
filename = os.path.join(os.path.abspath(u'..'), u'resources', u'i18n',
|
||||
language_file)
|
||||
print_verbose(u'Get Translation File: %s' % filename)
|
||||
download_file(language_file, filename)
|
||||
urllib.urlretrieve(SERVER_URL + language_file, filename)
|
||||
print_quiet(u' Done.')
|
||||
|
||||
def prepare_project():
|
||||
@ -304,7 +285,7 @@ def create_translation(language):
|
||||
if not language.endswith(u'.ts'):
|
||||
language += u'.ts'
|
||||
filename = os.path.join(os.path.abspath(u'..'), u'resources', u'i18n', language)
|
||||
download_file(u'en.ts', filename)
|
||||
urllib.urlretrieve(SERVER_URL + u'en.ts', filename)
|
||||
print_quiet(u' ** Please Note **')
|
||||
print_quiet(u' In order to get this file into OpenLP and onto the '
|
||||
u'Pootle translation server you will need to subscribe to the '
|
||||
|
Loading…
Reference in New Issue
Block a user