This commit is contained in:
Andreas Preikschat 2012-06-09 16:04:59 +02:00
commit 328f918c9f
15 changed files with 362 additions and 276 deletions

View File

@ -163,143 +163,144 @@ class ImageManager(QtCore.QObject):
def __init__(self): def __init__(self):
QtCore.QObject.__init__(self) QtCore.QObject.__init__(self)
current_screen = ScreenList().current currentScreen = ScreenList().current
self.width = current_screen[u'size'].width() self.width = currentScreen[u'size'].width()
self.height = current_screen[u'size'].height() self.height = currentScreen[u'size'].height()
self._cache = {} self._cache = {}
self._imageThread = ImageThread(self) self.imageThread = ImageThread(self)
self._conversion_queue = PriorityQueue() self._conversionQueue = PriorityQueue()
self.stopManager = False
QtCore.QObject.connect(Receiver.get_receiver(), QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'config_updated'), self.process_updates) QtCore.SIGNAL(u'config_updated'), self.processUpdates)
def update_display(self): def updateDisplay(self):
""" """
Screen has changed size so rebuild the cache to new size. Screen has changed size so rebuild the cache to new size.
""" """
log.debug(u'update_display') log.debug(u'updateDisplay')
current_screen = ScreenList().current currentScreen = ScreenList().current
self.width = current_screen[u'size'].width() self.width = currentScreen[u'size'].width()
self.height = current_screen[u'size'].height() self.height = currentScreen[u'size'].height()
# Mark the images as dirty for a rebuild by setting the image and byte # Mark the images as dirty for a rebuild by setting the image and byte
# stream to None. # stream to None.
for image in self._cache.values(): for image in self._cache.values():
self._reset_image(image) self._resetImage(image)
def update_images(self, image_type, background): def updateImages(self, imageType, background):
""" """
Border has changed so update all the images affected. Border has changed so update all the images affected.
""" """
log.debug(u'update_images') log.debug(u'updateImages')
# Mark the images as dirty for a rebuild by setting the image and byte # Mark the images as dirty for a rebuild by setting the image and byte
# stream to None. # stream to None.
for image in self._cache.values(): for image in self._cache.values():
if image.source == image_type: if image.source == imageType:
image.background = background image.background = background
self._reset_image(image) self._resetImage(image)
def update_image(self, name, image_type, background): def updateImage(self, name, imageType, background):
""" """
Border has changed so update the image affected. Border has changed so update the image affected.
""" """
log.debug(u'update_images') log.debug(u'updateImage')
# Mark the images as dirty for a rebuild by setting the image and byte # Mark the images as dirty for a rebuild by setting the image and byte
# stream to None. # stream to None.
for image in self._cache.values(): for image in self._cache.values():
if image.source == image_type and image.name == name: if image.source == imageType and image.name == name:
image.background = background image.background = background
self._reset_image(image) self._resetImage(image)
def _reset_image(self, image): def _resetImage(self, image):
image.image = None image.image = None
image.image_bytes = None image.image_bytes = None
self._conversion_queue.modify_priority(image, Priority.Normal) self._conversionQueue.modify_priority(image, Priority.Normal)
def process_updates(self): def processUpdates(self):
""" """
Flush the queue to updated any data to update Flush the queue to updated any data to update
""" """
# We want only one thread. # We want only one thread.
if not self._imageThread.isRunning(): if not self.imageThread.isRunning():
self._imageThread.start() self.imageThread.start()
def get_image(self, name): def getImage(self, name):
""" """
Return the ``QImage`` from the cache. If not present wait for the Return the ``QImage`` from the cache. If not present wait for the
background thread to process it. background thread to process it.
""" """
log.debug(u'get_image %s' % name) log.debug(u'getImage %s' % name)
image = self._cache[name] image = self._cache[name]
if image.image is None: if image.image is None:
self._conversion_queue.modify_priority(image, Priority.High) self._conversionQueue.modify_priority(image, Priority.High)
# make sure we are running and if not give it a kick # make sure we are running and if not give it a kick
self.process_updates() self.processUpdates()
while image.image is None: while image.image is None:
log.debug(u'get_image - waiting') log.debug(u'getImage - waiting')
time.sleep(0.1) time.sleep(0.1)
elif image.image_bytes is None: elif image.image_bytes is None:
# Set the priority to Low, because the image was requested but the # Set the priority to Low, because the image was requested but the
# byte stream was not generated yet. However, we only need to do # byte stream was not generated yet. However, we only need to do
# this, when the image was generated before it was requested # this, when the image was generated before it was requested
# (otherwise this is already taken care of). # (otherwise this is already taken care of).
self._conversion_queue.modify_priority(image, Priority.Low) self._conversionQueue.modify_priority(image, Priority.Low)
return image.image return image.image
def get_image_bytes(self, name): def getImageBytes(self, name):
""" """
Returns the byte string for an image. If not present wait for the Returns the byte string for an image. If not present wait for the
background thread to process it. background thread to process it.
""" """
log.debug(u'get_image_bytes %s' % name) log.debug(u'getImageBytes %s' % name)
image = self._cache[name] image = self._cache[name]
if image.image_bytes is None: if image.image_bytes is None:
self._conversion_queue.modify_priority(image, Priority.Urgent) self._conversionQueue.modify_priority(image, Priority.Urgent)
# make sure we are running and if not give it a kick # make sure we are running and if not give it a kick
self.process_updates() self.processUpdates()
while image.image_bytes is None: while image.image_bytes is None:
log.debug(u'get_image_bytes - waiting') log.debug(u'getImageBytes - waiting')
time.sleep(0.1) time.sleep(0.1)
return image.image_bytes return image.image_bytes
def del_image(self, name): def deleteImage(self, name):
""" """
Delete the Image from the cache. Delete the Image from the cache.
""" """
log.debug(u'del_image %s' % name) log.debug(u'deleteImage %s' % name)
if name in self._cache: if name in self._cache:
self._conversion_queue.remove(self._cache[name]) self._conversionQueue.remove(self._cache[name])
del self._cache[name] del self._cache[name]
def add_image(self, name, path, source, background): def addImage(self, name, path, source, background):
""" """
Add image to cache if it is not already there. Add image to cache if it is not already there.
""" """
log.debug(u'add_image %s:%s' % (name, path)) log.debug(u'addImage %s:%s' % (name, path))
if not name in self._cache: if not name in self._cache:
image = Image(name, path, source, background) image = Image(name, path, source, background)
self._cache[name] = image self._cache[name] = image
self._conversion_queue.put( self._conversionQueue.put(
(image.priority, image.secondary_priority, image)) (image.priority, image.secondary_priority, image))
else: else:
log.debug(u'Image in cache %s:%s' % (name, path)) log.debug(u'Image in cache %s:%s' % (name, path))
# We want only one thread. # We want only one thread.
if not self._imageThread.isRunning(): if not self.imageThread.isRunning():
self._imageThread.start() self.imageThread.start()
def _process(self): def _process(self):
""" """
Controls the processing called from a ``QtCore.QThread``. Controls the processing called from a ``QtCore.QThread``.
""" """
log.debug(u'_process - started') log.debug(u'_process - started')
while not self._conversion_queue.empty(): while not self._conversionQueue.empty() and not self.stopManager:
self._process_cache() self._processCache()
log.debug(u'_process - ended') log.debug(u'_process - ended')
def _process_cache(self): def _processCache(self):
""" """
Actually does the work. Actually does the work.
""" """
log.debug(u'_process_cache') log.debug(u'_processCache')
image = self._conversion_queue.get()[2] image = self._conversionQueue.get()[2]
# Generate the QImage for the image. # Generate the QImage for the image.
if image.image is None: if image.image is None:
image.image = resize_image(image.path, self.width, self.height, image.image = resize_image(image.path, self.width, self.height,
@ -307,14 +308,14 @@ class ImageManager(QtCore.QObject):
# Set the priority to Lowest and stop here as we need to process # Set the priority to Lowest and stop here as we need to process
# more important images first. # more important images first.
if image.priority == Priority.Normal: if image.priority == Priority.Normal:
self._conversion_queue.modify_priority(image, Priority.Lowest) self._conversionQueue.modify_priority(image, Priority.Lowest)
return return
# For image with high priority we set the priority to Low, as the # For image with high priority we set the priority to Low, as the
# byte stream might be needed earlier the byte stream of image with # byte stream might be needed earlier the byte stream of image with
# Normal priority. We stop here as we need to process more important # Normal priority. We stop here as we need to process more important
# images first. # images first.
elif image.priority == Priority.High: elif image.priority == Priority.High:
self._conversion_queue.modify_priority(image, Priority.Low) self._conversionQueue.modify_priority(image, Priority.Low)
return return
# Generate the byte stream for the image. # Generate the byte stream for the image.
if image.image_bytes is None: if image.image_bytes is None:

View File

@ -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,88 +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
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
def set_override_theme(self, override_theme, override_levels=False): def set_item_theme(self, item_theme_name):
""" """
Set the appropriate theme depending on the theme level. Set the item-level theme. **Note**, this has to be done for each item we
Called by the service item when building a display frame are rendering.
``override_theme`` ``item_theme_name``
The name of the song-level theme. None means the service The item theme's name.
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) self._set_theme(item_theme_name)
theme_level = self.theme_level self.item_theme_name = item_theme_name
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)
# if No file do not update cache
if self.theme_data.background_filename:
self.imageManager.add_image(self.theme_data.theme_name,
self.theme_data.background_filename, u'theme',
QtGui.QColor(self.theme_data.background_border_color))
return self._rect, self._rect_footer
def generate_preview(self, theme_data, force_page=False): def generate_preview(self, theme_data, force_page=False):
""" """
@ -183,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.del_image(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
@ -303,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.
@ -360,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)
@ -380,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()

View File

@ -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.add_image(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()

View File

@ -444,6 +444,20 @@ class ThemeXML(object):
element.appendChild(child) element.appendChild(child)
return child return child
def set_default_header_footer(self):
"""
Set the header and footer size into the current primary screen.
10 px on each side is removed to allow for a border.
"""
from openlp.core.ui import ScreenList
current_screen = ScreenList().current
self.font_main_y = 0
self.font_main_width = current_screen[u'size'].width() - 20
self.font_main_height = current_screen[u'size'].height() * 9 / 10
self.font_footer_width = current_screen[u'size'].width() - 20
self.font_footer_y = current_screen[u'size'].height() * 9 / 10
self.font_footer_height = current_screen[u'size'].height() / 10
def dump_xml(self): def dump_xml(self):
""" """
Dump the XML to file used for debugging Dump the XML to file used for debugging

View File

@ -56,6 +56,9 @@ class ThemeScreenshotThread(QtCore.QThread):
themes = themes.split(u',') themes = themes.split(u',')
config = self.parent().config config = self.parent().config
for theme in themes: for theme in themes:
# Stop if the wizard has been cancelled.
if self.parent().downloadCancelled:
return
title = config.get(u'theme_%s' % theme, u'title') title = config.get(u'theme_%s' % theme, u'title')
filename = config.get(u'theme_%s' % theme, u'filename') filename = config.get(u'theme_%s' % theme, u'filename')
screenshot = config.get(u'theme_%s' % theme, u'screenshot') screenshot = config.get(u'theme_%s' % theme, u'screenshot')
@ -86,7 +89,7 @@ class FirstTimeForm(QtGui.QWizard, Ui_FirstTimeWizard):
files = self.webAccess.read() files = self.webAccess.read()
self.config.readfp(io.BytesIO(files)) self.config.readfp(io.BytesIO(files))
self.updateScreenListCombo() self.updateScreenListCombo()
self.downloadCanceled = False self.downloadCancelled = False
self.downloading = unicode(translate('OpenLP.FirstTimeWizard', self.downloading = unicode(translate('OpenLP.FirstTimeWizard',
'Downloading %s...')) 'Downloading %s...'))
QtCore.QObject.connect(self.cancelButton, QtCore.SIGNAL('clicked()'), QtCore.QObject.connect(self.cancelButton, QtCore.SIGNAL('clicked()'),
@ -242,11 +245,12 @@ class FirstTimeForm(QtGui.QWizard, Ui_FirstTimeWizard):
Process the triggering of the cancel button. Process the triggering of the cancel button.
""" """
if self.lastId == FirstTimePage.NoInternet or \ if self.lastId == FirstTimePage.NoInternet or \
(self.lastId <= FirstTimePage.Plugins and \ (self.lastId <= FirstTimePage.Plugins and not self.hasRunWizard):
not self.hasRunWizard):
QtCore.QCoreApplication.exit() QtCore.QCoreApplication.exit()
sys.exit() sys.exit()
self.downloadCanceled = True self.downloadCancelled = True
while self.themeScreenshotThread.isRunning():
time.sleep(0.1)
Receiver.send_message(u'cursor_normal') Receiver.send_message(u'cursor_normal')
def onNoInternetFinishButtonClicked(self): def onNoInternetFinishButtonClicked(self):
@ -272,7 +276,7 @@ class FirstTimeForm(QtGui.QWizard, Ui_FirstTimeWizard):
filesize = urlfile.headers["Content-Length"] filesize = urlfile.headers["Content-Length"]
filename = open(fpath, "wb") filename = open(fpath, "wb")
# Download until finished or canceled. # Download until finished or canceled.
while not self.downloadCanceled: while not self.downloadCancelled:
data = urlfile.read(block_size) data = urlfile.read(block_size)
if not data: if not data:
break break
@ -280,8 +284,8 @@ class FirstTimeForm(QtGui.QWizard, Ui_FirstTimeWizard):
block_count += 1 block_count += 1
self._downloadProgress(block_count, block_size, filesize) self._downloadProgress(block_count, block_size, filesize)
filename.close() filename.close()
# Delete file if canceled, it may be a partial file. # Delete file if cancelled, it may be a partial file.
if self.downloadCanceled: if self.downloadCancelled:
os.remove(fpath) os.remove(fpath)
def _buildThemeScreenshots(self): def _buildThemeScreenshots(self):

View File

@ -277,7 +277,7 @@ class MainDisplay(Display):
""" """
API for replacement backgrounds so Images are added directly to cache. API for replacement backgrounds so Images are added directly to cache.
""" """
self.imageManager.add_image(name, path, u'image', background) self.imageManager.addImage(name, path, u'image', background)
if hasattr(self, u'serviceItem'): if hasattr(self, u'serviceItem'):
self.override[u'image'] = name self.override[u'image'] = name
self.override[u'theme'] = self.serviceItem.themedata.theme_name self.override[u'theme'] = self.serviceItem.themedata.theme_name
@ -297,7 +297,7 @@ class MainDisplay(Display):
The name of the image to be displayed. The name of the image to be displayed.
""" """
log.debug(u'image to display') log.debug(u'image to display')
image = self.imageManager.get_image_bytes(name) image = self.imageManager.getImageBytes(name)
self.controller.mediaController.video_reset(self.controller) self.controller.mediaController.video_reset(self.controller)
self.displayImage(image) self.displayImage(image)
@ -382,14 +382,14 @@ class MainDisplay(Display):
else: else:
# replace the background # replace the background
background = self.imageManager. \ background = self.imageManager. \
get_image_bytes(self.override[u'image']) getImageBytes(self.override[u'image'])
self.setTransparency(self.serviceItem.themedata.background_type == self.setTransparency(self.serviceItem.themedata.background_type ==
BackgroundType.to_string(BackgroundType.Transparent)) BackgroundType.to_string(BackgroundType.Transparent))
if self.serviceItem.themedata.background_filename: if self.serviceItem.themedata.background_filename:
self.serviceItem.bg_image_bytes = self.imageManager. \ self.serviceItem.bg_image_bytes = self.imageManager. \
get_image_bytes(self.serviceItem.themedata.theme_name) getImageBytes(self.serviceItem.themedata.theme_name)
if image: if image:
image_bytes = self.imageManager.get_image_bytes(image) image_bytes = self.imageManager.getImageBytes(image)
else: else:
image_bytes = None image_bytes = None
html = build_html(self.serviceItem, self.screen, self.isLive, html = build_html(self.serviceItem, self.screen, self.isLive,

View File

@ -30,6 +30,7 @@ import os
import sys import sys
import shutil import shutil
from tempfile import gettempdir from tempfile import gettempdir
import time
from datetime import datetime from datetime import datetime
from PyQt4 import QtCore, QtGui from PyQt4 import QtCore, QtGui
@ -1121,7 +1122,7 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
""" """
log.debug(u'screenChanged') log.debug(u'screenChanged')
Receiver.send_message(u'cursor_busy') Receiver.send_message(u'cursor_busy')
self.imageManager.update_display() self.imageManager.updateDisplay()
self.renderer.update_display() self.renderer.update_display()
self.previewController.screenSizeChanged() self.previewController.screenSizeChanged()
self.liveController.screenSizeChanged() self.liveController.screenSizeChanged()
@ -1140,6 +1141,7 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
return return
# If we just did a settings import, close without saving changes. # If we just did a settings import, close without saving changes.
if self.settingsImported: if self.settingsImported:
self.cleanUp(False)
event.accept() event.accept()
if self.serviceManagerContents.isModified(): if self.serviceManagerContents.isModified():
ret = self.serviceManagerContents.saveModifiedService() ret = self.serviceManagerContents.saveModifiedService()
@ -1162,8 +1164,7 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
translate('OpenLP.MainWindow', translate('OpenLP.MainWindow',
'Are you sure you want to close OpenLP?'), 'Are you sure you want to close OpenLP?'),
QtGui.QMessageBox.StandardButtons( QtGui.QMessageBox.StandardButtons(
QtGui.QMessageBox.Yes | QtGui.QMessageBox.Yes | QtGui.QMessageBox.No),
QtGui.QMessageBox.No),
QtGui.QMessageBox.Yes) QtGui.QMessageBox.Yes)
if ret == QtGui.QMessageBox.Yes: if ret == QtGui.QMessageBox.Yes:
self.cleanUp() self.cleanUp()
@ -1174,21 +1175,29 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
self.cleanUp() self.cleanUp()
event.accept() event.accept()
def cleanUp(self): def cleanUp(self, save_settings=True):
""" """
Runs all the cleanup code before OpenLP shuts down Runs all the cleanup code before OpenLP shuts down.
``save_settings``
Switch to prevent saving settings. Defaults to **True**.
""" """
self.imageManager.stopManager = True
while self.imageManager.imageThread.isRunning():
time.sleep(0.1)
# Clean temporary files used by services # Clean temporary files used by services
self.serviceManagerContents.cleanUp() self.serviceManagerContents.cleanUp()
if QtCore.QSettings().value(u'advanced/save current plugin', if save_settings:
QtCore.QVariant(False)).toBool(): if QtCore.QSettings().value(u'advanced/save current plugin',
QtCore.QSettings().setValue(u'advanced/current media plugin', QtCore.QVariant(False)).toBool():
QtCore.QVariant(self.mediaToolBox.currentIndex())) QtCore.QSettings().setValue(u'advanced/current media plugin',
QtCore.QVariant(self.mediaToolBox.currentIndex()))
# Call the cleanup method to shutdown plugins. # Call the cleanup method to shutdown plugins.
log.info(u'cleanup plugins') log.info(u'cleanup plugins')
self.pluginManager.finalise_plugins() self.pluginManager.finalise_plugins()
# Save settings if save_settings:
self.saveSettings() # Save settings
self.saveSettings()
# Close down the display # Close down the display
if self.liveController.display: if self.liveController.display:
self.liveController.display.close() self.liveController.display.close()

View File

@ -859,11 +859,11 @@ class SlideController(Controller):
# If current slide set background to image # If current slide set background to image
if framenumber == slideno: if framenumber == slideno:
self.serviceItem.bg_image_bytes = \ self.serviceItem.bg_image_bytes = \
self.imageManager.get_image_bytes(frame[u'title']) self.imageManager.getImageBytes(frame[u'title'])
image = self.imageManager.get_image(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))

View File

@ -247,8 +247,7 @@ class ThemeManager(QtGui.QWidget):
QtCore.QSettings().setValue( QtCore.QSettings().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):
@ -257,6 +256,7 @@ class ThemeManager(QtGui.QWidget):
editing form for the user to make their customisations. editing form for the user to make their customisations.
""" """
theme = ThemeXML() theme = ThemeXML()
theme.set_default_header_footer()
self.themeForm.theme = theme self.themeForm.theme = theme
self.themeForm.exec_() self.themeForm.exec_()
@ -284,6 +284,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):
""" """
@ -320,9 +322,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()))
@ -331,6 +332,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):
""" """
@ -348,6 +350,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):
""" """
@ -663,9 +666,9 @@ class ThemeManager(QtGui.QWidget):
self._writeTheme(theme, image_from, image_to) self._writeTheme(theme, image_from, image_to)
if theme.background_type == \ if theme.background_type == \
BackgroundType.to_string(BackgroundType.Image): BackgroundType.to_string(BackgroundType.Image):
self.mainwindow.imageManager.update_image(theme.theme_name, self.mainwindow.imageManager.updateImage(theme.theme_name,
u'theme', QtGui.QColor(theme.background_border_color)) u'theme', QtGui.QColor(theme.background_border_color))
self.mainwindow.imageManager.process_updates() self.mainwindow.imageManager.processUpdates()
self.loadThemes() self.loadThemes()
def _writeTheme(self, theme, image_from, image_to): def _writeTheme(self, theme, image_from, image_to):

View File

@ -151,8 +151,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):
@ -169,8 +169,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):
@ -189,8 +189,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()

View File

@ -197,9 +197,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)
@ -220,10 +217,9 @@ class CustomMediaItem(MediaManagerItem):
service_item.add_from_text(slide[:30], slide) service_item.add_from_text(slide[:30], slide)
if QtCore.QSettings().value(self.settingsSection + u'/display footer', if QtCore.QSettings().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):

View File

@ -96,4 +96,4 @@ class ImagePlugin(Plugin):
""" """
background = QtGui.QColor(QtCore.QSettings().value(self.settingsSection background = QtGui.QColor(QtCore.QSettings().value(self.settingsSection
+ u'/background color', QtCore.QVariant(u'#000000'))) + u'/background color', QtCore.QVariant(u'#000000')))
self.liveController.imageManager.update_images(u'image', background) self.liveController.imageManager.updateImages(u'image', background)

View File

@ -164,7 +164,7 @@
<input type="search" name="search-text" id="search-text" value="" /> <input type="search" name="search-text" id="search-text" value="" />
</div> </div>
<a href="#" id="search-submit" data-role="button">${search}</a> <a href="#" id="search-submit" data-role="button">${search}</a>
<ul data-role="listview" data-inset="true"> <ul data-role="listview" data-inset="true"/>
</div> </div>
</div> </div>
<div data-role="page" id="options"> <div data-role="page" id="options">

View File

@ -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,
@ -707,7 +707,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
text = unicode(self.songBookComboBox.currentText()) text = unicode(self.songBookComboBox.currentText())
if item == 0 and text: if item == 0 and text:
temp_song_book = text temp_song_book = text
self.mediaitem.songMaintenanceForm.exec_() self.mediaitem.songMaintenanceForm.exec_(True)
self.loadAuthors() self.loadAuthors()
self.loadBooks() self.loadBooks()
self.loadTopics() self.loadTopics()
@ -865,12 +865,16 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
for row in xrange(self.authorsListView.count()): for row in xrange(self.authorsListView.count()):
item = self.authorsListView.item(row) item = self.authorsListView.item(row)
authorId = (item.data(QtCore.Qt.UserRole)).toInt()[0] authorId = (item.data(QtCore.Qt.UserRole)).toInt()[0]
self.song.authors.append(self.manager.get_object(Author, authorId)) author = self.manager.get_object(Author, authorId)
if author is not None:
self.song.authors.append(author)
self.song.topics = [] self.song.topics = []
for row in xrange(self.topicsListView.count()): for row in xrange(self.topicsListView.count()):
item = self.topicsListView.item(row) item = self.topicsListView.item(row)
topicId = (item.data(QtCore.Qt.UserRole)).toInt()[0] topicId = (item.data(QtCore.Qt.UserRole)).toInt()[0]
self.song.topics.append(self.manager.get_object(Topic, topicId)) topic = self.manager.get_object(Topic, topicId)
if topic is not None:
self.song.topics.append(topic)
# Save the song here because we need a valid id for the audio files. # Save the song here because we need a valid id for the audio files.
clean_song(self.manager, self.song) clean_song(self.manager, self.song)
self.manager.save_object(self.song) self.manager.save_object(self.song)

View File

@ -87,7 +87,15 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog):
QtCore.SIGNAL(u'currentRowChanged(int)'), QtCore.SIGNAL(u'currentRowChanged(int)'),
self.onBooksListRowChanged) self.onBooksListRowChanged)
def exec_(self): def exec_(self, fromSongEdit=False):
"""
Show the dialog.
``fromSongEdit``
Indicates if the maintenance dialog has been opened from song edit
or from the media manager. Defaults to **False**.
"""
self.fromSongEdit = fromSongEdit
self.typeListWidget.setCurrentRow(0) self.typeListWidget.setCurrentRow(0)
self.resetAuthors() self.resetAuthors()
self.resetTopics() self.resetTopics()
@ -103,20 +111,20 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog):
else: else:
return -1 return -1
def _deleteItem(self, item_class, list_widget, reset_func, dlg_title, def _deleteItem(self, itemClass, listWidget, resetFunc, dlgTitle,
del_text, err_text): del_text, err_text):
item_id = self._getCurrentItemId(list_widget) item_id = self._getCurrentItemId(listWidget)
if item_id != -1: if item_id != -1:
item = self.manager.get_object(item_class, item_id) item = self.manager.get_object(itemClass, item_id)
if item and not item.songs: if item and not item.songs:
if critical_error_message_box(dlg_title, del_text, self, if critical_error_message_box(dlgTitle, del_text, self,
True) == QtGui.QMessageBox.Yes: True) == QtGui.QMessageBox.Yes:
self.manager.delete_object(item_class, item.id) self.manager.delete_object(itemClass, item.id)
reset_func() resetFunc()
else: else:
critical_error_message_box(dlg_title, err_text) critical_error_message_box(dlgTitle, err_text)
else: else:
critical_error_message_box(dlg_title, UiStrings().NISs) critical_error_message_box(dlgTitle, UiStrings().NISs)
def resetAuthors(self): def resetAuthors(self):
""" """
@ -157,34 +165,34 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog):
book_name.setData(QtCore.Qt.UserRole, QtCore.QVariant(book.id)) book_name.setData(QtCore.Qt.UserRole, QtCore.QVariant(book.id))
self.booksListWidget.addItem(book_name) self.booksListWidget.addItem(book_name)
def checkAuthor(self, new_author, edit=False): def checkAuthor(self, newAuthor, edit=False):
""" """
Returns *False* if the given Author already exists, otherwise *True*. Returns *False* if the given Author already exists, otherwise *True*.
""" """
authors = self.manager.get_all_objects(Author, authors = self.manager.get_all_objects(Author,
and_(Author.first_name == new_author.first_name, and_(Author.first_name == newAuthor.first_name,
Author.last_name == new_author.last_name, Author.last_name == newAuthor.last_name,
Author.display_name == new_author.display_name)) Author.display_name == newAuthor.display_name))
return self.__checkObject(authors, new_author, edit) return self.__checkObject(authors, newAuthor, edit)
def checkTopic(self, new_topic, edit=False): def checkTopic(self, newTopic, edit=False):
""" """
Returns *False* if the given Topic already exists, otherwise *True*. Returns *False* if the given Topic already exists, otherwise *True*.
""" """
topics = self.manager.get_all_objects(Topic, topics = self.manager.get_all_objects(Topic,
Topic.name == new_topic.name) Topic.name == newTopic.name)
return self.__checkObject(topics, new_topic, edit) return self.__checkObject(topics, newTopic, edit)
def checkBook(self, new_book, edit=False): def checkBook(self, newBook, edit=False):
""" """
Returns *False* if the given Topic already exists, otherwise *True*. Returns *False* if the given Topic already exists, otherwise *True*.
""" """
books = self.manager.get_all_objects(Book, books = self.manager.get_all_objects(Book,
and_(Book.name == new_book.name, and_(Book.name == newBook.name,
Book.publisher == new_book.publisher)) Book.publisher == newBook.publisher))
return self.__checkObject(books, new_book, edit) return self.__checkObject(books, newBook, edit)
def __checkObject(self, objects, new_object, edit): def __checkObject(self, objects, newObject, edit):
""" """
Utility method to check for an existing object. Utility method to check for an existing object.
@ -196,7 +204,7 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog):
# not return False when nothing has changed. # not return False when nothing has changed.
if edit: if edit:
for object in objects: for object in objects:
if object.id != new_object.id: if object.id != newObject.id:
return False return False
return True return True
else: else:
@ -275,7 +283,8 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog):
if self.checkAuthor(author, True): if self.checkAuthor(author, True):
if self.manager.save_object(author): if self.manager.save_object(author):
self.resetAuthors() self.resetAuthors()
Receiver.send_message(u'songs_load_list') if not self.fromSongEdit:
Receiver.send_message(u'songs_load_list')
else: else:
critical_error_message_box( critical_error_message_box(
message=translate('SongsPlugin.SongMaintenanceForm', message=translate('SongsPlugin.SongMaintenanceForm',
@ -373,75 +382,76 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog):
Receiver.send_message(u'cursor_busy') Receiver.send_message(u'cursor_busy')
merge(dbObject) merge(dbObject)
reset() reset()
Receiver.send_message(u'songs_load_list') if not self.fromSongEdit:
Receiver.send_message(u'songs_load_list')
Receiver.send_message(u'cursor_normal') Receiver.send_message(u'cursor_normal')
def mergeAuthors(self, old_author): def mergeAuthors(self, oldAuthor):
""" """
Merges two authors into one author. Merges two authors into one author.
``old_author`` ``oldAuthor``
The object, which was edited, that will be deleted The object, which was edited, that will be deleted
""" """
# Find the duplicate. # Find the duplicate.
existing_author = self.manager.get_object_filtered(Author, existing_author = self.manager.get_object_filtered(Author,
and_(Author.first_name == old_author.first_name, and_(Author.first_name == oldAuthor.first_name,
Author.last_name == old_author.last_name, Author.last_name == oldAuthor.last_name,
Author.display_name == old_author.display_name, Author.display_name == oldAuthor.display_name,
Author.id != old_author.id)) Author.id != oldAuthor.id))
# Find the songs, which have the old_author as author. # Find the songs, which have the oldAuthor as author.
songs = self.manager.get_all_objects(Song, songs = self.manager.get_all_objects(Song,
Song.authors.contains(old_author)) Song.authors.contains(oldAuthor))
for song in songs: for song in songs:
# We check if the song has already existing_author as author. If # We check if the song has already existing_author as author. If
# that is not the case we add it. # that is not the case we add it.
if existing_author not in song.authors: if existing_author not in song.authors:
song.authors.append(existing_author) song.authors.append(existing_author)
song.authors.remove(old_author) song.authors.remove(oldAuthor)
self.manager.save_object(song) self.manager.save_object(song)
self.manager.delete_object(Author, old_author.id) self.manager.delete_object(Author, oldAuthor.id)
def mergeTopics(self, old_topic): def mergeTopics(self, oldTopic):
""" """
Merges two topics into one topic. Merges two topics into one topic.
``old_topic`` ``oldTopic``
The object, which was edited, that will be deleted The object, which was edited, that will be deleted
""" """
# Find the duplicate. # Find the duplicate.
existing_topic = self.manager.get_object_filtered(Topic, existing_topic = self.manager.get_object_filtered(Topic,
and_(Topic.name == old_topic.name, Topic.id != old_topic.id)) and_(Topic.name == oldTopic.name, Topic.id != oldTopic.id))
# Find the songs, which have the old_topic as topic. # Find the songs, which have the oldTopic as topic.
songs = self.manager.get_all_objects(Song, songs = self.manager.get_all_objects(Song,
Song.topics.contains(old_topic)) Song.topics.contains(oldTopic))
for song in songs: for song in songs:
# We check if the song has already existing_topic as topic. If that # We check if the song has already existing_topic as topic. If that
# is not the case we add it. # is not the case we add it.
if existing_topic not in song.topics: if existing_topic not in song.topics:
song.topics.append(existing_topic) song.topics.append(existing_topic)
song.topics.remove(old_topic) song.topics.remove(oldTopic)
self.manager.save_object(song) self.manager.save_object(song)
self.manager.delete_object(Topic, old_topic.id) self.manager.delete_object(Topic, oldTopic.id)
def mergeBooks(self, old_book): def mergeBooks(self, oldBook):
""" """
Merges two books into one book. Merges two books into one book.
``old_book`` ``oldBook``
The object, which was edited, that will be deleted The object, which was edited, that will be deleted
""" """
# Find the duplicate. # Find the duplicate.
existing_book = self.manager.get_object_filtered(Book, existing_book = self.manager.get_object_filtered(Book,
and_(Book.name == old_book.name, and_(Book.name == oldBook.name,
Book.publisher == old_book.publisher, Book.publisher == oldBook.publisher,
Book.id != old_book.id)) Book.id != oldBook.id))
# Find the songs, which have the old_book as book. # Find the songs, which have the oldBook as book.
songs = self.manager.get_all_objects(Song, songs = self.manager.get_all_objects(Song,
Song.song_book_id == old_book.id) Song.song_book_id == oldBook.id)
for song in songs: for song in songs:
song.song_book_id = existing_book.id song.song_book_id = existing_book.id
self.manager.save_object(song) self.manager.save_object(song)
self.manager.delete_object(Book, old_book.id) self.manager.delete_object(Book, oldBook.id)
def onAuthorDeleteButtonClicked(self): def onAuthorDeleteButtonClicked(self):
""" """