diff --git a/openlp/core/lib/imagemanager.py b/openlp/core/lib/imagemanager.py index 47a7ed3f6..1e2a3a698 100644 --- a/openlp/core/lib/imagemanager.py +++ b/openlp/core/lib/imagemanager.py @@ -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: diff --git a/openlp/core/lib/renderer.py b/openlp/core/lib/renderer.py index 9705e13df..e782585d0 100644 --- a/openlp/core/lib/renderer.py +++ b/openlp/core/lib/renderer.py @@ -132,7 +132,7 @@ class Renderer(object): """ # if No file do not update cache if temp_theme.background_filename: - self.imageManager.add_image(temp_theme.theme_name, + self.imageManager.addImage(temp_theme.theme_name, temp_theme.background_filename, u'theme', QtGui.QColor(temp_theme.background_border_color)) @@ -204,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 diff --git a/openlp/core/lib/serviceitem.py b/openlp/core/lib/serviceitem.py index 36314ac7f..0cf5626ff 100644 --- a/openlp/core/lib/serviceitem.py +++ b/openlp/core/lib/serviceitem.py @@ -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() diff --git a/openlp/core/ui/maindisplay.py b/openlp/core/ui/maindisplay.py index a04203387..b642240a3 100644 --- a/openlp/core/ui/maindisplay.py +++ b/openlp/core/ui/maindisplay.py @@ -277,7 +277,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 @@ -297,7 +297,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) @@ -382,14 +382,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, diff --git a/openlp/core/ui/mainwindow.py b/openlp/core/ui/mainwindow.py index fe0d69c32..3923cad26 100644 --- a/openlp/core/ui/mainwindow.py +++ b/openlp/core/ui/mainwindow.py @@ -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 @@ -1121,7 +1122,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() @@ -1140,6 +1141,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() @@ -1162,8 +1164,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() @@ -1174,21 +1175,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 QtCore.QSettings().value(u'advanced/save current plugin', - QtCore.QVariant(False)).toBool(): - QtCore.QSettings().setValue(u'advanced/current media plugin', - QtCore.QVariant(self.mediaToolBox.currentIndex())) + if save_settings: + if QtCore.QSettings().value(u'advanced/save current plugin', + QtCore.QVariant(False)).toBool(): + QtCore.QSettings().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() diff --git a/openlp/core/ui/slidecontroller.py b/openlp/core/ui/slidecontroller.py index 4224222ca..f9e5656c5 100644 --- a/openlp/core/ui/slidecontroller.py +++ b/openlp/core/ui/slidecontroller.py @@ -859,8 +859,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 diff --git a/openlp/core/ui/thememanager.py b/openlp/core/ui/thememanager.py index ea18379e2..09b54fc07 100644 --- a/openlp/core/ui/thememanager.py +++ b/openlp/core/ui/thememanager.py @@ -664,9 +664,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): diff --git a/openlp/plugins/images/imageplugin.py b/openlp/plugins/images/imageplugin.py index 53f825916..a72eab1af 100644 --- a/openlp/plugins/images/imageplugin.py +++ b/openlp/plugins/images/imageplugin.py @@ -96,4 +96,4 @@ class ImagePlugin(Plugin): """ background = QtGui.QColor(QtCore.QSettings().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) diff --git a/openlp/plugins/songs/forms/editsongform.py b/openlp/plugins/songs/forms/editsongform.py index 195dda729..61043f805 100644 --- a/openlp/plugins/songs/forms/editsongform.py +++ b/openlp/plugins/songs/forms/editsongform.py @@ -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) diff --git a/openlp/plugins/songs/forms/songmaintenanceform.py b/openlp/plugins/songs/forms/songmaintenanceform.py index 7ba49a102..22210f552 100644 --- a/openlp/plugins/songs/forms/songmaintenanceform.py +++ b/openlp/plugins/songs/forms/songmaintenanceform.py @@ -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): """