Merge branch. Fixed conflict

This commit is contained in:
Stevan Pettit 2012-06-09 00:23:24 -04:00
commit cb50427af1
12 changed files with 183 additions and 132 deletions

View File

@ -163,143 +163,144 @@ class ImageManager(QtCore.QObject):
def __init__(self):
QtCore.QObject.__init__(self)
current_screen = ScreenList().current
self.width = current_screen[u'size'].width()
self.height = current_screen[u'size'].height()
currentScreen = ScreenList().current
self.width = currentScreen[u'size'].width()
self.height = currentScreen[u'size'].height()
self._cache = {}
self._imageThread = ImageThread(self)
self._conversion_queue = PriorityQueue()
self.imageThread = ImageThread(self)
self._conversionQueue = PriorityQueue()
self.stopManager = False
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.
"""
log.debug(u'update_display')
current_screen = ScreenList().current
self.width = current_screen[u'size'].width()
self.height = current_screen[u'size'].height()
log.debug(u'updateDisplay')
currentScreen = ScreenList().current
self.width = currentScreen[u'size'].width()
self.height = currentScreen[u'size'].height()
# Mark the images as dirty for a rebuild by setting the image and byte
# stream to None.
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.
"""
log.debug(u'update_images')
log.debug(u'updateImages')
# Mark the images as dirty for a rebuild by setting the image and byte
# stream to None.
for image in self._cache.values():
if image.source == image_type:
if image.source == imageType:
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.
"""
log.debug(u'update_images')
log.debug(u'updateImage')
# Mark the images as dirty for a rebuild by setting the image and byte
# stream to None.
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
self._reset_image(image)
self._resetImage(image)
def _reset_image(self, image):
def _resetImage(self, image):
image.image = 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
"""
# We want only one thread.
if not self._imageThread.isRunning():
self._imageThread.start()
if not self.imageThread.isRunning():
self.imageThread.start()
def get_image(self, name):
def getImage(self, name):
"""
Return the ``QImage`` from the cache. If not present wait for the
background thread to process it.
"""
log.debug(u'get_image %s' % name)
log.debug(u'getImage %s' % name)
image = self._cache[name]
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
self.process_updates()
self.processUpdates()
while image.image is None:
log.debug(u'get_image - waiting')
log.debug(u'getImage - waiting')
time.sleep(0.1)
elif image.image_bytes is None:
# Set the priority to Low, because the image was requested but the
# byte stream was not generated yet. However, we only need to do
# this, when the image was generated before it was requested
# (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
def get_image_bytes(self, name):
def getImageBytes(self, name):
"""
Returns the byte string for an image. If not present wait for the
background thread to process it.
"""
log.debug(u'get_image_bytes %s' % name)
log.debug(u'getImageBytes %s' % name)
image = self._cache[name]
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
self.process_updates()
self.processUpdates()
while image.image_bytes is None:
log.debug(u'get_image_bytes - waiting')
log.debug(u'getImageBytes - waiting')
time.sleep(0.1)
return image.image_bytes
def del_image(self, name):
def deleteImage(self, name):
"""
Delete the Image from the cache.
"""
log.debug(u'del_image %s' % name)
log.debug(u'deleteImage %s' % name)
if name in self._cache:
self._conversion_queue.remove(self._cache[name])
self._conversionQueue.remove(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.
"""
log.debug(u'add_image %s:%s' % (name, path))
log.debug(u'addImage %s:%s' % (name, path))
if not name in self._cache:
image = Image(name, path, source, background)
self._cache[name] = image
self._conversion_queue.put(
self._conversionQueue.put(
(image.priority, image.secondary_priority, image))
else:
log.debug(u'Image in cache %s:%s' % (name, path))
# We want only one thread.
if not self._imageThread.isRunning():
self._imageThread.start()
if not self.imageThread.isRunning():
self.imageThread.start()
def _process(self):
"""
Controls the processing called from a ``QtCore.QThread``.
"""
log.debug(u'_process - started')
while not self._conversion_queue.empty():
self._process_cache()
while not self._conversionQueue.empty() and not self.stopManager:
self._processCache()
log.debug(u'_process - ended')
def _process_cache(self):
def _processCache(self):
"""
Actually does the work.
"""
log.debug(u'_process_cache')
image = self._conversion_queue.get()[2]
log.debug(u'_processCache')
image = self._conversionQueue.get()[2]
# Generate the QImage for the image.
if image.image is None:
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
# more important images first.
if image.priority == Priority.Normal:
self._conversion_queue.modify_priority(image, Priority.Lowest)
self._conversionQueue.modify_priority(image, Priority.Lowest)
return
# 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
# Normal priority. We stop here as we need to process more important
# images first.
elif image.priority == Priority.High:
self._conversion_queue.modify_priority(image, Priority.Low)
self._conversionQueue.modify_priority(image, Priority.Low)
return
# Generate the byte stream for the image.
if image.image_bytes is None:

View File

@ -109,6 +109,7 @@ class Renderer(object):
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):
"""
@ -119,6 +120,21 @@ class Renderer(object):
"""
self.service_theme = service_theme
self.theme_data = None
self._cache_background_image(self.themeManager.getThemeData
(service_theme))
def _cache_background_image(self, temp_theme):
"""
Adds a background image to the image cache if necessary.
``temp_theme``
The theme object containing the theme data.
"""
# if No file do not update cache
if temp_theme.background_filename:
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):
"""
@ -163,11 +179,7 @@ class Renderer(object):
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))
self._cache_background_image(self.theme_data)
return self._rect, self._rect_footer
def generate_preview(self, theme_data, force_page=False):
@ -192,7 +204,7 @@ class Renderer(object):
# make big page for theme edit dialog to get line count
serviceItem.add_from_text(u'', VERSE_FOR_LINE_COUNT)
else:
self.imageManager.del_image(theme_data.theme_name)
self.imageManager.deleteImage(theme_data.theme_name)
serviceItem.add_from_text(u'', VERSE)
serviceItem.renderer = self
serviceItem.raw_footer = FOOTER

View File

@ -211,7 +211,7 @@ class ServiceItem(object):
self.image_border = background
self.service_item_type = ServiceItemType.Image
self._raw_frames.append({u'title': title, u'path': path})
self.renderer.imageManager.add_image(title, path, u'image',
self.renderer.imageManager.addImage(title, path, u'image',
self.image_border)
self._new_item()

View File

@ -444,6 +444,20 @@ class ThemeXML(object):
element.appendChild(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):
"""
Dump the XML to file used for debugging

View File

@ -278,7 +278,7 @@ class MainDisplay(Display):
"""
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'):
self.override[u'image'] = name
self.override[u'theme'] = self.serviceItem.themedata.theme_name
@ -298,7 +298,7 @@ class MainDisplay(Display):
The name of the image to be displayed.
"""
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.displayImage(image)
@ -383,14 +383,14 @@ class MainDisplay(Display):
else:
# replace the background
background = self.imageManager. \
get_image_bytes(self.override[u'image'])
getImageBytes(self.override[u'image'])
self.setTransparency(self.serviceItem.themedata.background_type ==
BackgroundType.to_string(BackgroundType.Transparent))
if self.serviceItem.themedata.background_filename:
self.serviceItem.bg_image_bytes = self.imageManager. \
get_image_bytes(self.serviceItem.themedata.theme_name)
getImageBytes(self.serviceItem.themedata.theme_name)
if image:
image_bytes = self.imageManager.get_image_bytes(image)
image_bytes = self.imageManager.getImageBytes(image)
else:
image_bytes = None
html = build_html(self.serviceItem, self.screen, self.isLive,

View File

@ -30,6 +30,7 @@ import os
import sys
import shutil
from tempfile import gettempdir
import time
from datetime import datetime
from PyQt4 import QtCore, QtGui
@ -1122,7 +1123,7 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
"""
log.debug(u'screenChanged')
Receiver.send_message(u'cursor_busy')
self.imageManager.update_display()
self.imageManager.updateDisplay()
self.renderer.update_display()
self.previewController.screenSizeChanged()
self.liveController.screenSizeChanged()
@ -1141,6 +1142,7 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
return
# If we just did a settings import, close without saving changes.
if self.settingsImported:
self.cleanUp(False)
event.accept()
if self.serviceManagerContents.isModified():
ret = self.serviceManagerContents.saveModifiedService()
@ -1163,8 +1165,7 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
translate('OpenLP.MainWindow',
'Are you sure you want to close OpenLP?'),
QtGui.QMessageBox.StandardButtons(
QtGui.QMessageBox.Yes |
QtGui.QMessageBox.No),
QtGui.QMessageBox.Yes | QtGui.QMessageBox.No),
QtGui.QMessageBox.Yes)
if ret == QtGui.QMessageBox.Yes:
self.cleanUp()
@ -1175,21 +1176,29 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
self.cleanUp()
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
self.serviceManagerContents.cleanUp()
if Settings().value(u'advanced/save current plugin',
QtCore.QVariant(False)).toBool():
Settings().setValue(u'advanced/current media plugin',
if save_settings:
if Settings().value(u'advanced/save current plugin',
QtCore.QVariant(False)).toBool():
Settings().setValue(u'advanced/current media plugin',
QtCore.QVariant(self.mediaToolBox.currentIndex()))
# Call the cleanup method to shutdown plugins.
log.info(u'cleanup plugins')
self.pluginManager.finalise_plugins()
# Save settings
self.saveSettings()
if save_settings:
# Save settings
self.saveSettings()
# Close down the display
if self.liveController.display:
self.liveController.display.close()

View File

@ -860,8 +860,8 @@ class SlideController(Controller):
# If current slide set background to image
if framenumber == slideno:
self.serviceItem.bg_image_bytes = \
self.imageManager.get_image_bytes(frame[u'title'])
image = self.imageManager.get_image(frame[u'title'])
self.imageManager.getImageBytes(frame[u'title'])
image = self.imageManager.getImage(frame[u'title'])
label.setPixmap(QtGui.QPixmap.fromImage(image))
self.previewListWidget.setCellWidget(framenumber, 0, label)
slideHeight = width * self.parent().renderer.screen_ratio

View File

@ -258,6 +258,7 @@ class ThemeManager(QtGui.QWidget):
editing form for the user to make their customisations.
"""
theme = ThemeXML()
theme.set_default_header_footer()
self.themeForm.theme = theme
self.themeForm.exec_()
@ -664,9 +665,9 @@ class ThemeManager(QtGui.QWidget):
self._writeTheme(theme, image_from, image_to)
if theme.background_type == \
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))
self.mainwindow.imageManager.process_updates()
self.mainwindow.imageManager.processUpdates()
self.loadThemes()
def _writeTheme(self, theme, image_from, image_to):

View File

@ -97,4 +97,4 @@ class ImagePlugin(Plugin):
"""
background = QtGui.QColor(Settings().value(self.settingsSection
+ 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="" />
</div>
<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 data-role="page" id="options">

View File

@ -707,7 +707,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
text = unicode(self.songBookComboBox.currentText())
if item == 0 and text:
temp_song_book = text
self.mediaitem.songMaintenanceForm.exec_()
self.mediaitem.songMaintenanceForm.exec_(True)
self.loadAuthors()
self.loadBooks()
self.loadTopics()
@ -865,12 +865,16 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
for row in xrange(self.authorsListView.count()):
item = self.authorsListView.item(row)
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 = []
for row in xrange(self.topicsListView.count()):
item = self.topicsListView.item(row)
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.
clean_song(self.manager, 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)'),
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.resetAuthors()
self.resetTopics()
@ -103,20 +111,20 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog):
else:
return -1
def _deleteItem(self, item_class, list_widget, reset_func, dlg_title,
def _deleteItem(self, itemClass, listWidget, resetFunc, dlgTitle,
del_text, err_text):
item_id = self._getCurrentItemId(list_widget)
item_id = self._getCurrentItemId(listWidget)
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 critical_error_message_box(dlg_title, del_text, self,
if critical_error_message_box(dlgTitle, del_text, self,
True) == QtGui.QMessageBox.Yes:
self.manager.delete_object(item_class, item.id)
reset_func()
self.manager.delete_object(itemClass, item.id)
resetFunc()
else:
critical_error_message_box(dlg_title, err_text)
critical_error_message_box(dlgTitle, err_text)
else:
critical_error_message_box(dlg_title, UiStrings().NISs)
critical_error_message_box(dlgTitle, UiStrings().NISs)
def resetAuthors(self):
"""
@ -157,34 +165,34 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog):
book_name.setData(QtCore.Qt.UserRole, QtCore.QVariant(book.id))
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*.
"""
authors = self.manager.get_all_objects(Author,
and_(Author.first_name == new_author.first_name,
Author.last_name == new_author.last_name,
Author.display_name == new_author.display_name))
return self.__checkObject(authors, new_author, edit)
and_(Author.first_name == newAuthor.first_name,
Author.last_name == newAuthor.last_name,
Author.display_name == newAuthor.display_name))
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*.
"""
topics = self.manager.get_all_objects(Topic,
Topic.name == new_topic.name)
return self.__checkObject(topics, new_topic, edit)
Topic.name == newTopic.name)
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*.
"""
books = self.manager.get_all_objects(Book,
and_(Book.name == new_book.name,
Book.publisher == new_book.publisher))
return self.__checkObject(books, new_book, edit)
and_(Book.name == newBook.name,
Book.publisher == newBook.publisher))
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.
@ -196,7 +204,7 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog):
# not return False when nothing has changed.
if edit:
for object in objects:
if object.id != new_object.id:
if object.id != newObject.id:
return False
return True
else:
@ -275,7 +283,8 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog):
if self.checkAuthor(author, True):
if self.manager.save_object(author):
self.resetAuthors()
Receiver.send_message(u'songs_load_list')
if not self.fromSongEdit:
Receiver.send_message(u'songs_load_list')
else:
critical_error_message_box(
message=translate('SongsPlugin.SongMaintenanceForm',
@ -373,75 +382,76 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog):
Receiver.send_message(u'cursor_busy')
merge(dbObject)
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')
def mergeAuthors(self, old_author):
def mergeAuthors(self, oldAuthor):
"""
Merges two authors into one author.
``old_author``
``oldAuthor``
The object, which was edited, that will be deleted
"""
# Find the duplicate.
existing_author = self.manager.get_object_filtered(Author,
and_(Author.first_name == old_author.first_name,
Author.last_name == old_author.last_name,
Author.display_name == old_author.display_name,
Author.id != old_author.id))
# Find the songs, which have the old_author as author.
and_(Author.first_name == oldAuthor.first_name,
Author.last_name == oldAuthor.last_name,
Author.display_name == oldAuthor.display_name,
Author.id != oldAuthor.id))
# Find the songs, which have the oldAuthor as author.
songs = self.manager.get_all_objects(Song,
Song.authors.contains(old_author))
Song.authors.contains(oldAuthor))
for song in songs:
# We check if the song has already existing_author as author. If
# that is not the case we add it.
if existing_author not in song.authors:
song.authors.append(existing_author)
song.authors.remove(old_author)
song.authors.remove(oldAuthor)
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.
``old_topic``
``oldTopic``
The object, which was edited, that will be deleted
"""
# Find the duplicate.
existing_topic = self.manager.get_object_filtered(Topic,
and_(Topic.name == old_topic.name, Topic.id != old_topic.id))
# Find the songs, which have the old_topic as topic.
and_(Topic.name == oldTopic.name, Topic.id != oldTopic.id))
# Find the songs, which have the oldTopic as topic.
songs = self.manager.get_all_objects(Song,
Song.topics.contains(old_topic))
Song.topics.contains(oldTopic))
for song in songs:
# We check if the song has already existing_topic as topic. If that
# is not the case we add it.
if existing_topic not in song.topics:
song.topics.append(existing_topic)
song.topics.remove(old_topic)
song.topics.remove(oldTopic)
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.
``old_book``
``oldBook``
The object, which was edited, that will be deleted
"""
# Find the duplicate.
existing_book = self.manager.get_object_filtered(Book,
and_(Book.name == old_book.name,
Book.publisher == old_book.publisher,
Book.id != old_book.id))
# Find the songs, which have the old_book as book.
and_(Book.name == oldBook.name,
Book.publisher == oldBook.publisher,
Book.id != oldBook.id))
# Find the songs, which have the oldBook as book.
songs = self.manager.get_all_objects(Song,
Song.song_book_id == old_book.id)
Song.song_book_id == oldBook.id)
for song in songs:
song.song_book_id = existing_book.id
self.manager.save_object(song)
self.manager.delete_object(Book, old_book.id)
self.manager.delete_object(Book, oldBook.id)
def onAuthorDeleteButtonClicked(self):
"""