From 1b536ab0c882707b0cfacba569ebb189c7e85118 Mon Sep 17 00:00:00 2001 From: andreas Date: Sat, 10 Jul 2010 19:02:42 +0200 Subject: [PATCH 01/21] Books, Topics and Authors redundancy imporvements. --- .../songs/forms/songmaintenanceform.py | 174 +++++++++++++----- 1 file changed, 127 insertions(+), 47 deletions(-) diff --git a/openlp/plugins/songs/forms/songmaintenanceform.py b/openlp/plugins/songs/forms/songmaintenanceform.py index 54a8d539f..116bb7843 100644 --- a/openlp/plugins/songs/forms/songmaintenanceform.py +++ b/openlp/plugins/songs/forms/songmaintenanceform.py @@ -97,6 +97,9 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog): QtGui.QMessageBox.critical(self, dlg_title, sel_text) def resetAuthors(self): + """ + Reloads the Authors list. + """ self.AuthorsListWidget.clear() authors = self.songmanager.get_all_objects(Author, Author.display_name) for author in authors: @@ -109,6 +112,9 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog): self.AuthorsListWidget.addItem(author_name) def resetTopics(self): + """ + Reloads the Topics list. + """ self.TopicsListWidget.clear() topics = self.songmanager.get_all_objects(Topic, Topic.name) for topic in topics: @@ -117,13 +123,89 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog): self.TopicsListWidget.addItem(topic_name) def resetBooks(self): + """ + Reloads the Books list. + """ self.BooksListWidget.clear() books = self.songmanager.get_all_objects(Book, Book.name) for book in books: - book_name = QtGui.QListWidgetItem(book.name) + book_name = QtGui.QListWidgetItem(u'%s (%s)' % (book.name, + book.publisher)) book_name.setData(QtCore.Qt.UserRole, QtCore.QVariant(book.id)) self.BooksListWidget.addItem(book_name) + def checkAuthor(self, new_author, edit=False): + """ + Returns True when the given Author is already in the list elsewise False. + """ + new_author_first_name = new_author.first_name + new_author_last_name = new_author.last_name + new_author_display_name = new_author.display_name + authors = self.songmanager.get_all_objects(Author) + author_exsists = False + for author in authors: + author_fist_name = author.first_name + author_last_name = author.last_name + author_display_name = author.display_name + if author_fist_name == new_author_first_name and \ + author_last_name == new_author_last_name and \ + author_display_name == new_author_display_name: + author_exsists = True + #If we edit an exsisting Author, we need to make sure that we do + #not return True when nothing has changed (because this would + #cause an error message later on) + if edit: + new_author_id = new_author.id + author_id = author.id + if new_author_id == author_id: + author_exsists = False + return author_exsists + + def checkTopic(self, new_topic, edit=False): + """ + Returns True when the given Topic is already in the list elsewise False. + """ + new_topic_name = new_topic.name + topics = self.songmanager.get_all_objects(Topic) + topic_exsists = False + for topic in topics: + topic_name = topic.name + if topic_name == new_topic_name: + topic_exsists = True + #If we edit an exsisting Topic, we need to make sure that we do + #not return True when nothing has changed (because this would + #cause an error message later on) + if edit: + new_topic_id = new_topic.id + topic_id = topic.id + if new_topic_id == topic_id: + topic_exsists = False + return topic_exsists + + def checkBook(self, new_book, edit=False): + """ + Returns True when the given Book is already in the list elsewise False. + """ + new_book_name = new_book.name + new_book_publisher = new_book.publisher + books = self.songmanager.get_all_objects(Book) + book_exsists = False + for book in books: + book_name = book.name + book_publisher = book.publisher + if book_publisher == new_book_publisher and \ + book_name == new_book_name: + book_exsists = True + #If we edit an exsisting Book, we need to make sure that we do + #not return True when nothing has changed (because this would + #cause an error message later on) + if edit: + new_book_id = new_book.id + book_id = book.id + if new_book_id == book_id: + book_exsists = False + return book_exsists + def onAuthorAddButtonClick(self): self.authorform.setAutoDisplayName(True) if self.authorform.exec_(): @@ -131,40 +213,40 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog): first_name=unicode(self.authorform.FirstNameEdit.text()), last_name=unicode(self.authorform.LastNameEdit.text()), display_name=unicode(self.authorform.DisplayEdit.text())) - if self.songmanager.save_object(author): + if self.checkAuthor(author) is False and \ + self.songmanager.save_object(author): self.resetAuthors() else: - QtGui.QMessageBox.critical( - self, translate('SongsPlugin.SongMaintenanceForm', - 'Error'), + QtGui.QMessageBox.critical(self, + translate('SongsPlugin.SongMaintenanceForm', 'Error'), translate('SongsPlugin.SongMaintenanceForm', - 'Couldn\'t add your author.')) + 'Could not add your author.')) def onTopicAddButtonClick(self): if self.topicform.exec_(): topic = Topic.populate(name=unicode(self.topicform.NameEdit.text())) - if self.songmanager.save_object(topic): + if self.checkTopic(topic) is False and \ + self.songmanager.save_object(topic): self.resetTopics() else: - QtGui.QMessageBox.critical( - self, translate('SongsPlugin.SongMaintenanceForm', - 'Error'), + QtGui.QMessageBox.critical(self, + translate('SongsPlugin.SongMaintenanceForm', 'Error'), translate('SongsPlugin.SongMaintenanceForm', - 'Couldn\'t add your topic.')) + 'Could not add your topic.')) def onBookAddButtonClick(self): if self.bookform.exec_(): book = Book.populate( name=unicode(self.bookform.NameEdit.text()), publisher=unicode(self.bookform.PublisherEdit.text())) - if self.songmanager.save_object(book): + if self.checkBook(book) is False and \ + self.songmanager.save_object(book): self.resetBooks() else: - QtGui.QMessageBox.critical( - self, translate('SongsPlugin.SongMaintenanceForm', - 'Error'), + QtGui.QMessageBox.critical(self, + translate('SongsPlugin.SongMaintenanceForm', 'Error'), translate('SongsPlugin.SongMaintenanceForm', - 'Couldn\'t add your book.')) + 'Could not add your book.')) def onAuthorEditButtonClick(self): author_id = self._getCurrentItemId(self.AuthorsListWidget) @@ -187,14 +269,14 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog): author.last_name = unicode(self.authorform.LastNameEdit.text()) author.display_name = unicode( self.authorform.DisplayEdit.text()) - if self.songmanager.save_object(author): + if self.checkAuthor(author, True) is False and \ + self.songmanager.save_object(author): self.resetAuthors() else: - QtGui.QMessageBox.critical( - self, translate('SongsPlugin.SongMaintenanceForm', - 'Error'), + QtGui.QMessageBox.critical(self, + translate('SongsPlugin.SongMaintenanceForm', 'Error'), translate('SongsPlugin.SongMaintenanceForm', - 'Couldn\'t save your author.')) + 'Could not save your author.')) def onTopicEditButtonClick(self): topic_id = self._getCurrentItemId(self.TopicsListWidget) @@ -203,14 +285,14 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog): self.topicform.NameEdit.setText(topic.name) if self.topicform.exec_(False): topic.name = unicode(self.topicform.NameEdit.text()) - if self.songmanager.save_object(topic): + if self.checkTopic(topic, True) is False and \ + self.songmanager.save_object(topic): self.resetTopics() else: - QtGui.QMessageBox.critical( - self, translate('SongsPlugin.SongMaintenanceForm', - 'Error'), + QtGui.QMessageBox.critical(self, + translate('SongsPlugin.SongMaintenanceForm', 'Error'), translate('SongsPlugin.SongMaintenanceForm', - 'Couldn\'t save your topic.')) + 'Could not save your topic.')) def onBookEditButtonClick(self): book_id = self._getCurrentItemId(self.BooksListWidget) @@ -221,14 +303,14 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog): if self.bookform.exec_(False): book.name = unicode(self.bookform.NameEdit.text()) book.publisher = unicode(self.bookform.PublisherEdit.text()) - if self.songmanager.save_object(book): + if self.checkBook(book, True) is False and \ + self.songmanager.save_object(book): self.resetBooks() else: - QtGui.QMessageBox.critical( - self, translate('SongsPlugin.SongMaintenanceForm', - 'Error'), + QtGui.QMessageBox.critical(self, + translate('SongsPlugin.SongMaintenanceForm', 'Error'), translate('SongsPlugin.SongMaintenanceForm', - 'Couldn\'t save your book.')) + 'Could not save your book.')) def onAuthorDeleteButtonClick(self): """ @@ -236,13 +318,12 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog): """ self._deleteItem(Author, self.AuthorsListWidget, self.resetAuthors, translate('SongsPlugin.SongMaintenanceForm', 'Delete Author'), - translate('SongsPlugin.SongMaintenanceForm', - 'Are you sure you want to delete the selected author?'), translate('SongsPlugin.SongMaintenanceForm', - 'This author can\'t be deleted, they are currently ' - 'assigned to at least one song.'), + 'Are you sure you want to delete the selected author?'), translate('SongsPlugin.SongMaintenanceForm', - 'No author selected!')) + 'This author ca not be deleted, they are currently ' + 'assigned to at least one song.'), + translate('SongsPlugin.SongMaintenanceForm', 'No author selected!')) def onTopicDeleteButtonClick(self): """ @@ -250,13 +331,12 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog): """ self._deleteItem(Topic, self.TopicsListWidget, self.resetTopics, translate('SongsPlugin.SongMaintenanceForm', 'Delete Topic'), - translate('SongsPlugin.SongMaintenanceForm', - 'Are you sure you want to delete the selected topic?'), - translate('SongsPlugin.SongMaintenanceForm', - 'This topic can\'t be deleted, it is currently ' - 'assigned to at least one song.'), translate('SongsPlugin.SongMaintenanceForm', - 'No topic selected!')) + 'Are you sure you want to delete the selected topic?'), + translate('SongsPlugin.SongMaintenanceForm', + 'This topic cannot be deleted, it is currently ' + 'assigned to at least one song.'), + translate('SongsPlugin.SongMaintenanceForm', 'No topic selected!')) def onBookDeleteButtonClick(self): """ @@ -265,8 +345,8 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog): self._deleteItem(Book, self.BooksListWidget, self.resetBooks, translate('SongsPlugin.SongMaintenanceForm', 'Delete Book'), translate('SongsPlugin.SongMaintenanceForm', - 'Are you sure you want to delete the selected book?'), - translate('SongsPlugin.SongMaintenanceForm', - 'This book can\'t be deleted, it is currently ' - 'assigned to at least one song.'), - translate('SongsPlugin.SongMaintenanceForm', u'No book selected!')) + 'Are you sure you want to delete the selected book?'), + translate('SongsPlugin.SongMaintenanceForm', + 'This book cannot be deleted, it is currently ' + 'assigned to at least one song.'), + translate('SongsPlugin.SongMaintenanceForm', 'No book selected!')) From 639ce2bfbcb341cd35916256ef5ce110f26243b0 Mon Sep 17 00:00:00 2001 From: andreas Date: Sat, 10 Jul 2010 19:30:01 +0200 Subject: [PATCH 02/21] fixed double whitespace --- openlp/plugins/songs/forms/songmaintenanceform.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openlp/plugins/songs/forms/songmaintenanceform.py b/openlp/plugins/songs/forms/songmaintenanceform.py index 116bb7843..2ac5f93a7 100644 --- a/openlp/plugins/songs/forms/songmaintenanceform.py +++ b/openlp/plugins/songs/forms/songmaintenanceform.py @@ -134,7 +134,7 @@ 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, new_author, edit=False): """ Returns True when the given Author is already in the list elsewise False. """ @@ -182,7 +182,7 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog): topic_exsists = False return topic_exsists - def checkBook(self, new_book, edit=False): + def checkBook(self, new_book, edit=False): """ Returns True when the given Book is already in the list elsewise False. """ From d6fe2d1c9f18b35883a9dcf66124360d80391047 Mon Sep 17 00:00:00 2001 From: andreas Date: Sat, 10 Jul 2010 20:19:54 +0200 Subject: [PATCH 03/21] oppps... spelling --- .../songs/forms/songmaintenanceform.py | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/openlp/plugins/songs/forms/songmaintenanceform.py b/openlp/plugins/songs/forms/songmaintenanceform.py index 2ac5f93a7..2ba5e455d 100644 --- a/openlp/plugins/songs/forms/songmaintenanceform.py +++ b/openlp/plugins/songs/forms/songmaintenanceform.py @@ -142,7 +142,7 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog): new_author_last_name = new_author.last_name new_author_display_name = new_author.display_name authors = self.songmanager.get_all_objects(Author) - author_exsists = False + author_exists = False for author in authors: author_fist_name = author.first_name author_last_name = author.last_name @@ -150,7 +150,7 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog): if author_fist_name == new_author_first_name and \ author_last_name == new_author_last_name and \ author_display_name == new_author_display_name: - author_exsists = True + author_exists = True #If we edit an exsisting Author, we need to make sure that we do #not return True when nothing has changed (because this would #cause an error message later on) @@ -158,8 +158,8 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog): new_author_id = new_author.id author_id = author.id if new_author_id == author_id: - author_exsists = False - return author_exsists + author_exists = False + return author_exists def checkTopic(self, new_topic, edit=False): """ @@ -167,11 +167,11 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog): """ new_topic_name = new_topic.name topics = self.songmanager.get_all_objects(Topic) - topic_exsists = False + topic_exists = False for topic in topics: topic_name = topic.name if topic_name == new_topic_name: - topic_exsists = True + topic_exists = True #If we edit an exsisting Topic, we need to make sure that we do #not return True when nothing has changed (because this would #cause an error message later on) @@ -179,8 +179,8 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog): new_topic_id = new_topic.id topic_id = topic.id if new_topic_id == topic_id: - topic_exsists = False - return topic_exsists + topic_exists = False + return topic_exists def checkBook(self, new_book, edit=False): """ @@ -189,13 +189,13 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog): new_book_name = new_book.name new_book_publisher = new_book.publisher books = self.songmanager.get_all_objects(Book) - book_exsists = False + book_exists = False for book in books: book_name = book.name book_publisher = book.publisher if book_publisher == new_book_publisher and \ book_name == new_book_name: - book_exsists = True + book_exists = True #If we edit an exsisting Book, we need to make sure that we do #not return True when nothing has changed (because this would #cause an error message later on) @@ -203,8 +203,8 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog): new_book_id = new_book.id book_id = book.id if new_book_id == book_id: - book_exsists = False - return book_exsists + book_exists = False + return book_exists def onAuthorAddButtonClick(self): self.authorform.setAutoDisplayName(True) From ab179f8e1235b61caacb962f60351158288aab53 Mon Sep 17 00:00:00 2001 From: andreas Date: Sat, 10 Jul 2010 23:26:09 +0200 Subject: [PATCH 04/21] fixes for merge --- .../songs/forms/songmaintenanceform.py | 43 ++++++------------- 1 file changed, 12 insertions(+), 31 deletions(-) diff --git a/openlp/plugins/songs/forms/songmaintenanceform.py b/openlp/plugins/songs/forms/songmaintenanceform.py index 2ba5e455d..925e77de1 100644 --- a/openlp/plugins/songs/forms/songmaintenanceform.py +++ b/openlp/plugins/songs/forms/songmaintenanceform.py @@ -138,26 +138,18 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog): """ Returns True when the given Author is already in the list elsewise False. """ - new_author_first_name = new_author.first_name - new_author_last_name = new_author.last_name - new_author_display_name = new_author.display_name authors = self.songmanager.get_all_objects(Author) author_exists = False for author in authors: - author_fist_name = author.first_name - author_last_name = author.last_name - author_display_name = author.display_name - if author_fist_name == new_author_first_name and \ - author_last_name == new_author_last_name and \ - author_display_name == new_author_display_name: + if author.fist_name == new_author.first_name and \ + author.last_name == new_author.last_name and \ + author.display_name == new_author.display_name: author_exists = True #If we edit an exsisting Author, we need to make sure that we do #not return True when nothing has changed (because this would #cause an error message later on) if edit: - new_author_id = new_author.id - author_id = author.id - if new_author_id == author_id: + if new_author.id == author.id: author_exists = False return author_exists @@ -165,20 +157,16 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog): """ Returns True when the given Topic is already in the list elsewise False. """ - new_topic_name = new_topic.name topics = self.songmanager.get_all_objects(Topic) topic_exists = False for topic in topics: - topic_name = topic.name - if topic_name == new_topic_name: + if topic.name == new_topic.name: topic_exists = True #If we edit an exsisting Topic, we need to make sure that we do #not return True when nothing has changed (because this would #cause an error message later on) if edit: - new_topic_id = new_topic.id - topic_id = topic.id - if new_topic_id == topic_id: + if new_topic.id == topic.id: topic_exists = False return topic_exists @@ -186,23 +174,17 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog): """ Returns True when the given Book is already in the list elsewise False. """ - new_book_name = new_book.name - new_book_publisher = new_book.publisher books = self.songmanager.get_all_objects(Book) book_exists = False for book in books: - book_name = book.name - book_publisher = book.publisher - if book_publisher == new_book_publisher and \ - book_name == new_book_name: + if book.publisher == new_book.publisher and \ + book.name == new_book.name: book_exists = True #If we edit an exsisting Book, we need to make sure that we do #not return True when nothing has changed (because this would #cause an error message later on) if edit: - new_book_id = new_book.id - book_id = book.id - if new_book_id == book_id: + if new_book.id == book.id: book_exists = False return book_exists @@ -213,7 +195,7 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog): first_name=unicode(self.authorform.FirstNameEdit.text()), last_name=unicode(self.authorform.LastNameEdit.text()), display_name=unicode(self.authorform.DisplayEdit.text())) - if self.checkAuthor(author) is False and \ + if not self.checkAuthor(author) and \ self.songmanager.save_object(author): self.resetAuthors() else: @@ -225,7 +207,7 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog): def onTopicAddButtonClick(self): if self.topicform.exec_(): topic = Topic.populate(name=unicode(self.topicform.NameEdit.text())) - if self.checkTopic(topic) is False and \ + if not self.checkTopic(topic) and \ self.songmanager.save_object(topic): self.resetTopics() else: @@ -239,8 +221,7 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog): book = Book.populate( name=unicode(self.bookform.NameEdit.text()), publisher=unicode(self.bookform.PublisherEdit.text())) - if self.checkBook(book) is False and \ - self.songmanager.save_object(book): + if not self.checkBook(book) and self.songmanager.save_object(book): self.resetBooks() else: QtGui.QMessageBox.critical(self, From 931f572a4e4ce6da0f58410b116381b95b058857 Mon Sep 17 00:00:00 2001 From: andreas Date: Sat, 10 Jul 2010 23:39:54 +0200 Subject: [PATCH 05/21] spelling mistakes --- openlp/plugins/songs/forms/songmaintenanceform.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/openlp/plugins/songs/forms/songmaintenanceform.py b/openlp/plugins/songs/forms/songmaintenanceform.py index 925e77de1..f320e6883 100644 --- a/openlp/plugins/songs/forms/songmaintenanceform.py +++ b/openlp/plugins/songs/forms/songmaintenanceform.py @@ -145,7 +145,7 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog): author.last_name == new_author.last_name and \ author.display_name == new_author.display_name: author_exists = True - #If we edit an exsisting Author, we need to make sure that we do + #If we edit an existing Author, we need to make sure that we do #not return True when nothing has changed (because this would #cause an error message later on) if edit: @@ -162,7 +162,7 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog): for topic in topics: if topic.name == new_topic.name: topic_exists = True - #If we edit an exsisting Topic, we need to make sure that we do + #If we edit an existing Topic, we need to make sure that we do #not return True when nothing has changed (because this would #cause an error message later on) if edit: @@ -180,7 +180,7 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog): if book.publisher == new_book.publisher and \ book.name == new_book.name: book_exists = True - #If we edit an exsisting Book, we need to make sure that we do + #If we edit an existing Book, we need to make sure that we do #not return True when nothing has changed (because this would #cause an error message later on) if edit: @@ -302,7 +302,7 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog): translate('SongsPlugin.SongMaintenanceForm', 'Are you sure you want to delete the selected author?'), translate('SongsPlugin.SongMaintenanceForm', - 'This author ca not be deleted, they are currently ' + 'This author cannot be deleted, they are currently ' 'assigned to at least one song.'), translate('SongsPlugin.SongMaintenanceForm', 'No author selected!')) From 0623d2bb4e702491ea55797c012052b05901e05e Mon Sep 17 00:00:00 2001 From: andreas Date: Sat, 10 Jul 2010 23:44:43 +0200 Subject: [PATCH 06/21] fixes for merge --- openlp/plugins/songs/forms/songmaintenanceform.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/openlp/plugins/songs/forms/songmaintenanceform.py b/openlp/plugins/songs/forms/songmaintenanceform.py index f320e6883..32c1a6359 100644 --- a/openlp/plugins/songs/forms/songmaintenanceform.py +++ b/openlp/plugins/songs/forms/songmaintenanceform.py @@ -250,7 +250,7 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog): author.last_name = unicode(self.authorform.LastNameEdit.text()) author.display_name = unicode( self.authorform.DisplayEdit.text()) - if self.checkAuthor(author, True) is False and \ + if not self.checkAuthor(author, True) and \ self.songmanager.save_object(author): self.resetAuthors() else: @@ -266,8 +266,8 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog): self.topicform.NameEdit.setText(topic.name) if self.topicform.exec_(False): topic.name = unicode(self.topicform.NameEdit.text()) - if self.checkTopic(topic, True) is False and \ - self.songmanager.save_object(topic): + if not self.checkTopic(topic, True) and \ + self.songmanager.save_object(topic): self.resetTopics() else: QtGui.QMessageBox.critical(self, @@ -284,8 +284,8 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog): if self.bookform.exec_(False): book.name = unicode(self.bookform.NameEdit.text()) book.publisher = unicode(self.bookform.PublisherEdit.text()) - if self.checkBook(book, True) is False and \ - self.songmanager.save_object(book): + if not self.checkBook(book, True) and \ + self.songmanager.save_object(book): self.resetBooks() else: QtGui.QMessageBox.critical(self, From 27bccda0b61adfb4d3c83e97831dc391dca20b07 Mon Sep 17 00:00:00 2001 From: Jonathan Corwin Date: Sat, 10 Jul 2010 23:21:14 +0100 Subject: [PATCH 07/21] Presentation thumbnail changes --- openlp/core/lib/__init__.py | 2 + openlp/core/lib/mediamanageritem.py | 9 +- .../presentations/lib/impresscontroller.py | 72 +++++++++-- openlp/plugins/presentations/lib/mediaitem.py | 114 +++++++++++------- .../presentations/lib/messagelistener.py | 63 +++++++++- .../presentations/lib/powerpointcontroller.py | 31 +++-- .../presentations/lib/pptviewcontroller.py | 44 ++++--- .../lib/presentationcontroller.py | 88 +++++++++----- .../presentations/lib/presentationtab.py | 15 +++ .../presentations/presentationplugin.py | 25 +++- 10 files changed, 350 insertions(+), 113 deletions(-) diff --git a/openlp/core/lib/__init__.py b/openlp/core/lib/__init__.py index 4718e289e..7659a2889 100644 --- a/openlp/core/lib/__init__.py +++ b/openlp/core/lib/__init__.py @@ -174,6 +174,8 @@ def resize_image(image, width, height): """ preview = QtGui.QImage(image) if not preview.isNull(): + if(preview.width() == width and preview.height == height): + return preview preview = preview.scaled(width, height, QtCore.Qt.KeepAspectRatio, QtCore.Qt.SmoothTransformation) realw = preview.width() diff --git a/openlp/core/lib/mediamanageritem.py b/openlp/core/lib/mediamanageritem.py index 8ebfe88ae..9fc2c91f6 100644 --- a/openlp/core/lib/mediamanageritem.py +++ b/openlp/core/lib/mediamanageritem.py @@ -383,14 +383,17 @@ class MediaManagerItem(QtGui.QWidget): """ Validates to see if the file still exists or thumbnail is up to date """ - if os.path.exists(file): + if not os.path.exists(file): + return False + if os.path.exists(thumb): filedate = os.stat(file).st_mtime thumbdate = os.stat(thumb).st_mtime #if file updated rebuild icon if filedate > thumbdate: self.IconFromFile(file, thumb) - return True - return False + else: + self.IconFromFile(file, thumb) + return True def IconFromFile(self, file, thumb): """ diff --git a/openlp/plugins/presentations/lib/impresscontroller.py b/openlp/plugins/presentations/lib/impresscontroller.py index 8dc4861aa..79c724f6a 100644 --- a/openlp/plugins/presentations/lib/impresscontroller.py +++ b/openlp/plugins/presentations/lib/impresscontroller.py @@ -104,6 +104,10 @@ class ImpressController(PresentationController): self.process.waitForStarted() def get_uno_desktop(self): + """ + On non-Windows platforms, use Uno. Get the OpenOffice desktop + which will be used to manage impress + """ log.debug(u'get UNO Desktop Openoffice') ctx = None loop = 0 @@ -134,10 +138,17 @@ class ImpressController(PresentationController): return None def get_com_desktop(self): + """ + On Windows platforms, use COM. Return the desktop object which + will be used to manage Impress + """ log.debug(u'get COM Desktop OpenOffice') return self.manager.createInstance(u'com.sun.star.frame.Desktop') def get_com_servicemanager(self): + """ + Return the OOo service manager for windows + """ log.debug(u'get_com_servicemanager openoffice') try: return Dispatch(u'com.sun.star.ServiceManager') @@ -171,13 +182,23 @@ class ImpressController(PresentationController): log.exception(u'Failed to terminate OpenOffice') def add_doc(self, name): + """ + Called when a new Impress document is opened + """ log.debug(u'Add Doc OpenOffice') doc = ImpressDocument(self, name) self.docs.append(doc) return doc class ImpressDocument(PresentationDocument): + """ + Class which holds information and controls a single presentation + """ + def __init__(self, controller, presentation): + """ + Constructor, store information about the file and initialise + """ log.debug(u'Init Presentation OpenOffice') PresentationDocument.__init__(self, controller, presentation) self.document = None @@ -208,9 +229,8 @@ class ImpressDocument(PresentationDocument): desktop = self.controller.get_uno_desktop() url = uno.systemPathToFileUrl(self.filepath) if desktop is None: - return + return False self.desktop = desktop - #print "s.dsk2 ", self.desktop properties = [] properties.append(self.create_property(u'Minimized', True)) properties = tuple(properties) @@ -219,12 +239,13 @@ class ImpressDocument(PresentationDocument): 0, properties) except: log.exception(u'Failed to load presentation') - return + return False self.presentation = self.document.getPresentation() self.presentation.Display = \ self.controller.plugin.renderManager.screens.current_display + 1 self.control = None self.create_thumbnails() + return True def create_thumbnails(self): """ @@ -234,10 +255,10 @@ class ImpressDocument(PresentationDocument): if self.check_thumbnails(): return if os.name == u'nt': - thumbdir = u'file:///' + self.thumbnailpath.replace( + thumbdirurl = u'file:///' + self.get_temp_folder().replace( u'\\', u'/').replace(u':', u'|').replace(u' ', u'%20') else: - thumbdir = uno.systemPathToFileUrl(self.thumbnailpath) + thumbdirurl = uno.systemPathToFileUrl(self.get_temp_folder()) props = [] props.append(self.create_property(u'FilterName', u'impress_png_Export')) props = tuple(props) @@ -246,18 +267,22 @@ class ImpressDocument(PresentationDocument): for idx in range(pages.getCount()): page = pages.getByIndex(idx) doc.getCurrentController().setCurrentPage(page) - path = u'%s/%s%s.png' % (thumbdir, self.controller.thumbnailprefix, - unicode(idx + 1)) + urlpath = u'%s/%s.png' % (thumbdirurl, unicode(idx + 1)) + path = os.path.join(self.get_temp_folder(), + unicode(idx + 1) + u'.png') try: - doc.storeToURL(path , props) - preview = resize_image(path, 640, 480) + doc.storeToURL(urlpath, props) + self.convert_thumbnail(path, idx) if os.path.exists(path): os.remove(path) - preview.save(path, u'png') except: log.exception(u'%s - Unable to store openoffice preview' % path) def create_property(self, name, value): + """ + Create an OOo style property object which are passed into some + Uno methods + """ log.debug(u'create property OpenOffice') if os.name == u'nt': prop = self.controller.manager.\ @@ -288,6 +313,9 @@ class ImpressDocument(PresentationDocument): self.controller.remove_doc(self) def is_loaded(self): + """ + Returns true if a presentation is loaded + """ log.debug(u'is loaded OpenOffice') #print "is_loaded " if self.presentation is None or self.document is None: @@ -302,6 +330,9 @@ class ImpressDocument(PresentationDocument): return True def is_active(self): + """ + Returns true if a presentation is active and running + """ log.debug(u'is active OpenOffice') #print "is_active " if not self.is_loaded(): @@ -313,10 +344,16 @@ class ImpressDocument(PresentationDocument): return True def unblank_screen(self): + """ + Unblanks the screen + """ log.debug(u'unblank screen OpenOffice') return self.control.resume() def blank_screen(self): + """ + Blanks the screen + """ log.debug(u'blank screen OpenOffice') self.control.blankScreen(0) @@ -331,6 +368,9 @@ class ImpressDocument(PresentationDocument): return False def stop_presentation(self): + """ + Stop the presentation, remove from screen + """ log.debug(u'stop presentation OpenOffice') # deactivate should hide the screen according to docs, but doesn't #self.control.deactivate() @@ -338,6 +378,9 @@ class ImpressDocument(PresentationDocument): self.control = None def start_presentation(self): + """ + Start the presentation from the beginning + """ log.debug(u'start presentation OpenOffice') if self.control is None or not self.control.isRunning(): self.presentation.start() @@ -354,12 +397,21 @@ class ImpressDocument(PresentationDocument): self.goto_slide(1) def get_slide_number(self): + """ + Return the current slide number on the screen, from 1 + """ return self.control.getCurrentSlideIndex() + 1 def get_slide_count(self): + """ + Return the total number of slides + """ return self.document.getDrawPages().getCount() def goto_slide(self, slideno): + """ + Go to a specific slide (from 1) + """ self.control.gotoSlideIndex(slideno-1) def next_step(self): diff --git a/openlp/plugins/presentations/lib/mediaitem.py b/openlp/plugins/presentations/lib/mediaitem.py index 6f6d6fdb1..6f6fa285f 100644 --- a/openlp/plugins/presentations/lib/mediaitem.py +++ b/openlp/plugins/presentations/lib/mediaitem.py @@ -35,9 +35,13 @@ from openlp.plugins.presentations.lib import MessageListener log = logging.getLogger(__name__) -# We have to explicitly create separate classes for each plugin -# in order for DnD to the Service manager to work correctly. class PresentationListView(BaseListWithDnD): + """ + Class for the list of Presentations + + We have to explicitly create separate classes for each plugin + in order for DnD to the Service manager to work correctly. + """ def __init__(self, parent=None): self.PluginName = u'Presentations' BaseListWithDnD.__init__(self, parent) @@ -45,11 +49,14 @@ class PresentationListView(BaseListWithDnD): class PresentationMediaItem(MediaManagerItem): """ This is the Presentation media manager item for Presentation Items. - It can present files using Openoffice + It can present files using Openoffice and Powerpoint """ log.info(u'Presentations Media Item loaded') def __init__(self, parent, icon, title, controllers): + """ + Constructor. Setup defaults + """ self.controllers = controllers self.PluginNameShort = u'Presentation' self.IconPath = u'presentations/presentation' @@ -61,10 +68,16 @@ class PresentationMediaItem(MediaManagerItem): self.message_listener = MessageListener(self) def initPluginNameVisible(self): + """ + The name of the plugin media displayed in UI + """ self.PluginNameVisible = translate('PresentationPlugin.MediaItem', 'Presentation') def retranslateUi(self): + """ + The name of the plugin media displayed in UI + """ self.OnNewPrompt = translate('PresentationPlugin.MediaItem', 'Select Presentation(s)') self.Automatic = translate('PresentationPlugin.MediaItem', @@ -82,12 +95,18 @@ class PresentationMediaItem(MediaManagerItem): 'Presentations (%s)' % fileType) def requiredIcons(self): + """ + Set which icons the media manager tab should show + """ MediaManagerItem.requiredIcons(self) self.hasFileIcon = True self.hasNewIcon = False self.hasEditIcon = False def addEndHeaderBar(self): + """ + Display custom media manager items for presentations + """ self.PresentationWidget = QtGui.QWidget(self) sizePolicy = QtGui.QSizePolicy( QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum) @@ -111,12 +130,10 @@ class PresentationMediaItem(MediaManagerItem): self.PageLayout.addWidget(self.PresentationWidget) def initialise(self): - self.servicePath = os.path.join( - AppLocation.get_section_data_path(self.settingsSection), - u'thumbnails') + """ + Populate the media manager tab + """ self.ListView.setIconSize(QtCore.QSize(88, 50)) - if not os.path.exists(self.servicePath): - os.mkdir(self.servicePath) list = SettingsManager.load_list( self.settingsSection, u'presentations') self.loadList(list) @@ -129,6 +146,11 @@ class PresentationMediaItem(MediaManagerItem): self.DisplayTypeComboBox.setCurrentIndex(0) def loadList(self, list): + """ + Add presentations into the media manager + This is called both on initial load of the plugin to populate with + existing files, and when the user adds new files via the media manager + """ currlist = self.getFileList() titles = [] for file in currlist: @@ -141,37 +163,35 @@ class PresentationMediaItem(MediaManagerItem): QtGui.QMessageBox.critical( self, translate('PresentationPlugin.MediaItem', 'File exists'), - translate('PresentationPlugin.MediaItem', - 'A presentation with that filename already exists.'), + translate('PresentationPlugin.MediaItem', + 'A presentation with that filename already exists.'), QtGui.QMessageBox.Ok) + continue + controller_name = self.findControllerByType(filename) + if not controller_name: + QtGui.QMessageBox.critical( + self, translate('PresentationPlugin.MediaItem', + 'Unsupported file'), + translate('PresentationPlugin.MediaItem', + 'This type of presentation is not supported'), + QtGui.QMessageBox.Ok) + continue + controller = self.controllers[controller_name] + doc = controller.add_doc(unicode(file)) + thumb = os.path.join(doc.get_thumbnail_folder(), u'icon.png') + preview = doc.get_thumbnail_path(1, True) + if not preview: + doc.load_presentation() + preview = doc.get_thumbnail_path(1, True) + doc.close_presentation() + if preview and self.validate(preview, thumb): + icon = build_icon(thumb) else: - icon = None - for controller in self.controllers: - thumbPath = os.path.join( - AppLocation.get_section_data_path( - self.settingsSection), - u'thumbnails', controller, filename) - thumb = os.path.join(thumbPath, u'slide1.png') - preview = os.path.join( - AppLocation.get_section_data_path( - self.settingsSection), - controller, u'thumbnails', filename, u'slide1.png') - if os.path.exists(preview): - if os.path.exists(thumb): - if self.validate(preview, thumb): - icon = build_icon(thumb) - else: - icon = build_icon( - u':/general/general_delete.png') - else: - os.makedirs(thumbPath) - icon = self.IconFromFile(preview, thumb) - if not icon: - icon = build_icon(u':/general/general_delete.png') - item_name = QtGui.QListWidgetItem(filename) - item_name.setData(QtCore.Qt.UserRole, QtCore.QVariant(file)) - item_name.setIcon(icon) - self.ListView.addItem(item_name) + icon = build_icon(u':/general/general_delete.png') + item_name = QtGui.QListWidgetItem(filename) + item_name.setData(QtCore.Qt.UserRole, QtCore.QVariant(file)) + item_name.setIcon(icon) + self.ListView.addItem(item_name) def onDeleteClick(self): """ @@ -186,8 +206,6 @@ class PresentationMediaItem(MediaManagerItem): for item in items: filepath = unicode(item.data( QtCore.Qt.UserRole).toString()) - #not sure of this has errors - #John please can you look at . for cidx in self.controllers: doc = self.controllers[cidx].add_doc(filepath) doc.presentation_deleted() @@ -198,6 +216,11 @@ class PresentationMediaItem(MediaManagerItem): self.settingsSection, self.getFileList()) def generateSlideData(self, service_item, item=None): + """ + Load the relevant information for displaying the presentation + in the slidecontroller. In the case of powerpoints, an image + for each slide + """ items = self.ListView.selectedIndexes() if len(items) > 1: return False @@ -215,20 +238,27 @@ class PresentationMediaItem(MediaManagerItem): controller = self.controllers[service_item.shortname] (path, name) = os.path.split(filename) doc = controller.add_doc(filename) - if doc.get_slide_preview_file(1) is None: + if doc.get_thumbnail_path(1, True) is None: doc.load_presentation() i = 1 - img = doc.get_slide_preview_file(i) + img = doc.get_thumbnail_path(i, True) while img: service_item.add_from_command(path, name, img) i = i + 1 - img = doc.get_slide_preview_file(i) + img = doc.get_thumbnail_path(i, True) doc.close_presentation() return True else: return False def findControllerByType(self, filename): + """ + Determine the default application controller to use for the selected + file type. This is used if "Automatic" is set as the preferred + controller. Find the first (alphabetic) enabled controller which + "supports" the extension. If none found, then look for a controller + which "alsosupports" it instead. + """ filetype = os.path.splitext(filename)[1] if not filetype: return None diff --git a/openlp/plugins/presentations/lib/messagelistener.py b/openlp/plugins/presentations/lib/messagelistener.py index 2f8a1aec7..a7e6f10ce 100644 --- a/openlp/plugins/presentations/lib/messagelistener.py +++ b/openlp/plugins/presentations/lib/messagelistener.py @@ -41,17 +41,28 @@ class Controller(object): log.info(u'Controller loaded') def __init__(self, live): + """ + Constructor + """ self.is_live = live self.doc = None log.info(u'%s controller loaded' % live) def add_handler(self, controller, file, is_blank): + """ + Add a handler, which is an instance of a presentation and + slidecontroller combination. If the slidecontroller has a display + then load the presentation. + """ log.debug(u'Live = %s, add_handler %s' % (self.is_live, file)) self.controller = controller if self.doc is not None: self.shutdown() self.doc = self.controller.add_doc(file) - self.doc.load_presentation() + if not self.doc.load_presentation(): + # Display error message to user + # Inform slidecontroller that the action failed? + return if self.is_live: self.doc.start_presentation() if is_blank: @@ -60,6 +71,10 @@ class Controller(object): self.doc.slidenumber = 0 def activate(self): + """ + Active the presentation, and show it on the screen. + Use the last slide number. + """ log.debug(u'Live = %s, activate' % self.is_live) if self.doc.is_active(): return @@ -71,6 +86,9 @@ class Controller(object): self.doc.goto_slide(self.doc.slidenumber) def slide(self, slide): + """ + Go to a specific slide + """ log.debug(u'Live = %s, slide' % self.is_live) if not self.is_live: return @@ -152,6 +170,9 @@ class Controller(object): #self.timer.stop() def blank(self): + """ + Instruct the controller to blank the presentation + """ log.debug(u'Live = %s, blank' % self.is_live) if not self.is_live: return @@ -162,6 +183,9 @@ class Controller(object): self.doc.blank_screen() def stop(self): + """ + Instruct the controller to stop and hide the presentation + """ log.debug(u'Live = %s, stop' % self.is_live) if not self.is_live: return @@ -172,6 +196,9 @@ class Controller(object): self.doc.stop_presentation() def unblank(self): + """ + Instruct the controller to unblank the presentation + """ log.debug(u'Live = %s, unblank' % self.is_live) if not self.is_live: return @@ -246,6 +273,9 @@ class MessageListener(object): controller.add_handler(self.controllers[self.handler], file, is_blank) def slide(self, message): + """ + React to the message to move to a specific slide + """ is_live = message[1] slide = message[2] if is_live: @@ -254,6 +284,9 @@ class MessageListener(object): self.preview_handler.slide(slide) def first(self, message): + """ + React to the message to move to the first slide + """ is_live = message[1] if is_live: self.live_handler.first() @@ -261,6 +294,9 @@ class MessageListener(object): self.preview_handler.first() def last(self, message): + """ + React to the message to move to the last slide + """ is_live = message[1] if is_live: self.live_handler.last() @@ -268,6 +304,9 @@ class MessageListener(object): self.preview_handler.last() def next(self, message): + """ + React to the message to move to the next animation/slide + """ is_live = message[1] if is_live: self.live_handler.next() @@ -275,6 +314,9 @@ class MessageListener(object): self.preview_handler.next() def previous(self, message): + """ + React to the message to move to the previous animation/slide + """ is_live = message[1] if is_live: self.live_handler.previous() @@ -282,6 +324,10 @@ class MessageListener(object): self.preview_handler.previous() def shutdown(self, message): + """ + React to message to shutdown the presentation. I.e. end the show + and close the file + """ is_live = message[1] if is_live: Receiver.send_message(u'maindisplay_show') @@ -290,19 +336,34 @@ class MessageListener(object): self.preview_handler.shutdown() def hide(self, message): + """ + React to the message to show the desktop + """ is_live = message[1] if is_live: self.live_handler.stop() def blank(self, message): + """ + React to the message to blank the display + """ is_live = message[1] if is_live: self.live_handler.blank() def unblank(self, message): + """ + React to the message to unblank the display + """ is_live = message[1] if is_live: self.live_handler.unblank() def timeout(self): + """ + The presentation may be timed or might be controlled by the + application directly, rather than through OpenLP. Poll occassionally + to check which slide is currently displayed so the slidecontroller + view can be updated + """ self.live_handler.poll() diff --git a/openlp/plugins/presentations/lib/powerpointcontroller.py b/openlp/plugins/presentations/lib/powerpointcontroller.py index 00ded85f6..ea757a07d 100644 --- a/openlp/plugins/presentations/lib/powerpointcontroller.py +++ b/openlp/plugins/presentations/lib/powerpointcontroller.py @@ -24,7 +24,7 @@ ############################################################################### import os -import logging +import loggin if os.name == u'nt': from win32com.client import Dispatch @@ -97,13 +97,23 @@ class PowerpointController(PresentationController): self.process = None def add_doc(self, name): + """ + Called when a new powerpoint document is opened + """ log.debug(u'Add Doc PowerPoint') doc = PowerpointDocument(self, name) self.docs.append(doc) return doc class PowerpointDocument(PresentationDocument): + """ + Class which holds information and controls a single presentation + """ + def __init__(self, controller, presentation): + """ + Constructor, store information about the file and initialise + """ log.debug(u'Init Presentation Powerpoint') PresentationDocument.__init__(self, controller, presentation) self.presentation = None @@ -111,10 +121,7 @@ class PowerpointDocument(PresentationDocument): def load_presentation(self): """ Called when a presentation is added to the SlideController. - It builds the environment, starts communcations with the background - OpenOffice task started earlier. If OpenOffice is not present is is - started. Once the environment is available the presentation is loaded - and started. + Opens the PowerPoint file using the process created earlier ``presentation`` The file name of the presentations to run. @@ -122,11 +129,15 @@ class PowerpointDocument(PresentationDocument): log.debug(u'LoadPresentation') if not self.controller.process.Visible: self.controller.start_process() - self.controller.process.Presentations.Open(self.filepath, False, False, - True) + try: + self.controller.process.Presentations.Open(self.filepath, False, + False, True) + except pywintypes.com_error: + return False self.presentation = self.controller.process.Presentations( self.controller.process.Presentations.Count) self.create_thumbnails() + return True def create_thumbnails(self): """ @@ -139,8 +150,8 @@ class PowerpointDocument(PresentationDocument): """ if self.check_thumbnails(): return - self.presentation.Export(os.path.join(self.thumbnailpath, ''), 'png', - 320, 240) + self.presentation.Export(os.path.join(self.get_thumbnail_folder(), ''), + 'png', 320, 240) def close_presentation(self): """ @@ -298,4 +309,4 @@ class PowerpointDocument(PresentationDocument): shape = shapes(idx + 1) if shape.HasTextFrame: text += shape.TextFrame.TextRange.Text + '\n' - return text \ No newline at end of file + return text diff --git a/openlp/plugins/presentations/lib/pptviewcontroller.py b/openlp/plugins/presentations/lib/pptviewcontroller.py index 2c108333c..e92cf1054 100644 --- a/openlp/plugins/presentations/lib/pptviewcontroller.py +++ b/openlp/plugins/presentations/lib/pptviewcontroller.py @@ -31,6 +31,7 @@ if os.name == u'nt': from ctypes.wintypes import RECT from presentationcontroller import PresentationController, PresentationDocument +from openlp.core.lib import resize_image log = logging.getLogger(__name__) @@ -93,13 +94,22 @@ class PptviewController(PresentationController): self.docs[0].close_presentation() def add_doc(self, name): + """ + Called when a new powerpoint document is opened + """ log.debug(u'Add Doc PPTView') doc = PptviewDocument(self, name) self.docs.append(doc) return doc class PptviewDocument(PresentationDocument): + """ + Class which holds information and controls a single presentation + """ def __init__(self, controller, presentation): + """ + Constructor, store information about the file and initialise + """ log.debug(u'Init Presentation PowerPoint') PresentationDocument.__init__(self, controller, presentation) self.presentation = None @@ -117,17 +127,29 @@ class PptviewDocument(PresentationDocument): The file name of the presentations to run. """ log.debug(u'LoadPresentation') - #if self.pptid >= 0: - # self.close_presentation() rendermanager = self.controller.plugin.renderManager rect = rendermanager.screens.current[u'size'] rect = RECT(rect.x(), rect.y(), rect.right(), rect.bottom()) filepath = str(self.filepath.replace(u'/', u'\\')) self.pptid = self.controller.process.OpenPPT(filepath, None, rect, - str(os.path.join(self.thumbnailpath, - self.controller.thumbnailprefix))) + str(self.get_temp_folder()) + '\\') if self.pptid: + self.create_thumbnails() self.stop_presentation() + return True + else: + return False + + def create_thumbnails(self): + """ + PPTviewLib creates large BMP's, but we want small PNG's for consistency. + Convert them here. + """ + if self.check_thumbnails(): + return + for idx in range(self.get_slide_count()): + path = u'%s\\%s.bmp' % (self.get_temp_folder(), unicode(idx + 1)) + self.convert_image(path, idx) def close_presentation(self): """ @@ -224,17 +246,3 @@ class PptviewDocument(PresentationDocument): """ self.controller.process.PrevStep(self.pptid) - def get_slide_preview_file(self, slide_no): - """ - Returns an image path containing a preview for the requested slide - - ``slide_no`` - The slide an image is required for, starting at 1 - """ - path = os.path.join(self.thumbnailpath, - self.controller.thumbnailprefix + unicode(slide_no) + u'.bmp') - if os.path.isfile(path): - return path - else: - return None - diff --git a/openlp/plugins/presentations/lib/presentationcontroller.py b/openlp/plugins/presentations/lib/presentationcontroller.py index 501776a19..ed4081ed6 100644 --- a/openlp/plugins/presentations/lib/presentationcontroller.py +++ b/openlp/plugins/presentations/lib/presentationcontroller.py @@ -29,7 +29,7 @@ import shutil from PyQt4 import QtCore -from openlp.core.lib import Receiver +from openlp.core.lib import Receiver, resize_image from openlp.core.utils import AppLocation log = logging.getLogger(__name__) @@ -63,6 +63,13 @@ class PresentationController(object): ``plugin`` The presentationplugin object + ``supports`` + The primary native file types this application supports + + ``alsosupports`` + Other file types the application can import, although not necessarily + the first choice due to potential incompatibilities + **Hook Functions** ``kill()`` @@ -109,12 +116,16 @@ class PresentationController(object): QtCore.Qt.Checked else: self.enabled = False - self.thumbnailroot = os.path.join( + self.temp_folder = os.path.join( + AppLocation.get_section_data_path(self.settings_section), name) + self.thumbnail_folder = os.path.join( AppLocation.get_section_data_path(self.settings_section), - name, u'thumbnails') - self.thumbnailprefix = u'slide' - if not os.path.isdir(self.thumbnailroot): - os.makedirs(self.thumbnailroot) + u'thumbnails') + self.thumbnail_prefix = u'slide' + if not os.path.isdir(self.thumbnail_folder): + os.makedirs(self.thumbnail_folder) + if not os.path.isdir(self.temp_folder): + os.makedirs(self.temp_folder) def check_available(self): """ @@ -208,14 +219,19 @@ class PresentationDocument(object): ``previous_step()`` Triggers the previous slide on the running presentation - ``get_slide_preview_file(slide_no)`` + ``get_thumbnail_path(slide_no, check_exists)`` Returns a path to an image containing a preview for the requested slide """ def __init__(self, controller, name): + """ + Constructor for the PresentationController class + """ self.slidenumber = 0 self.controller = controller - self.store_filename(name) + self.filepath = name + if not os.path.isdir(self.get_thumbnail_folder()): + os.mkdir(self.get_thumbnail_folder()) def load_presentation(self): """ @@ -224,9 +240,10 @@ class PresentationDocument(object): ``presentation`` The file name of the presentations to the run. - + + Returns False if the file could not be opened """ - pass + return False def presentation_deleted(self): """ @@ -234,33 +251,37 @@ class PresentationDocument(object): a file, e.g. thumbnails """ try: - shutil.rmtree(self.thumbnailpath) + shutil.rmtree(self.get_thumbnail_folder()) + shutil.rmtree(self.get_temp_folder()) except OSError: log.exception(u'Failed to delete presentation controller files') - def store_filename(self, presentation): + def get_file_name(self): """ - Set properties for the filename and thumbnail paths + Return just the filename of the presention, without the directory """ - self.filepath = presentation - self.filename = self.get_file_name(presentation) - self.thumbnailpath = self.get_thumbnail_path(presentation) - if not os.path.isdir(self.thumbnailpath): - os.mkdir(self.thumbnailpath) + return os.path.split(self.filepath)[1] - def get_file_name(self, presentation): - return os.path.split(presentation)[1] - - def get_thumbnail_path(self, presentation): + def get_thumbnail_folder(self): + """ + The location where thumbnail images will be stored + """ return os.path.join( - self.controller.thumbnailroot, self.get_file_name(presentation)) + self.controller.thumbnail_folder, self.get_file_name()) + + def get_temp_folder(self): + """ + The location where thumbnail images will be stored + """ + return os.path.join( + self.controller.temp_folder, self.get_file_name()) def check_thumbnails(self): """ Returns true if the thumbnail images look to exist and are more recent than the powerpoint """ - lastimage = self.get_slide_preview_file(self.get_slide_count()) + lastimage = self.get_thumbnail_path(self.get_slide_count(), True) if not (lastimage and os.path.isfile(lastimage)): return False imgdate = os.stat(lastimage).st_mtime @@ -350,16 +371,27 @@ class PresentationDocument(object): """ pass - def get_slide_preview_file(self, slide_no): + def convert_thumbnail(self, file, idx): + """ + Convert the slide image the application made to a standard 320x240 + .png image. + """ + if self.check_thumbnails(): + return + if os.path.isfile(file): + img = resize_image(file, 320, 240) + img.save(self.get_thumbnail_path(idx, False)) + + def get_thumbnail_path(self, slide_no, check_exists): """ Returns an image path containing a preview for the requested slide ``slide_no`` The slide an image is required for, starting at 1 """ - path = os.path.join(self.thumbnailpath, - self.controller.thumbnailprefix + unicode(slide_no) + u'.png') - if os.path.isfile(path): + path = os.path.join(self.get_thumbnail_folder(), + self.controller.thumbnail_prefix + unicode(slide_no) + u'.png') + if os.path.isfile(path) or not check_exists: return path else: return None diff --git a/openlp/plugins/presentations/lib/presentationtab.py b/openlp/plugins/presentations/lib/presentationtab.py index 099376ce9..980c62f47 100644 --- a/openlp/plugins/presentations/lib/presentationtab.py +++ b/openlp/plugins/presentations/lib/presentationtab.py @@ -32,10 +32,16 @@ class PresentationTab(SettingsTab): PresentationsTab is the Presentations settings tab in the settings dialog. """ def __init__(self, title, controllers): + """ + Constructor + """ self.controllers = controllers SettingsTab.__init__(self, title) def setupUi(self): + """ + Create the controls for the settings tab + """ self.setObjectName(u'PresentationTab') self.tabTitleVisible = translate('PresentationPlugin.PresentationTab', 'Presentations') @@ -89,6 +95,9 @@ class PresentationTab(SettingsTab): self.PresentationLayout.addWidget(self.PresentationRightWidget) def retranslateUi(self): + """ + Make any translation changes + """ self.VerseDisplayGroupBox.setTitle( translate('PresentationPlugin.PresentationTab', 'Available Controllers')) @@ -100,6 +109,9 @@ class PresentationTab(SettingsTab): translate('PresentationPlugin.PresentationTab', 'available'))) def load(self): + """ + Load the settings. + """ for key in self.controllers: controller = self.controllers[key] if controller.available: @@ -109,6 +121,9 @@ class PresentationTab(SettingsTab): QtCore.QVariant(0)).toInt()[0]) def save(self): + """ + Save the settings. + """ for key in self.controllers: controller = self.controllers[key] checkbox = self.PresenterCheckboxes[controller.name] diff --git a/openlp/plugins/presentations/presentationplugin.py b/openlp/plugins/presentations/presentationplugin.py index a905a3312..89139cca3 100644 --- a/openlp/plugins/presentations/presentationplugin.py +++ b/openlp/plugins/presentations/presentationplugin.py @@ -33,9 +33,17 @@ from openlp.plugins.presentations.lib import * log = logging.getLogger(__name__) class PresentationPlugin(Plugin): + """ + This plugin allowed a Presentation to be opened, controlled and displayed + on the output display. The plugin controls third party applications such + as OpenOffice.org Impress, Microsoft PowerPoint and the PowerPoint viewer + """ log = logging.getLogger(u'PresentationPlugin') def __init__(self, plugin_helpers): + """ + PluginPresentation constructor. + """ log.debug(u'Initialised') self.controllers = {} Plugin.__init__(self, u'Presentations', u'1.9.2', plugin_helpers) @@ -50,6 +58,10 @@ class PresentationPlugin(Plugin): return PresentationTab(self.name, self.controllers) def initialise(self): + """ + Initialise the plugin. Determine which controllers are enabled + are start their processes. + """ log.info(u'Presentations Initialising') Plugin.initialise(self) self.insertToolboxItem() @@ -58,6 +70,10 @@ class PresentationPlugin(Plugin): self.controllers[controller].start_process() def finalise(self): + """ + Finalise the plugin. Ask all the enabled presentation applications + to close down their applications and release resources. + """ log.info(u'Plugin Finalise') #Ask each controller to tidy up for key in self.controllers: @@ -74,6 +90,10 @@ class PresentationPlugin(Plugin): self, self.icon, self.name, self.controllers) def registerControllers(self, controller): + """ + Register each presentation controller (Impress, PPT etc) and + store for later use + """ self.controllers[controller.name] = controller def checkPreConditions(self): @@ -108,9 +128,12 @@ class PresentationPlugin(Plugin): return False def about(self): + """ + Return information about this plugin + """ about_text = translate('PresentationPlugin', 'Presentation Plugin
Delivers ' 'the ability to show presentations using a number of different ' 'programs. The choice of available presentation programs is ' 'available to the user in a drop down box.') - return about_text \ No newline at end of file + return about_text From 91b3c06849276fb4f31d6e97f84d5e41a7681087 Mon Sep 17 00:00:00 2001 From: Jonathan Corwin Date: Sat, 10 Jul 2010 23:21:50 +0100 Subject: [PATCH 08/21] Missed one --- openlp/plugins/presentations/lib/pptviewcontroller.py | 1 - 1 file changed, 1 deletion(-) diff --git a/openlp/plugins/presentations/lib/pptviewcontroller.py b/openlp/plugins/presentations/lib/pptviewcontroller.py index e92cf1054..e3f945d45 100644 --- a/openlp/plugins/presentations/lib/pptviewcontroller.py +++ b/openlp/plugins/presentations/lib/pptviewcontroller.py @@ -31,7 +31,6 @@ if os.name == u'nt': from ctypes.wintypes import RECT from presentationcontroller import PresentationController, PresentationDocument -from openlp.core.lib import resize_image log = logging.getLogger(__name__) From 08c36f7e1c3052ddf8a8d591c4fc7c6ec6068eed Mon Sep 17 00:00:00 2001 From: Jonathan Corwin Date: Sat, 10 Jul 2010 23:33:09 +0100 Subject: [PATCH 09/21] Got a conflict fix wrong --- openlp/plugins/presentations/lib/mediaitem.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openlp/plugins/presentations/lib/mediaitem.py b/openlp/plugins/presentations/lib/mediaitem.py index 237645d35..25e02dfc9 100644 --- a/openlp/plugins/presentations/lib/mediaitem.py +++ b/openlp/plugins/presentations/lib/mediaitem.py @@ -186,7 +186,7 @@ class PresentationMediaItem(MediaManagerItem): item_name = QtGui.QListWidgetItem(filename) item_name.setData(QtCore.Qt.UserRole, QtCore.QVariant(file)) item_name.setIcon(icon) - self.ListView.addItem(item_name) + self.listView.addItem(item_name) def onDeleteClick(self): """ From fb4faf0dbba2cb65976a39073f481178840e329c Mon Sep 17 00:00:00 2001 From: Jonathan Corwin Date: Sat, 10 Jul 2010 23:41:36 +0100 Subject: [PATCH 10/21] Powerpoint fix --- openlp/plugins/presentations/lib/powerpointcontroller.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openlp/plugins/presentations/lib/powerpointcontroller.py b/openlp/plugins/presentations/lib/powerpointcontroller.py index ea757a07d..6c725e583 100644 --- a/openlp/plugins/presentations/lib/powerpointcontroller.py +++ b/openlp/plugins/presentations/lib/powerpointcontroller.py @@ -24,7 +24,7 @@ ############################################################################### import os -import loggin +import logging if os.name == u'nt': from win32com.client import Dispatch @@ -127,7 +127,7 @@ class PowerpointDocument(PresentationDocument): The file name of the presentations to run. """ log.debug(u'LoadPresentation') - if not self.controller.process.Visible: + if not self.controller.process or not self.controller.process.Visible: self.controller.start_process() try: self.controller.process.Presentations.Open(self.filepath, False, From 488ead81626fc37a21717e8700107785c0264412 Mon Sep 17 00:00:00 2001 From: andreas Date: Sun, 11 Jul 2010 15:23:12 +0200 Subject: [PATCH 11/21] cleanup --- .../plugins/songs/forms/songmaintenanceform.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/openlp/plugins/songs/forms/songmaintenanceform.py b/openlp/plugins/songs/forms/songmaintenanceform.py index 32c1a6359..f9d6e253d 100644 --- a/openlp/plugins/songs/forms/songmaintenanceform.py +++ b/openlp/plugins/songs/forms/songmaintenanceform.py @@ -300,10 +300,10 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog): self._deleteItem(Author, self.AuthorsListWidget, self.resetAuthors, translate('SongsPlugin.SongMaintenanceForm', 'Delete Author'), translate('SongsPlugin.SongMaintenanceForm', - 'Are you sure you want to delete the selected author?'), + 'Are you sure you want to delete the selected author?'), translate('SongsPlugin.SongMaintenanceForm', - 'This author cannot be deleted, they are currently ' - 'assigned to at least one song.'), + 'This author cannot be deleted, they are currently ' + 'assigned to at least one song.'), translate('SongsPlugin.SongMaintenanceForm', 'No author selected!')) def onTopicDeleteButtonClick(self): @@ -313,10 +313,10 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog): self._deleteItem(Topic, self.TopicsListWidget, self.resetTopics, translate('SongsPlugin.SongMaintenanceForm', 'Delete Topic'), translate('SongsPlugin.SongMaintenanceForm', - 'Are you sure you want to delete the selected topic?'), + 'Are you sure you want to delete the selected topic?'), translate('SongsPlugin.SongMaintenanceForm', - 'This topic cannot be deleted, it is currently ' - 'assigned to at least one song.'), + 'This topic cannot be deleted, it is currently ' + 'assigned to at least one song.'), translate('SongsPlugin.SongMaintenanceForm', 'No topic selected!')) def onBookDeleteButtonClick(self): @@ -326,8 +326,8 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog): self._deleteItem(Book, self.BooksListWidget, self.resetBooks, translate('SongsPlugin.SongMaintenanceForm', 'Delete Book'), translate('SongsPlugin.SongMaintenanceForm', - 'Are you sure you want to delete the selected book?'), + 'Are you sure you want to delete the selected book?'), translate('SongsPlugin.SongMaintenanceForm', - 'This book cannot be deleted, it is currently ' - 'assigned to at least one song.'), + 'This book cannot be deleted, it is currently ' + 'assigned to at least one song.'), translate('SongsPlugin.SongMaintenanceForm', 'No book selected!')) From fe5b3e6771e4ba6440ca2bb442905483b6f18731 Mon Sep 17 00:00:00 2001 From: Jonathan Corwin Date: Sun, 11 Jul 2010 21:22:53 +0100 Subject: [PATCH 12/21] pptview/impress fixes to thumbnails on Windows --- .../presentations/lib/impresscontroller.py | 931 +++++++++--------- openlp/plugins/presentations/lib/mediaitem.py | 57 +- .../presentations/lib/pptviewcontroller.py | 10 +- 3 files changed, 505 insertions(+), 493 deletions(-) diff --git a/openlp/plugins/presentations/lib/impresscontroller.py b/openlp/plugins/presentations/lib/impresscontroller.py index 79c724f6a..f3c4cb68c 100644 --- a/openlp/plugins/presentations/lib/impresscontroller.py +++ b/openlp/plugins/presentations/lib/impresscontroller.py @@ -1,463 +1,468 @@ -# -*- coding: utf-8 -*- -# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4 - -############################################################################### -# OpenLP - Open Source Lyrics Projection # -# --------------------------------------------------------------------------- # -# Copyright (c) 2008-2010 Raoul Snyman # -# Portions copyright (c) 2008-2010 Tim Bentley, Jonathan Corwin, Michael # -# Gorven, Scott Guerrieri, Christian Richter, Maikel Stuivenberg, Martin # -# Thompson, Jon Tibble, Carsten Tinggaard # -# --------------------------------------------------------------------------- # -# This program is free software; you can redistribute it and/or modify it # -# under the terms of the GNU General Public License as published by the Free # -# Software Foundation; version 2 of the License. # -# # -# This program is distributed in the hope that it will be useful, but WITHOUT # -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or # -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for # -# more details. # -# # -# You should have received a copy of the GNU General Public License along # -# with this program; if not, write to the Free Software Foundation, Inc., 59 # -# Temple Place, Suite 330, Boston, MA 02111-1307 USA # -############################################################################### - -# OOo API documentation: -# http://api.openoffice.org/docs/common/ref/com/sun/star/presentation/XSlideShowController.html -# http://wiki.services.openoffice.org/wiki/Documentation/DevGuide/ProUNO/Basic/Getting_Information_about_UNO_Objects#Inspecting_interfaces_during_debugging -# http://docs.go-oo.org/sd/html/classsd_1_1SlideShow.html -# http://www.oooforum.org/forum/viewtopic.phtml?t=5252 -# http://wiki.services.openoffice.org/wiki/Documentation/DevGuide/Working_with_Presentations -# http://mail.python.org/pipermail/python-win32/2008-January/006676.html -# http://www.linuxjournal.com/content/starting-stopping-and-connecting-openoffice-python -# http://nxsy.org/comparing-documents-with-openoffice-and-python - -import logging -import os -import time - -from openlp.core.lib import resize_image - -if os.name == u'nt': - from win32com.client import Dispatch - import pywintypes -else: - try: - import uno - from com.sun.star.beans import PropertyValue - uno_available = True - except ImportError: - uno_available = False - -from PyQt4 import QtCore - -from presentationcontroller import PresentationController, PresentationDocument - -log = logging.getLogger(__name__) - -class ImpressController(PresentationController): - """ - Class to control interactions with Impress presentations. - It creates the runtime environment, loads and closes the presentation as - well as triggering the correct activities based on the users input - """ - log.info(u'ImpressController loaded') - - def __init__(self, plugin): - """ - Initialise the class - """ - log.debug(u'Initialising') - PresentationController.__init__(self, plugin, u'Impress') - self.supports = [u'.odp'] - self.alsosupports = [u'.ppt', u'.pps', u'.pptx', u'.ppsx'] - self.process = None - self.desktop = None - - def check_available(self): - """ - Impress is able to run on this machine - """ - log.debug(u'check_available') - if os.name == u'nt': - return self.get_com_servicemanager() is not None - else: - return uno_available - - def start_process(self): - """ - Loads a running version of OpenOffice in the background. - It is not displayed to the user but is available to the UNO interface - when required. - """ - log.debug(u'start process Openoffice') - if os.name == u'nt': - self.manager = self.get_com_servicemanager() - self.manager._FlagAsMethod(u'Bridge_GetStruct') - self.manager._FlagAsMethod(u'Bridge_GetValueObject') - else: - # -headless - cmd = u'openoffice.org -nologo -norestore -minimized -invisible -nofirststartwizard -accept="socket,host=localhost,port=2002;urp;"' - self.process = QtCore.QProcess() - self.process.startDetached(cmd) - self.process.waitForStarted() - - def get_uno_desktop(self): - """ - On non-Windows platforms, use Uno. Get the OpenOffice desktop - which will be used to manage impress - """ - log.debug(u'get UNO Desktop Openoffice') - ctx = None - loop = 0 - log.debug(u'get UNO Desktop Openoffice - getComponentContext') - context = uno.getComponentContext() - log.debug(u'get UNO Desktop Openoffice - createInstaneWithContext - ' - u'UnoUrlResolver') - resolver = context.ServiceManager.createInstanceWithContext( - u'com.sun.star.bridge.UnoUrlResolver', context) - while ctx is None and loop < 3: - try: - log.debug(u'get UNO Desktop Openoffice - resolve') - ctx = resolver.resolve(u'uno:socket,host=localhost,port=2002;' - u'urp;StarOffice.ComponentContext') - except: - log.exception(u'Unable to find running instance ') - self.start_process() - loop += 1 - try: - self.manager = ctx.ServiceManager - log.debug(u'get UNO Desktop Openoffice - createInstanceWithContext' - u' - Desktop') - desktop = self.manager.createInstanceWithContext( - "com.sun.star.frame.Desktop", ctx ) - return desktop - except: - log.exception(u'Failed to get UNO desktop') - return None - - def get_com_desktop(self): - """ - On Windows platforms, use COM. Return the desktop object which - will be used to manage Impress - """ - log.debug(u'get COM Desktop OpenOffice') - return self.manager.createInstance(u'com.sun.star.frame.Desktop') - - def get_com_servicemanager(self): - """ - Return the OOo service manager for windows - """ - log.debug(u'get_com_servicemanager openoffice') - try: - return Dispatch(u'com.sun.star.ServiceManager') - except pywintypes.com_error: - log.exception(u'Failed to get COM service manager') - return None - - def kill(self): - """ - Called at system exit to clean up any running presentations - """ - log.debug(u'Kill OpenOffice') - while self.docs: - self.docs[0].close_presentation() - if os.name != u'nt': - desktop = self.get_uno_desktop() - else: - desktop = self.get_com_desktop() - #Sometimes we get a failure and desktop is None - if not desktop: - log.exception(u'Failed to terminate OpenOffice') - return - docs = desktop.getComponents() - if docs.hasElements(): - log.debug(u'OpenOffice not terminated') - else: - try: - desktop.terminate() - log.debug(u'OpenOffice killed') - except: - log.exception(u'Failed to terminate OpenOffice') - - def add_doc(self, name): - """ - Called when a new Impress document is opened - """ - log.debug(u'Add Doc OpenOffice') - doc = ImpressDocument(self, name) - self.docs.append(doc) - return doc - -class ImpressDocument(PresentationDocument): - """ - Class which holds information and controls a single presentation - """ - - def __init__(self, controller, presentation): - """ - Constructor, store information about the file and initialise - """ - log.debug(u'Init Presentation OpenOffice') - PresentationDocument.__init__(self, controller, presentation) - self.document = None - self.presentation = None - self.control = None - - def load_presentation(self): - """ - Called when a presentation is added to the SlideController. - It builds the environment, starts communcations with the background - OpenOffice task started earlier. If OpenOffice is not present is is - started. Once the environment is available the presentation is loaded - and started. - - ``presentation`` - The file name of the presentatios to the run. - """ - log.debug(u'Load Presentation OpenOffice') - #print "s.dsk1 ", self.desktop - if os.name == u'nt': - desktop = self.controller.get_com_desktop() - if desktop is None: - self.controller.start_process() - desktop = self.controller.get_com_desktop() - url = u'file:///' + self.filepath.replace(u'\\', u'/').replace( - u':', u'|').replace(u' ', u'%20') - else: - desktop = self.controller.get_uno_desktop() - url = uno.systemPathToFileUrl(self.filepath) - if desktop is None: - return False - self.desktop = desktop - properties = [] - properties.append(self.create_property(u'Minimized', True)) - properties = tuple(properties) - try: - self.document = desktop.loadComponentFromURL(url, u'_blank', - 0, properties) - except: - log.exception(u'Failed to load presentation') - return False - self.presentation = self.document.getPresentation() - self.presentation.Display = \ - self.controller.plugin.renderManager.screens.current_display + 1 - self.control = None - self.create_thumbnails() - return True - - def create_thumbnails(self): - """ - Create thumbnail images for presentation - """ - log.debug(u'create thumbnails OpenOffice') - if self.check_thumbnails(): - return - if os.name == u'nt': - thumbdirurl = u'file:///' + self.get_temp_folder().replace( - u'\\', u'/').replace(u':', u'|').replace(u' ', u'%20') - else: - thumbdirurl = uno.systemPathToFileUrl(self.get_temp_folder()) - props = [] - props.append(self.create_property(u'FilterName', u'impress_png_Export')) - props = tuple(props) - doc = self.document - pages = doc.getDrawPages() - for idx in range(pages.getCount()): - page = pages.getByIndex(idx) - doc.getCurrentController().setCurrentPage(page) - urlpath = u'%s/%s.png' % (thumbdirurl, unicode(idx + 1)) - path = os.path.join(self.get_temp_folder(), - unicode(idx + 1) + u'.png') - try: - doc.storeToURL(urlpath, props) - self.convert_thumbnail(path, idx) - if os.path.exists(path): - os.remove(path) - except: - log.exception(u'%s - Unable to store openoffice preview' % path) - - def create_property(self, name, value): - """ - Create an OOo style property object which are passed into some - Uno methods - """ - log.debug(u'create property OpenOffice') - if os.name == u'nt': - prop = self.controller.manager.\ - Bridge_GetStruct(u'com.sun.star.beans.PropertyValue') - else: - prop = PropertyValue() - prop.Name = name - prop.Value = value - return prop - - def close_presentation(self): - """ - Close presentation and clean up objects - Triggered by new object being added to SlideController or OpenLP - being shutdown - """ - log.debug(u'close Presentation OpenOffice') - if self.document: - if self.presentation: - try: - self.presentation.end() - self.presentation = None - self.document.dispose() - except: - #We tried! - pass - self.document = None - self.controller.remove_doc(self) - - def is_loaded(self): - """ - Returns true if a presentation is loaded - """ - log.debug(u'is loaded OpenOffice') - #print "is_loaded " - if self.presentation is None or self.document is None: - #print "no present or document" - return False - try: - if self.document.getPresentation() is None: - #print "no getPresentation" - return False - except: - return False - return True - - def is_active(self): - """ - Returns true if a presentation is active and running - """ - log.debug(u'is active OpenOffice') - #print "is_active " - if not self.is_loaded(): - #print "False " - return False - #print "self.con ", self.control - if self.control is None: - return False - return True - - def unblank_screen(self): - """ - Unblanks the screen - """ - log.debug(u'unblank screen OpenOffice') - return self.control.resume() - - def blank_screen(self): - """ - Blanks the screen - """ - log.debug(u'blank screen OpenOffice') - self.control.blankScreen(0) - - def is_blank(self): - """ - Returns true if screen is blank - """ - log.debug(u'is blank OpenOffice') - if self.control: - return self.control.isPaused() - else: - return False - - def stop_presentation(self): - """ - Stop the presentation, remove from screen - """ - log.debug(u'stop presentation OpenOffice') - # deactivate should hide the screen according to docs, but doesn't - #self.control.deactivate() - self.presentation.end() - self.control = None - - def start_presentation(self): - """ - Start the presentation from the beginning - """ - log.debug(u'start presentation OpenOffice') - if self.control is None or not self.control.isRunning(): - self.presentation.start() - # start() returns before the getCurrentComponent is ready. - # Try for 5 seconds - i = 1 - while self.desktop.getCurrentComponent() is None and i < 50: - time.sleep(0.1) - i = i + 1 - self.control = \ - self.desktop.getCurrentComponent().Presentation.getController() - else: - self.control.activate() - self.goto_slide(1) - - def get_slide_number(self): - """ - Return the current slide number on the screen, from 1 - """ - return self.control.getCurrentSlideIndex() + 1 - - def get_slide_count(self): - """ - Return the total number of slides - """ - return self.document.getDrawPages().getCount() - - def goto_slide(self, slideno): - """ - Go to a specific slide (from 1) - """ - self.control.gotoSlideIndex(slideno-1) - - def next_step(self): - """ - Triggers the next effect of slide on the running presentation - """ - self.control.gotoNextEffect() - - def previous_step(self): - """ - Triggers the previous slide on the running presentation - """ - self.control.gotoPreviousSlide() - - def get_slide_text(self, slide_no): - """ - Returns the text on the slide - - ``slide_no`` - The slide the text is required for, starting at 1 - """ - doc = self.document - pages = doc.getDrawPages() - text = '' - page = pages.getByIndex(slide_no - 1) - for idx in range(page.getCount()): - shape = page.getByIndex(idx) - if shape.supportsService("com.sun.star.drawing.Text"): - text += shape.getString() + '\n' - return text - - def get_slide_notes(self, slide_no): - """ - Returns the text on the slide - - ``slide_no`` - The slide the notes are required for, starting at 1 - """ - doc = self.document - pages = doc.getDrawPages() - text = '' - page = pages.getByIndex(slide_no - 1) - notes = page.getNotesPage() - for idx in range(notes.getCount()): - shape = notes.getByIndex(idx) - if shape.supportsService("com.sun.star.drawing.Text"): - text += shape.getString() + '\n' - return text - +# -*- coding: utf-8 -*- +# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4 + +############################################################################### +# OpenLP - Open Source Lyrics Projection # +# --------------------------------------------------------------------------- # +# Copyright (c) 2008-2010 Raoul Snyman # +# Portions copyright (c) 2008-2010 Tim Bentley, Jonathan Corwin, Michael # +# Gorven, Scott Guerrieri, Christian Richter, Maikel Stuivenberg, Martin # +# Thompson, Jon Tibble, Carsten Tinggaard # +# --------------------------------------------------------------------------- # +# This program is free software; you can redistribute it and/or modify it # +# under the terms of the GNU General Public License as published by the Free # +# Software Foundation; version 2 of the License. # +# # +# This program is distributed in the hope that it will be useful, but WITHOUT # +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or # +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for # +# more details. # +# # +# You should have received a copy of the GNU General Public License along # +# with this program; if not, write to the Free Software Foundation, Inc., 59 # +# Temple Place, Suite 330, Boston, MA 02111-1307 USA # +############################################################################### + +# OOo API documentation: +# http://api.openoffice.org/docs/common/ref/com/sun/star/presentation/XSlideShowController.html +# http://wiki.services.openoffice.org/wiki/Documentation/DevGuide/ProUNO/Basic/Getting_Information_about_UNO_Objects#Inspecting_interfaces_during_debugging +# http://docs.go-oo.org/sd/html/classsd_1_1SlideShow.html +# http://www.oooforum.org/forum/viewtopic.phtml?t=5252 +# http://wiki.services.openoffice.org/wiki/Documentation/DevGuide/Working_with_Presentations +# http://mail.python.org/pipermail/python-win32/2008-January/006676.html +# http://www.linuxjournal.com/content/starting-stopping-and-connecting-openoffice-python +# http://nxsy.org/comparing-documents-with-openoffice-and-python + +import logging +import os +import time + +from openlp.core.lib import resize_image + +if os.name == u'nt': + from win32com.client import Dispatch + import pywintypes +else: + try: + import uno + from com.sun.star.beans import PropertyValue + uno_available = True + except ImportError: + uno_available = False + +from PyQt4 import QtCore + +from presentationcontroller import PresentationController, PresentationDocument + +log = logging.getLogger(__name__) + +class ImpressController(PresentationController): + """ + Class to control interactions with Impress presentations. + It creates the runtime environment, loads and closes the presentation as + well as triggering the correct activities based on the users input + """ + log.info(u'ImpressController loaded') + + def __init__(self, plugin): + """ + Initialise the class + """ + log.debug(u'Initialising') + PresentationController.__init__(self, plugin, u'Impress') + self.supports = [u'.odp'] + self.alsosupports = [u'.ppt', u'.pps', u'.pptx', u'.ppsx'] + self.process = None + self.desktop = None + self.manager = None + + def check_available(self): + """ + Impress is able to run on this machine + """ + log.debug(u'check_available') + if os.name == u'nt': + return self.get_com_servicemanager() is not None + else: + return uno_available + + def start_process(self): + """ + Loads a running version of OpenOffice in the background. + It is not displayed to the user but is available to the UNO interface + when required. + """ + log.debug(u'start process Openoffice') + if os.name == u'nt': + self.manager = self.get_com_servicemanager() + self.manager._FlagAsMethod(u'Bridge_GetStruct') + self.manager._FlagAsMethod(u'Bridge_GetValueObject') + else: + # -headless + cmd = u'openoffice.org -nologo -norestore -minimized -invisible -nofirststartwizard -accept="socket,host=localhost,port=2002;urp;"' + self.process = QtCore.QProcess() + self.process.startDetached(cmd) + self.process.waitForStarted() + + def get_uno_desktop(self): + """ + On non-Windows platforms, use Uno. Get the OpenOffice desktop + which will be used to manage impress + """ + log.debug(u'get UNO Desktop Openoffice') + ctx = None + loop = 0 + log.debug(u'get UNO Desktop Openoffice - getComponentContext') + context = uno.getComponentContext() + log.debug(u'get UNO Desktop Openoffice - createInstaneWithContext - ' + u'UnoUrlResolver') + resolver = context.ServiceManager.createInstanceWithContext( + u'com.sun.star.bridge.UnoUrlResolver', context) + while ctx is None and loop < 3: + try: + log.debug(u'get UNO Desktop Openoffice - resolve') + ctx = resolver.resolve(u'uno:socket,host=localhost,port=2002;' + u'urp;StarOffice.ComponentContext') + except: + log.exception(u'Unable to find running instance ') + self.start_process() + loop += 1 + try: + self.manager = ctx.ServiceManager + log.debug(u'get UNO Desktop Openoffice - createInstanceWithContext' + u' - Desktop') + desktop = self.manager.createInstanceWithContext( + "com.sun.star.frame.Desktop", ctx ) + return desktop + except: + log.exception(u'Failed to get UNO desktop') + return None + + def get_com_desktop(self): + """ + On Windows platforms, use COM. Return the desktop object which + will be used to manage Impress + """ + log.debug(u'get COM Desktop OpenOffice') + if not self.manager: + return None + return self.manager.createInstance(u'com.sun.star.frame.Desktop') + + def get_com_servicemanager(self): + """ + Return the OOo service manager for windows + """ + log.debug(u'get_com_servicemanager openoffice') + try: + return Dispatch(u'com.sun.star.ServiceManager') + except pywintypes.com_error: + log.exception(u'Failed to get COM service manager') + return None + + def kill(self): + """ + Called at system exit to clean up any running presentations + """ + log.debug(u'Kill OpenOffice') + while self.docs: + self.docs[0].close_presentation() + if os.name != u'nt': + desktop = self.get_uno_desktop() + else: + desktop = self.get_com_desktop() + #Sometimes we get a failure and desktop is None + if not desktop: + log.exception(u'Failed to terminate OpenOffice') + return + docs = desktop.getComponents() + if docs.hasElements(): + log.debug(u'OpenOffice not terminated') + else: + try: + desktop.terminate() + log.debug(u'OpenOffice killed') + except: + log.exception(u'Failed to terminate OpenOffice') + + def add_doc(self, name): + """ + Called when a new Impress document is opened + """ + log.debug(u'Add Doc OpenOffice') + doc = ImpressDocument(self, name) + self.docs.append(doc) + return doc + +class ImpressDocument(PresentationDocument): + """ + Class which holds information and controls a single presentation + """ + + def __init__(self, controller, presentation): + """ + Constructor, store information about the file and initialise + """ + log.debug(u'Init Presentation OpenOffice') + PresentationDocument.__init__(self, controller, presentation) + self.document = None + self.presentation = None + self.control = None + + def load_presentation(self): + """ + Called when a presentation is added to the SlideController. + It builds the environment, starts communcations with the background + OpenOffice task started earlier. If OpenOffice is not present is is + started. Once the environment is available the presentation is loaded + and started. + + ``presentation`` + The file name of the presentatios to the run. + """ + log.debug(u'Load Presentation OpenOffice') + #print "s.dsk1 ", self.desktop + if os.name == u'nt': + desktop = self.controller.get_com_desktop() + if desktop is None: + self.controller.start_process() + desktop = self.controller.get_com_desktop() + url = u'file:///' + self.filepath.replace(u'\\', u'/').replace( + u':', u'|').replace(u' ', u'%20') + else: + desktop = self.controller.get_uno_desktop() + url = uno.systemPathToFileUrl(self.filepath) + if desktop is None: + return False + self.desktop = desktop + properties = [] + properties.append(self.create_property(u'Minimized', True)) + properties = tuple(properties) + try: + self.document = desktop.loadComponentFromURL(url, u'_blank', + 0, properties) + except: + log.exception(u'Failed to load presentation') + return False + self.presentation = self.document.getPresentation() + self.presentation.Display = \ + self.controller.plugin.renderManager.screens.current_display + 1 + self.control = None + self.create_thumbnails() + return True + + def create_thumbnails(self): + """ + Create thumbnail images for presentation + """ + log.debug(u'create thumbnails OpenOffice') + if self.check_thumbnails(): + return + if os.name == u'nt': + thumbdirurl = u'file:///' + self.get_temp_folder().replace( + u'\\', u'/').replace(u':', u'|').replace(u' ', u'%20') + else: + thumbdirurl = uno.systemPathToFileUrl(self.get_temp_folder()) + props = [] + props.append(self.create_property(u'FilterName', u'impress_png_Export')) + props = tuple(props) + doc = self.document + pages = doc.getDrawPages() + if not os.path.isdir(self.get_temp_folder()): + os.makedirs(self.get_temp_folder()) + for idx in range(pages.getCount()): + page = pages.getByIndex(idx) + doc.getCurrentController().setCurrentPage(page) + urlpath = u'%s/%s.png' % (thumbdirurl, unicode(idx + 1)) + path = os.path.join(self.get_temp_folder(), + unicode(idx + 1) + u'.png') + try: + doc.storeToURL(urlpath, props) + self.convert_thumbnail(path, idx + 1) + if os.path.exists(path): + os.remove(path) + except: + log.exception(u'%s - Unable to store openoffice preview' % path) + + def create_property(self, name, value): + """ + Create an OOo style property object which are passed into some + Uno methods + """ + log.debug(u'create property OpenOffice') + if os.name == u'nt': + prop = self.controller.manager.\ + Bridge_GetStruct(u'com.sun.star.beans.PropertyValue') + else: + prop = PropertyValue() + prop.Name = name + prop.Value = value + return prop + + def close_presentation(self): + """ + Close presentation and clean up objects + Triggered by new object being added to SlideController or OpenLP + being shutdown + """ + log.debug(u'close Presentation OpenOffice') + if self.document: + if self.presentation: + try: + self.presentation.end() + self.presentation = None + self.document.dispose() + except: + #We tried! + pass + self.document = None + self.controller.remove_doc(self) + + def is_loaded(self): + """ + Returns true if a presentation is loaded + """ + log.debug(u'is loaded OpenOffice') + #print "is_loaded " + if self.presentation is None or self.document is None: + #print "no present or document" + return False + try: + if self.document.getPresentation() is None: + #print "no getPresentation" + return False + except: + return False + return True + + def is_active(self): + """ + Returns true if a presentation is active and running + """ + log.debug(u'is active OpenOffice') + #print "is_active " + if not self.is_loaded(): + #print "False " + return False + #print "self.con ", self.control + if self.control is None: + return False + return True + + def unblank_screen(self): + """ + Unblanks the screen + """ + log.debug(u'unblank screen OpenOffice') + return self.control.resume() + + def blank_screen(self): + """ + Blanks the screen + """ + log.debug(u'blank screen OpenOffice') + self.control.blankScreen(0) + + def is_blank(self): + """ + Returns true if screen is blank + """ + log.debug(u'is blank OpenOffice') + if self.control: + return self.control.isPaused() + else: + return False + + def stop_presentation(self): + """ + Stop the presentation, remove from screen + """ + log.debug(u'stop presentation OpenOffice') + # deactivate should hide the screen according to docs, but doesn't + #self.control.deactivate() + self.presentation.end() + self.control = None + + def start_presentation(self): + """ + Start the presentation from the beginning + """ + log.debug(u'start presentation OpenOffice') + if self.control is None or not self.control.isRunning(): + self.presentation.start() + # start() returns before the getCurrentComponent is ready. + # Try for 5 seconds + i = 1 + while self.desktop.getCurrentComponent() is None and i < 50: + time.sleep(0.1) + i = i + 1 + self.control = \ + self.desktop.getCurrentComponent().Presentation.getController() + else: + self.control.activate() + self.goto_slide(1) + + def get_slide_number(self): + """ + Return the current slide number on the screen, from 1 + """ + return self.control.getCurrentSlideIndex() + 1 + + def get_slide_count(self): + """ + Return the total number of slides + """ + return self.document.getDrawPages().getCount() + + def goto_slide(self, slideno): + """ + Go to a specific slide (from 1) + """ + self.control.gotoSlideIndex(slideno-1) + + def next_step(self): + """ + Triggers the next effect of slide on the running presentation + """ + self.control.gotoNextEffect() + + def previous_step(self): + """ + Triggers the previous slide on the running presentation + """ + self.control.gotoPreviousSlide() + + def get_slide_text(self, slide_no): + """ + Returns the text on the slide + + ``slide_no`` + The slide the text is required for, starting at 1 + """ + doc = self.document + pages = doc.getDrawPages() + text = '' + page = pages.getByIndex(slide_no - 1) + for idx in range(page.getCount()): + shape = page.getByIndex(idx) + if shape.supportsService("com.sun.star.drawing.Text"): + text += shape.getString() + '\n' + return text + + def get_slide_notes(self, slide_no): + """ + Returns the text on the slide + + ``slide_no`` + The slide the notes are required for, starting at 1 + """ + doc = self.document + pages = doc.getDrawPages() + text = '' + page = pages.getByIndex(slide_no - 1) + notes = page.getNotesPage() + for idx in range(notes.getCount()): + shape = notes.getByIndex(idx) + if shape.supportsService("com.sun.star.drawing.Text"): + text += shape.getString() + '\n' + return text + diff --git a/openlp/plugins/presentations/lib/mediaitem.py b/openlp/plugins/presentations/lib/mediaitem.py index 25e02dfc9..3587cc0c4 100644 --- a/openlp/plugins/presentations/lib/mediaitem.py +++ b/openlp/plugins/presentations/lib/mediaitem.py @@ -131,7 +131,7 @@ class PresentationMediaItem(MediaManagerItem): self.listView.setIconSize(QtCore.QSize(88, 50)) list = SettingsManager.load_list( self.settingsSection, u'presentations') - self.loadList(list) + self.loadList(list, True) for item in self.controllers: #load the drop down selection if self.controllers[item].enabled: @@ -140,7 +140,7 @@ class PresentationMediaItem(MediaManagerItem): self.DisplayTypeComboBox.insertItem(0, self.Automatic) self.DisplayTypeComboBox.setCurrentIndex(0) - def loadList(self, list): + def loadList(self, list, initialLoad=False): """ Add presentations into the media manager This is called both on initial load of the plugin to populate with @@ -155,34 +155,39 @@ class PresentationMediaItem(MediaManagerItem): continue filename = os.path.split(unicode(file))[1] if titles.count(filename) > 0: - QtGui.QMessageBox.critical( - self, translate('PresentationPlugin.MediaItem', - 'File exists'), - translate('PresentationPlugin.MediaItem', - 'A presentation with that filename already exists.'), - QtGui.QMessageBox.Ok) + if not initialLoad: + QtGui.QMessageBox.critical( + self, translate('PresentationPlugin.MediaItem', + 'File exists'), + translate('PresentationPlugin.MediaItem', + 'A presentation with that filename already exists.'), + QtGui.QMessageBox.Ok) continue controller_name = self.findControllerByType(filename) - if not controller_name: - QtGui.QMessageBox.critical( - self, translate('PresentationPlugin.MediaItem', - 'Unsupported file'), - translate('PresentationPlugin.MediaItem', - 'This type of presentation is not supported'), - QtGui.QMessageBox.Ok) - continue - controller = self.controllers[controller_name] - doc = controller.add_doc(unicode(file)) - thumb = os.path.join(doc.get_thumbnail_folder(), u'icon.png') - preview = doc.get_thumbnail_path(1, True) - if not preview: - doc.load_presentation() + if controller_name: + controller = self.controllers[controller_name] + doc = controller.add_doc(unicode(file)) + thumb = os.path.join(doc.get_thumbnail_folder(), u'icon.png') preview = doc.get_thumbnail_path(1, True) - doc.close_presentation() - if preview and self.validate(preview, thumb): - icon = build_icon(thumb) + if not preview and not initialLoad: + doc.load_presentation() + preview = doc.get_thumbnail_path(1, True) + doc.close_presentation() + if preview and self.validate(preview, thumb): + icon = build_icon(thumb) + else: + icon = build_icon(u':/general/general_delete.png') else: - icon = build_icon(u':/general/general_delete.png') + if initialLoad: + icon = build_icon(u':/general/general_delete.png') + else: + QtGui.QMessageBox.critical( + self, translate('PresentationPlugin.MediaItem', + 'Unsupported file'), + translate('PresentationPlugin.MediaItem', + 'This type of presentation is not supported'), + QtGui.QMessageBox.Ok) + continue item_name = QtGui.QListWidgetItem(filename) item_name.setData(QtCore.Qt.UserRole, QtCore.QVariant(file)) item_name.setIcon(icon) diff --git a/openlp/plugins/presentations/lib/pptviewcontroller.py b/openlp/plugins/presentations/lib/pptviewcontroller.py index e3f945d45..2810c88d2 100644 --- a/openlp/plugins/presentations/lib/pptviewcontroller.py +++ b/openlp/plugins/presentations/lib/pptviewcontroller.py @@ -130,9 +130,11 @@ class PptviewDocument(PresentationDocument): rect = rendermanager.screens.current[u'size'] rect = RECT(rect.x(), rect.y(), rect.right(), rect.bottom()) filepath = str(self.filepath.replace(u'/', u'\\')) + if not os.path.isdir(self.get_temp_folder()): + os.makedirs(self.get_temp_folder()) self.pptid = self.controller.process.OpenPPT(filepath, None, rect, - str(self.get_temp_folder()) + '\\') - if self.pptid: + str(self.get_temp_folder()) + '\\slide') + if self.pptid >= 0: self.create_thumbnails() self.stop_presentation() return True @@ -147,8 +149,8 @@ class PptviewDocument(PresentationDocument): if self.check_thumbnails(): return for idx in range(self.get_slide_count()): - path = u'%s\\%s.bmp' % (self.get_temp_folder(), unicode(idx + 1)) - self.convert_image(path, idx) + path = u'%s\\slide%s.bmp' % (self.get_temp_folder(), unicode(idx + 1)) + self.convert_thumbnail(path, idx + 1) def close_presentation(self): """ From bff74507d7f36e72eff9b580abe0f69240cc0b5b Mon Sep 17 00:00:00 2001 From: Jonathan Corwin Date: Sun, 11 Jul 2010 21:53:53 +0100 Subject: [PATCH 13/21] Coding standards --- openlp/core/lib/__init__.py | 2 +- .../presentations/lib/impresscontroller.py | 936 +++++++++--------- .../presentations/presentationplugin.py | 1 + 3 files changed, 470 insertions(+), 469 deletions(-) diff --git a/openlp/core/lib/__init__.py b/openlp/core/lib/__init__.py index 7659a2889..f62778820 100644 --- a/openlp/core/lib/__init__.py +++ b/openlp/core/lib/__init__.py @@ -174,7 +174,7 @@ def resize_image(image, width, height): """ preview = QtGui.QImage(image) if not preview.isNull(): - if(preview.width() == width and preview.height == height): + if preview.width() == width and preview.height == height: return preview preview = preview.scaled(width, height, QtCore.Qt.KeepAspectRatio, QtCore.Qt.SmoothTransformation) diff --git a/openlp/plugins/presentations/lib/impresscontroller.py b/openlp/plugins/presentations/lib/impresscontroller.py index f3c4cb68c..30a95e3b8 100644 --- a/openlp/plugins/presentations/lib/impresscontroller.py +++ b/openlp/plugins/presentations/lib/impresscontroller.py @@ -1,468 +1,468 @@ -# -*- coding: utf-8 -*- -# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4 - -############################################################################### -# OpenLP - Open Source Lyrics Projection # -# --------------------------------------------------------------------------- # -# Copyright (c) 2008-2010 Raoul Snyman # -# Portions copyright (c) 2008-2010 Tim Bentley, Jonathan Corwin, Michael # -# Gorven, Scott Guerrieri, Christian Richter, Maikel Stuivenberg, Martin # -# Thompson, Jon Tibble, Carsten Tinggaard # -# --------------------------------------------------------------------------- # -# This program is free software; you can redistribute it and/or modify it # -# under the terms of the GNU General Public License as published by the Free # -# Software Foundation; version 2 of the License. # -# # -# This program is distributed in the hope that it will be useful, but WITHOUT # -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or # -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for # -# more details. # -# # -# You should have received a copy of the GNU General Public License along # -# with this program; if not, write to the Free Software Foundation, Inc., 59 # -# Temple Place, Suite 330, Boston, MA 02111-1307 USA # -############################################################################### - -# OOo API documentation: -# http://api.openoffice.org/docs/common/ref/com/sun/star/presentation/XSlideShowController.html -# http://wiki.services.openoffice.org/wiki/Documentation/DevGuide/ProUNO/Basic/Getting_Information_about_UNO_Objects#Inspecting_interfaces_during_debugging -# http://docs.go-oo.org/sd/html/classsd_1_1SlideShow.html -# http://www.oooforum.org/forum/viewtopic.phtml?t=5252 -# http://wiki.services.openoffice.org/wiki/Documentation/DevGuide/Working_with_Presentations -# http://mail.python.org/pipermail/python-win32/2008-January/006676.html -# http://www.linuxjournal.com/content/starting-stopping-and-connecting-openoffice-python -# http://nxsy.org/comparing-documents-with-openoffice-and-python - -import logging -import os -import time - -from openlp.core.lib import resize_image - -if os.name == u'nt': - from win32com.client import Dispatch - import pywintypes -else: - try: - import uno - from com.sun.star.beans import PropertyValue - uno_available = True - except ImportError: - uno_available = False - -from PyQt4 import QtCore - -from presentationcontroller import PresentationController, PresentationDocument - -log = logging.getLogger(__name__) - -class ImpressController(PresentationController): - """ - Class to control interactions with Impress presentations. - It creates the runtime environment, loads and closes the presentation as - well as triggering the correct activities based on the users input - """ - log.info(u'ImpressController loaded') - - def __init__(self, plugin): - """ - Initialise the class - """ - log.debug(u'Initialising') - PresentationController.__init__(self, plugin, u'Impress') - self.supports = [u'.odp'] - self.alsosupports = [u'.ppt', u'.pps', u'.pptx', u'.ppsx'] - self.process = None - self.desktop = None - self.manager = None - - def check_available(self): - """ - Impress is able to run on this machine - """ - log.debug(u'check_available') - if os.name == u'nt': - return self.get_com_servicemanager() is not None - else: - return uno_available - - def start_process(self): - """ - Loads a running version of OpenOffice in the background. - It is not displayed to the user but is available to the UNO interface - when required. - """ - log.debug(u'start process Openoffice') - if os.name == u'nt': - self.manager = self.get_com_servicemanager() - self.manager._FlagAsMethod(u'Bridge_GetStruct') - self.manager._FlagAsMethod(u'Bridge_GetValueObject') - else: - # -headless - cmd = u'openoffice.org -nologo -norestore -minimized -invisible -nofirststartwizard -accept="socket,host=localhost,port=2002;urp;"' - self.process = QtCore.QProcess() - self.process.startDetached(cmd) - self.process.waitForStarted() - - def get_uno_desktop(self): - """ - On non-Windows platforms, use Uno. Get the OpenOffice desktop - which will be used to manage impress - """ - log.debug(u'get UNO Desktop Openoffice') - ctx = None - loop = 0 - log.debug(u'get UNO Desktop Openoffice - getComponentContext') - context = uno.getComponentContext() - log.debug(u'get UNO Desktop Openoffice - createInstaneWithContext - ' - u'UnoUrlResolver') - resolver = context.ServiceManager.createInstanceWithContext( - u'com.sun.star.bridge.UnoUrlResolver', context) - while ctx is None and loop < 3: - try: - log.debug(u'get UNO Desktop Openoffice - resolve') - ctx = resolver.resolve(u'uno:socket,host=localhost,port=2002;' - u'urp;StarOffice.ComponentContext') - except: - log.exception(u'Unable to find running instance ') - self.start_process() - loop += 1 - try: - self.manager = ctx.ServiceManager - log.debug(u'get UNO Desktop Openoffice - createInstanceWithContext' - u' - Desktop') - desktop = self.manager.createInstanceWithContext( - "com.sun.star.frame.Desktop", ctx ) - return desktop - except: - log.exception(u'Failed to get UNO desktop') - return None - - def get_com_desktop(self): - """ - On Windows platforms, use COM. Return the desktop object which - will be used to manage Impress - """ - log.debug(u'get COM Desktop OpenOffice') - if not self.manager: - return None - return self.manager.createInstance(u'com.sun.star.frame.Desktop') - - def get_com_servicemanager(self): - """ - Return the OOo service manager for windows - """ - log.debug(u'get_com_servicemanager openoffice') - try: - return Dispatch(u'com.sun.star.ServiceManager') - except pywintypes.com_error: - log.exception(u'Failed to get COM service manager') - return None - - def kill(self): - """ - Called at system exit to clean up any running presentations - """ - log.debug(u'Kill OpenOffice') - while self.docs: - self.docs[0].close_presentation() - if os.name != u'nt': - desktop = self.get_uno_desktop() - else: - desktop = self.get_com_desktop() - #Sometimes we get a failure and desktop is None - if not desktop: - log.exception(u'Failed to terminate OpenOffice') - return - docs = desktop.getComponents() - if docs.hasElements(): - log.debug(u'OpenOffice not terminated') - else: - try: - desktop.terminate() - log.debug(u'OpenOffice killed') - except: - log.exception(u'Failed to terminate OpenOffice') - - def add_doc(self, name): - """ - Called when a new Impress document is opened - """ - log.debug(u'Add Doc OpenOffice') - doc = ImpressDocument(self, name) - self.docs.append(doc) - return doc - -class ImpressDocument(PresentationDocument): - """ - Class which holds information and controls a single presentation - """ - - def __init__(self, controller, presentation): - """ - Constructor, store information about the file and initialise - """ - log.debug(u'Init Presentation OpenOffice') - PresentationDocument.__init__(self, controller, presentation) - self.document = None - self.presentation = None - self.control = None - - def load_presentation(self): - """ - Called when a presentation is added to the SlideController. - It builds the environment, starts communcations with the background - OpenOffice task started earlier. If OpenOffice is not present is is - started. Once the environment is available the presentation is loaded - and started. - - ``presentation`` - The file name of the presentatios to the run. - """ - log.debug(u'Load Presentation OpenOffice') - #print "s.dsk1 ", self.desktop - if os.name == u'nt': - desktop = self.controller.get_com_desktop() - if desktop is None: - self.controller.start_process() - desktop = self.controller.get_com_desktop() - url = u'file:///' + self.filepath.replace(u'\\', u'/').replace( - u':', u'|').replace(u' ', u'%20') - else: - desktop = self.controller.get_uno_desktop() - url = uno.systemPathToFileUrl(self.filepath) - if desktop is None: - return False - self.desktop = desktop - properties = [] - properties.append(self.create_property(u'Minimized', True)) - properties = tuple(properties) - try: - self.document = desktop.loadComponentFromURL(url, u'_blank', - 0, properties) - except: - log.exception(u'Failed to load presentation') - return False - self.presentation = self.document.getPresentation() - self.presentation.Display = \ - self.controller.plugin.renderManager.screens.current_display + 1 - self.control = None - self.create_thumbnails() - return True - - def create_thumbnails(self): - """ - Create thumbnail images for presentation - """ - log.debug(u'create thumbnails OpenOffice') - if self.check_thumbnails(): - return - if os.name == u'nt': - thumbdirurl = u'file:///' + self.get_temp_folder().replace( - u'\\', u'/').replace(u':', u'|').replace(u' ', u'%20') - else: - thumbdirurl = uno.systemPathToFileUrl(self.get_temp_folder()) - props = [] - props.append(self.create_property(u'FilterName', u'impress_png_Export')) - props = tuple(props) - doc = self.document - pages = doc.getDrawPages() - if not os.path.isdir(self.get_temp_folder()): - os.makedirs(self.get_temp_folder()) - for idx in range(pages.getCount()): - page = pages.getByIndex(idx) - doc.getCurrentController().setCurrentPage(page) - urlpath = u'%s/%s.png' % (thumbdirurl, unicode(idx + 1)) - path = os.path.join(self.get_temp_folder(), - unicode(idx + 1) + u'.png') - try: - doc.storeToURL(urlpath, props) - self.convert_thumbnail(path, idx + 1) - if os.path.exists(path): - os.remove(path) - except: - log.exception(u'%s - Unable to store openoffice preview' % path) - - def create_property(self, name, value): - """ - Create an OOo style property object which are passed into some - Uno methods - """ - log.debug(u'create property OpenOffice') - if os.name == u'nt': - prop = self.controller.manager.\ - Bridge_GetStruct(u'com.sun.star.beans.PropertyValue') - else: - prop = PropertyValue() - prop.Name = name - prop.Value = value - return prop - - def close_presentation(self): - """ - Close presentation and clean up objects - Triggered by new object being added to SlideController or OpenLP - being shutdown - """ - log.debug(u'close Presentation OpenOffice') - if self.document: - if self.presentation: - try: - self.presentation.end() - self.presentation = None - self.document.dispose() - except: - #We tried! - pass - self.document = None - self.controller.remove_doc(self) - - def is_loaded(self): - """ - Returns true if a presentation is loaded - """ - log.debug(u'is loaded OpenOffice') - #print "is_loaded " - if self.presentation is None or self.document is None: - #print "no present or document" - return False - try: - if self.document.getPresentation() is None: - #print "no getPresentation" - return False - except: - return False - return True - - def is_active(self): - """ - Returns true if a presentation is active and running - """ - log.debug(u'is active OpenOffice') - #print "is_active " - if not self.is_loaded(): - #print "False " - return False - #print "self.con ", self.control - if self.control is None: - return False - return True - - def unblank_screen(self): - """ - Unblanks the screen - """ - log.debug(u'unblank screen OpenOffice') - return self.control.resume() - - def blank_screen(self): - """ - Blanks the screen - """ - log.debug(u'blank screen OpenOffice') - self.control.blankScreen(0) - - def is_blank(self): - """ - Returns true if screen is blank - """ - log.debug(u'is blank OpenOffice') - if self.control: - return self.control.isPaused() - else: - return False - - def stop_presentation(self): - """ - Stop the presentation, remove from screen - """ - log.debug(u'stop presentation OpenOffice') - # deactivate should hide the screen according to docs, but doesn't - #self.control.deactivate() - self.presentation.end() - self.control = None - - def start_presentation(self): - """ - Start the presentation from the beginning - """ - log.debug(u'start presentation OpenOffice') - if self.control is None or not self.control.isRunning(): - self.presentation.start() - # start() returns before the getCurrentComponent is ready. - # Try for 5 seconds - i = 1 - while self.desktop.getCurrentComponent() is None and i < 50: - time.sleep(0.1) - i = i + 1 - self.control = \ - self.desktop.getCurrentComponent().Presentation.getController() - else: - self.control.activate() - self.goto_slide(1) - - def get_slide_number(self): - """ - Return the current slide number on the screen, from 1 - """ - return self.control.getCurrentSlideIndex() + 1 - - def get_slide_count(self): - """ - Return the total number of slides - """ - return self.document.getDrawPages().getCount() - - def goto_slide(self, slideno): - """ - Go to a specific slide (from 1) - """ - self.control.gotoSlideIndex(slideno-1) - - def next_step(self): - """ - Triggers the next effect of slide on the running presentation - """ - self.control.gotoNextEffect() - - def previous_step(self): - """ - Triggers the previous slide on the running presentation - """ - self.control.gotoPreviousSlide() - - def get_slide_text(self, slide_no): - """ - Returns the text on the slide - - ``slide_no`` - The slide the text is required for, starting at 1 - """ - doc = self.document - pages = doc.getDrawPages() - text = '' - page = pages.getByIndex(slide_no - 1) - for idx in range(page.getCount()): - shape = page.getByIndex(idx) - if shape.supportsService("com.sun.star.drawing.Text"): - text += shape.getString() + '\n' - return text - - def get_slide_notes(self, slide_no): - """ - Returns the text on the slide - - ``slide_no`` - The slide the notes are required for, starting at 1 - """ - doc = self.document - pages = doc.getDrawPages() - text = '' - page = pages.getByIndex(slide_no - 1) - notes = page.getNotesPage() - for idx in range(notes.getCount()): - shape = notes.getByIndex(idx) - if shape.supportsService("com.sun.star.drawing.Text"): - text += shape.getString() + '\n' - return text - +# -*- coding: utf-8 -*- +# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4 + +############################################################################### +# OpenLP - Open Source Lyrics Projection # +# --------------------------------------------------------------------------- # +# Copyright (c) 2008-2010 Raoul Snyman # +# Portions copyright (c) 2008-2010 Tim Bentley, Jonathan Corwin, Michael # +# Gorven, Scott Guerrieri, Christian Richter, Maikel Stuivenberg, Martin # +# Thompson, Jon Tibble, Carsten Tinggaard # +# --------------------------------------------------------------------------- # +# This program is free software; you can redistribute it and/or modify it # +# under the terms of the GNU General Public License as published by the Free # +# Software Foundation; version 2 of the License. # +# # +# This program is distributed in the hope that it will be useful, but WITHOUT # +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or # +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for # +# more details. # +# # +# You should have received a copy of the GNU General Public License along # +# with this program; if not, write to the Free Software Foundation, Inc., 59 # +# Temple Place, Suite 330, Boston, MA 02111-1307 USA # +############################################################################### + +# OOo API documentation: +# http://api.openoffice.org/docs/common/ref/com/sun/star/presentation/XSlideShowController.html +# http://wiki.services.openoffice.org/wiki/Documentation/DevGuide/ProUNO/Basic/Getting_Information_about_UNO_Objects#Inspecting_interfaces_during_debugging +# http://docs.go-oo.org/sd/html/classsd_1_1SlideShow.html +# http://www.oooforum.org/forum/viewtopic.phtml?t=5252 +# http://wiki.services.openoffice.org/wiki/Documentation/DevGuide/Working_with_Presentations +# http://mail.python.org/pipermail/python-win32/2008-January/006676.html +# http://www.linuxjournal.com/content/starting-stopping-and-connecting-openoffice-python +# http://nxsy.org/comparing-documents-with-openoffice-and-python + +import logging +import os +import time + +from openlp.core.lib import resize_image + +if os.name == u'nt': + from win32com.client import Dispatch + import pywintypes +else: + try: + import uno + from com.sun.star.beans import PropertyValue + uno_available = True + except ImportError: + uno_available = False + +from PyQt4 import QtCore + +from presentationcontroller import PresentationController, PresentationDocument + +log = logging.getLogger(__name__) + +class ImpressController(PresentationController): + """ + Class to control interactions with Impress presentations. + It creates the runtime environment, loads and closes the presentation as + well as triggering the correct activities based on the users input + """ + log.info(u'ImpressController loaded') + + def __init__(self, plugin): + """ + Initialise the class + """ + log.debug(u'Initialising') + PresentationController.__init__(self, plugin, u'Impress') + self.supports = [u'.odp'] + self.alsosupports = [u'.ppt', u'.pps', u'.pptx', u'.ppsx'] + self.process = None + self.desktop = None + self.manager = None + + def check_available(self): + """ + Impress is able to run on this machine + """ + log.debug(u'check_available') + if os.name == u'nt': + return self.get_com_servicemanager() is not None + else: + return uno_available + + def start_process(self): + """ + Loads a running version of OpenOffice in the background. + It is not displayed to the user but is available to the UNO interface + when required. + """ + log.debug(u'start process Openoffice') + if os.name == u'nt': + self.manager = self.get_com_servicemanager() + self.manager._FlagAsMethod(u'Bridge_GetStruct') + self.manager._FlagAsMethod(u'Bridge_GetValueObject') + else: + # -headless + cmd = u'openoffice.org -nologo -norestore -minimized -invisible -nofirststartwizard -accept="socket,host=localhost,port=2002;urp;"' + self.process = QtCore.QProcess() + self.process.startDetached(cmd) + self.process.waitForStarted() + + def get_uno_desktop(self): + """ + On non-Windows platforms, use Uno. Get the OpenOffice desktop + which will be used to manage impress + """ + log.debug(u'get UNO Desktop Openoffice') + ctx = None + loop = 0 + log.debug(u'get UNO Desktop Openoffice - getComponentContext') + context = uno.getComponentContext() + log.debug(u'get UNO Desktop Openoffice - createInstaneWithContext - ' + u'UnoUrlResolver') + resolver = context.ServiceManager.createInstanceWithContext( + u'com.sun.star.bridge.UnoUrlResolver', context) + while ctx is None and loop < 3: + try: + log.debug(u'get UNO Desktop Openoffice - resolve') + ctx = resolver.resolve(u'uno:socket,host=localhost,port=2002;' + u'urp;StarOffice.ComponentContext') + except: + log.exception(u'Unable to find running instance ') + self.start_process() + loop += 1 + try: + self.manager = ctx.ServiceManager + log.debug(u'get UNO Desktop Openoffice - createInstanceWithContext' + u' - Desktop') + desktop = self.manager.createInstanceWithContext( + "com.sun.star.frame.Desktop", ctx ) + return desktop + except: + log.exception(u'Failed to get UNO desktop') + return None + + def get_com_desktop(self): + """ + On Windows platforms, use COM. Return the desktop object which + will be used to manage Impress + """ + log.debug(u'get COM Desktop OpenOffice') + if not self.manager: + return None + return self.manager.createInstance(u'com.sun.star.frame.Desktop') + + def get_com_servicemanager(self): + """ + Return the OOo service manager for windows + """ + log.debug(u'get_com_servicemanager openoffice') + try: + return Dispatch(u'com.sun.star.ServiceManager') + except pywintypes.com_error: + log.exception(u'Failed to get COM service manager') + return None + + def kill(self): + """ + Called at system exit to clean up any running presentations + """ + log.debug(u'Kill OpenOffice') + while self.docs: + self.docs[0].close_presentation() + if os.name != u'nt': + desktop = self.get_uno_desktop() + else: + desktop = self.get_com_desktop() + #Sometimes we get a failure and desktop is None + if not desktop: + log.exception(u'Failed to terminate OpenOffice') + return + docs = desktop.getComponents() + if docs.hasElements(): + log.debug(u'OpenOffice not terminated') + else: + try: + desktop.terminate() + log.debug(u'OpenOffice killed') + except: + log.exception(u'Failed to terminate OpenOffice') + + def add_doc(self, name): + """ + Called when a new Impress document is opened + """ + log.debug(u'Add Doc OpenOffice') + doc = ImpressDocument(self, name) + self.docs.append(doc) + return doc + +class ImpressDocument(PresentationDocument): + """ + Class which holds information and controls a single presentation + """ + + def __init__(self, controller, presentation): + """ + Constructor, store information about the file and initialise + """ + log.debug(u'Init Presentation OpenOffice') + PresentationDocument.__init__(self, controller, presentation) + self.document = None + self.presentation = None + self.control = None + + def load_presentation(self): + """ + Called when a presentation is added to the SlideController. + It builds the environment, starts communcations with the background + OpenOffice task started earlier. If OpenOffice is not present is is + started. Once the environment is available the presentation is loaded + and started. + + ``presentation`` + The file name of the presentatios to the run. + """ + log.debug(u'Load Presentation OpenOffice') + #print "s.dsk1 ", self.desktop + if os.name == u'nt': + desktop = self.controller.get_com_desktop() + if desktop is None: + self.controller.start_process() + desktop = self.controller.get_com_desktop() + url = u'file:///' + self.filepath.replace(u'\\', u'/').replace( + u':', u'|').replace(u' ', u'%20') + else: + desktop = self.controller.get_uno_desktop() + url = uno.systemPathToFileUrl(self.filepath) + if desktop is None: + return False + self.desktop = desktop + properties = [] + properties.append(self.create_property(u'Minimized', True)) + properties = tuple(properties) + try: + self.document = desktop.loadComponentFromURL(url, u'_blank', + 0, properties) + except: + log.exception(u'Failed to load presentation') + return False + self.presentation = self.document.getPresentation() + self.presentation.Display = \ + self.controller.plugin.renderManager.screens.current_display + 1 + self.control = None + self.create_thumbnails() + return True + + def create_thumbnails(self): + """ + Create thumbnail images for presentation + """ + log.debug(u'create thumbnails OpenOffice') + if self.check_thumbnails(): + return + if os.name == u'nt': + thumbdirurl = u'file:///' + self.get_temp_folder().replace( + u'\\', u'/').replace(u':', u'|').replace(u' ', u'%20') + else: + thumbdirurl = uno.systemPathToFileUrl(self.get_temp_folder()) + props = [] + props.append(self.create_property(u'FilterName', u'impress_png_Export')) + props = tuple(props) + doc = self.document + pages = doc.getDrawPages() + if not os.path.isdir(self.get_temp_folder()): + os.makedirs(self.get_temp_folder()) + for idx in range(pages.getCount()): + page = pages.getByIndex(idx) + doc.getCurrentController().setCurrentPage(page) + urlpath = u'%s/%s.png' % (thumbdirurl, unicode(idx + 1)) + path = os.path.join(self.get_temp_folder(), + unicode(idx + 1) + u'.png') + try: + doc.storeToURL(urlpath, props) + self.convert_thumbnail(path, idx + 1) + if os.path.exists(path): + os.remove(path) + except: + log.exception(u'%s - Unable to store openoffice preview' % path) + + def create_property(self, name, value): + """ + Create an OOo style property object which are passed into some + Uno methods + """ + log.debug(u'create property OpenOffice') + if os.name == u'nt': + prop = self.controller.manager.\ + Bridge_GetStruct(u'com.sun.star.beans.PropertyValue') + else: + prop = PropertyValue() + prop.Name = name + prop.Value = value + return prop + + def close_presentation(self): + """ + Close presentation and clean up objects + Triggered by new object being added to SlideController or OpenLP + being shutdown + """ + log.debug(u'close Presentation OpenOffice') + if self.document: + if self.presentation: + try: + self.presentation.end() + self.presentation = None + self.document.dispose() + except: + #We tried! + pass + self.document = None + self.controller.remove_doc(self) + + def is_loaded(self): + """ + Returns true if a presentation is loaded + """ + log.debug(u'is loaded OpenOffice') + #print "is_loaded " + if self.presentation is None or self.document is None: + #print "no present or document" + return False + try: + if self.document.getPresentation() is None: + #print "no getPresentation" + return False + except: + return False + return True + + def is_active(self): + """ + Returns true if a presentation is active and running + """ + log.debug(u'is active OpenOffice') + #print "is_active " + if not self.is_loaded(): + #print "False " + return False + #print "self.con ", self.control + if self.control is None: + return False + return True + + def unblank_screen(self): + """ + Unblanks the screen + """ + log.debug(u'unblank screen OpenOffice') + return self.control.resume() + + def blank_screen(self): + """ + Blanks the screen + """ + log.debug(u'blank screen OpenOffice') + self.control.blankScreen(0) + + def is_blank(self): + """ + Returns true if screen is blank + """ + log.debug(u'is blank OpenOffice') + if self.control: + return self.control.isPaused() + else: + return False + + def stop_presentation(self): + """ + Stop the presentation, remove from screen + """ + log.debug(u'stop presentation OpenOffice') + # deactivate should hide the screen according to docs, but doesn't + #self.control.deactivate() + self.presentation.end() + self.control = None + + def start_presentation(self): + """ + Start the presentation from the beginning + """ + log.debug(u'start presentation OpenOffice') + if self.control is None or not self.control.isRunning(): + self.presentation.start() + # start() returns before the getCurrentComponent is ready. + # Try for 5 seconds + i = 1 + while self.desktop.getCurrentComponent() is None and i < 50: + time.sleep(0.1) + i = i + 1 + self.control = \ + self.desktop.getCurrentComponent().Presentation.getController() + else: + self.control.activate() + self.goto_slide(1) + + def get_slide_number(self): + """ + Return the current slide number on the screen, from 1 + """ + return self.control.getCurrentSlideIndex() + 1 + + def get_slide_count(self): + """ + Return the total number of slides + """ + return self.document.getDrawPages().getCount() + + def goto_slide(self, slideno): + """ + Go to a specific slide (from 1) + """ + self.control.gotoSlideIndex(slideno-1) + + def next_step(self): + """ + Triggers the next effect of slide on the running presentation + """ + self.control.gotoNextEffect() + + def previous_step(self): + """ + Triggers the previous slide on the running presentation + """ + self.control.gotoPreviousSlide() + + def get_slide_text(self, slide_no): + """ + Returns the text on the slide + + ``slide_no`` + The slide the text is required for, starting at 1 + """ + doc = self.document + pages = doc.getDrawPages() + text = '' + page = pages.getByIndex(slide_no - 1) + for idx in range(page.getCount()): + shape = page.getByIndex(idx) + if shape.supportsService("com.sun.star.drawing.Text"): + text += shape.getString() + '\n' + return text + + def get_slide_notes(self, slide_no): + """ + Returns the text on the slide + + ``slide_no`` + The slide the notes are required for, starting at 1 + """ + doc = self.document + pages = doc.getDrawPages() + text = '' + page = pages.getByIndex(slide_no - 1) + notes = page.getNotesPage() + for idx in range(notes.getCount()): + shape = notes.getByIndex(idx) + if shape.supportsService("com.sun.star.drawing.Text"): + text += shape.getString() + '\n' + return text + diff --git a/openlp/plugins/presentations/presentationplugin.py b/openlp/plugins/presentations/presentationplugin.py index 5369515b3..50784ed51 100644 --- a/openlp/plugins/presentations/presentationplugin.py +++ b/openlp/plugins/presentations/presentationplugin.py @@ -138,3 +138,4 @@ class PresentationPlugin(Plugin): 'programs. The choice of available presentation programs is ' 'available to the user in a drop down box.') return about_text + From 4bffb39567133d1b21694b67ed49c63c9e58a4a9 Mon Sep 17 00:00:00 2001 From: Jon Tibble Date: Mon, 12 Jul 2010 11:59:48 +0100 Subject: [PATCH 14/21] Import fixes --- openlp/plugins/presentations/lib/impresscontroller.py | 2 -- openlp/plugins/presentations/lib/mediaitem.py | 1 - 2 files changed, 3 deletions(-) diff --git a/openlp/plugins/presentations/lib/impresscontroller.py b/openlp/plugins/presentations/lib/impresscontroller.py index 30a95e3b8..0b401f484 100644 --- a/openlp/plugins/presentations/lib/impresscontroller.py +++ b/openlp/plugins/presentations/lib/impresscontroller.py @@ -37,8 +37,6 @@ import logging import os import time -from openlp.core.lib import resize_image - if os.name == u'nt': from win32com.client import Dispatch import pywintypes diff --git a/openlp/plugins/presentations/lib/mediaitem.py b/openlp/plugins/presentations/lib/mediaitem.py index 3587cc0c4..b9ee2b79f 100644 --- a/openlp/plugins/presentations/lib/mediaitem.py +++ b/openlp/plugins/presentations/lib/mediaitem.py @@ -30,7 +30,6 @@ from PyQt4 import QtCore, QtGui from openlp.core.lib import MediaManagerItem, BaseListWithDnD, build_icon, \ SettingsManager, translate, check_item_selected -from openlp.core.utils import AppLocation from openlp.plugins.presentations.lib import MessageListener log = logging.getLogger(__name__) From 75f9f809a829e365b3a0f1ec19a983b7b2481eda Mon Sep 17 00:00:00 2001 From: andreas Date: Mon, 12 Jul 2010 15:18:07 +0200 Subject: [PATCH 15/21] use database for checking --- .../songs/forms/songmaintenanceform.py | 163 +++++++++++------- 1 file changed, 98 insertions(+), 65 deletions(-) diff --git a/openlp/plugins/songs/forms/songmaintenanceform.py b/openlp/plugins/songs/forms/songmaintenanceform.py index f9d6e253d..d50bf747f 100644 --- a/openlp/plugins/songs/forms/songmaintenanceform.py +++ b/openlp/plugins/songs/forms/songmaintenanceform.py @@ -24,6 +24,7 @@ ############################################################################### from PyQt4 import QtGui, QtCore +from sqlalchemy.sql import and_ from openlp.core.lib import translate from openlp.plugins.songs.forms import AuthorsForm, TopicsForm, SongBookForm @@ -136,57 +137,74 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog): def checkAuthor(self, new_author, edit=False): """ - Returns True when the given Author is already in the list elsewise False. + Returns False when the given Author is already in the list elsewise + True. """ - authors = self.songmanager.get_all_objects(Author) - author_exists = False - for author in authors: - if author.fist_name == new_author.first_name and \ - author.last_name == new_author.last_name and \ - author.display_name == new_author.display_name: - author_exists = True - #If we edit an existing Author, we need to make sure that we do - #not return True when nothing has changed (because this would - #cause an error message later on) - if edit: - if new_author.id == author.id: - author_exists = False - return author_exists + authors = self.songmanager.get_all_objects_filtered(Author, + and_( + Author.first_name == new_author.first_name, + Author.last_name == new_author.last_name, + Author.display_name == new_author.display_name + ) + ) + if len(authors) > 0: + # If we edit an existing Author, we need to make sure that we do + # not return False when nothing has changed (because this would + # cause an error message later on). + if edit: + if authors[0].id == new_author.id: + return True + else: + return False + else: + return False + else: + return True def checkTopic(self, new_topic, edit=False): """ - Returns True when the given Topic is already in the list elsewise False. + Returns False when the given Topic is already in the list elsewise True. """ - topics = self.songmanager.get_all_objects(Topic) - topic_exists = False - for topic in topics: - if topic.name == new_topic.name: - topic_exists = True - #If we edit an existing Topic, we need to make sure that we do - #not return True when nothing has changed (because this would - #cause an error message later on) - if edit: - if new_topic.id == topic.id: - topic_exists = False - return topic_exists + topics = self.songmanager.get_all_objects_filtered(Topic, + Topic.name == new_topic.name + ) + if len(topics) > 0: + # If we edit an existing Topic, we need to make sure that we do + # not return False when nothing has changed (because this would + # cause an error message later on). + if edit: + if topics[0].id == new_topic.id: + return True + else: + return False + else: + return False + else: + return True def checkBook(self, new_book, edit=False): """ - Returns True when the given Book is already in the list elsewise False. + Returns False when the given Book is already in the list elsewise True. """ - books = self.songmanager.get_all_objects(Book) - book_exists = False - for book in books: - if book.publisher == new_book.publisher and \ - book.name == new_book.name: - book_exists = True - #If we edit an existing Book, we need to make sure that we do - #not return True when nothing has changed (because this would - #cause an error message later on) + books = self.songmanager.get_all_objects_filtered(Book, + and_( + Book.name == new_book.name, + Book.publisher == new_book.publisher + ) + ) + if len(books) > 0: + # If we edit an existing Book, we need to make sure that we do + # not return False when nothing has changed (because this would + # cause an error message later on). if edit: - if new_book.id == book.id: - book_exists = False - return book_exists + if books[0].id == new_book.id: + return True + else: + return False + else: + return False + else: + return True def onAuthorAddButtonClick(self): self.authorform.setAutoDisplayName(True) @@ -195,9 +213,9 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog): first_name=unicode(self.authorform.FirstNameEdit.text()), last_name=unicode(self.authorform.LastNameEdit.text()), display_name=unicode(self.authorform.DisplayEdit.text())) - if not self.checkAuthor(author) and \ - self.songmanager.save_object(author): - self.resetAuthors() + if self.checkAuthor(author): + if self.songmanager.save_object(author): + self.resetAuthors() else: QtGui.QMessageBox.critical(self, translate('SongsPlugin.SongMaintenanceForm', 'Error'), @@ -207,9 +225,9 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog): def onTopicAddButtonClick(self): if self.topicform.exec_(): topic = Topic.populate(name=unicode(self.topicform.NameEdit.text())) - if not self.checkTopic(topic) and \ - self.songmanager.save_object(topic): - self.resetTopics() + if self.checkTopic(topic): + if self.songmanager.save_object(topic): + self.resetTopics() else: QtGui.QMessageBox.critical(self, translate('SongsPlugin.SongMaintenanceForm', 'Error'), @@ -221,8 +239,9 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog): book = Book.populate( name=unicode(self.bookform.NameEdit.text()), publisher=unicode(self.bookform.PublisherEdit.text())) - if not self.checkBook(book) and self.songmanager.save_object(book): - self.resetBooks() + if self.checkBook(book): + if self.songmanager.save_object(book): + self.resetBooks() else: QtGui.QMessageBox.critical(self, translate('SongsPlugin.SongMaintenanceForm', 'Error'), @@ -233,27 +252,30 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog): author_id = self._getCurrentItemId(self.AuthorsListWidget) if author_id != -1: author = self.songmanager.get_object(Author, author_id) - # Just make sure none of the fields is None - if author.first_name is None: - author.first_name = u'' - if author.last_name is None: - author.last_name = u'' - if author.display_name is None: - author.display_name = u'' self.authorform.setAutoDisplayName(False) self.authorform.FirstNameEdit.setText(author.first_name) self.authorform.LastNameEdit.setText(author.last_name) self.authorform.DisplayEdit.setText(author.display_name) + # Save the author's first and last name as well as the display name + # for the case that they have to be restored. + temp_first_name = author.first_name + temp_last_name = author.last_name + temp_display_name = author.display_name if self.authorform.exec_(False): author.first_name = unicode( self.authorform.FirstNameEdit.text()) author.last_name = unicode(self.authorform.LastNameEdit.text()) author.display_name = unicode( self.authorform.DisplayEdit.text()) - if not self.checkAuthor(author, True) and \ - self.songmanager.save_object(author): - self.resetAuthors() + if self.checkAuthor(author, True): + if self.songmanager.save_object(author): + self.resetAuthors() else: + # We restore the author's old first and last name as well as + # his display name. + author.first_name = temp_first_name + author.last_name = temp_last_name + author.display_name = temp_display_name QtGui.QMessageBox.critical(self, translate('SongsPlugin.SongMaintenanceForm', 'Error'), translate('SongsPlugin.SongMaintenanceForm', @@ -264,12 +286,16 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog): if topic_id != -1: topic = self.songmanager.get_object(Topic, topic_id) self.topicform.NameEdit.setText(topic.name) + # Save the topic's name for the case that he has to be restored. + temp_name = topic.name if self.topicform.exec_(False): topic.name = unicode(self.topicform.NameEdit.text()) - if not self.checkTopic(topic, True) and \ - self.songmanager.save_object(topic): - self.resetTopics() + if self.checkTopic(topic, True): + if self.songmanager.save_object(topic): + self.resetTopics() else: + # We restore the topics's old name. + topic.name = temp_name QtGui.QMessageBox.critical(self, translate('SongsPlugin.SongMaintenanceForm', 'Error'), translate('SongsPlugin.SongMaintenanceForm', @@ -281,13 +307,20 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog): book = self.songmanager.get_object(Book, book_id) self.bookform.NameEdit.setText(book.name) self.bookform.PublisherEdit.setText(book.publisher) + # Save the book's name and publisher for the case that they have to + # be restored. + temp_name = book.name + temp_publisher = book.publisher if self.bookform.exec_(False): book.name = unicode(self.bookform.NameEdit.text()) book.publisher = unicode(self.bookform.PublisherEdit.text()) - if not self.checkBook(book, True) and \ - self.songmanager.save_object(book): - self.resetBooks() + if self.checkBook(book, True): + if self.songmanager.save_object(book): + self.resetBooks() else: + # We restore the book's old name and publisher. + book.name = temp_name + book.publisher = temp_publisher QtGui.QMessageBox.critical(self, translate('SongsPlugin.SongMaintenanceForm', 'Error'), translate('SongsPlugin.SongMaintenanceForm', From 46d561b320e3bd3f217a96c71954cf83ed893c2c Mon Sep 17 00:00:00 2001 From: Jon Tibble Date: Mon, 12 Jul 2010 15:32:48 +0100 Subject: [PATCH 16/21] Add saving current media plugin (Bug #596540) --- openlp/core/ui/advancedtab.py | 43 +++++++++++++++++++++++++---------- openlp/core/ui/mainwindow.py | 10 ++++++++ 2 files changed, 41 insertions(+), 12 deletions(-) diff --git a/openlp/core/ui/advancedtab.py b/openlp/core/ui/advancedtab.py index d2f0c22b5..b2efd92cd 100644 --- a/openlp/core/ui/advancedtab.py +++ b/openlp/core/ui/advancedtab.py @@ -53,22 +53,32 @@ class AdvancedTab(SettingsTab): self.leftLayout = QtGui.QVBoxLayout(self.leftWidget) self.leftLayout.setSpacing(8) self.leftLayout.setMargin(0) - self.recentGroupBox = QtGui.QGroupBox(self.leftWidget) - self.recentGroupBox.setObjectName(u'recentGroupBox') - self.recentGroupBox.setGeometry(QtCore.QRect(0, 0, 220, 57)) - self.recentGroupBox.setMaximumSize(QtCore.QSize(220, 57)) - self.recentLayout = QtGui.QHBoxLayout(self.recentGroupBox) + self.uiGroupBox = QtGui.QGroupBox(self.leftWidget) + self.uiGroupBox.setObjectName(u'uiGroupBox') + self.uiGroupBox.setMaximumWidth(260) + self.uiLayout = QtGui.QVBoxLayout(self.uiGroupBox) + self.uiLayout.setSpacing(8) + self.uiLayout.setMargin(6) + self.uiLayout.setObjectName(u'uiLayout') + self.recentLayout = QtGui.QHBoxLayout() self.recentLayout.setSpacing(8) - self.recentLayout.setMargin(6) + self.recentLayout.setMargin(0) self.recentLayout.setObjectName(u'recentLayout') - self.recentLabel = QtGui.QLabel(self.recentGroupBox) + self.recentLabel = QtGui.QLabel(self.uiGroupBox) self.recentLabel.setObjectName(u'recentLabel') self.recentLayout.addWidget(self.recentLabel) - self.recentSpinBox = QtGui.QSpinBox(self.recentGroupBox) - self.recentSpinBox.setMinimum(0) + self.recentSpinBox = QtGui.QSpinBox(self.uiGroupBox) self.recentSpinBox.setObjectName(u'recentSpinBox') + self.recentSpinBox.setMinimum(0) self.recentLayout.addWidget(self.recentSpinBox) - self.leftLayout.addWidget(self.recentGroupBox) + self.recentSpacer = QtGui.QSpacerItem(50, 20, + QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum) + self.recentLayout.addItem(self.recentSpacer) + self.uiLayout.addLayout(self.recentLayout) + self.mediaPluginCheckBox = QtGui.QCheckBox(self.uiGroupBox) + self.mediaPluginCheckBox.setObjectName(u'mediaPluginCheckBox') + self.uiLayout.addWidget(self.mediaPluginCheckBox) + self.leftLayout.addWidget(self.uiGroupBox) # self.sharedDirGroupBox = QtGui.QGroupBox(self.leftWidget) # self.sharedDirGroupBox.setObjectName(u'sharedDirGroupBox') # self.sharedDirGroupBox.setGeometry(QtCore.QRect(0, 65, 500, 85)) @@ -108,6 +118,8 @@ class AdvancedTab(SettingsTab): # self.databaseLayout.setSpacing(8) # self.databaseLayout.setMargin(8) # self.rightLayout.addWidget(self.databaseGroupBox) +# self.rightSpacer = QtGui.QSpacerItem(20, 40, +# QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding) # self.advancedTabLayout.addWidget(self.rightWidget) # QtCore.QObject.connect(self.sharedCheckBox, # QtCore.SIGNAL(u'stateChanged(int)'), self.onSharedCheckBoxChanged) @@ -116,9 +128,11 @@ class AdvancedTab(SettingsTab): """ Setup the interface translation strings. """ - self.recentGroupBox.setTitle(translate('AdvancedTab', 'Recent Files')) + self.uiGroupBox.setTitle(translate('AdvancedTab', 'UI Settings')) self.recentLabel.setText( - translate('AdvancedTab', 'Number of recent files to list:')) + translate('AdvancedTab', 'Number of recent files to display:')) + self.mediaPluginCheckBox.setText(translate('AdvancedTab', + 'Save currently selected media manager plugin')) # self.sharedDirGroupBox.setTitle( # translate('AdvancedTab', 'Central Data Store')) # self.sharedCheckBox.setText( @@ -140,6 +154,9 @@ class AdvancedTab(SettingsTab): u'max recent files', QtCore.QVariant(20)).toInt()[0]) self.recentSpinBox.setValue(settings.value(u'recent file count', QtCore.QVariant(4)).toInt()[0]) + self.mediaPluginCheckBox.setChecked( + settings.value(u'save current plugin', + QtCore.QVariant(False)).toBool()) settings.endGroup() def save(self): @@ -150,6 +167,8 @@ class AdvancedTab(SettingsTab): settings.beginGroup(self.settingsSection) settings.setValue(u'recent file count', QtCore.QVariant(self.recentSpinBox.value())) + settings.setValue(u'save current plugin', + QtCore.QVariant(self.mediaPluginCheckBox.isChecked())) settings.endGroup() def onSharedCheckBoxChanged(self, checked): diff --git a/openlp/core/ui/mainwindow.py b/openlp/core/ui/mainwindow.py index ccef8a5ea..ec34a483b 100644 --- a/openlp/core/ui/mainwindow.py +++ b/openlp/core/ui/mainwindow.py @@ -658,6 +658,12 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow): log.info(u'Load Themes') self.ThemeManagerContents.loadThemes() log.info(u'Load data from Settings') + if QtCore.QSettings().value(u'advanced/save current plugin', + QtCore.QVariant(False)).toBool(): + savedPlugin = QtCore.QSettings().value( + u'advanced/current media plugin', QtCore.QVariant()).toInt()[0] + if savedPlugin != -1: + self.MediaToolBox.setCurrentIndex(savedPlugin) self.settingsForm.postSetUp() def setAutoLanguage(self, value): @@ -820,6 +826,10 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow): """ # 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())) # Call the cleanup method to shutdown plugins. log.info(u'cleanup plugins') self.plugin_manager.finalise_plugins() From eac602389c1d31fac35c5d0585b4149d76721588 Mon Sep 17 00:00:00 2001 From: Jon Tibble Date: Mon, 12 Jul 2010 15:45:55 +0100 Subject: [PATCH 17/21] Code layout tweaks --- openlp/core/ui/amendthemeform.py | 37 +++++++++++++++++++------------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/openlp/core/ui/amendthemeform.py b/openlp/core/ui/amendthemeform.py index 1c2658dc3..64c6a4021 100644 --- a/openlp/core/ui/amendthemeform.py +++ b/openlp/core/ui/amendthemeform.py @@ -36,15 +36,21 @@ from amendthemedialog import Ui_AmendThemeDialog log = logging.getLogger(u'AmendThemeForm') class AmendThemeForm(QtGui.QDialog, Ui_AmendThemeDialog): - + """ + The :class:`AmendThemeForm` class provides the user interface to set up + new and edit existing themes. + """ def __init__(self, parent): + """ + Initialise the theme editor user interface + """ QtGui.QDialog.__init__(self, parent) self.thememanager = parent self.path = None self.theme = ThemeXML() self.setupUi(self) - #define signals - #Buttons + # define signals + # Buttons QtCore.QObject.connect(self.Color1PushButton, QtCore.SIGNAL(u'pressed()'), self.onColor1PushButtonClicked) QtCore.QObject.connect(self.Color2PushButton, @@ -60,7 +66,7 @@ class AmendThemeForm(QtGui.QDialog, Ui_AmendThemeDialog): QtCore.SIGNAL(u'pressed()'), self.onShadowColorPushButtonClicked) QtCore.QObject.connect(self.ImageToolButton, QtCore.SIGNAL(u'pressed()'), self.onImageToolButtonClicked) - #Combo boxes + # Combo boxes QtCore.QObject.connect(self.BackgroundComboBox, QtCore.SIGNAL(u'activated(int)'), self.onBackgroundComboBoxSelected) QtCore.QObject.connect(self.BackgroundTypeComboBox, @@ -82,16 +88,13 @@ class AmendThemeForm(QtGui.QDialog, Ui_AmendThemeDialog): QtCore.SIGNAL(u'activated(int)'), self.onHorizontalComboBoxSelected) QtCore.QObject.connect(self.VerticalComboBox, QtCore.SIGNAL(u'activated(int)'), self.onVerticalComboBoxSelected) - #Spin boxes + # Spin boxes QtCore.QObject.connect(self.FontMainSizeSpinBox, QtCore.SIGNAL(u'editingFinished()'), self.onFontMainSizeSpinBoxChanged) QtCore.QObject.connect(self.FontFooterSizeSpinBox, QtCore.SIGNAL(u'editingFinished()'), self.onFontFooterSizeSpinBoxChanged) - QtCore.QObject.connect(self.FontMainDefaultCheckBox, - QtCore.SIGNAL(u'stateChanged(int)'), - self.onFontMainDefaultCheckBoxChanged) QtCore.QObject.connect(self.FontMainXSpinBox, QtCore.SIGNAL(u'editingFinished()'), self.onFontMainXSpinBoxChanged) QtCore.QObject.connect(self.FontMainYSpinBox, @@ -108,9 +111,6 @@ class AmendThemeForm(QtGui.QDialog, Ui_AmendThemeDialog): QtCore.QObject.connect(self.FontMainLineSpacingSpinBox, QtCore.SIGNAL(u'editingFinished()'), self.onFontMainLineSpacingSpinBoxChanged) - QtCore.QObject.connect(self.FontFooterDefaultCheckBox, - QtCore.SIGNAL(u'stateChanged(int)'), - self.onFontFooterDefaultCheckBoxChanged) QtCore.QObject.connect(self.FontFooterXSpinBox, QtCore.SIGNAL(u'editingFinished()'), self.onFontFooterXSpinBoxChanged) @@ -123,16 +123,23 @@ class AmendThemeForm(QtGui.QDialog, Ui_AmendThemeDialog): QtCore.QObject.connect(self.FontFooterHeightSpinBox, QtCore.SIGNAL(u'editingFinished()'), self.onFontFooterHeightSpinBoxChanged) - QtCore.QObject.connect(self.OutlineCheckBox, - QtCore.SIGNAL(u'stateChanged(int)'), self.onOutlineCheckBoxChanged) QtCore.QObject.connect(self.ShadowSpinBox, QtCore.SIGNAL(u'editingFinished()'), self.onShadowSpinBoxChanged) - QtCore.QObject.connect(self.ShadowCheckBox, - QtCore.SIGNAL(u'stateChanged(int)'), self.onShadowCheckBoxChanged) QtCore.QObject.connect(self.OutlineSpinBox, QtCore.SIGNAL(u'editingFinished()'), self.onOutlineSpinBoxChanged) + # CheckBoxes + QtCore.QObject.connect(self.FontMainDefaultCheckBox, + QtCore.SIGNAL(u'stateChanged(int)'), + self.onFontMainDefaultCheckBoxChanged) + QtCore.QObject.connect(self.FontFooterDefaultCheckBox, + QtCore.SIGNAL(u'stateChanged(int)'), + self.onFontFooterDefaultCheckBoxChanged) + QtCore.QObject.connect(self.OutlineCheckBox, + QtCore.SIGNAL(u'stateChanged(int)'), self.onOutlineCheckBoxChanged) + QtCore.QObject.connect(self.ShadowCheckBox, + QtCore.SIGNAL(u'stateChanged(int)'), self.onShadowCheckBoxChanged) QtCore.QObject.connect(self.SlideTransitionCheckBox, QtCore.SIGNAL(u'stateChanged(int)'), self.onSlideTransitionCheckBoxChanged) From 02b33ed660df31a2ec486d4b12d0e2c0491fb173 Mon Sep 17 00:00:00 2001 From: Jon Tibble Date: Mon, 12 Jul 2010 23:32:46 +0100 Subject: [PATCH 18/21] Theme background images (Bug #594911) --- openlp/core/ui/amendthemedialog.py | 4 ++-- openlp/core/ui/amendthemeform.py | 2 +- openlp/core/ui/thememanager.py | 9 +++++++++ 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/openlp/core/ui/amendthemedialog.py b/openlp/core/ui/amendthemedialog.py index a5cd69610..45fdf236a 100644 --- a/openlp/core/ui/amendthemedialog.py +++ b/openlp/core/ui/amendthemedialog.py @@ -130,9 +130,9 @@ class Ui_AmendThemeDialog(object): self.ImageLineEdit.setObjectName(u'ImageLineEdit') self.horizontalLayout_2.addWidget(self.ImageLineEdit) self.ImageToolButton = QtGui.QToolButton(self.ImageFilenameWidget) - icon1 = build_icon(u':/general/general_open.png') - self.ImageToolButton.setIcon(icon1) + self.ImageToolButton.setIcon(build_icon(u':/general/general_open.png')) self.ImageToolButton.setObjectName(u'ImageToolButton') + self.ImageToolButton.setAutoRaise(True) self.horizontalLayout_2.addWidget(self.ImageToolButton) self.BackgroundLayout.setWidget(4, QtGui.QFormLayout.FieldRole, self.ImageFilenameWidget) diff --git a/openlp/core/ui/amendthemeform.py b/openlp/core/ui/amendthemeform.py index 1c2658dc3..c397d6f42 100644 --- a/openlp/core/ui/amendthemeform.py +++ b/openlp/core/ui/amendthemeform.py @@ -59,7 +59,7 @@ class AmendThemeForm(QtGui.QDialog, Ui_AmendThemeDialog): QtCore.QObject.connect(self.ShadowColorPushButton, QtCore.SIGNAL(u'pressed()'), self.onShadowColorPushButtonClicked) QtCore.QObject.connect(self.ImageToolButton, - QtCore.SIGNAL(u'pressed()'), self.onImageToolButtonClicked) + QtCore.SIGNAL(u'clicked()'), self.onImageToolButtonClicked) #Combo boxes QtCore.QObject.connect(self.BackgroundComboBox, QtCore.SIGNAL(u'activated(int)'), self.onBackgroundComboBoxSelected) diff --git a/openlp/core/ui/thememanager.py b/openlp/core/ui/thememanager.py index 46b4d0c50..381719514 100644 --- a/openlp/core/ui/thememanager.py +++ b/openlp/core/ui/thememanager.py @@ -116,6 +116,7 @@ class ThemeManager(QtGui.QWidget): self.thumbPath = os.path.join(self.path, u'thumbnails') self.checkThemesExists(self.thumbPath) self.amendThemeForm.path = self.path + self.oldBackgroundImage = None # Last little bits of setting up self.global_theme = unicode(QtCore.QSettings().value( self.settingsSection + u'/global theme', @@ -187,6 +188,8 @@ class ThemeManager(QtGui.QWidget): item = self.ThemeListWidget.currentItem() theme = self.getThemeData( unicode(item.data(QtCore.Qt.UserRole).toString())) + if theme.background_type == u'image': + self.oldBackgroundImage = theme.background_filename self.amendThemeForm.loadTheme(theme) self.saveThemeName = unicode( item.data(QtCore.Qt.UserRole).toString()) @@ -544,6 +547,12 @@ class ThemeManager(QtGui.QWidget): QtGui.QMessageBox.No) if result == QtGui.QMessageBox.Yes: # Save the theme, overwriting the existing theme if necessary. + if image_to and self.oldBackgroundImage and \ + image_to != self.oldBackgroundImage: + try: + os.remove(self.oldBackgroundImage) + except OSError: + log.exception(u'Unable to remove old theme background') outfile = None try: outfile = open(theme_file, u'w') From 44506f2c84581ba74becdee72c6656869d656678 Mon Sep 17 00:00:00 2001 From: Jon Tibble Date: Tue, 13 Jul 2010 17:00:33 +0100 Subject: [PATCH 19/21] AdvancedTab: Double click to live --- openlp/core/lib/mediamanageritem.py | 12 +++++++++--- openlp/core/ui/advancedtab.py | 13 +++++++++++-- openlp/core/ui/slidecontroller.py | 30 ++++++++++++++++++----------- 3 files changed, 39 insertions(+), 16 deletions(-) diff --git a/openlp/core/lib/mediamanageritem.py b/openlp/core/lib/mediamanageritem.py index 7ad6a9b17..cad0b35cb 100644 --- a/openlp/core/lib/mediamanageritem.py +++ b/openlp/core/lib/mediamanageritem.py @@ -323,9 +323,15 @@ class MediaManagerItem(QtGui.QWidget): translate('MediaManagerItem', '&Add to selected Service Item'), self.onAddEditClick)) - QtCore.QObject.connect( - self.listView, QtCore.SIGNAL(u'doubleClicked(QModelIndex)'), - self.onPreviewClick) + if QtCore.QSettings().value(u'advanced/double click live', + QtCore.QVariant(False)).toBool(): + QtCore.QObject.connect(self.listView, + QtCore.SIGNAL(u'doubleClicked(QModelIndex)'), + self.onLiveClick) + else: + QtCore.QObject.connect(self.listView, + QtCore.SIGNAL(u'doubleClicked(QModelIndex)'), + self.onPreviewClick) def initialise(self): """ diff --git a/openlp/core/ui/advancedtab.py b/openlp/core/ui/advancedtab.py index b2efd92cd..8a6edef20 100644 --- a/openlp/core/ui/advancedtab.py +++ b/openlp/core/ui/advancedtab.py @@ -55,7 +55,7 @@ class AdvancedTab(SettingsTab): self.leftLayout.setMargin(0) self.uiGroupBox = QtGui.QGroupBox(self.leftWidget) self.uiGroupBox.setObjectName(u'uiGroupBox') - self.uiGroupBox.setMaximumWidth(260) + self.uiGroupBox.setMaximumWidth(320) self.uiLayout = QtGui.QVBoxLayout(self.uiGroupBox) self.uiLayout.setSpacing(8) self.uiLayout.setMargin(6) @@ -78,6 +78,9 @@ class AdvancedTab(SettingsTab): self.mediaPluginCheckBox = QtGui.QCheckBox(self.uiGroupBox) self.mediaPluginCheckBox.setObjectName(u'mediaPluginCheckBox') self.uiLayout.addWidget(self.mediaPluginCheckBox) + self.doubleClickLiveCheckBox = QtGui.QCheckBox(self.uiGroupBox) + self.doubleClickLiveCheckBox.setObjectName(u'doubleClickLiveCheckBox') + self.uiLayout.addWidget(self.doubleClickLiveCheckBox) self.leftLayout.addWidget(self.uiGroupBox) # self.sharedDirGroupBox = QtGui.QGroupBox(self.leftWidget) # self.sharedDirGroupBox.setObjectName(u'sharedDirGroupBox') @@ -133,6 +136,8 @@ class AdvancedTab(SettingsTab): translate('AdvancedTab', 'Number of recent files to display:')) self.mediaPluginCheckBox.setText(translate('AdvancedTab', 'Save currently selected media manager plugin')) + self.doubleClickLiveCheckBox.setText(translate('AdvancedTab', + 'Double-click to send items straight to live (requires restart)')) # self.sharedDirGroupBox.setTitle( # translate('AdvancedTab', 'Central Data Store')) # self.sharedCheckBox.setText( @@ -157,6 +162,9 @@ class AdvancedTab(SettingsTab): self.mediaPluginCheckBox.setChecked( settings.value(u'save current plugin', QtCore.QVariant(False)).toBool()) + self.doubleClickLiveCheckBox.setChecked( + settings.value(u'double click live', + QtCore.QVariant(False)).toBool()) settings.endGroup() def save(self): @@ -169,6 +177,8 @@ class AdvancedTab(SettingsTab): QtCore.QVariant(self.recentSpinBox.value())) settings.setValue(u'save current plugin', QtCore.QVariant(self.mediaPluginCheckBox.isChecked())) + settings.setValue(u'double click live', + QtCore.QVariant(self.doubleClickLiveCheckBox.isChecked())) settings.endGroup() def onSharedCheckBoxChanged(self, checked): @@ -178,4 +188,3 @@ class AdvancedTab(SettingsTab): self.sharedLabel.setEnabled(checked) self.sharedTextEdit.setEnabled(checked) self.sharedPushButton.setEnabled(checked) - diff --git a/openlp/core/ui/slidecontroller.py b/openlp/core/ui/slidecontroller.py index 8f9242764..e3c545f82 100644 --- a/openlp/core/ui/slidecontroller.py +++ b/openlp/core/ui/slidecontroller.py @@ -202,15 +202,21 @@ class SlideController(QtGui.QWidget): self.Toolbar.addToolbarWidget(u'Hide Menu', self.HideMenu) self.HideMenu.setMenu(QtGui.QMenu( translate('SlideController', 'Hide'), self.Toolbar)) - self.BlankScreen = QtGui.QAction(QtGui.QIcon( u':/slides/slide_blank.png'), u'Blank Screen', self.HideMenu) + self.BlankScreen = QtGui.QAction(QtGui.QIcon( + u':/slides/slide_blank.png'), u'Blank Screen', self.HideMenu) self.BlankScreen.setCheckable(True) - QtCore.QObject.connect(self.BlankScreen, QtCore.SIGNAL("triggered(bool)"), self.onBlankDisplay) - self.ThemeScreen = QtGui.QAction(QtGui.QIcon(u':/slides/slide_theme.png'), u'Blank to Theme', self.HideMenu) + QtCore.QObject.connect(self.BlankScreen, + QtCore.SIGNAL("triggered(bool)"), self.onBlankDisplay) + self.ThemeScreen = QtGui.QAction(QtGui.QIcon( + u':/slides/slide_theme.png'), u'Blank to Theme', self.HideMenu) self.ThemeScreen.setCheckable(True) - QtCore.QObject.connect(self.ThemeScreen, QtCore.SIGNAL("triggered(bool)"), self.onThemeDisplay) - self.DesktopScreen = QtGui.QAction(QtGui.QIcon(u':/slides/slide_desktop.png'), u'Show Desktop', self.HideMenu) + QtCore.QObject.connect(self.ThemeScreen, + QtCore.SIGNAL("triggered(bool)"), self.onThemeDisplay) + self.DesktopScreen = QtGui.QAction(QtGui.QIcon( + u':/slides/slide_desktop.png'), u'Show Desktop', self.HideMenu) self.DesktopScreen.setCheckable(True) - QtCore.QObject.connect(self.DesktopScreen, QtCore.SIGNAL("triggered(bool)"), self.onHideDisplay) + QtCore.QObject.connect(self.DesktopScreen, + QtCore.SIGNAL("triggered(bool)"), self.onHideDisplay) self.HideMenu.setDefaultAction(self.BlankScreen) self.HideMenu.menu().addAction(self.BlankScreen) self.HideMenu.menu().addAction(self.ThemeScreen) @@ -241,9 +247,8 @@ class SlideController(QtGui.QWidget): self.Toolbar.addToolbarWidget( u'Image SpinBox', self.DelaySpinBox) self.DelaySpinBox.setSuffix(translate('SlideController', 's')) - self.DelaySpinBox.setToolTip( - translate('SlideController', - 'Delay between slides in seconds')) + self.DelaySpinBox.setToolTip(translate('SlideController', + 'Delay between slides in seconds')) self.ControllerLayout.addWidget(self.Toolbar) #Build a Media ToolBar self.Mediabar = OpenLPToolbar(self) @@ -268,8 +273,7 @@ class SlideController(QtGui.QWidget): self.volumeSlider = Phonon.VolumeSlider() self.volumeSlider.setGeometry(QtCore.QRect(90, 260, 221, 24)) self.volumeSlider.setObjectName(u'volumeSlider') - self.Mediabar.addToolbarWidget( - u'Audio Volume', self.volumeSlider) + self.Mediabar.addToolbarWidget(u'Audio Volume', self.volumeSlider) self.ControllerLayout.addWidget(self.Mediabar) # Build the Song Toolbar if isLive: @@ -328,6 +332,10 @@ class SlideController(QtGui.QWidget): # Signals QtCore.QObject.connect(self.PreviewListWidget, QtCore.SIGNAL(u'clicked(QModelIndex)'), self.onSlideSelected) + if QtCore.QSettings().value(u'advanced/double click live', + QtCore.QVariant(False)).toBool(): + QtCore.QObject.connect(self.PreviewListWidget, + QtCore.SIGNAL(u'doubleClicked(QModelIndex)'), self.onGoLive) if isLive: QtCore.QObject.connect(Receiver.get_receiver(), QtCore.SIGNAL(u'slidecontroller_live_spin_delay'), From 022935fe580e17a3dd89eb04f1b6e383d91890d5 Mon Sep 17 00:00:00 2001 From: Jon Tibble Date: Tue, 13 Jul 2010 17:31:19 +0100 Subject: [PATCH 20/21] Fix last merge --- openlp/core/ui/slidecontroller.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/openlp/core/ui/slidecontroller.py b/openlp/core/ui/slidecontroller.py index e3c545f82..70ec3ac6e 100644 --- a/openlp/core/ui/slidecontroller.py +++ b/openlp/core/ui/slidecontroller.py @@ -332,10 +332,11 @@ class SlideController(QtGui.QWidget): # Signals QtCore.QObject.connect(self.PreviewListWidget, QtCore.SIGNAL(u'clicked(QModelIndex)'), self.onSlideSelected) - if QtCore.QSettings().value(u'advanced/double click live', - QtCore.QVariant(False)).toBool(): - QtCore.QObject.connect(self.PreviewListWidget, - QtCore.SIGNAL(u'doubleClicked(QModelIndex)'), self.onGoLive) + if not self.isLive: + if QtCore.QSettings().value(u'advanced/double click live', + QtCore.QVariant(False)).toBool(): + QtCore.QObject.connect(self.PreviewListWidget, + QtCore.SIGNAL(u'doubleClicked(QModelIndex)'), self.onGoLive) if isLive: QtCore.QObject.connect(Receiver.get_receiver(), QtCore.SIGNAL(u'slidecontroller_live_spin_delay'), From e1c8aabe0a0baff68db7dcef820efbf4e9348daa Mon Sep 17 00:00:00 2001 From: Jon Tibble Date: Tue, 13 Jul 2010 19:45:40 +0100 Subject: [PATCH 21/21] Fix AdvancedTab layout --- openlp/core/ui/advancedtab.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/openlp/core/ui/advancedtab.py b/openlp/core/ui/advancedtab.py index 8a6edef20..c0c5a6109 100644 --- a/openlp/core/ui/advancedtab.py +++ b/openlp/core/ui/advancedtab.py @@ -55,7 +55,6 @@ class AdvancedTab(SettingsTab): self.leftLayout.setMargin(0) self.uiGroupBox = QtGui.QGroupBox(self.leftWidget) self.uiGroupBox.setObjectName(u'uiGroupBox') - self.uiGroupBox.setMaximumWidth(320) self.uiLayout = QtGui.QVBoxLayout(self.uiGroupBox) self.uiLayout.setSpacing(8) self.uiLayout.setMargin(6) @@ -110,10 +109,10 @@ class AdvancedTab(SettingsTab): QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding) self.leftLayout.addItem(self.leftSpacer) self.advancedTabLayout.addWidget(self.leftWidget) -# self.rightWidget = QtGui.QWidget(self) -# self.rightLayout = QtGui.QVBoxLayout(self.rightWidget) -# self.rightLayout.setSpacing(8) -# self.rightLayout.setMargin(0) + self.rightWidget = QtGui.QWidget(self) + self.rightLayout = QtGui.QVBoxLayout(self.rightWidget) + self.rightLayout.setSpacing(8) + self.rightLayout.setMargin(0) # self.databaseGroupBox = QtGui.QGroupBox(self.rightWidget) # self.databaseGroupBox.setObjectName(u'databaseGroupBox') # self.databaseGroupBox.setEnabled(False) @@ -121,9 +120,10 @@ class AdvancedTab(SettingsTab): # self.databaseLayout.setSpacing(8) # self.databaseLayout.setMargin(8) # self.rightLayout.addWidget(self.databaseGroupBox) -# self.rightSpacer = QtGui.QSpacerItem(20, 40, -# QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding) -# self.advancedTabLayout.addWidget(self.rightWidget) + self.rightSpacer = QtGui.QSpacerItem(20, 40, + QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding) + self.rightLayout.addItem(self.rightSpacer) + self.advancedTabLayout.addWidget(self.rightWidget) # QtCore.QObject.connect(self.sharedCheckBox, # QtCore.SIGNAL(u'stateChanged(int)'), self.onSharedCheckBoxChanged)