forked from openlp/openlp
Merge with trunk, resolve conflicts
This commit is contained in:
commit
245c0acd89
@ -268,7 +268,7 @@ class Receiver(object):
|
|||||||
<<ACTION>>
|
<<ACTION>>
|
||||||
)``
|
)``
|
||||||
"""
|
"""
|
||||||
eventreceiver = EventReceiver()
|
__eventreceiver__ = EventReceiver()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def send_message(event, msg=None):
|
def send_message(event, msg=None):
|
||||||
@ -281,11 +281,11 @@ class Receiver(object):
|
|||||||
``msg``
|
``msg``
|
||||||
Defaults to *None*. The message to send with the event.
|
Defaults to *None*. The message to send with the event.
|
||||||
"""
|
"""
|
||||||
Receiver.eventreceiver.send_message(event, msg)
|
Receiver.__eventreceiver__.send_message(event, msg)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_receiver():
|
def get_receiver():
|
||||||
"""
|
"""
|
||||||
Get the global ``eventreceiver`` instance.
|
Get the global ``__eventreceiver__`` instance.
|
||||||
"""
|
"""
|
||||||
return Receiver.eventreceiver
|
return Receiver.__eventreceiver__
|
||||||
|
@ -41,13 +41,13 @@ class PluginManager(object):
|
|||||||
and executes all the hooks, as and when necessary.
|
and executes all the hooks, as and when necessary.
|
||||||
"""
|
"""
|
||||||
log.info(u'Plugin manager loaded')
|
log.info(u'Plugin manager loaded')
|
||||||
|
__instance__ = None
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_instance():
|
def get_instance():
|
||||||
"""
|
"""
|
||||||
Obtain a single instance of class.
|
Obtain a single instance of class.
|
||||||
"""
|
"""
|
||||||
return PluginManager.instance
|
return PluginManager.__instance__
|
||||||
|
|
||||||
def __init__(self, plugin_dir):
|
def __init__(self, plugin_dir):
|
||||||
"""
|
"""
|
||||||
@ -58,7 +58,7 @@ class PluginManager(object):
|
|||||||
The directory to search for plugins.
|
The directory to search for plugins.
|
||||||
"""
|
"""
|
||||||
log.info(u'Plugin manager Initialising')
|
log.info(u'Plugin manager Initialising')
|
||||||
PluginManager.instance = self
|
PluginManager.__instance__ = self
|
||||||
if not plugin_dir in sys.path:
|
if not plugin_dir in sys.path:
|
||||||
log.debug(u'Inserting %s into sys.path', plugin_dir)
|
log.debug(u'Inserting %s into sys.path', plugin_dir)
|
||||||
sys.path.insert(0, plugin_dir)
|
sys.path.insert(0, plugin_dir)
|
||||||
|
@ -55,29 +55,32 @@ class Renderer(object):
|
|||||||
"""
|
"""
|
||||||
log.info(u'Renderer Loaded')
|
log.info(u'Renderer Loaded')
|
||||||
|
|
||||||
def __init__(self, imageManager, themeManager):
|
def __init__(self, image_manager, theme_manager):
|
||||||
"""
|
"""
|
||||||
Initialise the renderer.
|
Initialise the renderer.
|
||||||
|
|
||||||
``imageManager``
|
``image_manager``
|
||||||
A imageManager instance which takes care of e. g. caching and resizing
|
A image_manager instance which takes care of e. g. caching and
|
||||||
images.
|
resizing images.
|
||||||
|
|
||||||
``themeManager``
|
``theme_manager``
|
||||||
The themeManager instance, used to get the current theme details.
|
The theme_manager instance, used to get the current theme details.
|
||||||
"""
|
"""
|
||||||
log.debug(u'Initialisation started')
|
log.debug(u'Initialisation started')
|
||||||
self.themeManager = themeManager
|
self.theme_manager = theme_manager
|
||||||
self.imageManager = imageManager
|
self.image_manager = image_manager
|
||||||
self.screens = ScreenList()
|
self.screens = ScreenList()
|
||||||
self.service_theme = u''
|
self.theme_level = ThemeLevel.Global
|
||||||
self.theme_level = u''
|
self.global_theme_name = u''
|
||||||
self.override_background = None
|
self.service_theme_name = u''
|
||||||
self.theme_data = None
|
self.item_theme_name = u''
|
||||||
self.bg_frame = None
|
|
||||||
self.force_page = False
|
self.force_page = False
|
||||||
self.display = MainDisplay(None, self.imageManager, False, self)
|
self.display = MainDisplay(None, self.image_manager, False, self)
|
||||||
self.display.setup()
|
self.display.setup()
|
||||||
|
self._theme_dimensions = {}
|
||||||
|
self._calculate_default()
|
||||||
|
QtCore.QObject.connect(Receiver.get_receiver(),
|
||||||
|
QtCore.SIGNAL(u'theme_update_global'), self.set_global_theme)
|
||||||
|
|
||||||
def update_display(self):
|
def update_display(self):
|
||||||
"""
|
"""
|
||||||
@ -87,100 +90,132 @@ class Renderer(object):
|
|||||||
self._calculate_default()
|
self._calculate_default()
|
||||||
if self.display:
|
if self.display:
|
||||||
self.display.close()
|
self.display.close()
|
||||||
self.display = MainDisplay(None, self.imageManager, False, self)
|
self.display = MainDisplay(None, self.image_manager, False, self)
|
||||||
self.display.setup()
|
self.display.setup()
|
||||||
self.bg_frame = None
|
self._theme_dimensions = {}
|
||||||
self.theme_data = None
|
|
||||||
|
|
||||||
def set_global_theme(self, global_theme, theme_level=ThemeLevel.Global):
|
def update_theme(self, theme_name, old_theme_name=None, only_delete=False):
|
||||||
"""
|
"""
|
||||||
Set the global-level theme and the theme level.
|
This method updates the theme in ``_theme_dimensions`` when a theme
|
||||||
|
has been edited or renamed.
|
||||||
|
|
||||||
``global_theme``
|
``theme_name``
|
||||||
The global-level theme to be set.
|
The current theme name.
|
||||||
|
|
||||||
|
``old_theme_name``
|
||||||
|
The old theme name. Has only to be passed, when the theme has been
|
||||||
|
renamed. Defaults to *None*.
|
||||||
|
|
||||||
|
``only_delete``
|
||||||
|
Only remove the given ``theme_name`` from the ``_theme_dimensions``
|
||||||
|
list. This can be used when a theme is permanently deleted.
|
||||||
|
"""
|
||||||
|
if old_theme_name is not None and \
|
||||||
|
old_theme_name in self._theme_dimensions:
|
||||||
|
del self._theme_dimensions[old_theme_name]
|
||||||
|
if theme_name in self._theme_dimensions:
|
||||||
|
del self._theme_dimensions[theme_name]
|
||||||
|
if not only_delete:
|
||||||
|
self._set_theme(theme_name)
|
||||||
|
|
||||||
|
def _set_theme(self, theme_name):
|
||||||
|
"""
|
||||||
|
Helper method to save theme names and theme data.
|
||||||
|
|
||||||
|
``theme_name``
|
||||||
|
The theme name.
|
||||||
|
"""
|
||||||
|
if theme_name not in self._theme_dimensions:
|
||||||
|
theme_data = self.theme_manager.getThemeData(theme_name)
|
||||||
|
main_rect = self.get_main_rectangle(theme_data)
|
||||||
|
footer_rect = self.get_footer_rectangle(theme_data)
|
||||||
|
self._theme_dimensions[theme_name] = \
|
||||||
|
[theme_data, main_rect, footer_rect]
|
||||||
|
else:
|
||||||
|
theme_data, main_rect, footer_rect = \
|
||||||
|
self._theme_dimensions[theme_name]
|
||||||
|
# if No file do not update cache
|
||||||
|
if theme_data.background_filename:
|
||||||
|
self.image_manager.addImage(theme_data.theme_name,
|
||||||
|
theme_data.background_filename, u'theme',
|
||||||
|
QtGui.QColor(theme_data.background_border_color))
|
||||||
|
|
||||||
|
def pre_render(self, override_theme_data=None):
|
||||||
|
"""
|
||||||
|
Set up the theme to be used before rendering an item.
|
||||||
|
|
||||||
|
``override_theme_data``
|
||||||
|
The theme data should be passed, when we want to use our own theme
|
||||||
|
data, regardless of the theme level. This should for example be used
|
||||||
|
in the theme manager. **Note**, this is **not** to be mixed up with
|
||||||
|
the ``set_item_theme`` method.
|
||||||
|
"""
|
||||||
|
# Just assume we use the global theme.
|
||||||
|
theme_to_use = self.global_theme_name
|
||||||
|
# The theme level is either set to Service or Item. Use the service
|
||||||
|
# theme if one is set. We also have to use the service theme, even when
|
||||||
|
# the theme level is set to Item, because the item does not necessarily
|
||||||
|
# have to have a theme.
|
||||||
|
if self.theme_level != ThemeLevel.Global:
|
||||||
|
# When the theme level is at Service and we actually have a service
|
||||||
|
# theme then use it.
|
||||||
|
if self.service_theme_name:
|
||||||
|
theme_to_use = self.service_theme_name
|
||||||
|
# If we have Item level and have an item theme then use it.
|
||||||
|
if self.theme_level == ThemeLevel.Song and self.item_theme_name:
|
||||||
|
theme_to_use = self.item_theme_name
|
||||||
|
if override_theme_data is None:
|
||||||
|
if theme_to_use not in self._theme_dimensions:
|
||||||
|
self._set_theme(theme_to_use)
|
||||||
|
theme_data, main_rect, footer_rect = \
|
||||||
|
self._theme_dimensions[theme_to_use]
|
||||||
|
else:
|
||||||
|
# Ignore everything and use own theme data.
|
||||||
|
theme_data = override_theme_data
|
||||||
|
main_rect = self.get_main_rectangle(override_theme_data)
|
||||||
|
footer_rect = self.get_footer_rectangle(override_theme_data)
|
||||||
|
self._set_text_rectangle(theme_data, main_rect, footer_rect)
|
||||||
|
return theme_data, self._rect, self._rect_footer
|
||||||
|
|
||||||
|
def set_theme_level(self, theme_level):
|
||||||
|
"""
|
||||||
|
Sets the theme level.
|
||||||
|
|
||||||
``theme_level``
|
``theme_level``
|
||||||
Defaults to ``ThemeLevel.Global``. The theme level, can be
|
The theme level to be used.
|
||||||
``ThemeLevel.Global``, ``ThemeLevel.Service`` or
|
|
||||||
``ThemeLevel.Song``.
|
|
||||||
"""
|
"""
|
||||||
self.global_theme = global_theme
|
|
||||||
self.theme_level = theme_level
|
self.theme_level = theme_level
|
||||||
self.global_theme_data = \
|
|
||||||
self.themeManager.getThemeData(self.global_theme)
|
|
||||||
self.theme_data = None
|
|
||||||
self._cache_background_image(self.global_theme_data)
|
|
||||||
|
|
||||||
def set_service_theme(self, service_theme):
|
def set_global_theme(self, global_theme_name):
|
||||||
|
"""
|
||||||
|
Set the global-level theme name.
|
||||||
|
|
||||||
|
``global_theme_name``
|
||||||
|
The global-level theme's name.
|
||||||
|
"""
|
||||||
|
self._set_theme(global_theme_name)
|
||||||
|
self.global_theme_name = global_theme_name
|
||||||
|
|
||||||
|
def set_service_theme(self, service_theme_name):
|
||||||
"""
|
"""
|
||||||
Set the service-level theme.
|
Set the service-level theme.
|
||||||
|
|
||||||
``service_theme``
|
``service_theme_name``
|
||||||
The service-level theme to be set.
|
The service level theme's name.
|
||||||
"""
|
"""
|
||||||
self.service_theme = service_theme
|
self._set_theme(service_theme_name)
|
||||||
self.theme_data = None
|
self.service_theme_name = service_theme_name
|
||||||
self._cache_background_image(self.themeManager.getThemeData
|
|
||||||
(service_theme))
|
|
||||||
|
|
||||||
def _cache_background_image(self, temp_theme):
|
def set_item_theme(self, item_theme_name):
|
||||||
"""
|
"""
|
||||||
Adds a background image to the image cache if necessary.
|
Set the item-level theme. **Note**, this has to be done for each item we
|
||||||
|
are rendering.
|
||||||
|
|
||||||
``temp_theme``
|
``item_theme_name``
|
||||||
The theme object containing the theme data.
|
The item theme's name.
|
||||||
"""
|
"""
|
||||||
# if No file do not update cache
|
self._set_theme(item_theme_name)
|
||||||
if temp_theme.background_filename:
|
self.item_theme_name = item_theme_name
|
||||||
self.imageManager.addImage(temp_theme.theme_name,
|
|
||||||
temp_theme.background_filename, u'theme',
|
|
||||||
QtGui.QColor(temp_theme.background_border_color))
|
|
||||||
|
|
||||||
def set_override_theme(self, override_theme, override_levels=False):
|
|
||||||
"""
|
|
||||||
Set the appropriate theme depending on the theme level.
|
|
||||||
Called by the service item when building a display frame
|
|
||||||
|
|
||||||
``override_theme``
|
|
||||||
The name of the song-level theme. None means the service
|
|
||||||
item wants to use the given value.
|
|
||||||
|
|
||||||
``override_levels``
|
|
||||||
Used to force the theme data passed in to be used.
|
|
||||||
"""
|
|
||||||
log.debug(u'set override theme to %s', override_theme)
|
|
||||||
theme_level = self.theme_level
|
|
||||||
if override_levels:
|
|
||||||
theme_level = ThemeLevel.Song
|
|
||||||
if theme_level == ThemeLevel.Global:
|
|
||||||
theme = self.global_theme
|
|
||||||
elif theme_level == ThemeLevel.Service:
|
|
||||||
if self.service_theme == u'':
|
|
||||||
theme = self.global_theme
|
|
||||||
else:
|
|
||||||
theme = self.service_theme
|
|
||||||
else:
|
|
||||||
# Images have a theme of -1
|
|
||||||
if override_theme and override_theme != -1:
|
|
||||||
theme = override_theme
|
|
||||||
elif theme_level == ThemeLevel.Song or \
|
|
||||||
theme_level == ThemeLevel.Service:
|
|
||||||
if self.service_theme == u'':
|
|
||||||
theme = self.global_theme
|
|
||||||
else:
|
|
||||||
theme = self.service_theme
|
|
||||||
else:
|
|
||||||
theme = self.global_theme
|
|
||||||
log.debug(u'theme is now %s', theme)
|
|
||||||
# Force the theme to be the one passed in.
|
|
||||||
if override_levels:
|
|
||||||
self.theme_data = override_theme
|
|
||||||
else:
|
|
||||||
self.theme_data = self.themeManager.getThemeData(theme)
|
|
||||||
self._calculate_default()
|
|
||||||
self._build_text_rectangle(self.theme_data)
|
|
||||||
self._cache_background_image(self.theme_data)
|
|
||||||
return self._rect, self._rect_footer
|
|
||||||
|
|
||||||
def generate_preview(self, theme_data, force_page=False):
|
def generate_preview(self, theme_data, force_page=False):
|
||||||
"""
|
"""
|
||||||
@ -195,27 +230,31 @@ class Renderer(object):
|
|||||||
log.debug(u'generate preview')
|
log.debug(u'generate preview')
|
||||||
# save value for use in format_slide
|
# save value for use in format_slide
|
||||||
self.force_page = force_page
|
self.force_page = force_page
|
||||||
# set the default image size for previews
|
|
||||||
self._calculate_default()
|
|
||||||
# build a service item to generate preview
|
# build a service item to generate preview
|
||||||
serviceItem = ServiceItem()
|
serviceItem = ServiceItem()
|
||||||
serviceItem.theme = theme_data
|
|
||||||
if self.force_page:
|
if self.force_page:
|
||||||
# make big page for theme edit dialog to get line count
|
# make big page for theme edit dialog to get line count
|
||||||
serviceItem.add_from_text(u'', VERSE_FOR_LINE_COUNT)
|
serviceItem.add_from_text(u'', VERSE_FOR_LINE_COUNT)
|
||||||
else:
|
else:
|
||||||
self.imageManager.deleteImage(theme_data.theme_name)
|
self.image_manager.deleteImage(theme_data.theme_name)
|
||||||
serviceItem.add_from_text(u'', VERSE)
|
serviceItem.add_from_text(u'', VERSE)
|
||||||
serviceItem.renderer = self
|
serviceItem.renderer = self
|
||||||
serviceItem.raw_footer = FOOTER
|
serviceItem.raw_footer = FOOTER
|
||||||
|
# if No file do not update cache
|
||||||
|
if theme_data.background_filename:
|
||||||
|
self.image_manager.addImage(theme_data.theme_name,
|
||||||
|
theme_data.background_filename, u'theme',
|
||||||
|
QtGui.QColor(theme_data.background_border_color))
|
||||||
|
theme_data, main, footer = self.pre_render(theme_data)
|
||||||
|
serviceItem.themedata = theme_data
|
||||||
|
serviceItem.main = main
|
||||||
|
serviceItem.footer = footer
|
||||||
serviceItem.render(True)
|
serviceItem.render(True)
|
||||||
if not self.force_page:
|
if not self.force_page:
|
||||||
self.display.buildHtml(serviceItem)
|
self.display.buildHtml(serviceItem)
|
||||||
raw_html = serviceItem.get_rendered_frame(0)
|
raw_html = serviceItem.get_rendered_frame(0)
|
||||||
self.display.text(raw_html)
|
self.display.text(raw_html)
|
||||||
preview = self.display.preview()
|
preview = self.display.preview()
|
||||||
# Reset the real screen size for subsequent render requests
|
|
||||||
self._calculate_default()
|
|
||||||
return preview
|
return preview
|
||||||
self.force_page = False
|
self.force_page = False
|
||||||
|
|
||||||
@ -264,7 +303,7 @@ class Renderer(object):
|
|||||||
try:
|
try:
|
||||||
text_to_render, text = \
|
text_to_render, text = \
|
||||||
text.split(u'\n[---]\n', 1)
|
text.split(u'\n[---]\n', 1)
|
||||||
except:
|
except ValueError:
|
||||||
text_to_render = text.split(u'\n[---]\n')[0]
|
text_to_render = text.split(u'\n[---]\n')[0]
|
||||||
text = u''
|
text = u''
|
||||||
text_to_render, raw_tags, html_tags = \
|
text_to_render, raw_tags, html_tags = \
|
||||||
@ -315,52 +354,41 @@ class Renderer(object):
|
|||||||
# 90% is start of footer
|
# 90% is start of footer
|
||||||
self.footer_start = int(self.height * 0.90)
|
self.footer_start = int(self.height * 0.90)
|
||||||
|
|
||||||
def _build_text_rectangle(self, theme):
|
def get_main_rectangle(self, theme_data):
|
||||||
"""
|
|
||||||
Builds a text block using the settings in ``theme``
|
|
||||||
and the size of the display screen.height.
|
|
||||||
Note the system has a 10 pixel border round the screen
|
|
||||||
|
|
||||||
``theme``
|
|
||||||
The theme to build a text block for.
|
|
||||||
"""
|
|
||||||
log.debug(u'_build_text_rectangle')
|
|
||||||
main_rect = self.get_main_rectangle(theme)
|
|
||||||
footer_rect = self.get_footer_rectangle(theme)
|
|
||||||
self._set_text_rectangle(main_rect, footer_rect)
|
|
||||||
|
|
||||||
def get_main_rectangle(self, theme):
|
|
||||||
"""
|
"""
|
||||||
Calculates the placement and size of the main rectangle.
|
Calculates the placement and size of the main rectangle.
|
||||||
|
|
||||||
``theme``
|
``theme_data``
|
||||||
The theme information
|
The theme information
|
||||||
"""
|
"""
|
||||||
if not theme.font_main_override:
|
if not theme_data.font_main_override:
|
||||||
return QtCore.QRect(10, 0, self.width - 20, self.footer_start)
|
return QtCore.QRect(10, 0, self.width, self.footer_start)
|
||||||
else:
|
else:
|
||||||
return QtCore.QRect(theme.font_main_x, theme.font_main_y,
|
return QtCore.QRect(theme_data.font_main_x, theme_data.font_main_y,
|
||||||
theme.font_main_width - 1, theme.font_main_height - 1)
|
theme_data.font_main_width - 1, theme_data.font_main_height - 1)
|
||||||
|
|
||||||
def get_footer_rectangle(self, theme):
|
def get_footer_rectangle(self, theme_data):
|
||||||
"""
|
"""
|
||||||
Calculates the placement and size of the footer rectangle.
|
Calculates the placement and size of the footer rectangle.
|
||||||
|
|
||||||
``theme``
|
``theme_data``
|
||||||
The theme information
|
The theme data.
|
||||||
"""
|
"""
|
||||||
if not theme.font_footer_override:
|
if not theme_data.font_footer_override:
|
||||||
return QtCore.QRect(10, self.footer_start, self.width - 20,
|
return QtCore.QRect(10, self.footer_start, self.width - 20,
|
||||||
self.height - self.footer_start)
|
self.height - self.footer_start)
|
||||||
else:
|
else:
|
||||||
return QtCore.QRect(theme.font_footer_x,
|
return QtCore.QRect(theme_data.font_footer_x,
|
||||||
theme.font_footer_y, theme.font_footer_width - 1,
|
theme_data.font_footer_y, theme_data.font_footer_width - 1,
|
||||||
theme.font_footer_height - 1)
|
theme_data.font_footer_height - 1)
|
||||||
|
|
||||||
def _set_text_rectangle(self, rect_main, rect_footer):
|
def _set_text_rectangle(self, theme_data, rect_main, rect_footer):
|
||||||
"""
|
"""
|
||||||
Sets the rectangle within which text should be rendered.
|
Sets the rectangle within which text should be rendered.
|
||||||
|
|
||||||
|
``theme_data``
|
||||||
|
The theme data.
|
||||||
|
|
||||||
``rect_main``
|
``rect_main``
|
||||||
The main text block.
|
The main text block.
|
||||||
|
|
||||||
@ -372,9 +400,9 @@ class Renderer(object):
|
|||||||
self._rect_footer = rect_footer
|
self._rect_footer = rect_footer
|
||||||
self.page_width = self._rect.width()
|
self.page_width = self._rect.width()
|
||||||
self.page_height = self._rect.height()
|
self.page_height = self._rect.height()
|
||||||
if self.theme_data.font_main_shadow:
|
if theme_data.font_main_shadow:
|
||||||
self.page_width -= int(self.theme_data.font_main_shadow_size)
|
self.page_width -= int(theme_data.font_main_shadow_size)
|
||||||
self.page_height -= int(self.theme_data.font_main_shadow_size)
|
self.page_height -= int(theme_data.font_main_shadow_size)
|
||||||
self.web = QtWebKit.QWebView()
|
self.web = QtWebKit.QWebView()
|
||||||
self.web.setVisible(False)
|
self.web.setVisible(False)
|
||||||
self.web.resize(self.page_width, self.page_height)
|
self.web.resize(self.page_width, self.page_height)
|
||||||
@ -392,8 +420,8 @@ class Renderer(object):
|
|||||||
</script><style>*{margin: 0; padding: 0; border: 0;}
|
</script><style>*{margin: 0; padding: 0; border: 0;}
|
||||||
#main {position: absolute; top: 0px; %s %s}</style></head><body>
|
#main {position: absolute; top: 0px; %s %s}</style></head><body>
|
||||||
<div id="main"></div></body></html>""" % \
|
<div id="main"></div></body></html>""" % \
|
||||||
(build_lyrics_format_css(self.theme_data, self.page_width,
|
(build_lyrics_format_css(theme_data, self.page_width,
|
||||||
self.page_height), build_lyrics_outline_css(self.theme_data))
|
self.page_height), build_lyrics_outline_css(theme_data))
|
||||||
self.web.setHtml(html)
|
self.web.setHtml(html)
|
||||||
self.empty_height = self.web_frame.contentsSize().height()
|
self.empty_height = self.web_frame.contentsSize().height()
|
||||||
|
|
||||||
|
@ -158,19 +158,24 @@ class ServiceItem(object):
|
|||||||
self.icon = icon
|
self.icon = icon
|
||||||
self.iconic_representation = build_icon(icon)
|
self.iconic_representation = build_icon(icon)
|
||||||
|
|
||||||
def render(self, use_override=False):
|
def render(self, provides_own_theme_data=False):
|
||||||
"""
|
"""
|
||||||
The render method is what generates the frames for the screen and
|
The render method is what generates the frames for the screen and
|
||||||
obtains the display information from the renderer. At this point all
|
obtains the display information from the renderer. At this point all
|
||||||
slides are built for the given display size.
|
slides are built for the given display size.
|
||||||
|
|
||||||
|
``provides_own_theme_data``
|
||||||
|
This switch disables the usage of the item's theme. However, this is
|
||||||
|
disabled by default. If this is used, it has to be taken care, that
|
||||||
|
the renderer knows the correct theme data. However, this is needed
|
||||||
|
for the theme manager.
|
||||||
"""
|
"""
|
||||||
log.debug(u'Render called')
|
log.debug(u'Render called')
|
||||||
self._display_frames = []
|
self._display_frames = []
|
||||||
self.bg_image_bytes = None
|
self.bg_image_bytes = None
|
||||||
theme = self.theme if self.theme else None
|
if not provides_own_theme_data:
|
||||||
self.main, self.footer = \
|
self.renderer.set_item_theme(self.theme)
|
||||||
self.renderer.set_override_theme(theme, use_override)
|
self.themedata, self.main, self.footer = self.renderer.pre_render()
|
||||||
self.themedata = self.renderer.theme_data
|
|
||||||
if self.service_item_type == ServiceItemType.Text:
|
if self.service_item_type == ServiceItemType.Text:
|
||||||
log.debug(u'Formatting slides')
|
log.debug(u'Formatting slides')
|
||||||
for slide in self._raw_frames:
|
for slide in self._raw_frames:
|
||||||
@ -211,7 +216,7 @@ class ServiceItem(object):
|
|||||||
self.image_border = background
|
self.image_border = background
|
||||||
self.service_item_type = ServiceItemType.Image
|
self.service_item_type = ServiceItemType.Image
|
||||||
self._raw_frames.append({u'title': title, u'path': path})
|
self._raw_frames.append({u'title': title, u'path': path})
|
||||||
self.renderer.imageManager.addImage(title, path, u'image',
|
self.renderer.image_manager.addImage(title, path, u'image',
|
||||||
self.image_border)
|
self.image_border)
|
||||||
self._new_item()
|
self._new_item()
|
||||||
|
|
||||||
|
@ -80,6 +80,10 @@ class UiStrings(object):
|
|||||||
self.Help = translate('OpenLP.Ui', 'Help')
|
self.Help = translate('OpenLP.Ui', 'Help')
|
||||||
self.Hours = translate('OpenLP.Ui', 'h',
|
self.Hours = translate('OpenLP.Ui', 'h',
|
||||||
'The abbreviated unit for hours')
|
'The abbreviated unit for hours')
|
||||||
|
self.IFdSs = translate('OpenLP.Ui', 'Invalid Folder Selected',
|
||||||
|
'Singular')
|
||||||
|
self.IFSs = translate('OpenLP.Ui', 'Invalid File Selected', 'Singular')
|
||||||
|
self.IFSp = translate('OpenLP.Ui', 'Invalid Files Selected', 'Plural')
|
||||||
self.Image = translate('OpenLP.Ui', 'Image')
|
self.Image = translate('OpenLP.Ui', 'Image')
|
||||||
self.Import = translate('OpenLP.Ui', 'Import')
|
self.Import = translate('OpenLP.Ui', 'Import')
|
||||||
self.LayoutStyle = translate('OpenLP.Ui', 'Layout style:')
|
self.LayoutStyle = translate('OpenLP.Ui', 'Layout style:')
|
||||||
|
@ -31,11 +31,16 @@ from datetime import datetime, timedelta
|
|||||||
|
|
||||||
from PyQt4 import QtCore, QtGui
|
from PyQt4 import QtCore, QtGui
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
from openlp.core.lib import SettingsTab, translate, build_icon, Receiver
|
from openlp.core.lib import SettingsTab, translate, build_icon, Receiver
|
||||||
from openlp.core.lib.settings import Settings
|
from openlp.core.lib.settings import Settings
|
||||||
from openlp.core.lib.ui import UiStrings
|
from openlp.core.lib.ui import UiStrings
|
||||||
|
from openlp.core.utils import get_images_filter, AppLocation
|
||||||
from openlp.core.lib import SlideLimits
|
from openlp.core.lib import SlideLimits
|
||||||
from openlp.core.utils import get_images_filter
|
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
class AdvancedTab(SettingsTab):
|
class AdvancedTab(SettingsTab):
|
||||||
"""
|
"""
|
||||||
@ -60,6 +65,7 @@ class AdvancedTab(SettingsTab):
|
|||||||
'#strftime-strptime-behavior for more information.'))
|
'#strftime-strptime-behavior for more information.'))
|
||||||
self.defaultImage = u':/graphics/openlp-splash-screen.png'
|
self.defaultImage = u':/graphics/openlp-splash-screen.png'
|
||||||
self.defaultColor = u'#ffffff'
|
self.defaultColor = u'#ffffff'
|
||||||
|
self.dataExists = False
|
||||||
self.iconPath = u':/system/system_settings.png'
|
self.iconPath = u':/system/system_settings.png'
|
||||||
advanced_translated = translate('OpenLP.AdvancedTab', 'Advanced')
|
advanced_translated = translate('OpenLP.AdvancedTab', 'Advanced')
|
||||||
SettingsTab.__init__(self, parent, u'Advanced', advanced_translated)
|
SettingsTab.__init__(self, parent, u'Advanced', advanced_translated)
|
||||||
@ -152,6 +158,71 @@ class AdvancedTab(SettingsTab):
|
|||||||
self.serviceNameLayout.addRow(self.serviceNameExampleLabel,
|
self.serviceNameLayout.addRow(self.serviceNameExampleLabel,
|
||||||
self.serviceNameExample)
|
self.serviceNameExample)
|
||||||
self.leftLayout.addWidget(self.serviceNameGroupBox)
|
self.leftLayout.addWidget(self.serviceNameGroupBox)
|
||||||
|
# Data Directory
|
||||||
|
self.dataDirectoryGroupBox = QtGui.QGroupBox(self.leftColumn)
|
||||||
|
self.dataDirectoryGroupBox.setObjectName(u'dataDirectoryGroupBox')
|
||||||
|
self.dataDirectoryLayout = QtGui.QFormLayout(self.dataDirectoryGroupBox)
|
||||||
|
self.dataDirectoryLayout.setObjectName(u'dataDirectoryLayout')
|
||||||
|
self.dataDirectoryCurrentLabel = QtGui.QLabel(self.dataDirectoryGroupBox)
|
||||||
|
self.dataDirectoryCurrentLabel.setObjectName(
|
||||||
|
u'dataDirectoryCurrentLabel')
|
||||||
|
self.dataDirectoryLabel = QtGui.QLabel(self.dataDirectoryGroupBox)
|
||||||
|
self.dataDirectoryLabel.setObjectName(u'dataDirectoryLabel')
|
||||||
|
self.dataDirectoryNewLabel = QtGui.QLabel(self.dataDirectoryGroupBox)
|
||||||
|
self.dataDirectoryNewLabel.setObjectName(u'dataDirectoryCurrentLabel')
|
||||||
|
self.newDataDirectoryEdit = QtGui.QLineEdit(self.dataDirectoryGroupBox)
|
||||||
|
self.newDataDirectoryEdit.setObjectName(u'newDataDirectoryEdit')
|
||||||
|
self.newDataDirectoryEdit.setReadOnly(True)
|
||||||
|
self.newDataDirectoryHasFilesLabel = QtGui.QLabel(
|
||||||
|
self.dataDirectoryGroupBox)
|
||||||
|
self.newDataDirectoryHasFilesLabel.setObjectName(
|
||||||
|
u'newDataDirectoryHasFilesLabel')
|
||||||
|
self.newDataDirectoryHasFilesLabel.setWordWrap(True)
|
||||||
|
self.dataDirectoryBrowseButton = QtGui.QToolButton(
|
||||||
|
self.dataDirectoryGroupBox)
|
||||||
|
self.dataDirectoryBrowseButton.setObjectName(
|
||||||
|
u'dataDirectoryBrowseButton')
|
||||||
|
self.dataDirectoryBrowseButton.setIcon(
|
||||||
|
build_icon(u':/general/general_open.png'))
|
||||||
|
self.dataDirectoryDefaultButton = QtGui.QToolButton(
|
||||||
|
self.dataDirectoryGroupBox)
|
||||||
|
self.dataDirectoryDefaultButton.setObjectName(
|
||||||
|
u'dataDirectoryDefaultButton')
|
||||||
|
self.dataDirectoryDefaultButton.setIcon(
|
||||||
|
build_icon(u':/general/general_revert.png'))
|
||||||
|
self.dataDirectoryCancelButton = QtGui.QToolButton(
|
||||||
|
self.dataDirectoryGroupBox)
|
||||||
|
self.dataDirectoryCancelButton.setObjectName(
|
||||||
|
u'dataDirectoryCancelButton')
|
||||||
|
self.dataDirectoryCancelButton.setIcon(
|
||||||
|
build_icon(u':/general/general_delete.png'))
|
||||||
|
self.newDataDirectoryLabelHBox = QtGui.QHBoxLayout()
|
||||||
|
self.newDataDirectoryLabelHBox.setObjectName(
|
||||||
|
u'newDataDirectoryLabelHBox')
|
||||||
|
self.newDataDirectoryLabelHBox.addWidget(self.newDataDirectoryEdit)
|
||||||
|
self.newDataDirectoryLabelHBox.addWidget(
|
||||||
|
self.dataDirectoryBrowseButton)
|
||||||
|
self.newDataDirectoryLabelHBox.addWidget(
|
||||||
|
self.dataDirectoryDefaultButton)
|
||||||
|
self.dataDirectoryCopyCheckHBox = QtGui.QHBoxLayout()
|
||||||
|
self.dataDirectoryCopyCheckHBox.setObjectName(
|
||||||
|
u'dataDirectoryCopyCheckHBox')
|
||||||
|
self.dataDirectoryCopyCheckBox = QtGui.QCheckBox(
|
||||||
|
self.dataDirectoryGroupBox)
|
||||||
|
self.dataDirectoryCopyCheckBox.setObjectName(
|
||||||
|
u'dataDirectoryCopyCheckBox')
|
||||||
|
self.dataDirectoryCopyCheckHBox.addWidget(
|
||||||
|
self.dataDirectoryCopyCheckBox)
|
||||||
|
self.dataDirectoryCopyCheckHBox.addStretch()
|
||||||
|
self.dataDirectoryCopyCheckHBox.addWidget(
|
||||||
|
self.dataDirectoryCancelButton)
|
||||||
|
self.dataDirectoryLayout.addRow(self.dataDirectoryCurrentLabel,
|
||||||
|
self.dataDirectoryLabel)
|
||||||
|
self.dataDirectoryLayout.addRow(self.dataDirectoryNewLabel,
|
||||||
|
self.newDataDirectoryLabelHBox)
|
||||||
|
self.dataDirectoryLayout.addRow(self.dataDirectoryCopyCheckHBox)
|
||||||
|
self.dataDirectoryLayout.addRow(self.newDataDirectoryHasFilesLabel)
|
||||||
|
self.leftLayout.addWidget(self.dataDirectoryGroupBox)
|
||||||
self.leftLayout.addStretch()
|
self.leftLayout.addStretch()
|
||||||
# Default Image
|
# Default Image
|
||||||
self.defaultImageGroupBox = QtGui.QGroupBox(self.rightColumn)
|
self.defaultImageGroupBox = QtGui.QGroupBox(self.rightColumn)
|
||||||
@ -220,7 +291,6 @@ class AdvancedTab(SettingsTab):
|
|||||||
self.x11Layout.addWidget(self.x11BypassCheckBox)
|
self.x11Layout.addWidget(self.x11BypassCheckBox)
|
||||||
self.rightLayout.addWidget(self.x11GroupBox)
|
self.rightLayout.addWidget(self.x11GroupBox)
|
||||||
self.rightLayout.addStretch()
|
self.rightLayout.addStretch()
|
||||||
|
|
||||||
self.shouldUpdateServiceNameExample = False
|
self.shouldUpdateServiceNameExample = False
|
||||||
QtCore.QObject.connect(self.serviceNameCheckBox,
|
QtCore.QObject.connect(self.serviceNameCheckBox,
|
||||||
QtCore.SIGNAL(u'toggled(bool)'), self.serviceNameCheckBoxToggled)
|
QtCore.SIGNAL(u'toggled(bool)'), self.serviceNameCheckBoxToggled)
|
||||||
@ -244,6 +314,18 @@ class AdvancedTab(SettingsTab):
|
|||||||
QtCore.SIGNAL(u'clicked()'), self.onDefaultRevertButtonClicked)
|
QtCore.SIGNAL(u'clicked()'), self.onDefaultRevertButtonClicked)
|
||||||
QtCore.QObject.connect(self.x11BypassCheckBox,
|
QtCore.QObject.connect(self.x11BypassCheckBox,
|
||||||
QtCore.SIGNAL(u'toggled(bool)'), self.onX11BypassCheckBoxToggled)
|
QtCore.SIGNAL(u'toggled(bool)'), self.onX11BypassCheckBoxToggled)
|
||||||
|
QtCore.QObject.connect(self.dataDirectoryBrowseButton,
|
||||||
|
QtCore.SIGNAL(u'clicked()'),
|
||||||
|
self.onDataDirectoryBrowseButtonClicked)
|
||||||
|
QtCore.QObject.connect(self.dataDirectoryDefaultButton,
|
||||||
|
QtCore.SIGNAL(u'clicked()'),
|
||||||
|
self.onDataDirectoryDefaultButtonClicked)
|
||||||
|
QtCore.QObject.connect(self.dataDirectoryCancelButton,
|
||||||
|
QtCore.SIGNAL(u'clicked()'),
|
||||||
|
self.onDataDirectoryCancelButtonClicked)
|
||||||
|
QtCore.QObject.connect(self.dataDirectoryCopyCheckBox,
|
||||||
|
QtCore.SIGNAL(u'toggled(bool)'),
|
||||||
|
self.onDataDirectoryCopyCheckBoxToggled)
|
||||||
QtCore.QObject.connect(self.endSlideRadioButton,
|
QtCore.QObject.connect(self.endSlideRadioButton,
|
||||||
QtCore.SIGNAL(u'clicked()'), self.onEndSlideButtonClicked)
|
QtCore.SIGNAL(u'clicked()'), self.onEndSlideButtonClicked)
|
||||||
QtCore.QObject.connect(self.wrapSlideRadioButton,
|
QtCore.QObject.connect(self.wrapSlideRadioButton,
|
||||||
@ -258,6 +340,8 @@ class AdvancedTab(SettingsTab):
|
|||||||
self.tabTitleVisible = UiStrings().Advanced
|
self.tabTitleVisible = UiStrings().Advanced
|
||||||
self.uiGroupBox.setTitle(
|
self.uiGroupBox.setTitle(
|
||||||
translate('OpenLP.AdvancedTab', 'UI Settings'))
|
translate('OpenLP.AdvancedTab', 'UI Settings'))
|
||||||
|
self.dataDirectoryGroupBox.setTitle(
|
||||||
|
translate('OpenLP.AdvancedTab', 'Data Location'))
|
||||||
self.recentLabel.setText(
|
self.recentLabel.setText(
|
||||||
translate('OpenLP.AdvancedTab',
|
translate('OpenLP.AdvancedTab',
|
||||||
'Number of recent files to display:'))
|
'Number of recent files to display:'))
|
||||||
@ -321,6 +405,32 @@ class AdvancedTab(SettingsTab):
|
|||||||
'Browse for an image file to display.'))
|
'Browse for an image file to display.'))
|
||||||
self.defaultRevertButton.setToolTip(translate('OpenLP.AdvancedTab',
|
self.defaultRevertButton.setToolTip(translate('OpenLP.AdvancedTab',
|
||||||
'Revert to the default OpenLP logo.'))
|
'Revert to the default OpenLP logo.'))
|
||||||
|
self.dataDirectoryCurrentLabel.setText(translate('OpenLP.AdvancedTab',
|
||||||
|
'Current path:'))
|
||||||
|
self.dataDirectoryNewLabel.setText(translate('OpenLP.AdvancedTab',
|
||||||
|
'Custom path:'))
|
||||||
|
self.dataDirectoryBrowseButton.setToolTip(
|
||||||
|
translate('OpenLP.AdvancedTab',
|
||||||
|
'Browse for new data file location.'))
|
||||||
|
self.dataDirectoryDefaultButton.setToolTip(
|
||||||
|
translate('OpenLP.AdvancedTab',
|
||||||
|
'Set the data location to the default.'))
|
||||||
|
self.dataDirectoryCancelButton.setText(
|
||||||
|
translate('OpenLP.AdvancedTab',
|
||||||
|
'Cancel'))
|
||||||
|
self.dataDirectoryCancelButton.setToolTip(
|
||||||
|
translate('OpenLP.AdvancedTab',
|
||||||
|
'Cancel OpenLP data directory location change.'))
|
||||||
|
self.dataDirectoryCopyCheckBox.setText(
|
||||||
|
translate('OpenLP.AdvancedTab',
|
||||||
|
'Copy data to new location.'))
|
||||||
|
self.dataDirectoryCopyCheckBox.setToolTip(
|
||||||
|
translate('OpenLP.AdvancedTab',
|
||||||
|
'Copy the OpenLP data files to the new location.'))
|
||||||
|
self.newDataDirectoryHasFilesLabel.setText(
|
||||||
|
translate('OpenLP.AdvancedTab',
|
||||||
|
'<strong>WARNING:</strong> New data directory location contains '
|
||||||
|
'OpenLP data files. These files WILL be replaced during a copy.'))
|
||||||
self.x11GroupBox.setTitle(translate('OpenLP.AdvancedTab',
|
self.x11GroupBox.setTitle(translate('OpenLP.AdvancedTab',
|
||||||
'X11'))
|
'X11'))
|
||||||
self.x11BypassCheckBox.setText(translate('OpenLP.AdvancedTab',
|
self.x11BypassCheckBox.setText(translate('OpenLP.AdvancedTab',
|
||||||
@ -398,6 +508,40 @@ class AdvancedTab(SettingsTab):
|
|||||||
else:
|
else:
|
||||||
self.nextItemRadioButton.setChecked(True)
|
self.nextItemRadioButton.setChecked(True)
|
||||||
settings.endGroup()
|
settings.endGroup()
|
||||||
|
self.dataDirectoryCopyCheckBox.hide()
|
||||||
|
self.newDataDirectoryHasFilesLabel.hide()
|
||||||
|
self.dataDirectoryCancelButton.hide()
|
||||||
|
# Since data location can be changed, make sure the path is present.
|
||||||
|
self.currentDataPath = AppLocation.get_data_path()
|
||||||
|
if not os.path.exists(self.currentDataPath):
|
||||||
|
log.error(u'Data path not found %s' % self.currentDataPath)
|
||||||
|
answer = QtGui.QMessageBox.critical(self,
|
||||||
|
translate('OpenLP.AdvancedTab',
|
||||||
|
'Data Directory Error'),
|
||||||
|
translate('OpenLP.AdvancedTab',
|
||||||
|
'OpenLP data directory was not found\n\n%s\n\n'
|
||||||
|
'This data directory was previously changed from the OpenLP '
|
||||||
|
'default location. If the new location was on removable '
|
||||||
|
'media, that media needs to be made available.\n\n'
|
||||||
|
'Click "No" to stop loading OpenLP. allowing you to fix '
|
||||||
|
'the the problem.\n\n'
|
||||||
|
'Click "Yes" to reset the data directory to the default '
|
||||||
|
'location.' % self.currentDataPath),
|
||||||
|
QtGui.QMessageBox.StandardButtons(
|
||||||
|
QtGui.QMessageBox.Yes |
|
||||||
|
QtGui.QMessageBox.No),
|
||||||
|
QtGui.QMessageBox.No)
|
||||||
|
if answer == QtGui.QMessageBox.No:
|
||||||
|
log.info(u'User requested termination')
|
||||||
|
Receiver.send_message(u'cleanup')
|
||||||
|
sys.exit()
|
||||||
|
# Set data location to default.
|
||||||
|
settings.remove(u'advanced/data path')
|
||||||
|
self.currentDataPath = AppLocation.get_data_path()
|
||||||
|
log.warning(u'User requested data path set to default %s'
|
||||||
|
% self.currentDataPath)
|
||||||
|
self.dataDirectoryLabel.setText(os.path.abspath(
|
||||||
|
self.currentDataPath))
|
||||||
self.defaultColorButton.setStyleSheet(
|
self.defaultColorButton.setStyleSheet(
|
||||||
u'background-color: %s' % self.defaultColor)
|
u'background-color: %s' % self.defaultColor)
|
||||||
|
|
||||||
@ -447,6 +591,11 @@ class AdvancedTab(SettingsTab):
|
|||||||
self.displayChanged = False
|
self.displayChanged = False
|
||||||
Receiver.send_message(u'slidecontroller_update_slide_limits')
|
Receiver.send_message(u'slidecontroller_update_slide_limits')
|
||||||
|
|
||||||
|
def cancel(self):
|
||||||
|
# Dialogue was cancelled, remove any pending data path change.
|
||||||
|
self.onDataDirectoryCancelButtonClicked()
|
||||||
|
SettingsTab.cancel(self)
|
||||||
|
|
||||||
def serviceNameCheckBoxToggled(self, default_service_enabled):
|
def serviceNameCheckBoxToggled(self, default_service_enabled):
|
||||||
self.serviceNameDay.setEnabled(default_service_enabled)
|
self.serviceNameDay.setEnabled(default_service_enabled)
|
||||||
time_enabled = default_service_enabled and \
|
time_enabled = default_service_enabled and \
|
||||||
@ -508,6 +657,122 @@ class AdvancedTab(SettingsTab):
|
|||||||
self.defaultFileEdit.setText(filename)
|
self.defaultFileEdit.setText(filename)
|
||||||
self.defaultFileEdit.setFocus()
|
self.defaultFileEdit.setFocus()
|
||||||
|
|
||||||
|
def onDataDirectoryBrowseButtonClicked(self):
|
||||||
|
"""
|
||||||
|
Browse for a new data directory location.
|
||||||
|
"""
|
||||||
|
old_root_path = unicode(self.dataDirectoryLabel.text())
|
||||||
|
# Get the new directory location.
|
||||||
|
new_data_path = unicode(QtGui.QFileDialog.getExistingDirectory(self,
|
||||||
|
translate('OpenLP.AdvancedTab',
|
||||||
|
'Select Data Directory Location'), old_root_path,
|
||||||
|
options = QtGui.QFileDialog.ShowDirsOnly))
|
||||||
|
# Set the new data path.
|
||||||
|
if new_data_path:
|
||||||
|
if self.currentDataPath.lower() == new_data_path.lower():
|
||||||
|
self.onDataDirectoryCancelButtonClicked()
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
return
|
||||||
|
# Make sure they want to change the data.
|
||||||
|
answer = QtGui.QMessageBox.question(self,
|
||||||
|
translate('OpenLP.AdvancedTab', 'Confirm Data Directory Change'),
|
||||||
|
translate('OpenLP.AdvancedTab',
|
||||||
|
'Are you sure you want to change the location of the OpenLP '
|
||||||
|
'data directory to:\n\n%s\n\n'
|
||||||
|
'The data directory will be changed when OpenLP is closed.'
|
||||||
|
% new_data_path),
|
||||||
|
QtGui.QMessageBox.StandardButtons(
|
||||||
|
QtGui.QMessageBox.Yes |
|
||||||
|
QtGui.QMessageBox.No),
|
||||||
|
QtGui.QMessageBox.No)
|
||||||
|
if answer != QtGui.QMessageBox.Yes:
|
||||||
|
return
|
||||||
|
# Check if data already exists here.
|
||||||
|
self.checkDataOverwrite(new_data_path)
|
||||||
|
# Save the new location.
|
||||||
|
Receiver.send_message(u'set_new_data_path', new_data_path)
|
||||||
|
self.newDataDirectoryEdit.setText(new_data_path)
|
||||||
|
self.dataDirectoryCancelButton.show()
|
||||||
|
|
||||||
|
def onDataDirectoryDefaultButtonClicked(self):
|
||||||
|
"""
|
||||||
|
Re-set the data directory location to the 'default' location.
|
||||||
|
"""
|
||||||
|
new_data_path = AppLocation.get_directory(AppLocation.DataDir)
|
||||||
|
if self.currentDataPath.lower() != new_data_path.lower():
|
||||||
|
# Make sure they want to change the data location back to the default.
|
||||||
|
answer = QtGui.QMessageBox.question(self,
|
||||||
|
translate('OpenLP.AdvancedTab', 'Reset Data Directory'),
|
||||||
|
translate('OpenLP.AdvancedTab',
|
||||||
|
'Are you sure you want to change the location of the OpenLP '
|
||||||
|
'data directory to the default location?\n\n'
|
||||||
|
'This location will be used after OpenLP is closed.'),
|
||||||
|
QtGui.QMessageBox.StandardButtons(
|
||||||
|
QtGui.QMessageBox.Yes |
|
||||||
|
QtGui.QMessageBox.No),
|
||||||
|
QtGui.QMessageBox.No)
|
||||||
|
if answer != QtGui.QMessageBox.Yes:
|
||||||
|
return
|
||||||
|
self.checkDataOverwrite(new_data_path)
|
||||||
|
# Save the new location.
|
||||||
|
Receiver.send_message(u'set_new_data_path', new_data_path)
|
||||||
|
self.newDataDirectoryEdit.setText(os.path.abspath(new_data_path))
|
||||||
|
self.dataDirectoryCancelButton.show()
|
||||||
|
else:
|
||||||
|
# We cancel the change in case user changed their mind.
|
||||||
|
self.onDataDirectoryCancelButtonClicked()
|
||||||
|
|
||||||
|
def onDataDirectoryCopyCheckBoxToggled(self):
|
||||||
|
Receiver.send_message(u'set_copy_data',
|
||||||
|
self.dataDirectoryCopyCheckBox.isChecked())
|
||||||
|
if self.dataExists:
|
||||||
|
if self.dataDirectoryCopyCheckBox.isChecked():
|
||||||
|
self.newDataDirectoryHasFilesLabel.show()
|
||||||
|
else:
|
||||||
|
self.newDataDirectoryHasFilesLabel.hide()
|
||||||
|
|
||||||
|
def checkDataOverwrite(self, data_path ):
|
||||||
|
test_path = os.path.join(data_path, u'songs')
|
||||||
|
self.dataDirectoryCopyCheckBox.show()
|
||||||
|
if os.path.exists(test_path):
|
||||||
|
self.dataExists = True
|
||||||
|
# Check is they want to replace existing data.
|
||||||
|
answer = QtGui.QMessageBox.warning(self,
|
||||||
|
translate('OpenLP.AdvancedTab', 'Overwrite Existing Data'),
|
||||||
|
translate('OpenLP.AdvancedTab',
|
||||||
|
'WARNING: \n\n'
|
||||||
|
'The location you have selected \n\n%s\n\n'
|
||||||
|
'appears to contain OpenLP data files. Do you wish to replace '
|
||||||
|
'these files with the current data files?'
|
||||||
|
% os.path.abspath(data_path,)),
|
||||||
|
QtGui.QMessageBox.StandardButtons(
|
||||||
|
QtGui.QMessageBox.Yes |
|
||||||
|
QtGui.QMessageBox.No),
|
||||||
|
QtGui.QMessageBox.No)
|
||||||
|
if answer == QtGui.QMessageBox.Yes:
|
||||||
|
self.dataDirectoryCopyCheckBox.setChecked(True)
|
||||||
|
self.newDataDirectoryHasFilesLabel.show()
|
||||||
|
else:
|
||||||
|
self.dataDirectoryCopyCheckBox.setChecked(False)
|
||||||
|
self.newDataDirectoryHasFilesLabel.hide()
|
||||||
|
else:
|
||||||
|
self.dataExists = False
|
||||||
|
self.dataDirectoryCopyCheckBox.setChecked(True)
|
||||||
|
self.newDataDirectoryHasFilesLabel.hide()
|
||||||
|
|
||||||
|
def onDataDirectoryCancelButtonClicked(self):
|
||||||
|
"""
|
||||||
|
Cancel the data directory location change
|
||||||
|
"""
|
||||||
|
self.newDataDirectoryEdit.clear()
|
||||||
|
self.dataDirectoryCopyCheckBox.setChecked(False)
|
||||||
|
Receiver.send_message(u'set_new_data_path', u'')
|
||||||
|
Receiver.send_message(u'set_copy_data', False)
|
||||||
|
self.dataDirectoryCopyCheckBox.hide()
|
||||||
|
self.dataDirectoryCancelButton.hide()
|
||||||
|
self.newDataDirectoryHasFilesLabel.hide()
|
||||||
|
|
||||||
def onDefaultRevertButtonClicked(self):
|
def onDefaultRevertButtonClicked(self):
|
||||||
self.defaultFileEdit.setText(u':/graphics/openlp-splash-screen.png')
|
self.defaultFileEdit.setText(u':/graphics/openlp-splash-screen.png')
|
||||||
self.defaultFileEdit.setFocus()
|
self.defaultFileEdit.setFocus()
|
||||||
|
@ -101,9 +101,8 @@ class Display(QtGui.QGraphicsView):
|
|||||||
self.frame.setScrollBarPolicy(QtCore.Qt.Horizontal,
|
self.frame.setScrollBarPolicy(QtCore.Qt.Horizontal,
|
||||||
QtCore.Qt.ScrollBarAlwaysOff)
|
QtCore.Qt.ScrollBarAlwaysOff)
|
||||||
|
|
||||||
def resizeEvent(self, ev):
|
def resizeEvent(self, event):
|
||||||
self.webView.setGeometry(0, 0,
|
self.webView.setGeometry(0, 0, self.width(), self.height())
|
||||||
self.width(), self.height())
|
|
||||||
|
|
||||||
def isWebLoaded(self):
|
def isWebLoaded(self):
|
||||||
"""
|
"""
|
||||||
@ -121,7 +120,6 @@ class MainDisplay(Display):
|
|||||||
Display.__init__(self, parent, live, controller)
|
Display.__init__(self, parent, live, controller)
|
||||||
self.imageManager = imageManager
|
self.imageManager = imageManager
|
||||||
self.screens = ScreenList()
|
self.screens = ScreenList()
|
||||||
self.plugins = PluginManager.get_instance().plugins
|
|
||||||
self.rebuildCSS = False
|
self.rebuildCSS = False
|
||||||
self.hideMode = None
|
self.hideMode = None
|
||||||
self.override = {}
|
self.override = {}
|
||||||
|
@ -29,6 +29,8 @@ import logging
|
|||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import shutil
|
import shutil
|
||||||
|
from distutils import dir_util
|
||||||
|
from distutils.errors import DistutilsFileError
|
||||||
from tempfile import gettempdir
|
from tempfile import gettempdir
|
||||||
import time
|
import time
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
@ -583,6 +585,8 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
|
|||||||
# Once settings are loaded update the menu with the recent files.
|
# Once settings are loaded update the menu with the recent files.
|
||||||
self.updateRecentFilesMenu()
|
self.updateRecentFilesMenu()
|
||||||
self.pluginForm = PluginForm(self)
|
self.pluginForm = PluginForm(self)
|
||||||
|
self.newDataPath = u''
|
||||||
|
self.copyData = False
|
||||||
# Set up signals and slots
|
# Set up signals and slots
|
||||||
QtCore.QObject.connect(self.importThemeItem,
|
QtCore.QObject.connect(self.importThemeItem,
|
||||||
QtCore.SIGNAL(u'triggered()'),
|
QtCore.SIGNAL(u'triggered()'),
|
||||||
@ -635,6 +639,8 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
|
|||||||
QtCore.SIGNAL(u'config_screen_changed'), self.screenChanged)
|
QtCore.SIGNAL(u'config_screen_changed'), self.screenChanged)
|
||||||
QtCore.QObject.connect(Receiver.get_receiver(),
|
QtCore.QObject.connect(Receiver.get_receiver(),
|
||||||
QtCore.SIGNAL(u'mainwindow_status_text'), self.showStatusMessage)
|
QtCore.SIGNAL(u'mainwindow_status_text'), self.showStatusMessage)
|
||||||
|
QtCore.QObject.connect(Receiver.get_receiver(),
|
||||||
|
QtCore.SIGNAL(u'cleanup'), self.cleanUp)
|
||||||
# Media Manager
|
# Media Manager
|
||||||
QtCore.QObject.connect(self.mediaToolBox,
|
QtCore.QObject.connect(self.mediaToolBox,
|
||||||
QtCore.SIGNAL(u'currentChanged(int)'), self.onMediaToolBoxChanged)
|
QtCore.SIGNAL(u'currentChanged(int)'), self.onMediaToolBoxChanged)
|
||||||
@ -647,6 +653,10 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
|
|||||||
QtCore.QObject.connect(Receiver.get_receiver(),
|
QtCore.QObject.connect(Receiver.get_receiver(),
|
||||||
QtCore.SIGNAL(u'openlp_information_message'),
|
QtCore.SIGNAL(u'openlp_information_message'),
|
||||||
self.onInformationMessage)
|
self.onInformationMessage)
|
||||||
|
QtCore.QObject.connect(Receiver.get_receiver(),
|
||||||
|
QtCore.SIGNAL(u'set_new_data_path'), self.setNewDataPath)
|
||||||
|
QtCore.QObject.connect(Receiver.get_receiver(),
|
||||||
|
QtCore.SIGNAL(u'set_copy_data'), self.setCopyData)
|
||||||
# warning cyclic dependency
|
# warning cyclic dependency
|
||||||
# renderer needs to call ThemeManager and
|
# renderer needs to call ThemeManager and
|
||||||
# ThemeManager needs to call Renderer
|
# ThemeManager needs to call Renderer
|
||||||
@ -1199,6 +1209,9 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
|
|||||||
if save_settings:
|
if save_settings:
|
||||||
# Save settings
|
# Save settings
|
||||||
self.saveSettings()
|
self.saveSettings()
|
||||||
|
# Check if we need to change the data directory
|
||||||
|
if self.newDataPath:
|
||||||
|
self.changeDataDirectory()
|
||||||
# Close down the display
|
# Close down the display
|
||||||
if self.liveController.display:
|
if self.liveController.display:
|
||||||
self.liveController.display.close()
|
self.liveController.display.close()
|
||||||
@ -1475,3 +1488,45 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
|
|||||||
self.timer_id = 0
|
self.timer_id = 0
|
||||||
self.loadProgressBar.hide()
|
self.loadProgressBar.hide()
|
||||||
Receiver.send_message(u'openlp_process_events')
|
Receiver.send_message(u'openlp_process_events')
|
||||||
|
|
||||||
|
def setNewDataPath(self, new_data_path):
|
||||||
|
self.newDataPath = new_data_path
|
||||||
|
|
||||||
|
def setCopyData(self, copy_data):
|
||||||
|
self.copyData = copy_data
|
||||||
|
|
||||||
|
def changeDataDirectory(self):
|
||||||
|
log.info(u'Changing data path to %s' % self.newDataPath )
|
||||||
|
old_data_path = unicode(AppLocation.get_data_path())
|
||||||
|
# Copy OpenLP data to new location if requested.
|
||||||
|
if self.copyData:
|
||||||
|
log.info(u'Copying data to new path')
|
||||||
|
try:
|
||||||
|
Receiver.send_message(u'openlp_process_events')
|
||||||
|
Receiver.send_message(u'cursor_busy')
|
||||||
|
self.showStatusMessage(
|
||||||
|
translate('OpenLP.MainWindow',
|
||||||
|
'Copying OpenLP data to new data directory location - %s '
|
||||||
|
'- Please wait for copy to finish'
|
||||||
|
% self.newDataPath))
|
||||||
|
dir_util.copy_tree(old_data_path, self.newDataPath)
|
||||||
|
log.info(u'Copy sucessful')
|
||||||
|
except (IOError, os.error, DistutilsFileError), why:
|
||||||
|
Receiver.send_message(u'cursor_normal')
|
||||||
|
log.exception(u'Data copy failed %s' % unicode(why))
|
||||||
|
QtGui.QMessageBox.critical(self,
|
||||||
|
translate('OpenLP.MainWindow', 'New Data Directory Error'),
|
||||||
|
translate('OpenLP.MainWindow',
|
||||||
|
'OpenLP Data directory copy failed\n\n%s'
|
||||||
|
% unicode(why)),
|
||||||
|
QtGui.QMessageBox.StandardButtons(
|
||||||
|
QtGui.QMessageBox.Ok))
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
log.info(u'No data copy requested')
|
||||||
|
# Change the location of data directory in config file.
|
||||||
|
settings = QtCore.QSettings()
|
||||||
|
settings.setValue(u'advanced/data path', self.newDataPath)
|
||||||
|
# Check if the new data path is our default.
|
||||||
|
if self.newDataPath == AppLocation.get_directory(AppLocation.DataDir):
|
||||||
|
settings.remove(u'advanced/data path')
|
||||||
|
@ -336,8 +336,7 @@ class MediaController(object):
|
|||||||
if controller.isLive and \
|
if controller.isLive and \
|
||||||
(Settings().value(u'general/auto unblank',
|
(Settings().value(u'general/auto unblank',
|
||||||
QtCore.QVariant(False)).toBool() or \
|
QtCore.QVariant(False)).toBool() or \
|
||||||
controller.media_info.is_background == True) or \
|
controller.media_info.is_background) or not controller.isLive:
|
||||||
controller.isLive == False:
|
|
||||||
if not self.video_play([controller]):
|
if not self.video_play([controller]):
|
||||||
critical_error_message_box(
|
critical_error_message_box(
|
||||||
translate('MediaPlugin.MediaItem', 'Unsupported File'),
|
translate('MediaPlugin.MediaItem', 'Unsupported File'),
|
||||||
|
@ -101,7 +101,7 @@ class PhononPlayer(MediaPlayer):
|
|||||||
display.mediaObject = Phonon.MediaObject(display)
|
display.mediaObject = Phonon.MediaObject(display)
|
||||||
Phonon.createPath(display.mediaObject, display.phononWidget)
|
Phonon.createPath(display.mediaObject, display.phononWidget)
|
||||||
if display.hasAudio:
|
if display.hasAudio:
|
||||||
display.audio = Phonon.AudioOutput( \
|
display.audio = Phonon.AudioOutput(
|
||||||
Phonon.VideoCategory, display.mediaObject)
|
Phonon.VideoCategory, display.mediaObject)
|
||||||
Phonon.createPath(display.mediaObject, display.audio)
|
Phonon.createPath(display.mediaObject, display.audio)
|
||||||
display.phononWidget.raise_()
|
display.phononWidget.raise_()
|
||||||
@ -148,18 +148,17 @@ class PhononPlayer(MediaPlayer):
|
|||||||
controller.media_info.start_time > 0:
|
controller.media_info.start_time > 0:
|
||||||
start_time = controller.media_info.start_time
|
start_time = controller.media_info.start_time
|
||||||
display.mediaObject.play()
|
display.mediaObject.play()
|
||||||
if self.media_state_wait(display, Phonon.PlayingState):
|
if not self.media_state_wait(display, Phonon.PlayingState):
|
||||||
|
return False
|
||||||
if start_time > 0:
|
if start_time > 0:
|
||||||
self.seek(display, controller.media_info.start_time*1000)
|
self.seek(display, controller.media_info.start_time * 1000)
|
||||||
self.volume(display, controller.media_info.volume)
|
self.volume(display, controller.media_info.volume)
|
||||||
controller.media_info.length = \
|
controller.media_info.length = \
|
||||||
int(display.mediaObject.totalTime()/1000)
|
int(display.mediaObject.totalTime() / 1000)
|
||||||
controller.seekSlider.setMaximum(controller.media_info.length*1000)
|
controller.seekSlider.setMaximum(controller.media_info.length * 1000)
|
||||||
self.state = MediaState.Playing
|
self.state = MediaState.Playing
|
||||||
display.phononWidget.raise_()
|
display.phononWidget.raise_()
|
||||||
return True
|
return True
|
||||||
else:
|
|
||||||
return False
|
|
||||||
|
|
||||||
def pause(self, display):
|
def pause(self, display):
|
||||||
display.mediaObject.pause()
|
display.mediaObject.pause()
|
||||||
@ -198,9 +197,9 @@ class PhononPlayer(MediaPlayer):
|
|||||||
controller = display.controller
|
controller = display.controller
|
||||||
if controller.media_info.end_time > 0:
|
if controller.media_info.end_time > 0:
|
||||||
if display.mediaObject.currentTime() > \
|
if display.mediaObject.currentTime() > \
|
||||||
controller.media_info.end_time*1000:
|
controller.media_info.end_time * 1000:
|
||||||
self.stop(display)
|
self.stop(display)
|
||||||
self.set_visible(display, False)
|
self.set_visible(display, False)
|
||||||
if not controller.seekSlider.isSliderDown():
|
if not controller.seekSlider.isSliderDown():
|
||||||
controller.seekSlider.setSliderPosition( \
|
controller.seekSlider.setSliderPosition(
|
||||||
display.mediaObject.currentTime())
|
display.mediaObject.currentTime())
|
||||||
|
@ -48,7 +48,7 @@ import sys
|
|||||||
from inspect import getargspec
|
from inspect import getargspec
|
||||||
|
|
||||||
__version__ = "N/A"
|
__version__ = "N/A"
|
||||||
build_date = "Fri Apr 27 16:47:21 2012"
|
build_date = "Fri Jun 8 09:31:07 2012"
|
||||||
|
|
||||||
# Internal guard to prevent internal classes to be directly
|
# Internal guard to prevent internal classes to be directly
|
||||||
# instanciated.
|
# instanciated.
|
||||||
@ -905,10 +905,10 @@ class Instance(_Ctype):
|
|||||||
def media_new(self, mrl, *options):
|
def media_new(self, mrl, *options):
|
||||||
"""Create a new Media instance.
|
"""Create a new Media instance.
|
||||||
|
|
||||||
If mrl contains a colon (:), it will be treated as a
|
If mrl contains a colon (:) preceded by more than 1 letter, it
|
||||||
URL. Else, it will be considered as a local path. If you need
|
will be treated as a URL. Else, it will be considered as a
|
||||||
more control, directly use media_new_location/media_new_path
|
local path. If you need more control, directly use
|
||||||
methods.
|
media_new_location/media_new_path methods.
|
||||||
|
|
||||||
Options can be specified as supplementary string parameters, e.g.
|
Options can be specified as supplementary string parameters, e.g.
|
||||||
|
|
||||||
@ -920,7 +920,7 @@ class Instance(_Ctype):
|
|||||||
|
|
||||||
@param options: optional media option=value strings
|
@param options: optional media option=value strings
|
||||||
"""
|
"""
|
||||||
if ':' in mrl:
|
if ':' in mrl and mrl.index(':') > 1:
|
||||||
# Assume it is a URL
|
# Assume it is a URL
|
||||||
m = libvlc_media_new_location(self, mrl)
|
m = libvlc_media_new_location(self, mrl)
|
||||||
else:
|
else:
|
||||||
@ -1031,6 +1031,13 @@ class Instance(_Ctype):
|
|||||||
'''
|
'''
|
||||||
return libvlc_log_open(self)
|
return libvlc_log_open(self)
|
||||||
|
|
||||||
|
def media_discoverer_new_from_name(self, psz_name):
|
||||||
|
'''Discover media service by name.
|
||||||
|
@param psz_name: service name.
|
||||||
|
@return: media discover object or NULL in case of error.
|
||||||
|
'''
|
||||||
|
return libvlc_media_discoverer_new_from_name(self, psz_name)
|
||||||
|
|
||||||
def media_new_location(self, psz_mrl):
|
def media_new_location(self, psz_mrl):
|
||||||
'''Create a media with a certain given media resource location,
|
'''Create a media with a certain given media resource location,
|
||||||
for instance a valid URL.
|
for instance a valid URL.
|
||||||
@ -1080,13 +1087,6 @@ class Instance(_Ctype):
|
|||||||
'''
|
'''
|
||||||
return libvlc_media_new_as_node(self, psz_name)
|
return libvlc_media_new_as_node(self, psz_name)
|
||||||
|
|
||||||
def media_discoverer_new_from_name(self, psz_name):
|
|
||||||
'''Discover media service by name.
|
|
||||||
@param psz_name: service name.
|
|
||||||
@return: media discover object or NULL in case of error.
|
|
||||||
'''
|
|
||||||
return libvlc_media_discoverer_new_from_name(self, psz_name)
|
|
||||||
|
|
||||||
def media_library_new(self):
|
def media_library_new(self):
|
||||||
'''Create an new Media Library object.
|
'''Create an new Media Library object.
|
||||||
@return: a new object or NULL on error.
|
@return: a new object or NULL on error.
|
||||||
@ -1522,7 +1522,7 @@ class Media(_Ctype):
|
|||||||
|
|
||||||
def save_meta(self):
|
def save_meta(self):
|
||||||
'''Save the meta previously set.
|
'''Save the meta previously set.
|
||||||
@return: true if the write operation was successful.
|
@return: true if the write operation was successfull.
|
||||||
'''
|
'''
|
||||||
return libvlc_media_save_meta(self)
|
return libvlc_media_save_meta(self)
|
||||||
|
|
||||||
@ -3088,6 +3088,67 @@ def libvlc_clock():
|
|||||||
ctypes.c_int64)
|
ctypes.c_int64)
|
||||||
return f()
|
return f()
|
||||||
|
|
||||||
|
def libvlc_media_discoverer_new_from_name(p_inst, psz_name):
|
||||||
|
'''Discover media service by name.
|
||||||
|
@param p_inst: libvlc instance.
|
||||||
|
@param psz_name: service name.
|
||||||
|
@return: media discover object or NULL in case of error.
|
||||||
|
'''
|
||||||
|
f = _Cfunctions.get('libvlc_media_discoverer_new_from_name', None) or \
|
||||||
|
_Cfunction('libvlc_media_discoverer_new_from_name', ((1,), (1,),), class_result(MediaDiscoverer),
|
||||||
|
ctypes.c_void_p, Instance, ctypes.c_char_p)
|
||||||
|
return f(p_inst, psz_name)
|
||||||
|
|
||||||
|
def libvlc_media_discoverer_release(p_mdis):
|
||||||
|
'''Release media discover object. If the reference count reaches 0, then
|
||||||
|
the object will be released.
|
||||||
|
@param p_mdis: media service discover object.
|
||||||
|
'''
|
||||||
|
f = _Cfunctions.get('libvlc_media_discoverer_release', None) or \
|
||||||
|
_Cfunction('libvlc_media_discoverer_release', ((1,),), None,
|
||||||
|
None, MediaDiscoverer)
|
||||||
|
return f(p_mdis)
|
||||||
|
|
||||||
|
def libvlc_media_discoverer_localized_name(p_mdis):
|
||||||
|
'''Get media service discover object its localized name.
|
||||||
|
@param p_mdis: media discover object.
|
||||||
|
@return: localized name.
|
||||||
|
'''
|
||||||
|
f = _Cfunctions.get('libvlc_media_discoverer_localized_name', None) or \
|
||||||
|
_Cfunction('libvlc_media_discoverer_localized_name', ((1,),), string_result,
|
||||||
|
ctypes.c_void_p, MediaDiscoverer)
|
||||||
|
return f(p_mdis)
|
||||||
|
|
||||||
|
def libvlc_media_discoverer_media_list(p_mdis):
|
||||||
|
'''Get media service discover media list.
|
||||||
|
@param p_mdis: media service discover object.
|
||||||
|
@return: list of media items.
|
||||||
|
'''
|
||||||
|
f = _Cfunctions.get('libvlc_media_discoverer_media_list', None) or \
|
||||||
|
_Cfunction('libvlc_media_discoverer_media_list', ((1,),), class_result(MediaList),
|
||||||
|
ctypes.c_void_p, MediaDiscoverer)
|
||||||
|
return f(p_mdis)
|
||||||
|
|
||||||
|
def libvlc_media_discoverer_event_manager(p_mdis):
|
||||||
|
'''Get event manager from media service discover object.
|
||||||
|
@param p_mdis: media service discover object.
|
||||||
|
@return: event manager object.
|
||||||
|
'''
|
||||||
|
f = _Cfunctions.get('libvlc_media_discoverer_event_manager', None) or \
|
||||||
|
_Cfunction('libvlc_media_discoverer_event_manager', ((1,),), class_result(EventManager),
|
||||||
|
ctypes.c_void_p, MediaDiscoverer)
|
||||||
|
return f(p_mdis)
|
||||||
|
|
||||||
|
def libvlc_media_discoverer_is_running(p_mdis):
|
||||||
|
'''Query if media service discover object is running.
|
||||||
|
@param p_mdis: media service discover object.
|
||||||
|
@return: true if running, false if not \libvlc_return_bool.
|
||||||
|
'''
|
||||||
|
f = _Cfunctions.get('libvlc_media_discoverer_is_running', None) or \
|
||||||
|
_Cfunction('libvlc_media_discoverer_is_running', ((1,),), None,
|
||||||
|
ctypes.c_int, MediaDiscoverer)
|
||||||
|
return f(p_mdis)
|
||||||
|
|
||||||
def libvlc_media_new_location(p_instance, psz_mrl):
|
def libvlc_media_new_location(p_instance, psz_mrl):
|
||||||
'''Create a media with a certain given media resource location,
|
'''Create a media with a certain given media resource location,
|
||||||
for instance a valid URL.
|
for instance a valid URL.
|
||||||
@ -3258,7 +3319,7 @@ def libvlc_media_set_meta(p_md, e_meta, psz_value):
|
|||||||
def libvlc_media_save_meta(p_md):
|
def libvlc_media_save_meta(p_md):
|
||||||
'''Save the meta previously set.
|
'''Save the meta previously set.
|
||||||
@param p_md: the media desriptor.
|
@param p_md: the media desriptor.
|
||||||
@return: true if the write operation was successful.
|
@return: true if the write operation was successfull.
|
||||||
'''
|
'''
|
||||||
f = _Cfunctions.get('libvlc_media_save_meta', None) or \
|
f = _Cfunctions.get('libvlc_media_save_meta', None) or \
|
||||||
_Cfunction('libvlc_media_save_meta', ((1,),), None,
|
_Cfunction('libvlc_media_save_meta', ((1,),), None,
|
||||||
@ -3392,67 +3453,6 @@ def libvlc_media_get_tracks_info(p_md):
|
|||||||
ctypes.c_int, Media, ctypes.POINTER(ctypes.c_void_p))
|
ctypes.c_int, Media, ctypes.POINTER(ctypes.c_void_p))
|
||||||
return f(p_md)
|
return f(p_md)
|
||||||
|
|
||||||
def libvlc_media_discoverer_new_from_name(p_inst, psz_name):
|
|
||||||
'''Discover media service by name.
|
|
||||||
@param p_inst: libvlc instance.
|
|
||||||
@param psz_name: service name.
|
|
||||||
@return: media discover object or NULL in case of error.
|
|
||||||
'''
|
|
||||||
f = _Cfunctions.get('libvlc_media_discoverer_new_from_name', None) or \
|
|
||||||
_Cfunction('libvlc_media_discoverer_new_from_name', ((1,), (1,),), class_result(MediaDiscoverer),
|
|
||||||
ctypes.c_void_p, Instance, ctypes.c_char_p)
|
|
||||||
return f(p_inst, psz_name)
|
|
||||||
|
|
||||||
def libvlc_media_discoverer_release(p_mdis):
|
|
||||||
'''Release media discover object. If the reference count reaches 0, then
|
|
||||||
the object will be released.
|
|
||||||
@param p_mdis: media service discover object.
|
|
||||||
'''
|
|
||||||
f = _Cfunctions.get('libvlc_media_discoverer_release', None) or \
|
|
||||||
_Cfunction('libvlc_media_discoverer_release', ((1,),), None,
|
|
||||||
None, MediaDiscoverer)
|
|
||||||
return f(p_mdis)
|
|
||||||
|
|
||||||
def libvlc_media_discoverer_localized_name(p_mdis):
|
|
||||||
'''Get media service discover object its localized name.
|
|
||||||
@param p_mdis: media discover object.
|
|
||||||
@return: localized name.
|
|
||||||
'''
|
|
||||||
f = _Cfunctions.get('libvlc_media_discoverer_localized_name', None) or \
|
|
||||||
_Cfunction('libvlc_media_discoverer_localized_name', ((1,),), string_result,
|
|
||||||
ctypes.c_void_p, MediaDiscoverer)
|
|
||||||
return f(p_mdis)
|
|
||||||
|
|
||||||
def libvlc_media_discoverer_media_list(p_mdis):
|
|
||||||
'''Get media service discover media list.
|
|
||||||
@param p_mdis: media service discover object.
|
|
||||||
@return: list of media items.
|
|
||||||
'''
|
|
||||||
f = _Cfunctions.get('libvlc_media_discoverer_media_list', None) or \
|
|
||||||
_Cfunction('libvlc_media_discoverer_media_list', ((1,),), class_result(MediaList),
|
|
||||||
ctypes.c_void_p, MediaDiscoverer)
|
|
||||||
return f(p_mdis)
|
|
||||||
|
|
||||||
def libvlc_media_discoverer_event_manager(p_mdis):
|
|
||||||
'''Get event manager from media service discover object.
|
|
||||||
@param p_mdis: media service discover object.
|
|
||||||
@return: event manager object.
|
|
||||||
'''
|
|
||||||
f = _Cfunctions.get('libvlc_media_discoverer_event_manager', None) or \
|
|
||||||
_Cfunction('libvlc_media_discoverer_event_manager', ((1,),), class_result(EventManager),
|
|
||||||
ctypes.c_void_p, MediaDiscoverer)
|
|
||||||
return f(p_mdis)
|
|
||||||
|
|
||||||
def libvlc_media_discoverer_is_running(p_mdis):
|
|
||||||
'''Query if media service discover object is running.
|
|
||||||
@param p_mdis: media service discover object.
|
|
||||||
@return: true if running, false if not \libvlc_return_bool.
|
|
||||||
'''
|
|
||||||
f = _Cfunctions.get('libvlc_media_discoverer_is_running', None) or \
|
|
||||||
_Cfunction('libvlc_media_discoverer_is_running', ((1,),), None,
|
|
||||||
ctypes.c_int, MediaDiscoverer)
|
|
||||||
return f(p_mdis)
|
|
||||||
|
|
||||||
def libvlc_media_library_new(p_instance):
|
def libvlc_media_library_new(p_instance):
|
||||||
'''Create an new Media Library object.
|
'''Create an new Media Library object.
|
||||||
@param p_instance: the libvlc instance.
|
@param p_instance: the libvlc instance.
|
||||||
@ -5595,7 +5595,7 @@ if __name__ == '__main__':
|
|||||||
print('Aspect ratio: %s' % player.video_get_aspect_ratio())
|
print('Aspect ratio: %s' % player.video_get_aspect_ratio())
|
||||||
#print('Window:' % player.get_hwnd()
|
#print('Window:' % player.get_hwnd()
|
||||||
except Exception:
|
except Exception:
|
||||||
print('Error: %s', sys.exc_info()[1])
|
print('Error: %s' % sys.exc_info()[1])
|
||||||
|
|
||||||
def sec_forward():
|
def sec_forward():
|
||||||
"""Go forward one sec"""
|
"""Go forward one sec"""
|
||||||
|
@ -131,9 +131,9 @@ class VlcPlayer(MediaPlayer):
|
|||||||
# this is platform specific!
|
# this is platform specific!
|
||||||
# you have to give the id of the QFrame (or similar object) to
|
# you have to give the id of the QFrame (or similar object) to
|
||||||
# vlc, different platforms have different functions for this
|
# vlc, different platforms have different functions for this
|
||||||
if sys.platform == "win32": # for Windows
|
if sys.platform == "win32":
|
||||||
display.vlcMediaPlayer.set_hwnd(int(display.vlcWidget.winId()))
|
display.vlcMediaPlayer.set_hwnd(int(display.vlcWidget.winId()))
|
||||||
elif sys.platform == "darwin": # for MacOS
|
elif sys.platform == "darwin":
|
||||||
display.vlcMediaPlayer.set_agl(int(display.vlcWidget.winId()))
|
display.vlcMediaPlayer.set_agl(int(display.vlcWidget.winId()))
|
||||||
else:
|
else:
|
||||||
# for Linux using the X Server
|
# for Linux using the X Server
|
||||||
@ -182,7 +182,8 @@ class VlcPlayer(MediaPlayer):
|
|||||||
if controller.media_info.start_time > 0:
|
if controller.media_info.start_time > 0:
|
||||||
start_time = controller.media_info.start_time
|
start_time = controller.media_info.start_time
|
||||||
display.vlcMediaPlayer.play()
|
display.vlcMediaPlayer.play()
|
||||||
if self.media_state_wait(display, vlc.State.Playing):
|
if not self.media_state_wait(display, vlc.State.Playing):
|
||||||
|
return False
|
||||||
if start_time > 0:
|
if start_time > 0:
|
||||||
self.seek(display, controller.media_info.start_time * 1000)
|
self.seek(display, controller.media_info.start_time * 1000)
|
||||||
controller.media_info.length = \
|
controller.media_info.length = \
|
||||||
@ -191,8 +192,6 @@ class VlcPlayer(MediaPlayer):
|
|||||||
self.state = MediaState.Playing
|
self.state = MediaState.Playing
|
||||||
display.vlcWidget.raise_()
|
display.vlcWidget.raise_()
|
||||||
return True
|
return True
|
||||||
else:
|
|
||||||
return False
|
|
||||||
|
|
||||||
def pause(self, display):
|
def pause(self, display):
|
||||||
if display.vlcMedia.get_state() != vlc.State.Playing:
|
if display.vlcMedia.get_state() != vlc.State.Playing:
|
||||||
|
@ -339,7 +339,7 @@ class WebkitPlayer(MediaPlayer):
|
|||||||
else:
|
else:
|
||||||
display.frame.evaluateJavaScript(u'show_video("play");')
|
display.frame.evaluateJavaScript(u'show_video("play");')
|
||||||
if start_time > 0:
|
if start_time > 0:
|
||||||
self.seek(display, controller.media_info.start_time*1000)
|
self.seek(display, controller.media_info.start_time * 1000)
|
||||||
# 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
|
controller.media_info.length = length
|
||||||
self.state = MediaState.Playing
|
self.state = MediaState.Playing
|
||||||
@ -375,11 +375,11 @@ class WebkitPlayer(MediaPlayer):
|
|||||||
controller = display.controller
|
controller = display.controller
|
||||||
if controller.media_info.is_flash:
|
if controller.media_info.is_flash:
|
||||||
seek = seekVal
|
seek = seekVal
|
||||||
display.frame.evaluateJavaScript( \
|
display.frame.evaluateJavaScript(
|
||||||
u'show_flash("seek", null, null, "%s");' % (seek))
|
u'show_flash("seek", null, null, "%s");' % (seek))
|
||||||
else:
|
else:
|
||||||
seek = float(seekVal)/1000
|
seek = float(seekVal) / 1000
|
||||||
display.frame.evaluateJavaScript( \
|
display.frame.evaluateJavaScript(
|
||||||
u'show_video("seek", null, null, null, "%f");' % (seek))
|
u'show_video("seek", null, null, null, "%f");' % (seek))
|
||||||
|
|
||||||
def reset(self, display):
|
def reset(self, display):
|
||||||
@ -406,24 +406,24 @@ class WebkitPlayer(MediaPlayer):
|
|||||||
def update_ui(self, display):
|
def update_ui(self, display):
|
||||||
controller = display.controller
|
controller = display.controller
|
||||||
if controller.media_info.is_flash:
|
if controller.media_info.is_flash:
|
||||||
currentTime = display.frame.evaluateJavaScript( \
|
currentTime = display.frame.evaluateJavaScript(
|
||||||
u'show_flash("currentTime");').toInt()[0]
|
u'show_flash("currentTime");').toInt()[0]
|
||||||
length = display.frame.evaluateJavaScript( \
|
length = display.frame.evaluateJavaScript(
|
||||||
u'show_flash("length");').toInt()[0]
|
u'show_flash("length");').toInt()[0]
|
||||||
else:
|
else:
|
||||||
if display.frame.evaluateJavaScript( \
|
if display.frame.evaluateJavaScript(
|
||||||
u'show_video("isEnded");').toString() == 'true':
|
u'show_video("isEnded");').toString() == 'true':
|
||||||
self.stop(display)
|
self.stop(display)
|
||||||
(currentTime, ok) = display.frame.evaluateJavaScript( \
|
(currentTime, ok) = display.frame.evaluateJavaScript(
|
||||||
u'show_video("currentTime");').toFloat()
|
u'show_video("currentTime");').toFloat()
|
||||||
# check if conversion was ok and value is not 'NaN'
|
# check if conversion was ok and value is not 'NaN'
|
||||||
if ok and currentTime != float('inf'):
|
if ok and currentTime != float('inf'):
|
||||||
currentTime = int(currentTime*1000)
|
currentTime = int(currentTime * 1000)
|
||||||
(length, ok) = display.frame.evaluateJavaScript( \
|
(length, ok) = display.frame.evaluateJavaScript(
|
||||||
u'show_video("length");').toFloat()
|
u'show_video("length");').toFloat()
|
||||||
# check if conversion was ok and value is not 'NaN'
|
# check if conversion was ok and value is not 'NaN'
|
||||||
if ok and length != float('inf'):
|
if ok and length != float('inf'):
|
||||||
length = int(length*1000)
|
length = int(length * 1000)
|
||||||
if currentTime > 0:
|
if currentTime > 0:
|
||||||
controller.media_info.length = length
|
controller.media_info.length = length
|
||||||
controller.seekSlider.setMaximum(length)
|
controller.seekSlider.setMaximum(length)
|
||||||
|
@ -102,9 +102,9 @@ class PluginForm(QtGui.QDialog, Ui_PluginViewDialog):
|
|||||||
self.versionNumberLabel.setText(self.activePlugin.version)
|
self.versionNumberLabel.setText(self.activePlugin.version)
|
||||||
self.aboutTextBrowser.setHtml(self.activePlugin.about())
|
self.aboutTextBrowser.setHtml(self.activePlugin.about())
|
||||||
self.programaticChange = True
|
self.programaticChange = True
|
||||||
status = 1
|
status = PluginStatus.Active
|
||||||
if self.activePlugin.status == PluginStatus.Active:
|
if self.activePlugin.status == PluginStatus.Active:
|
||||||
status = 0
|
status = PluginStatus.Inactive
|
||||||
self.statusComboBox.setCurrentIndex(status)
|
self.statusComboBox.setCurrentIndex(status)
|
||||||
self.statusComboBox.setEnabled(True)
|
self.statusComboBox.setEnabled(True)
|
||||||
self.programaticChange = False
|
self.programaticChange = False
|
||||||
@ -129,7 +129,7 @@ class PluginForm(QtGui.QDialog, Ui_PluginViewDialog):
|
|||||||
def onStatusComboBoxChanged(self, status):
|
def onStatusComboBoxChanged(self, status):
|
||||||
if self.programaticChange or status == PluginStatus.Disabled:
|
if self.programaticChange or status == PluginStatus.Disabled:
|
||||||
return
|
return
|
||||||
if status == 0:
|
if status == PluginStatus.Inactive:
|
||||||
Receiver.send_message(u'cursor_busy')
|
Receiver.send_message(u'cursor_busy')
|
||||||
self.activePlugin.toggleStatus(PluginStatus.Active)
|
self.activePlugin.toggleStatus(PluginStatus.Active)
|
||||||
Receiver.send_message(u'cursor_normal')
|
Receiver.send_message(u'cursor_normal')
|
||||||
|
@ -581,10 +581,7 @@ class ServiceManager(QtGui.QWidget):
|
|||||||
return self.saveFileAs()
|
return self.saveFileAs()
|
||||||
self.mainwindow.addRecentFile(path_file_name)
|
self.mainwindow.addRecentFile(path_file_name)
|
||||||
self.setModified(False)
|
self.setModified(False)
|
||||||
try:
|
|
||||||
delete_file(temp_file_name)
|
delete_file(temp_file_name)
|
||||||
except:
|
|
||||||
pass
|
|
||||||
return success
|
return success
|
||||||
|
|
||||||
def saveFileAs(self):
|
def saveFileAs(self):
|
||||||
|
@ -29,7 +29,7 @@ The :mod:`settingsform` provides a user interface for the OpenLP settings
|
|||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from PyQt4 import QtGui
|
from PyQt4 import QtCore, QtGui
|
||||||
|
|
||||||
from openlp.core.lib import Receiver, build_icon, PluginStatus
|
from openlp.core.lib import Receiver, build_icon, PluginStatus
|
||||||
from openlp.core.ui import AdvancedTab, GeneralTab, ThemesTab
|
from openlp.core.ui import AdvancedTab, GeneralTab, ThemesTab
|
||||||
|
@ -864,7 +864,7 @@ class SlideController(Controller):
|
|||||||
image = self.imageManager.getImage(frame[u'title'])
|
image = self.imageManager.getImage(frame[u'title'])
|
||||||
label.setPixmap(QtGui.QPixmap.fromImage(image))
|
label.setPixmap(QtGui.QPixmap.fromImage(image))
|
||||||
self.previewListWidget.setCellWidget(framenumber, 0, label)
|
self.previewListWidget.setCellWidget(framenumber, 0, label)
|
||||||
slideHeight = width * self.parent().renderer.screen_ratio
|
slideHeight = width * (1 / self.ratio)
|
||||||
row += 1
|
row += 1
|
||||||
self.slideList[unicode(row)] = row - 1
|
self.slideList[unicode(row)] = row - 1
|
||||||
text.append(unicode(row))
|
text.append(unicode(row))
|
||||||
|
@ -248,8 +248,7 @@ class ThemeManager(QtGui.QWidget):
|
|||||||
Settings().setValue(
|
Settings().setValue(
|
||||||
self.settingsSection + u'/global theme',
|
self.settingsSection + u'/global theme',
|
||||||
QtCore.QVariant(self.global_theme))
|
QtCore.QVariant(self.global_theme))
|
||||||
Receiver.send_message(u'theme_update_global',
|
Receiver.send_message(u'theme_update_global', self.global_theme)
|
||||||
self.global_theme)
|
|
||||||
self._pushThemes()
|
self._pushThemes()
|
||||||
|
|
||||||
def onAddTheme(self):
|
def onAddTheme(self):
|
||||||
@ -286,6 +285,8 @@ class ThemeManager(QtGui.QWidget):
|
|||||||
if plugin.usesTheme(old_theme_name):
|
if plugin.usesTheme(old_theme_name):
|
||||||
plugin.renameTheme(old_theme_name, new_theme_name)
|
plugin.renameTheme(old_theme_name, new_theme_name)
|
||||||
self.loadThemes()
|
self.loadThemes()
|
||||||
|
self.mainwindow.renderer.update_theme(
|
||||||
|
new_theme_name, old_theme_name)
|
||||||
|
|
||||||
def onCopyTheme(self):
|
def onCopyTheme(self):
|
||||||
"""
|
"""
|
||||||
@ -322,9 +323,8 @@ class ThemeManager(QtGui.QWidget):
|
|||||||
Loads the settings for the theme that is to be edited and launches the
|
Loads the settings for the theme that is to be edited and launches the
|
||||||
theme editing form so the user can make their changes.
|
theme editing form so the user can make their changes.
|
||||||
"""
|
"""
|
||||||
if check_item_selected(self.themeListWidget,
|
if check_item_selected(self.themeListWidget, translate(
|
||||||
translate('OpenLP.ThemeManager',
|
'OpenLP.ThemeManager', 'You must select a theme to edit.')):
|
||||||
'You must select a theme to edit.')):
|
|
||||||
item = self.themeListWidget.currentItem()
|
item = self.themeListWidget.currentItem()
|
||||||
theme = self.getThemeData(
|
theme = self.getThemeData(
|
||||||
unicode(item.data(QtCore.Qt.UserRole).toString()))
|
unicode(item.data(QtCore.Qt.UserRole).toString()))
|
||||||
@ -333,6 +333,7 @@ class ThemeManager(QtGui.QWidget):
|
|||||||
self.themeForm.theme = theme
|
self.themeForm.theme = theme
|
||||||
self.themeForm.exec_(True)
|
self.themeForm.exec_(True)
|
||||||
self.old_background_image = None
|
self.old_background_image = None
|
||||||
|
self.mainwindow.renderer.update_theme(theme.theme_name)
|
||||||
|
|
||||||
def onDeleteTheme(self):
|
def onDeleteTheme(self):
|
||||||
"""
|
"""
|
||||||
@ -350,6 +351,7 @@ class ThemeManager(QtGui.QWidget):
|
|||||||
# As we do not reload the themes, push out the change. Reload the
|
# As we do not reload the themes, push out the change. Reload the
|
||||||
# list as the internal lists and events need to be triggered.
|
# list as the internal lists and events need to be triggered.
|
||||||
self._pushThemes()
|
self._pushThemes()
|
||||||
|
self.mainwindow.renderer.update_theme(theme, only_delete=True)
|
||||||
|
|
||||||
def deleteTheme(self, theme):
|
def deleteTheme(self, theme):
|
||||||
"""
|
"""
|
||||||
|
@ -152,8 +152,8 @@ class ThemesTab(SettingsTab):
|
|||||||
settings.setValue(u'theme level', QtCore.QVariant(self.theme_level))
|
settings.setValue(u'theme level', QtCore.QVariant(self.theme_level))
|
||||||
settings.setValue(u'global theme', QtCore.QVariant(self.global_theme))
|
settings.setValue(u'global theme', QtCore.QVariant(self.global_theme))
|
||||||
settings.endGroup()
|
settings.endGroup()
|
||||||
self.mainwindow.renderer.set_global_theme(
|
self.mainwindow.renderer.set_global_theme(self.global_theme)
|
||||||
self.global_theme, self.theme_level)
|
self.mainwindow.renderer.set_theme_level(self.theme_level)
|
||||||
Receiver.send_message(u'theme_update_global', self.global_theme)
|
Receiver.send_message(u'theme_update_global', self.global_theme)
|
||||||
|
|
||||||
def postSetUp(self):
|
def postSetUp(self):
|
||||||
@ -170,8 +170,8 @@ class ThemesTab(SettingsTab):
|
|||||||
|
|
||||||
def onDefaultComboBoxChanged(self, value):
|
def onDefaultComboBoxChanged(self, value):
|
||||||
self.global_theme = unicode(self.DefaultComboBox.currentText())
|
self.global_theme = unicode(self.DefaultComboBox.currentText())
|
||||||
self.mainwindow.renderer.set_global_theme(
|
self.mainwindow.renderer.set_global_theme(self.global_theme)
|
||||||
self.global_theme, self.theme_level)
|
self.mainwindow.renderer.set_theme_level(self.theme_level)
|
||||||
self.__previewGlobalTheme()
|
self.__previewGlobalTheme()
|
||||||
|
|
||||||
def updateThemeList(self, theme_list):
|
def updateThemeList(self, theme_list):
|
||||||
@ -190,8 +190,8 @@ class ThemesTab(SettingsTab):
|
|||||||
self.DefaultComboBox.clear()
|
self.DefaultComboBox.clear()
|
||||||
self.DefaultComboBox.addItems(theme_list)
|
self.DefaultComboBox.addItems(theme_list)
|
||||||
find_and_set_in_combo_box(self.DefaultComboBox, self.global_theme)
|
find_and_set_in_combo_box(self.DefaultComboBox, self.global_theme)
|
||||||
self.mainwindow.renderer.set_global_theme(
|
self.mainwindow.renderer.set_global_theme(self.global_theme)
|
||||||
self.global_theme, self.theme_level)
|
self.mainwindow.renderer.set_theme_level(self.theme_level)
|
||||||
if self.global_theme is not u'':
|
if self.global_theme is not u'':
|
||||||
self.__previewGlobalTheme()
|
self.__previewGlobalTheme()
|
||||||
|
|
||||||
|
@ -44,20 +44,9 @@ class WizardStrings(object):
|
|||||||
# Applications/Formats we import from or export to. These get used in
|
# Applications/Formats we import from or export to. These get used in
|
||||||
# multiple places but do not need translating unless you find evidence of
|
# multiple places but do not need translating unless you find evidence of
|
||||||
# the writers translating their own product name.
|
# the writers translating their own product name.
|
||||||
CCLI = u'CCLI/SongSelect'
|
|
||||||
CSV = u'CSV'
|
CSV = u'CSV'
|
||||||
DB = u'DreamBeam'
|
|
||||||
EW = u'EasyWorship'
|
|
||||||
ES = u'EasySlides'
|
|
||||||
FP = u'Foilpresenter'
|
|
||||||
OL = u'OpenLyrics'
|
|
||||||
OS = u'OpenSong'
|
OS = u'OpenSong'
|
||||||
OSIS = u'OSIS'
|
OSIS = u'OSIS'
|
||||||
PS = u'PowerSong 1.0'
|
|
||||||
SB = u'SongBeamer'
|
|
||||||
SoF = u'Songs of Fellowship'
|
|
||||||
SSP = u'SongShow Plus'
|
|
||||||
WoW = u'Words of Worship'
|
|
||||||
# These strings should need a good reason to be retranslated elsewhere.
|
# These strings should need a good reason to be retranslated elsewhere.
|
||||||
FinishedImport = translate('OpenLP.Ui', 'Finished import.')
|
FinishedImport = translate('OpenLP.Ui', 'Finished import.')
|
||||||
FormatLabel = translate('OpenLP.Ui', 'Format:')
|
FormatLabel = translate('OpenLP.Ui', 'Format:')
|
||||||
@ -76,10 +65,12 @@ class WizardStrings(object):
|
|||||||
PercentSymbolFormat = unicode(translate('OpenLP.Ui', '%p%'))
|
PercentSymbolFormat = unicode(translate('OpenLP.Ui', '%p%'))
|
||||||
Ready = translate('OpenLP.Ui', 'Ready.')
|
Ready = translate('OpenLP.Ui', 'Ready.')
|
||||||
StartingImport = translate('OpenLP.Ui', 'Starting import...')
|
StartingImport = translate('OpenLP.Ui', 'Starting import...')
|
||||||
YouSpecifyFile = unicode(translate('OpenLP.Ui', 'You need to specify at '
|
YouSpecifyFile = unicode(translate('OpenLP.Ui', 'You need to specify one '
|
||||||
|
'%s file to import from.', 'A file type e.g. OpenSong'))
|
||||||
|
YouSpecifyFiles = unicode(translate('OpenLP.Ui', 'You need to specify at '
|
||||||
'least one %s file to import from.', 'A file type e.g. OpenSong'))
|
'least one %s file to import from.', 'A file type e.g. OpenSong'))
|
||||||
YouSpecifyFolder = unicode(translate('OpenLP.Ui', 'You need to specify a '
|
YouSpecifyFolder = unicode(translate('OpenLP.Ui', 'You need to specify one '
|
||||||
'%s folder to import from.', 'A file type e.g. OpenSong'))
|
'%s folder to import from.', 'A song format e.g. PowerSong'))
|
||||||
|
|
||||||
|
|
||||||
class OpenLPWizard(QtGui.QWizard):
|
class OpenLPWizard(QtGui.QWizard):
|
||||||
@ -108,7 +99,7 @@ class OpenLPWizard(QtGui.QWizard):
|
|||||||
|
|
||||||
def setupUi(self, image):
|
def setupUi(self, image):
|
||||||
"""
|
"""
|
||||||
Set up the wizard UI
|
Set up the wizard UI.
|
||||||
"""
|
"""
|
||||||
self.setModal(True)
|
self.setModal(True)
|
||||||
self.setWizardStyle(QtGui.QWizard.ModernStyle)
|
self.setWizardStyle(QtGui.QWizard.ModernStyle)
|
||||||
|
@ -73,7 +73,7 @@ class CSVBible(BibleDB):
|
|||||||
|
|
||||||
def __init__(self, parent, **kwargs):
|
def __init__(self, parent, **kwargs):
|
||||||
"""
|
"""
|
||||||
Loads a Bible from a set of CVS files.
|
Loads a Bible from a set of CSV files.
|
||||||
This class assumes the files contain all the information and
|
This class assumes the files contain all the information and
|
||||||
a clean bible is being loaded.
|
a clean bible is being loaded.
|
||||||
"""
|
"""
|
||||||
|
@ -198,9 +198,6 @@ class CustomMediaItem(MediaManagerItem):
|
|||||||
|
|
||||||
def generateSlideData(self, service_item, item=None, xmlVersion=False,
|
def generateSlideData(self, service_item, item=None, xmlVersion=False,
|
||||||
remote=False):
|
remote=False):
|
||||||
raw_footer = []
|
|
||||||
slide = None
|
|
||||||
theme = None
|
|
||||||
item_id = self._getIdOfItemToGenerate(item, self.remoteCustom)
|
item_id = self._getIdOfItemToGenerate(item, self.remoteCustom)
|
||||||
service_item.add_capability(ItemCapabilities.CanEdit)
|
service_item.add_capability(ItemCapabilities.CanEdit)
|
||||||
service_item.add_capability(ItemCapabilities.CanPreview)
|
service_item.add_capability(ItemCapabilities.CanPreview)
|
||||||
@ -221,10 +218,9 @@ class CustomMediaItem(MediaManagerItem):
|
|||||||
service_item.add_from_text(slide[:30], slide)
|
service_item.add_from_text(slide[:30], slide)
|
||||||
if Settings().value(self.settingsSection + u'/display footer',
|
if Settings().value(self.settingsSection + u'/display footer',
|
||||||
QtCore.QVariant(True)).toBool() or credit:
|
QtCore.QVariant(True)).toBool() or credit:
|
||||||
raw_footer.append(title + u' ' + credit)
|
service_item.raw_footer.append(u' '.join([title, credit]))
|
||||||
else:
|
else:
|
||||||
raw_footer.append(u'')
|
service_item.raw_footer.append(u'')
|
||||||
service_item.raw_footer = raw_footer
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def onSearchTextButtonClicked(self):
|
def onSearchTextButtonClicked(self):
|
||||||
|
@ -97,7 +97,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
|
|||||||
self.onVerseOrderTextChanged)
|
self.onVerseOrderTextChanged)
|
||||||
QtCore.QObject.connect(self.themeAddButton,
|
QtCore.QObject.connect(self.themeAddButton,
|
||||||
QtCore.SIGNAL(u'clicked()'),
|
QtCore.SIGNAL(u'clicked()'),
|
||||||
self.mediaitem.plugin.renderer.themeManager.onAddTheme)
|
self.mediaitem.plugin.renderer.theme_manager.onAddTheme)
|
||||||
QtCore.QObject.connect(self.maintenanceButton,
|
QtCore.QObject.connect(self.maintenanceButton,
|
||||||
QtCore.SIGNAL(u'clicked()'), self.onMaintenanceButtonClicked)
|
QtCore.SIGNAL(u'clicked()'), self.onMaintenanceButtonClicked)
|
||||||
QtCore.QObject.connect(self.audioAddFromFileButton,
|
QtCore.QObject.connect(self.audioAddFromFileButton,
|
||||||
@ -172,17 +172,14 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
|
|||||||
def loadThemes(self, theme_list):
|
def loadThemes(self, theme_list):
|
||||||
self.themeComboBox.clear()
|
self.themeComboBox.clear()
|
||||||
self.themeComboBox.addItem(u'')
|
self.themeComboBox.addItem(u'')
|
||||||
self.themes = []
|
self.themes = theme_list
|
||||||
for theme in theme_list:
|
self.themeComboBox.addItems(theme_list)
|
||||||
self.themeComboBox.addItem(theme)
|
|
||||||
self.themes.append(theme)
|
|
||||||
set_case_insensitive_completer(self.themes, self.themeComboBox)
|
set_case_insensitive_completer(self.themes, self.themeComboBox)
|
||||||
|
|
||||||
def loadMediaFiles(self):
|
def loadMediaFiles(self):
|
||||||
self.audioAddFromMediaButton.setVisible(False)
|
self.audioAddFromMediaButton.setVisible(False)
|
||||||
for plugin in self.parent().pluginManager.plugins:
|
for plugin in self.parent().pluginManager.plugins:
|
||||||
if plugin.name == u'media' and \
|
if plugin.name == u'media' and plugin.status == PluginStatus.Active:
|
||||||
plugin.status == PluginStatus.Active:
|
|
||||||
self.audioAddFromMediaButton.setVisible(True)
|
self.audioAddFromMediaButton.setVisible(True)
|
||||||
self.mediaForm.populateFiles(
|
self.mediaForm.populateFiles(
|
||||||
plugin.mediaItem.getList(MediaType.Audio))
|
plugin.mediaItem.getList(MediaType.Audio))
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -29,6 +29,9 @@ The :mod:`importer` modules provides the general song import functionality.
|
|||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
from openlp.core.lib import translate
|
||||||
|
from openlp.core.lib.ui import UiStrings
|
||||||
|
from openlp.core.ui.wizard import WizardStrings
|
||||||
from opensongimport import OpenSongImport
|
from opensongimport import OpenSongImport
|
||||||
from easyslidesimport import EasySlidesImport
|
from easyslidesimport import EasySlidesImport
|
||||||
from olpimport import OpenLPSongImport
|
from olpimport import OpenLPSongImport
|
||||||
@ -41,6 +44,7 @@ from ewimport import EasyWorshipSongImport
|
|||||||
from songbeamerimport import SongBeamerImport
|
from songbeamerimport import SongBeamerImport
|
||||||
from songshowplusimport import SongShowPlusImport
|
from songshowplusimport import SongShowPlusImport
|
||||||
from foilpresenterimport import FoilPresenterImport
|
from foilpresenterimport import FoilPresenterImport
|
||||||
|
from zionworximport import ZionWorxImport
|
||||||
# Imports that might fail
|
# Imports that might fail
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
try:
|
try:
|
||||||
@ -62,13 +66,58 @@ except ImportError:
|
|||||||
log.exception('Error importing %s', 'OooImport')
|
log.exception('Error importing %s', 'OooImport')
|
||||||
HAS_OOO = False
|
HAS_OOO = False
|
||||||
|
|
||||||
|
class SongFormatSelect(object):
|
||||||
|
"""
|
||||||
|
This is a special enumeration class listing available file selection modes.
|
||||||
|
"""
|
||||||
|
SingleFile = 0
|
||||||
|
MultipleFiles = 1
|
||||||
|
SingleFolder = 2
|
||||||
|
|
||||||
class SongFormat(object):
|
class SongFormat(object):
|
||||||
"""
|
"""
|
||||||
This is a special enumeration class that holds the various types of songs,
|
This is a special static class that holds an enumeration of the various
|
||||||
plus a few helper functions to facilitate generic handling of song types
|
song formats handled by the importer, the attributes of each song format,
|
||||||
for importing.
|
and a few helper functions.
|
||||||
|
|
||||||
|
Required attributes for each song format:
|
||||||
|
|
||||||
|
``u'class'``
|
||||||
|
Import class, e.g. ``OpenLyricsImport``
|
||||||
|
``u'name'``
|
||||||
|
Name of the format, e.g. ``u'OpenLyrics'``
|
||||||
|
``u'prefix'``
|
||||||
|
Prefix for Qt objects. Use mixedCase, e.g. ``u'openLyrics'``
|
||||||
|
See ``SongImportForm.addFileSelectItem()``
|
||||||
|
|
||||||
|
Optional attributes for each song format:
|
||||||
|
|
||||||
|
``u'canDisable'``
|
||||||
|
Whether song format importer is disablable.
|
||||||
|
``u'availability'``
|
||||||
|
Whether song format importer is available.
|
||||||
|
``u'selectMode'``
|
||||||
|
Whether format accepts single file, multiple files, or single folder
|
||||||
|
(as per ``SongFormatSelect`` options).
|
||||||
|
``u'filter'``
|
||||||
|
File extension filter for ``QFileDialog``.
|
||||||
|
|
||||||
|
Optional/custom text Strings for ``SongImportForm`` widgets:
|
||||||
|
|
||||||
|
``u'comboBoxText'``
|
||||||
|
Combo box selector (default value is the format's ``u'name'``).
|
||||||
|
``u'disabledLabelText'``
|
||||||
|
Required for disablable song formats.
|
||||||
|
``u'getFilesTitle'``
|
||||||
|
Title for ``QFileDialog`` (default includes the format's ``u'name'``).
|
||||||
|
``u'invalidSourceMsg'``
|
||||||
|
Message displayed if ``isValidSource()`` returns ``False``.
|
||||||
|
``u'descriptionText'``
|
||||||
|
Short description (1-2 lines) about the song format.
|
||||||
"""
|
"""
|
||||||
_format_availability = {}
|
# Song formats (ordered alphabetically after Generic)
|
||||||
|
# * Numerical order of song formats is significant as it determines the
|
||||||
|
# order used by formatComboBox.
|
||||||
Unknown = -1
|
Unknown = -1
|
||||||
OpenLyrics = 0
|
OpenLyrics = 0
|
||||||
OpenLP2 = 1
|
OpenLP2 = 1
|
||||||
@ -85,50 +134,164 @@ class SongFormat(object):
|
|||||||
SongShowPlus = 12
|
SongShowPlus = 12
|
||||||
SongsOfFellowship = 13
|
SongsOfFellowship = 13
|
||||||
WordsOfWorship = 14
|
WordsOfWorship = 14
|
||||||
#CSV = 15
|
ZionWorx = 15
|
||||||
|
#CSV = 16
|
||||||
|
|
||||||
|
# Set optional attribute defaults
|
||||||
|
__defaults__ = {
|
||||||
|
u'canDisable': False,
|
||||||
|
u'availability': True,
|
||||||
|
u'selectMode': SongFormatSelect.MultipleFiles,
|
||||||
|
u'filter': u'',
|
||||||
|
u'comboBoxText': None,
|
||||||
|
u'disabledLabelText': u'',
|
||||||
|
u'getFilesTitle': None,
|
||||||
|
u'invalidSourceMsg': None,
|
||||||
|
u'descriptionText': None
|
||||||
|
}
|
||||||
|
|
||||||
|
# Set attribute values for each Song Format
|
||||||
|
__attributes__ = {
|
||||||
|
OpenLyrics: {
|
||||||
|
u'class': OpenLyricsImport,
|
||||||
|
u'name': u'OpenLyrics',
|
||||||
|
u'prefix': u'openLyrics',
|
||||||
|
u'filter': u'%s (*.xml)' % translate('SongsPlugin.ImportWizardForm',
|
||||||
|
'OpenLyrics Files'),
|
||||||
|
u'comboBoxText': translate('SongsPlugin.ImportWizardForm',
|
||||||
|
'OpenLyrics or OpenLP 2.0 Exported Song')
|
||||||
|
},
|
||||||
|
OpenLP2: {
|
||||||
|
u'class': OpenLPSongImport,
|
||||||
|
u'name': UiStrings().OLPV2,
|
||||||
|
u'prefix': u'openLP2',
|
||||||
|
u'selectMode': SongFormatSelect.SingleFile,
|
||||||
|
u'filter': u'%s (*.sqlite)' % (translate(
|
||||||
|
'SongsPlugin.ImportWizardForm', 'OpenLP 2.0 Databases'))
|
||||||
|
},
|
||||||
|
OpenLP1: {
|
||||||
|
u'name': UiStrings().OLPV1,
|
||||||
|
u'prefix': u'openLP1',
|
||||||
|
u'canDisable': True,
|
||||||
|
u'selectMode': SongFormatSelect.SingleFile,
|
||||||
|
u'filter': u'%s (*.olp)' % translate('SongsPlugin.ImportWizardForm',
|
||||||
|
'openlp.org v1.x Databases'),
|
||||||
|
u'disabledLabelText': WizardStrings.NoSqlite
|
||||||
|
},
|
||||||
|
Generic: {
|
||||||
|
u'name': translate('SongsPlugin.ImportWizardForm',
|
||||||
|
'Generic Document/Presentation'),
|
||||||
|
u'prefix': u'generic',
|
||||||
|
u'canDisable': True,
|
||||||
|
u'disabledLabelText': translate('SongsPlugin.ImportWizardForm',
|
||||||
|
'The generic document/presentation importer has been disabled '
|
||||||
|
'because OpenLP cannot access OpenOffice or LibreOffice.'),
|
||||||
|
u'getFilesTitle': translate('SongsPlugin.ImportWizardForm',
|
||||||
|
'Select Document/Presentation Files')
|
||||||
|
},
|
||||||
|
CCLI: {
|
||||||
|
u'class': CCLIFileImport,
|
||||||
|
u'name': u'CCLI/SongSelect',
|
||||||
|
u'prefix': u'ccli',
|
||||||
|
u'filter': u'%s (*.usr *.txt)' % translate(
|
||||||
|
'SongsPlugin.ImportWizardForm', 'CCLI SongSelect Files')
|
||||||
|
},
|
||||||
|
DreamBeam: {
|
||||||
|
u'class': DreamBeamImport,
|
||||||
|
u'name': u'DreamBeam',
|
||||||
|
u'prefix': u'dreamBeam',
|
||||||
|
u'filter': u'%s (*.xml)' % translate('SongsPlugin.ImportWizardForm',
|
||||||
|
'DreamBeam Song Files')
|
||||||
|
},
|
||||||
|
EasySlides: {
|
||||||
|
u'class': EasySlidesImport,
|
||||||
|
u'name': u'EasySlides',
|
||||||
|
u'prefix': u'easySlides',
|
||||||
|
u'selectMode': SongFormatSelect.SingleFile,
|
||||||
|
u'filter': u'%s (*.xml)' % translate('SongsPlugin.ImportWizardForm',
|
||||||
|
'EasySlides XML File')
|
||||||
|
},
|
||||||
|
EasyWorship: {
|
||||||
|
u'class': EasyWorshipSongImport,
|
||||||
|
u'name': u'EasyWorship',
|
||||||
|
u'prefix': u'ew',
|
||||||
|
u'selectMode': SongFormatSelect.SingleFile,
|
||||||
|
u'filter': u'%s (*.db)' % translate('SongsPlugin.ImportWizardForm',
|
||||||
|
'EasyWorship Song Database')
|
||||||
|
},
|
||||||
|
FoilPresenter: {
|
||||||
|
u'class': FoilPresenterImport,
|
||||||
|
u'name': u'Foilpresenter',
|
||||||
|
u'prefix': u'foilPresenter',
|
||||||
|
u'filter': u'%s (*.foil)' % translate(
|
||||||
|
'SongsPlugin.ImportWizardForm', 'Foilpresenter Song Files')
|
||||||
|
},
|
||||||
|
OpenSong: {
|
||||||
|
u'class': OpenSongImport,
|
||||||
|
u'name': WizardStrings.OS,
|
||||||
|
u'prefix': u'openSong'
|
||||||
|
},
|
||||||
|
PowerSong: {
|
||||||
|
u'class': PowerSongImport,
|
||||||
|
u'name': u'PowerSong 1.0',
|
||||||
|
u'prefix': u'powerSong',
|
||||||
|
u'selectMode': SongFormatSelect.SingleFolder,
|
||||||
|
u'invalidSourceMsg': translate('SongsPlugin.ImportWizardForm',
|
||||||
|
'You need to specify a valid PowerSong 1.0 database folder.')
|
||||||
|
},
|
||||||
|
SongBeamer: {
|
||||||
|
u'class': SongBeamerImport,
|
||||||
|
u'name': u'SongBeamer',
|
||||||
|
u'prefix': u'songBeamer',
|
||||||
|
u'filter': u'%s (*.sng)' % translate('SongsPlugin.ImportWizardForm',
|
||||||
|
'SongBeamer Files')
|
||||||
|
},
|
||||||
|
SongShowPlus: {
|
||||||
|
u'class': SongShowPlusImport,
|
||||||
|
u'name': u'SongShow Plus',
|
||||||
|
u'prefix': u'songShowPlus',
|
||||||
|
u'filter': u'%s (*.sbsong)' % translate(
|
||||||
|
'SongsPlugin.ImportWizardForm', 'SongShow Plus Song Files')
|
||||||
|
},
|
||||||
|
SongsOfFellowship: {
|
||||||
|
u'name': u'Songs of Fellowship',
|
||||||
|
u'prefix': u'songsOfFellowship',
|
||||||
|
u'canDisable': True,
|
||||||
|
u'filter': u'%s (*.rtf)' % translate('SongsPlugin.ImportWizardForm',
|
||||||
|
'Songs Of Fellowship Song Files'),
|
||||||
|
u'disabledLabelText': translate('SongsPlugin.ImportWizardForm',
|
||||||
|
'The Songs of Fellowship importer has been disabled because '
|
||||||
|
'OpenLP cannot access OpenOffice or LibreOffice.')
|
||||||
|
},
|
||||||
|
WordsOfWorship: {
|
||||||
|
u'class': WowImport,
|
||||||
|
u'name': u'Words of Worship',
|
||||||
|
u'prefix': u'wordsOfWorship',
|
||||||
|
u'filter': u'%s (*.wsg *.wow-song)' % translate(
|
||||||
|
'SongsPlugin.ImportWizardForm', 'Words Of Worship Song Files')
|
||||||
|
},
|
||||||
|
ZionWorx: {
|
||||||
|
u'class': ZionWorxImport,
|
||||||
|
u'name': u'ZionWorx',
|
||||||
|
u'prefix': u'zionWorx',
|
||||||
|
u'selectMode': SongFormatSelect.SingleFile,
|
||||||
|
u'comboBoxText': translate('SongsPlugin.ImportWizardForm',
|
||||||
|
'ZionWorx (CSV)'),
|
||||||
|
u'descriptionText': translate('SongsPlugin.ImportWizardForm',
|
||||||
|
'First convert your ZionWorx database to a CSV text file, as '
|
||||||
|
'explained in the <a href="http://manual.openlp.org/songs.html'
|
||||||
|
'#importing-from-zionworx">User Manual</a>.')
|
||||||
|
# },
|
||||||
|
# CSV: {
|
||||||
|
# u'class': CSVImport,
|
||||||
|
# u'name': WizardStrings.CSV,
|
||||||
|
# u'prefix': u'csv',
|
||||||
|
# u'selectMode': SongFormatSelect.SingleFile
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_class(format):
|
def get_format_list():
|
||||||
"""
|
|
||||||
Return the appropriate implementation class.
|
|
||||||
|
|
||||||
``format``
|
|
||||||
The song format.
|
|
||||||
"""
|
|
||||||
if format == SongFormat.OpenLP2:
|
|
||||||
return OpenLPSongImport
|
|
||||||
elif format == SongFormat.OpenLP1:
|
|
||||||
return OpenLP1SongImport
|
|
||||||
elif format == SongFormat.OpenLyrics:
|
|
||||||
return OpenLyricsImport
|
|
||||||
elif format == SongFormat.OpenSong:
|
|
||||||
return OpenSongImport
|
|
||||||
elif format == SongFormat.SongsOfFellowship:
|
|
||||||
return SofImport
|
|
||||||
elif format == SongFormat.WordsOfWorship:
|
|
||||||
return WowImport
|
|
||||||
elif format == SongFormat.Generic:
|
|
||||||
return OooImport
|
|
||||||
elif format == SongFormat.CCLI:
|
|
||||||
return CCLIFileImport
|
|
||||||
elif format == SongFormat.DreamBeam:
|
|
||||||
return DreamBeamImport
|
|
||||||
elif format == SongFormat.PowerSong:
|
|
||||||
return PowerSongImport
|
|
||||||
elif format == SongFormat.EasySlides:
|
|
||||||
return EasySlidesImport
|
|
||||||
elif format == SongFormat.EasyWorship:
|
|
||||||
return EasyWorshipSongImport
|
|
||||||
elif format == SongFormat.SongBeamer:
|
|
||||||
return SongBeamerImport
|
|
||||||
elif format == SongFormat.SongShowPlus:
|
|
||||||
return SongShowPlusImport
|
|
||||||
elif format == SongFormat.FoilPresenter:
|
|
||||||
return FoilPresenterImport
|
|
||||||
return None
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_formats_list():
|
|
||||||
"""
|
"""
|
||||||
Return a list of the supported song formats.
|
Return a list of the supported song formats.
|
||||||
"""
|
"""
|
||||||
@ -147,26 +310,55 @@ class SongFormat(object):
|
|||||||
SongFormat.SongBeamer,
|
SongFormat.SongBeamer,
|
||||||
SongFormat.SongShowPlus,
|
SongFormat.SongShowPlus,
|
||||||
SongFormat.SongsOfFellowship,
|
SongFormat.SongsOfFellowship,
|
||||||
SongFormat.WordsOfWorship
|
SongFormat.WordsOfWorship,
|
||||||
|
SongFormat.ZionWorx
|
||||||
]
|
]
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def set_availability(format, available):
|
def get(format, *attributes):
|
||||||
"""
|
"""
|
||||||
Set the availability for a given song format.
|
Return requested song format attribute(s).
|
||||||
|
|
||||||
|
``format``
|
||||||
|
A song format from SongFormat.
|
||||||
|
|
||||||
|
``*attributes``
|
||||||
|
Zero or more song format attributes from SongFormat.
|
||||||
|
|
||||||
|
Return type depends on number of supplied attributes:
|
||||||
|
:0: Return dict containing all defined attributes for the format.
|
||||||
|
:1: Return the attribute value.
|
||||||
|
:>1: Return tuple of requested attribute values.
|
||||||
"""
|
"""
|
||||||
SongFormat._format_availability[format] = available
|
if not attributes:
|
||||||
|
return SongFormat.__attributes__.get(format)
|
||||||
|
elif len(attributes) == 1:
|
||||||
|
default = SongFormat.__defaults__.get(attributes[0])
|
||||||
|
return SongFormat.__attributes__[format].get(attributes[0],
|
||||||
|
default)
|
||||||
|
else:
|
||||||
|
values = []
|
||||||
|
for attr in attributes:
|
||||||
|
default = SongFormat.__defaults__.get(attr)
|
||||||
|
values.append(SongFormat.__attributes__[format].get(attr,
|
||||||
|
default))
|
||||||
|
return tuple(values)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_availability(format):
|
def set(format, attribute, value):
|
||||||
"""
|
"""
|
||||||
Return the availability of a given song format.
|
Set specified song format attribute to the supplied value.
|
||||||
"""
|
"""
|
||||||
return SongFormat._format_availability.get(format, True)
|
SongFormat.__attributes__[format][attribute] = value
|
||||||
|
|
||||||
SongFormat.set_availability(SongFormat.OpenLP1, HAS_OPENLP1)
|
SongFormat.set(SongFormat.OpenLP1, u'availability', HAS_OPENLP1)
|
||||||
SongFormat.set_availability(SongFormat.SongsOfFellowship, HAS_SOF)
|
if HAS_OPENLP1:
|
||||||
SongFormat.set_availability(SongFormat.Generic, HAS_OOO)
|
SongFormat.set(SongFormat.OpenLP1, u'class', OpenLP1SongImport)
|
||||||
|
SongFormat.set(SongFormat.SongsOfFellowship, u'availability', HAS_SOF)
|
||||||
__all__ = [u'SongFormat']
|
if HAS_SOF:
|
||||||
|
SongFormat.set(SongFormat.SongsOfFellowship, u'class', SofImport)
|
||||||
|
SongFormat.set(SongFormat.Generic, u'availability', HAS_OOO)
|
||||||
|
if HAS_OOO:
|
||||||
|
SongFormat.set(SongFormat.Generic, u'class', OooImport)
|
||||||
|
|
||||||
|
__all__ = [u'SongFormat', u'SongFormatSelect']
|
||||||
|
@ -33,7 +33,6 @@ import fnmatch
|
|||||||
import os
|
import os
|
||||||
|
|
||||||
from openlp.core.lib import translate
|
from openlp.core.lib import translate
|
||||||
from openlp.core.ui.wizard import WizardStrings
|
|
||||||
from openlp.plugins.songs.lib.songimport import SongImport
|
from openlp.plugins.songs.lib.songimport import SongImport
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
@ -71,18 +70,15 @@ class PowerSongImport(SongImport):
|
|||||||
|
|
||||||
* .song
|
* .song
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def isValidSource(**kwargs):
|
def isValidSource(import_source):
|
||||||
"""
|
"""
|
||||||
Checks if source is a PowerSong 1.0 folder:
|
Checks if source is a PowerSong 1.0 folder:
|
||||||
* is a directory
|
* is a directory
|
||||||
* contains at least one *.song file
|
* contains at least one *.song file
|
||||||
"""
|
"""
|
||||||
if u'folder' in kwargs:
|
if os.path.isdir(import_source):
|
||||||
dir = kwargs[u'folder']
|
for file in os.listdir(import_source):
|
||||||
if os.path.isdir(dir):
|
|
||||||
for file in os.listdir(dir):
|
|
||||||
if fnmatch.fnmatch(file, u'*.song'):
|
if fnmatch.fnmatch(file, u'*.song'):
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
@ -91,6 +87,8 @@ class PowerSongImport(SongImport):
|
|||||||
"""
|
"""
|
||||||
Receive either a list of files or a folder (unicode) to import.
|
Receive either a list of files or a folder (unicode) to import.
|
||||||
"""
|
"""
|
||||||
|
from importer import SongFormat
|
||||||
|
PS_string = SongFormat.get(SongFormat.PowerSong, u'name')
|
||||||
if isinstance(self.importSource, unicode):
|
if isinstance(self.importSource, unicode):
|
||||||
if os.path.isdir(self.importSource):
|
if os.path.isdir(self.importSource):
|
||||||
dir = self.importSource
|
dir = self.importSource
|
||||||
@ -104,7 +102,7 @@ class PowerSongImport(SongImport):
|
|||||||
self.logError(unicode(translate('SongsPlugin.PowerSongImport',
|
self.logError(unicode(translate('SongsPlugin.PowerSongImport',
|
||||||
'No songs to import.')),
|
'No songs to import.')),
|
||||||
unicode(translate('SongsPlugin.PowerSongImport',
|
unicode(translate('SongsPlugin.PowerSongImport',
|
||||||
'No %s files found.' % WizardStrings.PS)))
|
'No %s files found.' % PS_string)))
|
||||||
return
|
return
|
||||||
self.importWizard.progressBar.setMaximum(len(self.importSource))
|
self.importWizard.progressBar.setMaximum(len(self.importSource))
|
||||||
for file in self.importSource:
|
for file in self.importSource:
|
||||||
@ -124,7 +122,7 @@ class PowerSongImport(SongImport):
|
|||||||
self.logError(os.path.basename(file), unicode(
|
self.logError(os.path.basename(file), unicode(
|
||||||
translate('SongsPlugin.PowerSongImport',
|
translate('SongsPlugin.PowerSongImport',
|
||||||
'Invalid %s file. Unexpected byte value.'
|
'Invalid %s file. Unexpected byte value.'
|
||||||
% WizardStrings.PS)))
|
% PS_string)))
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
if label == u'TITLE':
|
if label == u'TITLE':
|
||||||
@ -142,15 +140,14 @@ class PowerSongImport(SongImport):
|
|||||||
if not self.title:
|
if not self.title:
|
||||||
self.logError(os.path.basename(file), unicode(
|
self.logError(os.path.basename(file), unicode(
|
||||||
translate('SongsPlugin.PowerSongImport',
|
translate('SongsPlugin.PowerSongImport',
|
||||||
'Invalid %s file. Missing "TITLE" header.'
|
'Invalid %s file. Missing "TITLE" header.' % PS_string)))
|
||||||
% WizardStrings.PS)))
|
|
||||||
continue
|
continue
|
||||||
# Check that file had COPYRIGHTLINE label
|
# Check that file had COPYRIGHTLINE label
|
||||||
if not found_copyright:
|
if not found_copyright:
|
||||||
self.logError(self.title, unicode(
|
self.logError(self.title, unicode(
|
||||||
translate('SongsPlugin.PowerSongImport',
|
translate('SongsPlugin.PowerSongImport',
|
||||||
'Invalid %s file. Missing "COPYRIGHTLINE" '
|
'Invalid %s file. Missing "COPYRIGHTLINE" '
|
||||||
'header.' % WizardStrings.PS)))
|
'header.' % PS_string)))
|
||||||
continue
|
continue
|
||||||
# Check that file had at least one verse
|
# Check that file had at least one verse
|
||||||
if not self.verses:
|
if not self.verses:
|
||||||
|
@ -51,11 +51,11 @@ class SongImport(QtCore.QObject):
|
|||||||
as necessary
|
as necessary
|
||||||
"""
|
"""
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def isValidSource(**kwargs):
|
def isValidSource(import_source):
|
||||||
"""
|
"""
|
||||||
Override this method to validate the source prior to import.
|
Override this method to validate the source prior to import.
|
||||||
"""
|
"""
|
||||||
pass
|
return True
|
||||||
|
|
||||||
def __init__(self, manager, **kwargs):
|
def __init__(self, manager, **kwargs):
|
||||||
"""
|
"""
|
||||||
|
142
openlp/plugins/songs/lib/zionworximport.py
Normal file
142
openlp/plugins/songs/lib/zionworximport.py
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# OpenLP - Open Source Lyrics Projection #
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
# Copyright (c) 2008-2012 Raoul Snyman #
|
||||||
|
# Portions copyright (c) 2008-2012 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:`zionworximport` module provides the functionality for importing
|
||||||
|
ZionWorx songs into the OpenLP database.
|
||||||
|
"""
|
||||||
|
import csv
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from openlp.core.lib import translate
|
||||||
|
from openlp.plugins.songs.lib.songimport import SongImport
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
class ZionWorxImport(SongImport):
|
||||||
|
"""
|
||||||
|
The :class:`ZionWorxImport` class provides the ability to import songs
|
||||||
|
from ZionWorx, via a dump of the ZionWorx database to a CSV file.
|
||||||
|
|
||||||
|
ZionWorx song database fields:
|
||||||
|
|
||||||
|
* ``SongNum`` Song ID. (Discarded by importer)
|
||||||
|
* ``Title1`` Main Title.
|
||||||
|
* ``Title2`` Alternate Title.
|
||||||
|
* ``Lyrics`` Song verses, separated by blank lines.
|
||||||
|
* ``Writer`` Song author(s).
|
||||||
|
* ``Copyright`` Copyright information
|
||||||
|
* ``Keywords`` (Discarded by importer)
|
||||||
|
* ``DefaultStyle`` (Discarded by importer)
|
||||||
|
|
||||||
|
ZionWorx has no native export function; it uses the proprietary TurboDB
|
||||||
|
database engine. The TurboDB vendor, dataWeb, provides tools which can
|
||||||
|
export TurboDB tables to other formats, such as freeware console tool
|
||||||
|
TurboDB Data Exchange which is available for Windows and Linux. This command
|
||||||
|
exports the ZionWorx songs table to a CSV file:
|
||||||
|
|
||||||
|
``tdbdatax MainTable.dat songstable.csv -fsdf -s, -qd``
|
||||||
|
|
||||||
|
* -f Table format: ``sdf`` denotes text file.
|
||||||
|
* -s Separator character between fields.
|
||||||
|
* -q Quote character surrounding fields. ``d`` denotes double-quote.
|
||||||
|
|
||||||
|
CSV format expected by importer:
|
||||||
|
|
||||||
|
* Field separator character is comma ``,``
|
||||||
|
* Fields surrounded by double-quotes ``"``. This enables fields (such as
|
||||||
|
Lyrics) to include new-lines and commas. Double-quotes within a field
|
||||||
|
are denoted by two double-quotes ``""``
|
||||||
|
* Note: This is the default format of the Python ``csv`` module.
|
||||||
|
|
||||||
|
"""
|
||||||
|
def doImport(self):
|
||||||
|
"""
|
||||||
|
Receive a CSV file (from a ZionWorx database dump) to import.
|
||||||
|
"""
|
||||||
|
# Used to strip control chars (10=LF, 13=CR, 127=DEL)
|
||||||
|
self.control_chars_map = dict.fromkeys(
|
||||||
|
range(10) + [11, 12] + range(14,32) + [127])
|
||||||
|
with open(self.importSource, 'rb') as songs_file:
|
||||||
|
fieldnames = [u'SongNum', u'Title1', u'Title2', u'Lyrics',
|
||||||
|
u'Writer', u'Copyright', u'Keywords', u'DefaultStyle']
|
||||||
|
songs_reader = csv.DictReader(songs_file, fieldnames)
|
||||||
|
try:
|
||||||
|
records = list(songs_reader)
|
||||||
|
except csv.Error, e:
|
||||||
|
self.logError(unicode(translate('SongsPlugin.ZionWorxImport',
|
||||||
|
'Error reading CSV file.')),
|
||||||
|
unicode(translate('SongsPlugin.ZionWorxImport',
|
||||||
|
'Line %d: %s' % (songs_reader.line_num, e))))
|
||||||
|
return
|
||||||
|
num_records = len(records)
|
||||||
|
log.info(u'%s records found in CSV file' % num_records)
|
||||||
|
self.importWizard.progressBar.setMaximum(num_records)
|
||||||
|
for index, record in enumerate(records, 1):
|
||||||
|
if self.stopImportFlag:
|
||||||
|
return
|
||||||
|
self.setDefaults()
|
||||||
|
try:
|
||||||
|
self.title = self._decode(record[u'Title1'])
|
||||||
|
if record[u'Title2']:
|
||||||
|
self.alternateTitle = self._decode(record[u'Title2'])
|
||||||
|
self.parseAuthor(self._decode(record[u'Writer']))
|
||||||
|
self.addCopyright(self._decode(record[u'Copyright']))
|
||||||
|
lyrics = self._decode(record[u'Lyrics'])
|
||||||
|
except UnicodeDecodeError, e:
|
||||||
|
self.logError(unicode(translate(
|
||||||
|
'SongsPlugin.ZionWorxImport', 'Record %d' % index)),
|
||||||
|
unicode(translate('SongsPlugin.ZionWorxImport',
|
||||||
|
'Decoding error: %s' % e)))
|
||||||
|
continue
|
||||||
|
except TypeError, e:
|
||||||
|
self.logError(unicode(translate(
|
||||||
|
'SongsPlugin.ZionWorxImport', 'File not valid ZionWorx '
|
||||||
|
'CSV format.')), u'TypeError: %s' % e)
|
||||||
|
return
|
||||||
|
verse = u''
|
||||||
|
for line in lyrics.splitlines():
|
||||||
|
if line and not line.isspace():
|
||||||
|
verse += line + u'\n'
|
||||||
|
elif verse:
|
||||||
|
self.addVerse(verse)
|
||||||
|
verse = u''
|
||||||
|
if verse:
|
||||||
|
self.addVerse(verse)
|
||||||
|
title = self.title
|
||||||
|
if not self.finish():
|
||||||
|
self.logError(unicode(translate(
|
||||||
|
'SongsPlugin.ZionWorxImport', 'Record %d' % index))
|
||||||
|
+ (u': "' + title + u'"' if title else u''))
|
||||||
|
|
||||||
|
def _decode(self, str):
|
||||||
|
"""
|
||||||
|
Decodes CSV input to unicode, stripping all control characters (except
|
||||||
|
new lines).
|
||||||
|
"""
|
||||||
|
# This encoding choice seems OK. ZionWorx has no option for setting the
|
||||||
|
# encoding for its songs, so we assume encoding is always the same.
|
||||||
|
return unicode(str, u'cp1252').translate(self.control_chars_map)
|
@ -194,7 +194,7 @@ class SongsPlugin(Plugin):
|
|||||||
self.manager.save_object(song)
|
self.manager.save_object(song)
|
||||||
|
|
||||||
def importSongs(self, format, **kwargs):
|
def importSongs(self, format, **kwargs):
|
||||||
class_ = SongFormat.get_class(format)
|
class_ = SongFormat.get(format, u'class')
|
||||||
importer = class_(self.manager, **kwargs)
|
importer = class_(self.manager, **kwargs)
|
||||||
importer.register(self.mediaItem.importWizard)
|
importer.register(self.mediaItem.importWizard)
|
||||||
return importer
|
return importer
|
||||||
|
Loading…
Reference in New Issue
Block a user