diff --git a/documentation/SongFormat.txt b/documentation/SongFormat.txt deleted file mode 100644 index 31b202dd6..000000000 --- a/documentation/SongFormat.txt +++ /dev/null @@ -1,124 +0,0 @@ -openlp.org 2.x Song Database Structure -======================================================================== - -Introduction ------------- -The song database in openlp.org 2.x is similar to the 1.x format. The -biggest differences are the addition of extra tables, and the use of -SQLite version 3. - -The song database contains the following tables: -- authors -- authors_songs -- song_books -- songs -- songs_topics -- topics - - -"authors" Table ---------------- -This table holds the names of all the authors. It has the following -columns: - -* id -* first_name -* last_name -* display_name - - -"authors_songs" Table ---------------------- -This is a bridging table between the "authors" and "songs" tables, which -serves to create a many-to-many relationship between the two tables. It -has the following columns: - -* author_id -* song_id - - -"song_books" Table ------------------- -The "song_books" table holds a list of books that a congregation gets -their songs from, or old hymnals now no longer used. This table has the -following columns: - -* id -* name -* publisher - - -"songs" Table -------------- -This table contains the songs, and each song has a list of attributes. -The "songs" table has the following columns: - -* id -* song_book_id -* title -* lyrics -* verse_order -* copyright -* comments -* ccli_number -* song_number -* theme_name -* search_title -* search_lyrics - - -"songs_topics" Table --------------------- -This is a bridging table between the "songs" and "topics" tables, which -serves to create a many-to-many relationship between the two tables. It -has the following columns: - -* song_id -* topic_id - - -"topics" Table --------------- -The topics table holds a selection of topics that songs can cover. This -is useful when a worship leader wants to select songs with a certain -theme. This table has the following columns: - -* id -* name - - -The lyrics definition (more or less similar to interformat to/from ChangingSong -The tags can also be used within the lyrics test. - -! Please note that this format has been checked at http://validator.w3.org/#validate_by_upload - - - Amazing Grace - - name of verse specific theme (optional) - any text (optional) - - Amazing grace, how ... - - - A b c - D e f - - ... - - - name of verse specific theme (optional) - any text (optional) - ... - - - - Erstaunliche Anmut - - Erstaunliche Anmut, wie - ... - - - ... - - diff --git a/openlp.pyw b/openlp.pyw index 8d20080c9..100c3336f 100755 --- a/openlp.pyw +++ b/openlp.pyw @@ -76,7 +76,7 @@ class OpenLP(QtGui.QApplication): """ Load and store current Application Version """ - if u'--dev-version' in sys.argv: + if u'--dev-version' in sys.argv or u'-d' in sys.argv: # If we're running the dev version, let's use bzr to get the version try: # If bzrlib is availble, use it @@ -216,6 +216,7 @@ class OpenLP(QtGui.QApplication): Sets the Busy Cursor for the Application """ self.setOverrideCursor(QtCore.Qt.BusyCursor) + self.processEvents() def setNormalCursor(self): """ diff --git a/openlp/core/lib/dockwidget.py b/openlp/core/lib/dockwidget.py index 24841ec33..32d6ce765 100644 --- a/openlp/core/lib/dockwidget.py +++ b/openlp/core/lib/dockwidget.py @@ -31,6 +31,8 @@ import logging from PyQt4 import QtGui +from openlp.core.lib import build_icon + log = logging.getLogger(__name__) class OpenLPDockWidget(QtGui.QDockWidget): @@ -47,4 +49,4 @@ class OpenLPDockWidget(QtGui.QDockWidget): if name: self.setObjectName(name) if icon: - self.setWindowIcon(icon) + self.setWindowIcon(build_icon(icon)) diff --git a/openlp/core/lib/mediamanageritem.py b/openlp/core/lib/mediamanageritem.py index 39c37e2a9..683459825 100644 --- a/openlp/core/lib/mediamanageritem.py +++ b/openlp/core/lib/mediamanageritem.py @@ -366,33 +366,34 @@ class MediaManagerItem(QtGui.QWidget): count += 1 return filelist - def validate(self, file, thumb): + def validate(self, image, thumb): """ - Validates to see if the file still exists or thumbnail is up to date + Validates whether an image still exists and, if it does, is the + thumbnail representation of the image up to date. """ - if not os.path.exists(file): + if not os.path.exists(image): 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) + imageDate = os.stat(image).st_mtime + thumbDate = os.stat(thumb).st_mtime + # If image has been updated rebuild icon + if imageDate > thumbDate: + self.iconFromFile(image, thumb) else: - self.iconFromFile(file, thumb) + self.iconFromFile(image, thumb) return True - def iconFromFile(self, file, thumb): + def iconFromFile(self, image, thumb): """ - Create a thumbnail icon from a given file + Create a thumbnail icon from a given image. - ``file`` - The file to create the icon from + ``image`` + The image file to create the icon from. ``thumb`` The filename to save the thumbnail to """ - icon = build_icon(unicode(file)) + icon = build_icon(unicode(image)) pixmap = icon.pixmap(QtCore.QSize(88, 50)) ext = os.path.splitext(thumb)[1].lower() pixmap.save(thumb, ext[1:]) @@ -403,12 +404,16 @@ class MediaManagerItem(QtGui.QWidget): u'defined by the plugin') def onNewClick(self): - raise NotImplementedError(u'MediaManagerItem.onNewClick needs to be ' - u'defined by the plugin') + """ + Hook for plugins to define behaviour for adding new items. + """ + pass def onEditClick(self): - raise NotImplementedError(u'MediaManagerItem.onEditClick needs to be ' - u'defined by the plugin') + """ + Hook for plugins to define behaviour for editing items. + """ + pass def onDeleteClick(self): raise NotImplementedError(u'MediaManagerItem.onDeleteClick needs to ' diff --git a/openlp/core/lib/ui.py b/openlp/core/lib/ui.py index bb8238bf0..4a346b3e1 100644 --- a/openlp/core/lib/ui.py +++ b/openlp/core/lib/ui.py @@ -30,7 +30,7 @@ import logging from PyQt4 import QtCore, QtGui -from openlp.core.lib import Receiver, translate +from openlp.core.lib import build_icon, Receiver, translate log = logging.getLogger(__name__) @@ -38,6 +38,9 @@ def add_welcome_page(parent, image): """ Generate an opening welcome page for a wizard using a provided image. + ``parent`` + A ``QWizard`` object to add the welcome page to. + ``image`` A splash image for the wizard. """ @@ -58,9 +61,14 @@ def add_welcome_page(parent, image): parent.welcomeLayout.addStretch() parent.addPage(parent.welcomePage) -def save_cancel_button_box(parent): +def create_save_cancel_button_box(parent): """ - Return a standard dialog button box with save and cancel buttons. + Creates a standard dialog button box with save and cancel buttons. The + button box is connected to the parent's ``accept()`` and ``reject()`` + methods to handle the default ``accepted()`` and ``rejected()`` signals. + + ``parent`` + The parent object. This should be a ``QWidget`` descendant. """ button_box = QtGui.QDialogButtonBox(parent) button_box.setStandardButtons( @@ -108,3 +116,106 @@ def media_item_combo_box(parent, name): combo.setSizeAdjustPolicy(QtGui.QComboBox.AdjustToMinimumContentsLength) combo.setSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Fixed) return combo + +def create_delete_push_button(parent, icon=None): + """ + Creates a standard push button with a delete label and optional icon. The + button is connected to the parent's ``onDeleteButtonClicked()`` method to + handle the ``clicked()`` signal. + + ``parent`` + The parent object. This should be a ``QWidget`` descendant. + + ``icon`` + An icon to display on the button. This can be either a ``QIcon``, a + resource path or a file name. + """ + delete_button = QtGui.QPushButton(parent) + delete_button.setObjectName(u'deleteButton') + delete_icon = icon if icon else u':/general/general_delete.png' + delete_button.setIcon(build_icon(delete_icon)) + delete_button.setText(translate('OpenLP.Ui', '&Delete')) + delete_button.setToolTip( + translate('OpenLP.Ui', 'Delete the selected item.')) + QtCore.QObject.connect(delete_button, + QtCore.SIGNAL(u'clicked()'), parent.onDeleteButtonClicked) + return delete_button + +def create_up_down_push_button_set(parent): + """ + Creates a standard set of two push buttons, one for up and the other for + down, for use with lists. The buttons use arrow icons and no text and are + connected to the parent's ``onUpButtonClicked()`` and + ``onDownButtonClicked()`` to handle their respective ``clicked()`` signals. + + ``parent`` + The parent object. This should be a ``QWidget`` descendant. + """ + up_button = QtGui.QPushButton(parent) + up_button.setIcon(build_icon(u':/services/service_up.png')) + up_button.setObjectName(u'upButton') + up_button.setToolTip( + translate('OpenLP.Ui', 'Move selection up one position.')) + down_button = QtGui.QPushButton(parent) + down_button.setIcon(build_icon(u':/services/service_down.png')) + down_button.setObjectName(u'downButton') + down_button.setToolTip( + translate('OpenLP.Ui', 'Move selection down one position.')) + QtCore.QObject.connect(up_button, + QtCore.SIGNAL(u'clicked()'), parent.onUpButtonClicked) + QtCore.QObject.connect(down_button, + QtCore.SIGNAL(u'clicked()'), parent.onDownButtonClicked) + return up_button, down_button + +def base_action(parent, name): + """ + Return the most basic action with the object name set. + """ + action = QtGui.QAction(parent) + action.setObjectName(name) + return action + +def checkable_action(parent, name, checked=None): + """ + Return a standard action with the checkable attribute set. + """ + action = base_action(parent, name) + action.setCheckable(True) + if checked is not None: + action.setChecked(checked) + return action + +def icon_action(parent, name, icon, checked=None): + """ + Return a standard action with an icon. + """ + if checked is not None: + action = checkable_action(parent, name, checked) + else: + action = base_action(parent, name) + action.setIcon(build_icon(icon)) + return action + +def shortcut_action(parent, text, shortcuts, function): + """ + Return a shortcut enabled action. + """ + action = QtGui.QAction(text, parent) + action.setShortcuts(shortcuts) + action.setShortcutContext(QtCore.Qt.WidgetWithChildrenShortcut) + QtCore.QObject.connect(action, QtCore.SIGNAL(u'triggered()'), function) + return action + +def add_widget_completer(cache, widget): + """ + Adds a text autocompleter to a widget. + + ``cache`` + The list of items to use as suggestions. + + ``widget`` + The object to use the completer. + """ + completer = QtGui.QCompleter(cache) + completer.setCaseSensitivity(QtCore.Qt.CaseInsensitive) + widget.setCompleter(completer) diff --git a/openlp/core/ui/exceptiondialog.py b/openlp/core/ui/exceptiondialog.py index 69035dc4d..ba7bab496 100644 --- a/openlp/core/ui/exceptiondialog.py +++ b/openlp/core/ui/exceptiondialog.py @@ -46,6 +46,15 @@ class Ui_ExceptionDialog(object): self.messageLabel.setObjectName(u'messageLabel') self.messageLayout.addWidget(self.messageLabel) self.exceptionLayout.addLayout(self.messageLayout) + self.descriptionExplanation = QtGui.QLabel(exceptionDialog) + self.descriptionExplanation.setObjectName(u'descriptionExplanation') + self.exceptionLayout.addWidget(self.descriptionExplanation) + self.descriptionTextEdit = QtGui.QPlainTextEdit(exceptionDialog) + self.descriptionTextEdit.setObjectName(u'descriptionTextEdit') + self.exceptionLayout.addWidget(self.descriptionTextEdit) + self.descriptionWordCount = QtGui.QLabel(exceptionDialog) + self.descriptionWordCount.setObjectName(u'descriptionWordCount') + self.exceptionLayout.addWidget(self.descriptionWordCount) self.exceptionTextEdit = QtGui.QPlainTextEdit(exceptionDialog) self.exceptionTextEdit.setReadOnly(True) self.exceptionTextEdit.setObjectName(u'exceptionTextEdit') @@ -65,19 +74,31 @@ class Ui_ExceptionDialog(object): self.saveReportButton.setObjectName(u'saveReportButton') self.exceptionButtonBox.addButton(self.saveReportButton, QtGui.QDialogButtonBox.ActionRole) + self.attachFileButton = QtGui.QPushButton(exceptionDialog) + self.attachFileButton.setIcon(build_icon(u':/general/general_open.png')) + self.attachFileButton.setObjectName(u'attachFileButton') + self.exceptionButtonBox.addButton(self.attachFileButton, + QtGui.QDialogButtonBox.ActionRole) self.retranslateUi(exceptionDialog) + QtCore.QObject.connect(self.descriptionTextEdit, + QtCore.SIGNAL(u'textChanged()'), self.onDescriptionUpdated) QtCore.QObject.connect(self.exceptionButtonBox, QtCore.SIGNAL(u'rejected()'), exceptionDialog.reject) QtCore.QObject.connect(self.sendReportButton, QtCore.SIGNAL(u'pressed()'), self.onSendReportButtonPressed) QtCore.QObject.connect(self.saveReportButton, QtCore.SIGNAL(u'pressed()'), self.onSaveReportButtonPressed) + QtCore.QObject.connect(self.attachFileButton, + QtCore.SIGNAL(u'pressed()'), self.onAttachFileButtonPressed) QtCore.QMetaObject.connectSlotsByName(exceptionDialog) def retranslateUi(self, exceptionDialog): exceptionDialog.setWindowTitle( translate('OpenLP.ExceptionDialog', 'Error Occurred')) + self.descriptionExplanation.setText(translate('OpenLP.ExceptionDialog', + 'Please enter a description of what you were doing to cause this ' + 'error \n(Minimum 20 characters)')) self.messageLabel.setText(translate('OpenLP.ExceptionDialog', 'Oops! ' 'OpenLP hit a problem, and couldn\'t recover. The text in the box ' 'below contains information that might be helpful to the OpenLP ' @@ -88,3 +109,5 @@ class Ui_ExceptionDialog(object): 'Send E-Mail')) self.saveReportButton.setText(translate('OpenLP.ExceptionDialog', 'Save to File')) + self.attachFileButton.setText(translate('OpenLP.ExceptionDialog', + 'Attach File')) diff --git a/openlp/core/ui/exceptionform.py b/openlp/core/ui/exceptionform.py index 347bcf8f1..7f9e23c61 100644 --- a/openlp/core/ui/exceptionform.py +++ b/openlp/core/ui/exceptionform.py @@ -70,8 +70,15 @@ class ExceptionForm(QtGui.QDialog, Ui_ExceptionDialog): self.setupUi(self) self.settingsSection = u'crashreport' + def exec_(self): + self.descriptionTextEdit.setPlainText(u'') + self.onDescriptionUpdated() + self.fileAttachment = None + return QtGui.QDialog.exec_(self) + def _createReport(self): openlp_version = self.parent().applicationVersion[u'full'] + description = unicode(self.descriptionTextEdit.toPlainText()) traceback = unicode(self.exceptionTextEdit.toPlainText()) system = unicode(translate('OpenLP.ExceptionForm', 'Platform: %s\n')) % platform.platform() @@ -90,7 +97,7 @@ class ExceptionForm(QtGui.QDialog, Ui_ExceptionDialog): system = system + u'Desktop: KDE SC\n' elif os.environ.get(u'GNOME_DESKTOP_SESSION_ID'): system = system + u'Desktop: GNOME\n' - return (openlp_version, traceback, system, libraries) + return (openlp_version, description, traceback, system, libraries) def onSaveReportButtonPressed(self): """ @@ -99,6 +106,7 @@ class ExceptionForm(QtGui.QDialog, Ui_ExceptionDialog): report = unicode(translate('OpenLP.ExceptionForm', '**OpenLP Bug Report**\n' 'Version: %s\n\n' + '--- Details of the Exception. ---\n\n%s\n\n ' '--- Exception Traceback ---\n%s\n' '--- System information ---\n%s\n' '--- Library Versions ---\n%s\n')) @@ -132,18 +140,48 @@ class ExceptionForm(QtGui.QDialog, Ui_ExceptionDialog): body = unicode(translate('OpenLP.ExceptionForm', '*OpenLP Bug Report*\n' 'Version: %s\n\n' - '--- Please enter the report below this line. ---\n\n\n' + '--- Details of the Exception. ---\n\n%s\n\n ' '--- Exception Traceback ---\n%s\n' '--- System information ---\n%s\n' '--- Library Versions ---\n%s\n', 'Please add the information that bug reports are favoured written ' 'in English.')) content = self._createReport() - for line in content[1].split(u'\n'): + for line in content[2].split(u'\n'): if re.search(r'[/\\]openlp[/\\]', line): source = re.sub(r'.*[/\\]openlp[/\\](.*)".*', r'\1', line) if u':' in line: exception = line.split(u'\n')[-1].split(u':')[0] subject = u'Bug report: %s in %s' % (exception, source) - mailto(address=u'bugs@openlp.org', subject=subject, - body=body % content) + if self.fileAttachment: + mailto(address=u'bugs@openlp.org', subject=subject, + body=body % content, attach=self.fileAttachment) + else: + mailto(address=u'bugs@openlp.org', subject=subject, + body=body % content) + + def onDescriptionUpdated(self): + count = int(20 - len(self.descriptionTextEdit.toPlainText())) + if count < 0: + count = 0 + self.__buttonState(True) + else: + self.__buttonState(False) + self.descriptionWordCount.setText( + unicode(translate('OpenLP.ExceptionDialog', + 'Description characters to enter : %s')) % count ) + + def onAttachFileButtonPressed(self): + files = QtGui.QFileDialog.getOpenFileName( + self,translate('ImagePlugin.ExceptionDialog', + 'Select Attachment'), + SettingsManager.get_last_dir(u'exceptions'), + u'%s (*.*) (*)' % + unicode(translate('ImagePlugin.MediaItem', 'All Files'))) + log.info(u'New files(s) %s', unicode(files)) + if files: + self.fileAttachment = unicode(files) + + def __buttonState(self, state): + self.saveReportButton.setEnabled(state) + self.sendReportButton.setEnabled(state) diff --git a/openlp/core/ui/mainwindow.py b/openlp/core/ui/mainwindow.py index 6f57861c4..9af4931fb 100644 --- a/openlp/core/ui/mainwindow.py +++ b/openlp/core/ui/mainwindow.py @@ -28,11 +28,12 @@ import logging from PyQt4 import QtCore, QtGui +from openlp.core.lib import RenderManager, build_icon, OpenLPDockWidget, \ + SettingsManager, PluginManager, Receiver, translate +from openlp.core.lib.ui import base_action, checkable_action, icon_action from openlp.core.ui import AboutForm, SettingsForm, ServiceManager, \ ThemeManager, SlideController, PluginForm, MediaDockManager, \ ShortcutListForm -from openlp.core.lib import RenderManager, build_icon, OpenLPDockWidget, \ - SettingsManager, PluginManager, Receiver, translate from openlp.core.utils import AppLocation, add_actions, LanguageManager, \ ActionList @@ -124,9 +125,8 @@ class Ui_MainWindow(object): self.DefaultThemeLabel.setObjectName(u'DefaultThemeLabel') self.StatusBar.addPermanentWidget(self.DefaultThemeLabel) # Create the MediaManager - self.MediaManagerDock = OpenLPDockWidget( - mainWindow, u'MediaManagerDock', - build_icon(u':/system/system_mediamanager.png')) + self.MediaManagerDock = OpenLPDockWidget(mainWindow, + u'MediaManagerDock', u':/system/system_mediamanager.png') self.MediaManagerDock.setStyleSheet(MEDIA_MANAGER_STYLE) self.MediaManagerDock.setMinimumWidth( self.settingsmanager.mainwindow_left) @@ -137,9 +137,8 @@ class Ui_MainWindow(object): mainWindow.addDockWidget(QtCore.Qt.LeftDockWidgetArea, self.MediaManagerDock) # Create the service manager - self.ServiceManagerDock = OpenLPDockWidget( - mainWindow, u'ServiceManagerDock', - build_icon(u':/system/system_servicemanager.png')) + self.ServiceManagerDock = OpenLPDockWidget(mainWindow, + u'ServiceManagerDock', u':/system/system_servicemanager.png') self.ServiceManagerDock.setMinimumWidth( self.settingsmanager.mainwindow_right) self.ServiceManagerContents = ServiceManager(mainWindow, @@ -148,9 +147,8 @@ class Ui_MainWindow(object): mainWindow.addDockWidget(QtCore.Qt.RightDockWidgetArea, self.ServiceManagerDock) # Create the theme manager - self.ThemeManagerDock = OpenLPDockWidget( - mainWindow, u'ThemeManagerDock', - build_icon(u':/system/system_thememanager.png')) + self.ThemeManagerDock = OpenLPDockWidget(mainWindow, + u'ThemeManagerDock', u':/system/system_thememanager.png') self.ThemeManagerDock.setMinimumWidth( self.settingsmanager.mainwindow_right) self.ThemeManagerContents = ThemeManager(mainWindow, @@ -160,103 +158,70 @@ class Ui_MainWindow(object): mainWindow.addDockWidget(QtCore.Qt.RightDockWidgetArea, self.ThemeManagerDock) # Create the menu items - self.FileNewItem = QtGui.QAction(mainWindow) - self.FileNewItem.setIcon(build_icon(u':/general/general_new.png')) - self.FileNewItem.setObjectName(u'FileNewItem') + self.FileNewItem = icon_action(mainWindow, u'FileNewItem', + u':/general/general_new.png') mainWindow.actionList.add_action(self.FileNewItem, u'File') - self.FileOpenItem = QtGui.QAction(mainWindow) - self.FileOpenItem.setIcon(build_icon(u':/general/general_open.png')) - self.FileOpenItem.setObjectName(u'FileOpenItem') + self.FileOpenItem = icon_action(mainWindow, u'FileOpenItem', + u':/general/general_open.png') mainWindow.actionList.add_action(self.FileOpenItem, u'File') - self.FileSaveItem = QtGui.QAction(mainWindow) - self.FileSaveItem.setIcon(build_icon(u':/general/general_save.png')) - self.FileSaveItem.setObjectName(u'FileSaveItem') + self.FileSaveItem = icon_action(mainWindow, u'FileSaveItem', + u':/general/general_save.png') mainWindow.actionList.add_action(self.FileSaveItem, u'File') - self.FileSaveAsItem = QtGui.QAction(mainWindow) - self.FileSaveAsItem.setObjectName(u'FileSaveAsItem') + self.FileSaveAsItem = base_action(mainWindow, u'FileSaveAsItem') mainWindow.actionList.add_action(self.FileSaveAsItem, u'File') - self.printServiceOrderItem = QtGui.QAction(mainWindow) - self.printServiceOrderItem.setObjectName(u'printServiceItem') + self.printServiceOrderItem = base_action( + mainWindow, u'printServiceItem') mainWindow.actionList.add_action( self.printServiceOrderItem, u'Print Service Order') - self.FileExitItem = QtGui.QAction(mainWindow) - self.FileExitItem.setIcon(build_icon(u':/system/system_exit.png')) - self.FileExitItem.setObjectName(u'FileExitItem') + self.FileExitItem = icon_action(mainWindow, u'FileExitItem', + u':/system/system_exit.png') mainWindow.actionList.add_action(self.FileExitItem, u'File') - self.ImportThemeItem = QtGui.QAction(mainWindow) - self.ImportThemeItem.setObjectName(u'ImportThemeItem') + self.ImportThemeItem = base_action(mainWindow, u'ImportThemeItem') mainWindow.actionList.add_action(self.ImportThemeItem, u'Import') - self.ImportLanguageItem = QtGui.QAction(mainWindow) - self.ImportLanguageItem.setObjectName(u'ImportLanguageItem') + self.ImportLanguageItem = base_action(mainWindow, u'ImportLanguageItem') mainWindow.actionList.add_action(self.ImportLanguageItem, u'Import') - self.ExportThemeItem = QtGui.QAction(mainWindow) - self.ExportThemeItem.setObjectName(u'ExportThemeItem') + self.ExportThemeItem = base_action(mainWindow, u'ExportThemeItem') mainWindow.actionList.add_action(self.ExportThemeItem, u'Export') - self.ExportLanguageItem = QtGui.QAction(mainWindow) - self.ExportLanguageItem.setObjectName(u'ExportLanguageItem') + self.ExportLanguageItem = base_action(mainWindow, u'ExportLanguageItem') mainWindow.actionList.add_action(self.ExportLanguageItem, u'Export') - self.ViewMediaManagerItem = QtGui.QAction(mainWindow) - self.ViewMediaManagerItem.setCheckable(True) - self.ViewMediaManagerItem.setChecked(self.MediaManagerDock.isVisible()) - self.ViewMediaManagerItem.setIcon( - build_icon(u':/system/system_mediamanager.png')) - self.ViewMediaManagerItem.setObjectName(u'ViewMediaManagerItem') - self.ViewThemeManagerItem = QtGui.QAction(mainWindow) - self.ViewThemeManagerItem.setCheckable(True) - self.ViewThemeManagerItem.setChecked(self.ThemeManagerDock.isVisible()) - self.ViewThemeManagerItem.setIcon( - build_icon(u':/system/system_thememanager.png')) - self.ViewThemeManagerItem.setObjectName(u'ViewThemeManagerItem') + self.ViewMediaManagerItem = icon_action(mainWindow, + u'ViewMediaManagerItem', u':/system/system_mediamanager.png', + self.MediaManagerDock.isVisible()) + self.ViewThemeManagerItem = icon_action(mainWindow, + u'ViewThemeManagerItem', u':/system/system_thememanager.png', + self.ThemeManagerDock.isVisible()) mainWindow.actionList.add_action(self.ViewMediaManagerItem, u'View') - self.ViewServiceManagerItem = QtGui.QAction(mainWindow) - self.ViewServiceManagerItem.setCheckable(True) - self.ViewServiceManagerItem.setChecked( + self.ViewServiceManagerItem = icon_action(mainWindow, + u'ViewServiceManagerItem', u':/system/system_servicemanager.png', self.ServiceManagerDock.isVisible()) - self.ViewServiceManagerItem.setIcon( - build_icon(u':/system/system_servicemanager.png')) - self.ViewServiceManagerItem.setObjectName(u'ViewServiceManagerItem') mainWindow.actionList.add_action(self.ViewServiceManagerItem, u'View') - self.ViewPreviewPanel = QtGui.QAction(mainWindow) - self.ViewPreviewPanel.setCheckable(True) - self.ViewPreviewPanel.setChecked(previewVisible) - self.ViewPreviewPanel.setObjectName(u'ViewPreviewPanel') + self.ViewPreviewPanel = checkable_action(mainWindow, + u'ViewPreviewPanel', previewVisible) mainWindow.actionList.add_action(self.ViewPreviewPanel, u'View') - self.ViewLivePanel = QtGui.QAction(mainWindow) - self.ViewLivePanel.setCheckable(True) - self.ViewLivePanel.setChecked(liveVisible) - self.ViewLivePanel.setObjectName(u'ViewLivePanel') + self.ViewLivePanel = checkable_action(mainWindow, u'ViewLivePanel', + liveVisible) mainWindow.actionList.add_action(self.ViewLivePanel, u'View') - self.ModeDefaultItem = QtGui.QAction(mainWindow) - self.ModeDefaultItem.setCheckable(True) - self.ModeDefaultItem.setObjectName(u'ModeDefaultItem') + self.ModeDefaultItem = checkable_action(mainWindow, u'ModeDefaultItem') mainWindow.actionList.add_action(self.ModeDefaultItem, u'View Mode') - self.ModeSetupItem = QtGui.QAction(mainWindow) - self.ModeSetupItem.setCheckable(True) - self.ModeSetupItem.setObjectName(u'ModeLiveItem') + self.ModeSetupItem = checkable_action(mainWindow, u'ModeLiveItem') mainWindow.actionList.add_action(self.ModeSetupItem, u'View Mode') - self.ModeLiveItem = QtGui.QAction(mainWindow) - self.ModeLiveItem.setCheckable(True) - self.ModeLiveItem.setObjectName(u'ModeLiveItem') + self.ModeLiveItem = checkable_action(mainWindow, u'ModeLiveItem') mainWindow.actionList.add_action(self.ModeLiveItem, u'View Mode') self.ModeGroup = QtGui.QActionGroup(mainWindow) self.ModeGroup.addAction(self.ModeDefaultItem) self.ModeGroup.addAction(self.ModeSetupItem) self.ModeGroup.addAction(self.ModeLiveItem) self.ModeDefaultItem.setChecked(True) - self.ToolsAddToolItem = QtGui.QAction(mainWindow) - self.ToolsAddToolItem.setIcon(build_icon(u':/tools/tools_add.png')) - self.ToolsAddToolItem.setObjectName(u'ToolsAddToolItem') + self.ToolsAddToolItem = icon_action(mainWindow, u'ToolsAddToolItem', + u':/tools/tools_add.png') mainWindow.actionList.add_action(self.ToolsAddToolItem, u'Tools') - self.SettingsPluginListItem = QtGui.QAction(mainWindow) - self.SettingsPluginListItem.setIcon( - build_icon(u':/system/settings_plugin_list.png')) - self.SettingsPluginListItem.setObjectName(u'SettingsPluginListItem') + self.SettingsPluginListItem = icon_action(mainWindow, + u'SettingsPluginListItem', u':/system/settings_plugin_list.png') mainWindow.actionList.add_action(self.SettingsPluginListItem, u'Settings') # i18n Language Items - self.AutoLanguageItem = QtGui.QAction(mainWindow) - self.AutoLanguageItem.setObjectName(u'AutoLanguageItem') - self.AutoLanguageItem.setCheckable(True) + self.AutoLanguageItem = checkable_action(mainWindow, + u'AutoLanguageItem') mainWindow.actionList.add_action(self.AutoLanguageItem, u'Settings') self.LanguageGroup = QtGui.QActionGroup(mainWindow) self.LanguageGroup.setExclusive(True) @@ -266,39 +231,28 @@ class Ui_MainWindow(object): qmList = LanguageManager.get_qm_list() savedLanguage = LanguageManager.get_language() for key in sorted(qmList.keys()): - languageItem = QtGui.QAction(mainWindow) - languageItem.setObjectName(key) - languageItem.setCheckable(True) + languageItem = checkable_action(mainWindow, key) if qmList[key] == savedLanguage: languageItem.setChecked(True) add_actions(self.LanguageGroup, [languageItem]) - self.SettingsShortcutsItem = QtGui.QAction(mainWindow) - self.SettingsShortcutsItem.setIcon( - build_icon(u':/system/system_configure_shortcuts.png')) - self.SettingsShortcutsItem.setObjectName(u'SettingsShortcutsItem') - self.SettingsConfigureItem = QtGui.QAction(mainWindow) - self.SettingsConfigureItem.setIcon( - build_icon(u':/system/system_settings.png')) - self.SettingsConfigureItem.setObjectName(u'SettingsConfigureItem') + self.SettingsShortcutsItem = icon_action(mainWindow, + u'SettingsShortcutsItem', + u':/system/system_configure_shortcuts.png') + self.SettingsConfigureItem = icon_action(mainWindow, + u'SettingsConfigureItem', u':/system/system_settings.png') mainWindow.actionList.add_action(self.SettingsShortcutsItem, u'Settings') - self.HelpDocumentationItem = QtGui.QAction(mainWindow) - self.HelpDocumentationItem.setIcon( - build_icon(u':/system/system_help_contents.png')) - self.HelpDocumentationItem.setObjectName(u'HelpDocumentationItem') + self.HelpDocumentationItem = icon_action(mainWindow, + u'HelpDocumentationItem', u':/system/system_help_contents.png') self.HelpDocumentationItem.setEnabled(False) mainWindow.actionList.add_action(self.HelpDocumentationItem, u'Help') - self.HelpAboutItem = QtGui.QAction(mainWindow) - self.HelpAboutItem.setIcon( - build_icon(u':/system/system_about.png')) - self.HelpAboutItem.setObjectName(u'HelpAboutItem') + self.HelpAboutItem = icon_action(mainWindow, u'HelpAboutItem', + u':/system/system_about.png') mainWindow.actionList.add_action(self.HelpAboutItem, u'Help') - self.HelpOnlineHelpItem = QtGui.QAction(mainWindow) - self.HelpOnlineHelpItem.setObjectName(u'HelpOnlineHelpItem') + self.HelpOnlineHelpItem = base_action(mainWindow, u'HelpOnlineHelpItem') self.HelpOnlineHelpItem.setEnabled(False) mainWindow.actionList.add_action(self.HelpOnlineHelpItem, u'Help') - self.HelpWebSiteItem = QtGui.QAction(mainWindow) - self.HelpWebSiteItem.setObjectName(u'HelpWebSiteItem') + self.HelpWebSiteItem = base_action(mainWindow, u'HelpWebSiteItem') mainWindow.actionList.add_action(self.HelpWebSiteItem, u'Help') add_actions(self.FileImportMenu, (self.ImportThemeItem, self.ImportLanguageItem)) @@ -999,8 +953,8 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow): u'advanced/recent file count', QtCore.QVariant(4)).toInt()[0] self.FileMenu.clear() add_actions(self.FileMenu, self.FileMenuActions[:-1]) - existingRecentFiles = [file for file in self.recentFiles - if QtCore.QFile.exists(file)] + existingRecentFiles = [recentFile for recentFile in self.recentFiles + if QtCore.QFile.exists(recentFile)] recentFilesToDisplay = existingRecentFiles[0:recentFileCount] if recentFilesToDisplay: self.FileMenu.addSeparator() diff --git a/openlp/core/ui/serviceitemeditdialog.py b/openlp/core/ui/serviceitemeditdialog.py index d4c86fa61..f22f3dcf5 100644 --- a/openlp/core/ui/serviceitemeditdialog.py +++ b/openlp/core/ui/serviceitemeditdialog.py @@ -26,8 +26,9 @@ from PyQt4 import QtCore, QtGui -from openlp.core.lib import translate, build_icon -from openlp.core.lib.ui import save_cancel_button_box +from openlp.core.lib import translate +from openlp.core.lib.ui import create_save_cancel_button_box, \ + create_delete_push_button, create_up_down_push_button_set class Ui_ServiceItemEditDialog(object): def setupUi(self, serviceItemEditDialog): @@ -40,26 +41,19 @@ class Ui_ServiceItemEditDialog(object): self.dialogLayout.addWidget(self.listWidget, 0, 0) self.buttonLayout = QtGui.QVBoxLayout() self.buttonLayout.setObjectName(u'buttonLayout') - self.deleteButton = QtGui.QPushButton(serviceItemEditDialog) - self.deleteButton.setObjectName(u'deleteButton') + self.deleteButton = create_delete_push_button(serviceItemEditDialog) self.buttonLayout.addWidget(self.deleteButton) self.buttonLayout.addStretch() - self.upButton = QtGui.QPushButton(serviceItemEditDialog) - self.upButton.setIcon(build_icon(u':/services/service_up.png')) - self.upButton.setObjectName(u'upButton') + self.upButton, self.downButton = create_up_down_push_button_set( + serviceItemEditDialog) self.buttonLayout.addWidget(self.upButton) - self.downButton = QtGui.QPushButton(serviceItemEditDialog) - self.downButton.setIcon(build_icon(u':/services/service_down.png')) - self.downButton.setObjectName(u'downButton') self.buttonLayout.addWidget(self.downButton) self.dialogLayout.addLayout(self.buttonLayout, 0, 1) self.dialogLayout.addWidget( - save_cancel_button_box(serviceItemEditDialog), 1, 0, 1, 2) + create_save_cancel_button_box(serviceItemEditDialog), 1, 0, 1, 2) self.retranslateUi(serviceItemEditDialog) QtCore.QMetaObject.connectSlotsByName(serviceItemEditDialog) def retranslateUi(self, serviceItemEditDialog): serviceItemEditDialog.setWindowTitle( translate('OpenLP.ServiceItemEditForm', 'Reorder Service Item')) - self.deleteButton.setText(translate('OpenLP.ServiceItemEditForm', - 'Delete')) diff --git a/openlp/core/ui/serviceitemeditform.py b/openlp/core/ui/serviceitemeditform.py index 0621a44bd..70518f30c 100644 --- a/openlp/core/ui/serviceitemeditform.py +++ b/openlp/core/ui/serviceitemeditform.py @@ -39,13 +39,6 @@ class ServiceItemEditForm(QtGui.QDialog, Ui_ServiceItemEditDialog): QtGui.QDialog.__init__(self, parent) self.setupUi(self) self.itemList = [] - # enable drop - QtCore.QObject.connect(self.upButton, - QtCore.SIGNAL(u'clicked()'), self.onItemUp) - QtCore.QObject.connect(self.downButton, - QtCore.SIGNAL(u'clicked()'), self.onItemDown) - QtCore.QObject.connect(self.deleteButton, - QtCore.SIGNAL(u'clicked()'), self.onItemDelete) QtCore.QObject.connect(self.listWidget, QtCore.SIGNAL(u'currentRowChanged(int)'), self.onCurrentRowChanged) @@ -77,7 +70,7 @@ class ServiceItemEditForm(QtGui.QDialog, Ui_ServiceItemEditDialog): item_name = QtGui.QListWidgetItem(frame[u'title']) self.listWidget.addItem(item_name) - def onItemDelete(self): + def onDeleteButtonClicked(self): """ Delete the current row. """ @@ -92,13 +85,13 @@ class ServiceItemEditForm(QtGui.QDialog, Ui_ServiceItemEditDialog): else: self.listWidget.setCurrentRow(row) - def onItemUp(self): + def onUpButtonClicked(self): """ Move the current row up in the list. """ self.__moveItem(u'up') - def onItemDown(self): + def onDownButtonClicked(self): """ Move the current row down in the list """ diff --git a/openlp/core/ui/servicemanager.py b/openlp/core/ui/servicemanager.py index 4d36f4aec..bddd150b8 100644 --- a/openlp/core/ui/servicemanager.py +++ b/openlp/core/ui/servicemanager.py @@ -507,7 +507,6 @@ class ServiceManager(QtGui.QWidget): p_file = filePath if 'p_file' in locals(): Receiver.send_message(u'cursor_busy') - Receiver.send_message(u'openlp_process_events') fileTo = open(p_file, u'r') items = cPickle.load(fileTo) fileTo.close() diff --git a/openlp/core/ui/servicenoteform.py b/openlp/core/ui/servicenoteform.py index e659e50db..473cc1685 100644 --- a/openlp/core/ui/servicenoteform.py +++ b/openlp/core/ui/servicenoteform.py @@ -27,7 +27,7 @@ from PyQt4 import QtCore, QtGui from openlp.core.lib import translate -from openlp.core.lib.ui import save_cancel_button_box +from openlp.core.lib.ui import create_save_cancel_button_box class ServiceNoteForm(QtGui.QDialog): """ @@ -48,7 +48,7 @@ class ServiceNoteForm(QtGui.QDialog): self.textEdit = QtGui.QTextEdit(self) self.textEdit.setObjectName(u'textEdit') self.dialogLayout.addWidget(self.textEdit) - self.dialogLayout.addWidget(save_cancel_button_box(self)) + self.dialogLayout.addWidget(create_save_cancel_button_box(self)) QtCore.QMetaObject.connectSlotsByName(self) def retranslateUi(self): diff --git a/openlp/core/ui/slidecontroller.py b/openlp/core/ui/slidecontroller.py index ab8a656ff..bf0453e05 100644 --- a/openlp/core/ui/slidecontroller.py +++ b/openlp/core/ui/slidecontroller.py @@ -30,9 +30,10 @@ import os from PyQt4 import QtCore, QtGui from PyQt4.phonon import Phonon -from openlp.core.ui import HideMode, MainDisplay from openlp.core.lib import OpenLPToolbar, Receiver, resize_image, \ ItemCapabilities, translate +from openlp.core.lib.ui import shortcut_action +from openlp.core.ui import HideMode, MainDisplay log = logging.getLogger(__name__) @@ -317,18 +318,8 @@ class SlideController(QtGui.QWidget): self.mediabar.setVisible(False) if self.isLive: self.setLiveHotkeys(self) - self.previewListWidget.addActions( - [self.previousItem, - self.nextItem, - self.previousService, - self.nextService, - self.escapeItem]) - self.display.addActions( - [self.previousItem, - self.nextItem, - self.previousService, - self.nextService, - self.escapeItem]) + self.__addActionsToWidget(self.previewListWidget) + self.__addActionsToWidget(self.display) else: self.setPreviewHotkeys() self.previewListWidget.addActions( @@ -390,28 +381,17 @@ class SlideController(QtGui.QWidget): self.nextItem.setShortcuts([QtCore.Qt.Key_Down, QtCore.Qt.Key_PageDown]) self.nextItem.setShortcutContext(QtCore.Qt.WidgetWithChildrenShortcut) actionList.add_action(self.nextItem, u'Live') - self.previousService = QtGui.QAction(translate( - 'OpenLP.SlideController', 'Previous Service'), parent) - self.previousService.setShortcuts([QtCore.Qt.Key_Left, 0]) - self.previousService.setShortcutContext( - QtCore.Qt.WidgetWithChildrenShortcut) - QtCore.QObject.connect(self.previousService, - QtCore.SIGNAL(u'triggered()'), self.servicePrevious) + self.previousService = shortcut_action(parent, + translate('OpenLP.SlideController', 'Previous Service'), + [QtCore.Qt.Key_Left, 0], self.servicePrevious) actionList.add_action(self.previousService, u'Live') - self.nextService = QtGui.QAction(translate( - 'OpenLP.SlideController', 'Next Service'), parent) - self.nextService.setShortcuts([QtCore.Qt.Key_Right, 0]) - self.nextService.setShortcutContext( - QtCore.Qt.WidgetWithChildrenShortcut) - QtCore.QObject.connect(self.nextService, - QtCore.SIGNAL(u'triggered()'), self.serviceNext) + self.nextService = shortcut_action(parent, + translate('OpenLP.SlideController', 'Next Service'), + [QtCore.Qt.Key_Right, 0], self.serviceNext) actionList.add_action(self.nextService, u'Live') - self.escapeItem = QtGui.QAction(translate( - 'OpenLP.SlideController', 'Escape Item'), parent) - self.escapeItem.setShortcuts([QtCore.Qt.Key_Escape, 0]) - self.escapeItem.setShortcutContext(QtCore.Qt.WidgetWithChildrenShortcut) - QtCore.QObject.connect(self.escapeItem, - QtCore.SIGNAL(u'triggered()'), self.liveEscape) + self.escapeItem = shortcut_action(parent, + translate('OpenLP.SlideController', 'Escape Item'), + [QtCore.Qt.Key_Escape, 0], self.liveEscape) actionList.add_action(self.escapeItem, u'Live') def liveEscape(self): @@ -435,12 +415,7 @@ class SlideController(QtGui.QWidget): self.display.alertTab = self.alertTab self.display.setup() if self.isLive: - self.display.addActions( - [self.previousItem, - self.nextItem, - self.previousService, - self.nextService, - self.escapeItem]) + self.__addActionsToWidget(self.display) # The SlidePreview's ratio. self.ratio = float(self.screens.current[u'size'].width()) / \ float(self.screens.current[u'size'].height()) @@ -448,6 +423,12 @@ class SlideController(QtGui.QWidget): if self.serviceItem: self.refreshServiceItem() + def __addActionsToWidget(self, widget): + widget.addActions([ + self.previousItem, self.nextItem, + self.previousService, self.nextService, + self.escapeItem]) + def previewSizeChanged(self): """ Takes care of the SlidePreview's size. Is called when one of the the @@ -575,10 +556,7 @@ class SlideController(QtGui.QWidget): slideno = 0 # If service item is the same as the current on only change slide if item.__eq__(self.serviceItem): - if slideno + 1 < self.previewListWidget.rowCount(): - self.previewListWidget.scrollToItem( - self.previewListWidget.item(slideno + 1, 0)) - self.previewListWidget.selectRow(slideno) + self.__checkUpdateSelectedSlide(slideno) self.onSlideSelected() return self._processItem(item, slideno) @@ -677,10 +655,7 @@ class SlideController(QtGui.QWidget): self.previewListWidget.selectRow( self.previewListWidget.rowCount() - 1) else: - if slideno + 1 < self.previewListWidget.rowCount(): - self.previewListWidget.scrollToItem( - self.previewListWidget.item(slideno + 1, 0)) - self.previewListWidget.selectRow(slideno) + self.__checkUpdateSelectedSlide(slideno) def onTextRequest(self): """ @@ -729,10 +704,7 @@ class SlideController(QtGui.QWidget): [self.serviceItem, self.isLive, index]) self.updatePreview() else: - if index + 1 < self.previewListWidget.rowCount(): - self.previewListWidget.scrollToItem( - self.previewListWidget.item(index + 1, 0)) - self.previewListWidget.selectRow(index) + self.__checkUpdateSelectedSlide(index) self.onSlideSelected() def mainDisplaySetBackground(self): @@ -891,10 +863,7 @@ class SlideController(QtGui.QWidget): """ The slide has been changed. Update the slidecontroller accordingly """ - if row + 1 < self.previewListWidget.rowCount(): - self.previewListWidget.scrollToItem( - self.previewListWidget.item(row + 1, 0)) - self.previewListWidget.selectRow(row) + self.__checkUpdateSelectedSlide(row) self.updatePreview() Receiver.send_message(u'slidecontroller_%s_changed' % self.typePrefix, row) @@ -945,10 +914,7 @@ class SlideController(QtGui.QWidget): else: Receiver.send_message('servicemanager_next_item') return - if row + 1 < self.previewListWidget.rowCount(): - self.previewListWidget.scrollToItem( - self.previewListWidget.item(row + 1, 0)) - self.previewListWidget.selectRow(row) + self.__checkUpdateSelectedSlide(row) self.onSlideSelected() def onSlideSelectedPreviousNoloop(self): @@ -971,12 +937,15 @@ class SlideController(QtGui.QWidget): row = self.previewListWidget.rowCount() - 1 else: row = 0 - if row + 1 < self.previewListWidget.rowCount(): - self.previewListWidget.scrollToItem( - self.previewListWidget.item(row + 1, 0)) - self.previewListWidget.selectRow(row) + self.__checkUpdateSelectedSlide(row) self.onSlideSelected() + def __checkUpdateSelectedSlide(self, row): + if row + 1 < self.previewListWidget.rowCount(): + self.previewListWidget.scrollToItem( + self.previewListWidget.item(row + 1, 0)) + self.previewListWidget.selectRow(row) + def onSlideSelectedLast(self): """ Go to the last slide. diff --git a/openlp/core/ui/thememanager.py b/openlp/core/ui/thememanager.py index f403d7441..7a4dda882 100644 --- a/openlp/core/ui/thememanager.py +++ b/openlp/core/ui/thememanager.py @@ -241,7 +241,7 @@ class ThemeManager(QtGui.QWidget): QtCore.QVariant(self.global_theme)) Receiver.send_message(u'theme_update_global', self.global_theme) - self.pushThemes() + self._pushThemes() def onAddTheme(self): """ @@ -268,11 +268,12 @@ class ThemeManager(QtGui.QWidget): newThemeName = unicode(self.fileRenameForm.fileNameEdit.text()) if self.checkIfThemeExists(newThemeName): oldThemeData = self.getThemeData(oldThemeName) - self.deleteTheme(oldThemeName) self.cloneThemeData(oldThemeData, newThemeName) + self.deleteTheme(oldThemeName) for plugin in self.mainwindow.pluginManager.plugins: if plugin.usesTheme(oldThemeName): plugin.renameTheme(oldThemeName, newThemeName) + self.loadThemes() def onCopyTheme(self): """ @@ -300,6 +301,7 @@ class ThemeManager(QtGui.QWidget): os.path.split(unicode(themeData.background_filename))[1]) saveFrom = themeData.background_filename themeData.theme_name = newThemeName + themeData.extend_image_filename(self.path) self.saveTheme(themeData, saveFrom, saveTo) def onEditTheme(self): @@ -332,6 +334,10 @@ class ThemeManager(QtGui.QWidget): row = self.themeListWidget.row(item) self.themeListWidget.takeItem(row) self.deleteTheme(theme) + # As we do not reload the themes, push out the change + # Reaload the list as the internal lists and events need + # to be triggered + self._pushThemes() def deleteTheme(self, theme): """ @@ -349,10 +355,6 @@ class ThemeManager(QtGui.QWidget): shutil.rmtree(os.path.join(self.path, theme).encode(encoding)) except OSError: log.exception(u'Error deleting theme %s', theme) - # As we do not reload the themes push out the change - # Reaload the list as the internal lists and events need - # to be triggered - self.pushThemes() def onExportTheme(self): """ @@ -449,9 +451,9 @@ class ThemeManager(QtGui.QWidget): QtCore.QVariant(textName)) self.themeListWidget.addItem(item_name) self.themelist.append(textName) - self.pushThemes() + self._pushThemes() - def pushThemes(self): + def _pushThemes(self): """ Notify listeners that the theme list has been updated """ @@ -571,6 +573,14 @@ class ThemeManager(QtGui.QWidget): Called by thememaintenance Dialog to save the theme and to trigger the reload of the theme list """ + self._writeTheme(theme, imageFrom, imageTo) + self.loadThemes() + + def _writeTheme(self, theme, imageFrom, imageTo): + """ + Writes the theme to the disk and handles the background image if + necessary + """ name = theme.theme_name theme_pretty_xml = theme.extract_formatted_xml() log.debug(u'saveTheme %s %s', name, theme_pretty_xml) @@ -598,12 +608,9 @@ class ThemeManager(QtGui.QWidget): except IOError: log.exception(u'Failed to save theme image') self.generateAndSaveImage(self.path, name, theme) - self.loadThemes() - self.pushThemes() def generateAndSaveImage(self, dir, name, theme): log.debug(u'generateAndSaveImage %s %s', dir, name) - theme_xml = theme.extract_xml() frame = self.generateImage(theme) samplepathname = os.path.join(self.path, name + u'.png') if os.path.exists(samplepathname): diff --git a/openlp/core/ui/themewizard.py b/openlp/core/ui/themewizard.py index e8765b9e2..49522df70 100644 --- a/openlp/core/ui/themewizard.py +++ b/openlp/core/ui/themewizard.py @@ -36,6 +36,8 @@ class Ui_ThemeWizard(object): themeWizard.setWizardStyle(QtGui.QWizard.ModernStyle) themeWizard.setOptions(QtGui.QWizard.IndependentPages | QtGui.QWizard.NoBackButtonOnStartPage) + self.spacer = QtGui.QSpacerItem(10, 0, + QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Minimum) # Welcome Page add_welcome_page(themeWizard, u':/wizards/wizard_createtheme.bmp') # Background Page @@ -52,10 +54,8 @@ class Ui_ThemeWizard(object): self.backgroundComboBox.setObjectName(u'BackgroundComboBox') self.backgroundTypeLayout.addRow(self.backgroundLabel, self.backgroundComboBox) - self.backgroundTypeSpacer = QtGui.QSpacerItem(10, 0, - QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Minimum) self.backgroundTypeLayout.setItem(1, QtGui.QFormLayout.LabelRole, - self.backgroundTypeSpacer) + self.spacer) self.backgroundLayout.addLayout(self.backgroundTypeLayout) self.backgroundStack = QtGui.QStackedLayout() self.backgroundStack.setObjectName(u'BackgroundStack') @@ -69,10 +69,7 @@ class Ui_ThemeWizard(object): self.colorButton = QtGui.QPushButton(self.colorWidget) self.colorButton.setObjectName(u'ColorButton') self.colorLayout.addRow(self.colorLabel, self.colorButton) - self.colorSpacer = QtGui.QSpacerItem(10, 0, QtGui.QSizePolicy.Fixed, - QtGui.QSizePolicy.Minimum) - self.colorLayout.setItem(1, QtGui.QFormLayout.LabelRole, - self.colorSpacer) + self.colorLayout.setItem(1, QtGui.QFormLayout.LabelRole, self.spacer) self.backgroundStack.addWidget(self.colorWidget) self.gradientWidget = QtGui.QWidget(self.backgroundPage) self.gradientWidget.setObjectName(u'GradientWidget') @@ -98,10 +95,7 @@ class Ui_ThemeWizard(object): self.gradientComboBox.addItems([u'', u'', u'', u'', u'']) self.gradientLayout.addRow(self.gradientTypeLabel, self.gradientComboBox) - self.gradientSpacer = QtGui.QSpacerItem(10, 0, QtGui.QSizePolicy.Fixed, - QtGui.QSizePolicy.Minimum) - self.gradientLayout.setItem(3, QtGui.QFormLayout.LabelRole, - self.gradientSpacer) + self.gradientLayout.setItem(3, QtGui.QFormLayout.LabelRole, self.spacer) self.backgroundStack.addWidget(self.gradientWidget) self.imageWidget = QtGui.QWidget(self.backgroundPage) self.imageWidget.setObjectName(u'ImageWidget') @@ -121,10 +115,7 @@ class Ui_ThemeWizard(object): build_icon(u':/general/general_open.png')) self.imageFileLayout.addWidget(self.imageBrowseButton) self.imageLayout.addRow(self.imageLabel, self.imageFileLayout) - self.imageSpacer = QtGui.QSpacerItem(10, 0, QtGui.QSizePolicy.Fixed, - QtGui.QSizePolicy.Minimum) - self.imageLayout.setItem(1, QtGui.QFormLayout.LabelRole, - self.imageSpacer) + self.imageLayout.setItem(1, QtGui.QFormLayout.LabelRole, self.spacer) self.backgroundStack.addWidget(self.imageWidget) self.backgroundLayout.addLayout(self.backgroundStack) themeWizard.addPage(self.backgroundPage) @@ -236,6 +227,8 @@ class Ui_ThemeWizard(object): self.footerSizeSpinBox.setObjectName(u'FooterSizeSpinBox') self.footerAreaLayout.addRow(self.footerSizeLabel, self.footerSizeSpinBox) + self.footerAreaLayout.setItem(3, QtGui.QFormLayout.LabelRole, + self.spacer) themeWizard.addPage(self.footerAreaPage) # Alignment Page self.alignmentPage = QtGui.QWizardPage() @@ -261,6 +254,8 @@ class Ui_ThemeWizard(object): self.transitionsCheckBox.setObjectName(u'TransitionsCheckBox') self.alignmentLayout.addRow(self.transitionsLabel, self.transitionsCheckBox) + self.alignmentLayout.setItem(3, QtGui.QFormLayout.LabelRole, + self.spacer) themeWizard.addPage(self.alignmentPage) # Area Position Page self.areaPositionPage = QtGui.QWizardPage() @@ -552,16 +547,6 @@ class Ui_ThemeWizard(object): translate('OpenLP.ThemeWizard', 'Theme name:')) # Align all QFormLayouts towards each other. labelWidth = max(self.backgroundLabel.minimumSizeHint().width(), - self.colorLabel.minimumSizeHint().width(), - self.gradientStartLabel.minimumSizeHint().width(), - self.gradientEndLabel.minimumSizeHint().width(), - self.gradientTypeLabel.minimumSizeHint().width(), - self.imageLabel.minimumSizeHint().width()) - self.backgroundTypeSpacer.changeSize(labelWidth, 0, - QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed) - self.colorSpacer.changeSize(labelWidth, 0, - QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed) - self.gradientSpacer.changeSize(labelWidth, 0, - QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed) - self.imageSpacer.changeSize(labelWidth, 0, + self.horizontalLabel.minimumSizeHint().width()) + self.spacer.changeSize(labelWidth, 0, QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed) diff --git a/openlp/plugins/alerts/forms/alertdialog.py b/openlp/plugins/alerts/forms/alertdialog.py index 272a96f3d..93f7ead06 100644 --- a/openlp/plugins/alerts/forms/alertdialog.py +++ b/openlp/plugins/alerts/forms/alertdialog.py @@ -27,6 +27,7 @@ from PyQt4 import QtCore, QtGui from openlp.core.lib import build_icon, translate +from openlp.core.lib.ui import create_delete_push_button class Ui_AlertDialog(object): def setupUi(self, alertDialog): @@ -65,9 +66,8 @@ class Ui_AlertDialog(object): self.saveButton.setIcon(build_icon(u':/general/general_save.png')) self.saveButton.setObjectName(u'saveButton') self.manageButtonLayout.addWidget(self.saveButton) - self.deleteButton = QtGui.QPushButton(alertDialog) - self.deleteButton.setIcon(build_icon(u':/general/general_delete.png')) - self.deleteButton.setObjectName(u'deleteButton') + self.deleteButton = create_delete_push_button(alertDialog) + self.deleteButton.setEnabled(False) self.manageButtonLayout.addWidget(self.deleteButton) self.manageButtonLayout.addStretch() self.alertDialogLayout.addLayout(self.manageButtonLayout, 1, 1) @@ -75,11 +75,13 @@ class Ui_AlertDialog(object): self.buttonBox.addButton(QtGui.QDialogButtonBox.Close) displayIcon = build_icon(u':/general/general_live.png') self.displayButton = QtGui.QPushButton(alertDialog) + self.displayButton.setEnabled(False) self.displayButton.setIcon(displayIcon) self.displayButton.setObjectName(u'displayButton') self.buttonBox.addButton(self.displayButton, QtGui.QDialogButtonBox.ActionRole) self.displayCloseButton = QtGui.QPushButton(alertDialog) + self.displayCloseButton.setEnabled(False) self.displayCloseButton.setIcon(displayIcon) self.displayCloseButton.setObjectName(u'displayCloseButton') self.buttonBox.addButton(self.displayCloseButton, @@ -101,8 +103,6 @@ class Ui_AlertDialog(object): translate('AlertsPlugin.AlertForm', '&New')) self.saveButton.setText( translate('AlertsPlugin.AlertForm', '&Save')) - self.deleteButton.setText( - translate('AlertsPlugin.AlertForm', '&Delete')) self.displayButton.setText( translate('AlertsPlugin.AlertForm', 'Displ&ay')) self.displayCloseButton.setText( diff --git a/openlp/plugins/alerts/forms/alertform.py b/openlp/plugins/alerts/forms/alertform.py index f1a0f7fea..0639f2bb1 100644 --- a/openlp/plugins/alerts/forms/alertform.py +++ b/openlp/plugins/alerts/forms/alertform.py @@ -44,22 +44,22 @@ class AlertForm(QtGui.QDialog, Ui_AlertDialog): self.item_id = None QtGui.QDialog.__init__(self, plugin.formparent) self.setupUi(self) - QtCore.QObject.connect(self.displayButton, QtCore.SIGNAL(u'clicked()'), - self.onDisplayClicked) + QtCore.QObject.connect(self.displayButton, + QtCore.SIGNAL(u'clicked()'), self.onDisplayClicked) QtCore.QObject.connect(self.displayCloseButton, QtCore.SIGNAL(u'clicked()'), self.onDisplayCloseClicked) QtCore.QObject.connect(self.alertTextEdit, QtCore.SIGNAL(u'textChanged(const QString&)'), self.onTextChanged) - QtCore.QObject.connect(self.newButton, QtCore.SIGNAL(u'clicked()'), - self.onNewClick) - QtCore.QObject.connect(self.deleteButton, QtCore.SIGNAL(u'clicked()'), - self.onDeleteClick) - QtCore.QObject.connect(self.saveButton, QtCore.SIGNAL(u'clicked()'), - self.onSaveClick) + QtCore.QObject.connect(self.newButton, + QtCore.SIGNAL(u'clicked()'), self.onNewClick) + QtCore.QObject.connect(self.saveButton, + QtCore.SIGNAL(u'clicked()'), self.onSaveClick) QtCore.QObject.connect(self.alertListWidget, QtCore.SIGNAL(u'doubleClicked(QModelIndex)'), self.onDoubleClick) QtCore.QObject.connect(self.alertListWidget, QtCore.SIGNAL(u'clicked(QModelIndex)'), self.onSingleClick) + QtCore.QObject.connect(self.alertListWidget, + QtCore.SIGNAL(u'currentRowChanged(int)'), self.onCurrentRowChanged) def loadList(self): """ @@ -72,18 +72,15 @@ class AlertForm(QtGui.QDialog, Ui_AlertDialog): item_name = QtGui.QListWidgetItem(alert.text) item_name.setData(QtCore.Qt.UserRole, QtCore.QVariant(alert.id)) self.alertListWidget.addItem(item_name) - self.saveButton.setEnabled(False) - self.deleteButton.setEnabled(False) def onDisplayClicked(self): - if self.triggerAlert(unicode(self.alertTextEdit.text())): - self.loadList() + self.triggerAlert(unicode(self.alertTextEdit.text())) def onDisplayCloseClicked(self): if self.triggerAlert(unicode(self.alertTextEdit.text())): self.close() - def onDeleteClick(self): + def onDeleteButtonClicked(self): """ Deletes the selected item. """ @@ -95,8 +92,6 @@ class AlertForm(QtGui.QDialog, Ui_AlertDialog): self.alertListWidget.takeItem(row) self.item_id = None self.alertTextEdit.setText(u'') - self.saveButton.setEnabled(False) - self.deleteButton.setEnabled(False) def onNewClick(self): if len(self.alertTextEdit.text()) == 0: @@ -135,30 +130,26 @@ class AlertForm(QtGui.QDialog, Ui_AlertDialog): """ List item has been double clicked to display it """ - items = self.alertListWidget.selectedIndexes() - for item in items: - bitem = self.alertListWidget.item(item.row()) - self.triggerAlert(unicode(bitem.text())) - self.alertTextEdit.setText(unicode(bitem.text())) - self.item_id = (bitem.data(QtCore.Qt.UserRole)).toInt()[0] + item = self.alertListWidget.selectedIndexes()[0] + bitem = self.alertListWidget.item(item.row()) + self.triggerAlert(unicode(bitem.text())) + self.alertTextEdit.setText(unicode(bitem.text())) + self.item_id = (bitem.data(QtCore.Qt.UserRole)).toInt()[0] self.saveButton.setEnabled(False) - self.deleteButton.setEnabled(True) def onSingleClick(self): """ List item has been single clicked to add it to the edit field so it can be changed. """ - items = self.alertListWidget.selectedIndexes() - for item in items: - bitem = self.alertListWidget.item(item.row()) - self.alertTextEdit.setText(unicode(bitem.text())) - self.item_id = (bitem.data(QtCore.Qt.UserRole)).toInt()[0] + item = self.alertListWidget.selectedIndexes()[0] + bitem = self.alertListWidget.item(item.row()) + self.alertTextEdit.setText(unicode(bitem.text())) + self.item_id = (bitem.data(QtCore.Qt.UserRole)).toInt()[0] # If the alert does not contain '<>' we clear the ParameterEdit field. if unicode(self.alertTextEdit.text()).find(u'<>') == -1: self.parameterEdit.setText(u'') self.saveButton.setEnabled(False) - self.deleteButton.setEnabled(True) def triggerAlert(self, text): """ @@ -167,30 +158,49 @@ class AlertForm(QtGui.QDialog, Ui_AlertDialog): ``text`` The alert text (unicode). """ - if text: - # We found '<>' in the alert text, but the ParameterEdit field is - # empty. - if text.find(u'<>') != -1 and not self.parameterEdit.text() and \ - QtGui.QMessageBox.question(self, - translate('AlertPlugin.AlertForm', 'No Parameter found'), - translate('AlertPlugin.AlertForm', 'You have not entered a ' - 'parameter to be replaced.\nDo you want to continue anyway?'), - QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.No | - QtGui.QMessageBox.Yes)) == QtGui.QMessageBox.No: - self.parameterEdit.setFocus() - return False - # The ParameterEdit field is not empty, but we have not found '<>' - # in the alert text. - elif text.find(u'<>') == -1 and self.parameterEdit.text() and \ - QtGui.QMessageBox.question(self, - translate('AlertPlugin.AlertForm', 'No Placeholder found'), - translate('AlertPlugin.AlertForm', 'The alert text does not' - ' contain \'<>\'.\nDo want to continue anyway?'), - QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.No | - QtGui.QMessageBox.Yes)) == QtGui.QMessageBox.No: - self.parameterEdit.setFocus() - return False - text = text.replace(u'<>', unicode(self.parameterEdit.text())) - self.parent.alertsmanager.displayAlert(text) - return True - return False + if not text: + return False + # We found '<>' in the alert text, but the ParameterEdit field is empty. + if text.find(u'<>') != -1 and not self.parameterEdit.text() and \ + QtGui.QMessageBox.question(self, + translate('AlertPlugin.AlertForm', 'No Parameter found'), + translate('AlertPlugin.AlertForm', 'You have not entered a ' + 'parameter to be replaced.\nDo you want to continue anyway?'), + QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.No | + QtGui.QMessageBox.Yes)) == QtGui.QMessageBox.No: + self.parameterEdit.setFocus() + return False + # The ParameterEdit field is not empty, but we have not found '<>' + # in the alert text. + elif text.find(u'<>') == -1 and self.parameterEdit.text() and \ + QtGui.QMessageBox.question(self, + translate('AlertPlugin.AlertForm', 'No Placeholder found'), + translate('AlertPlugin.AlertForm', 'The alert text does not' + ' contain \'<>\'.\nDo want to continue anyway?'), + QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.No | + QtGui.QMessageBox.Yes)) == QtGui.QMessageBox.No: + self.parameterEdit.setFocus() + return False + text = text.replace(u'<>', unicode(self.parameterEdit.text())) + self.parent.alertsmanager.displayAlert(text) + return True + + def onCurrentRowChanged(self, row): + """ + Called when the *alertListWidget*'s current row has been changed. This + enables or disables buttons which require an item to act on. + + ``row`` + The row (int). If there is no current row, the value is -1. + """ + if row == -1: + self.displayButton.setEnabled(False) + self.displayCloseButton.setEnabled(False) + self.saveButton.setEnabled(False) + self.deleteButton.setEnabled(False) + else: + self.displayButton.setEnabled(True) + self.displayCloseButton.setEnabled(True) + self.deleteButton.setEnabled(True) + # We do not need to enable the save button, as it is only enabled + # when typing text in the "alertTextEdit". diff --git a/openlp/plugins/bibles/bibleplugin.py b/openlp/plugins/bibles/bibleplugin.py index 15181e871..e3b2ad4aa 100644 --- a/openlp/plugins/bibles/bibleplugin.py +++ b/openlp/plugins/bibles/bibleplugin.py @@ -102,7 +102,7 @@ class BiblePlugin(Plugin): Called to find out if the bible plugin is currently using a theme. Returns True if the theme is being used, otherwise returns False. """ - if self.settings_tab.bible_theme == theme: + if unicode(self.settings_tab.bible_theme) == theme: return True return False @@ -119,6 +119,7 @@ class BiblePlugin(Plugin): The new name the plugin should now use. """ self.settings_tab.bible_theme = newTheme + self.settings_tab.save() def setPluginTextStrings(self): """ diff --git a/openlp/plugins/bibles/forms/bibleimportform.py b/openlp/plugins/bibles/forms/bibleimportform.py index a2509ba97..f21dd0e07 100644 --- a/openlp/plugins/bibles/forms/bibleimportform.py +++ b/openlp/plugins/bibles/forms/bibleimportform.py @@ -157,10 +157,9 @@ class BibleImportForm(OpenLPWizard): self.formatComboBox.addItems([u'', u'', u'', u'', u'']) self.formatComboBox.setObjectName(u'FormatComboBox') self.formatLayout.addRow(self.formatLabel, self.formatComboBox) - self.formatSpacer = QtGui.QSpacerItem(10, 0, QtGui.QSizePolicy.Fixed, + self.spacer = QtGui.QSpacerItem(10, 0, QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Minimum) - self.formatLayout.setItem(1, QtGui.QFormLayout.LabelRole, - self.formatSpacer) + self.formatLayout.setItem(1, QtGui.QFormLayout.LabelRole, self.spacer) self.selectPageLayout.addLayout(self.formatLayout) self.selectStack = QtGui.QStackedLayout() self.selectStack.setObjectName(u'SelectStack') @@ -181,9 +180,7 @@ class BibleImportForm(OpenLPWizard): self.osisBrowseButton.setObjectName(u'OsisBrowseButton') self.osisFileLayout.addWidget(self.osisBrowseButton) self.osisLayout.addRow(self.osisFileLabel, self.osisFileLayout) - self.osisSpacer = QtGui.QSpacerItem(10, 0, QtGui.QSizePolicy.Fixed, - QtGui.QSizePolicy.Minimum) - self.osisLayout.setItem(1, QtGui.QFormLayout.LabelRole, self.osisSpacer) + self.osisLayout.setItem(1, QtGui.QFormLayout.LabelRole, self.spacer) self.selectStack.addWidget(self.osisWidget) self.csvWidget = QtGui.QWidget(self.selectPage) self.csvWidget.setObjectName(u'CsvWidget') @@ -226,9 +223,7 @@ class BibleImportForm(OpenLPWizard): self.csvVersesButton.setObjectName(u'CsvVersesButton') self.csvVersesLayout.addWidget(self.csvVersesButton) self.csvLayout.addRow(self.csvVersesLabel, self.csvVersesLayout) - self.csvSpacer = QtGui.QSpacerItem(10, 0, QtGui.QSizePolicy.Fixed, - QtGui.QSizePolicy.Minimum) - self.csvLayout.setItem(3, QtGui.QFormLayout.LabelRole, self.csvSpacer) + self.csvLayout.setItem(3, QtGui.QFormLayout.LabelRole, self.spacer) self.selectStack.addWidget(self.csvWidget) self.openSongWidget = QtGui.QWidget(self.selectPage) self.openSongWidget.setObjectName(u'OpenSongWidget') @@ -248,10 +243,7 @@ class BibleImportForm(OpenLPWizard): self.openSongFileLayout.addWidget(self.openSongBrowseButton) self.openSongLayout.addRow(self.openSongFileLabel, self.openSongFileLayout) - self.openSongSpacer = QtGui.QSpacerItem(10, 0, QtGui.QSizePolicy.Fixed, - QtGui.QSizePolicy.Minimum) - self.openSongLayout.setItem(1, QtGui.QFormLayout.LabelRole, - self.openSongSpacer) + self.openSongLayout.setItem(1, QtGui.QFormLayout.LabelRole, self.spacer) self.selectStack.addWidget(self.openSongWidget) self.webTabWidget = QtGui.QTabWidget(self.selectPage) self.webTabWidget.setObjectName(u'WebTabWidget') @@ -330,10 +322,7 @@ class BibleImportForm(OpenLPWizard): self.openlp1DisabledLabel.setWordWrap(True) self.openlp1DisabledLabel.setObjectName(u'Openlp1DisabledLabel') self.openlp1Layout.addRow(self.openlp1DisabledLabel) - self.openlp1Spacer = QtGui.QSpacerItem(10, 0, QtGui.QSizePolicy.Fixed, - QtGui.QSizePolicy.Minimum) - self.openlp1Layout.setItem(1, QtGui.QFormLayout.LabelRole, - self.openlp1Spacer) + self.openlp1Layout.setItem(1, QtGui.QFormLayout.LabelRole, self.spacer) self.selectStack.addWidget(self.openlp1Widget) self.selectPageLayout.addLayout(self.selectStack) self.addPage(self.selectPage) @@ -401,17 +390,17 @@ class BibleImportForm(OpenLPWizard): self.formatComboBox.setItemText(4, translate('BiblesPlugin.ImportWizardForm', 'openlp.org 1.x')) self.openlp1FileLabel.setText( - translate('BiblesPlugin.ImportWizardForm', 'File location:')) + translate('BiblesPlugin.ImportWizardForm', 'Bible file:')) self.osisFileLabel.setText( - translate('BiblesPlugin.ImportWizardForm', 'File location:')) + translate('BiblesPlugin.ImportWizardForm', 'Bible file:')) self.csvTestamentsLabel.setText( - translate('BiblesPlugin.ImportWizardForm', 'Testaments location:')) + translate('BiblesPlugin.ImportWizardForm', 'Testaments file:')) self.csvBooksLabel.setText( - translate('BiblesPlugin.ImportWizardForm', 'Books location:')) + translate('BiblesPlugin.ImportWizardForm', 'Books file:')) self.csvVersesLabel.setText( - translate('BiblesPlugin.ImportWizardForm', 'Verse location:')) + translate('BiblesPlugin.ImportWizardForm', 'Verses file:')) self.openSongFileLabel.setText( - translate('BiblesPlugin.ImportWizardForm', 'Bible filename:')) + translate('BiblesPlugin.ImportWizardForm', 'Bible file:')) self.webSourceLabel.setText( translate('BiblesPlugin.ImportWizardForm', 'Location:')) self.webSourceComboBox.setItemText(0, @@ -462,19 +451,12 @@ class BibleImportForm(OpenLPWizard): # Align all QFormLayouts towards each other. labelWidth = max(self.formatLabel.minimumSizeHint().width(), self.osisFileLabel.minimumSizeHint().width(), + self.csvTestamentsLabel.minimumSizeHint().width(), self.csvBooksLabel.minimumSizeHint().width(), self.csvVersesLabel.minimumSizeHint().width(), self.openSongFileLabel.minimumSizeHint().width(), self.openlp1FileLabel.minimumSizeHint().width()) - self.formatSpacer.changeSize(labelWidth, 0, - QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed) - self.osisSpacer.changeSize(labelWidth, 0, - QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed) - self.csvSpacer.changeSize(labelWidth, 0, - QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed) - self.openSongSpacer.changeSize(labelWidth, 0, - QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed) - self.openlp1Spacer.changeSize(labelWidth, 0, + self.spacer.changeSize(labelWidth, 0, QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed) def validateCurrentPage(self): diff --git a/openlp/plugins/bibles/lib/__init__.py b/openlp/plugins/bibles/lib/__init__.py index 34e1bf5a5..e6fff1cc8 100644 --- a/openlp/plugins/bibles/lib/__init__.py +++ b/openlp/plugins/bibles/lib/__init__.py @@ -77,9 +77,9 @@ def parse_reference(reference): - After a verse reference all further single values are treat as verse in the last selected chapter. ``John 3:16-18`` refers to John chapter 3 verses 16 to 18 - - After a list separator it is possible to refer to additional verses. They - are build analog to the first ones. This way it is possible to define - each number of verse references. It is not possible to refer to verses in + - After a list separator it is possible to refer to additional verses. They + are build analog to the first ones. This way it is possible to define each + number of verse references. It is not possible to refer to verses in additional books. ``John 3:16,18`` refers to John chapter 3 verses 16 and 18 ``John 3:16-18,20`` refers to John chapter 3 verses 16 to 18 and 20 @@ -91,7 +91,7 @@ def parse_reference(reference): ``range_string`` is a regular expression which matches for verse range declarations: - 1. ``(?:(?P[0-9]+)%(sep_v)s)?' + 1. ``(?:(?P[0-9]+)%(sep_v)s)?`` It starts with a optional chapter reference ``from_chapter`` followed by a verse separator. 2. ``(?P[0-9]+)`` @@ -105,7 +105,7 @@ def parse_reference(reference): 5. ``(?P[0-9]+)`` The ``to_verse`` reference is equivalent to group 2. - The full reference is matched against get_reference_match(u'full'). This + The full reference is matched against get_reference_match(u'full'). This regular expression looks like this: 1. ``^\s*(?!\s)(?P[\d]*[^\d]+)(?(?:`` + range_string + ``(?:%(sep_l)s|(?=\s*$)))+)\s*$`` - The second group contains all ``ranges``. This can be multiple + The second group contains all ``ranges``. This can be multiple declarations of a range_string separated by a list separator. The reference list is a list of tuples, with each tuple structured like diff --git a/openlp/plugins/bibles/lib/http.py b/openlp/plugins/bibles/lib/http.py index 8db214140..7cba6facb 100644 --- a/openlp/plugins/bibles/lib/http.py +++ b/openlp/plugins/bibles/lib/http.py @@ -443,7 +443,6 @@ class HTTPBible(BibleDB): book = db_book.name if BibleDB.get_verse_count(self, book, reference[1]) == 0: Receiver.send_message(u'cursor_busy') - Receiver.send_message(u'openlp_process_events') search_results = self.get_chapter(book, reference[1]) if search_results and search_results.has_verselist(): ## We have found a book of the bible lets check to see diff --git a/openlp/plugins/bibles/lib/mediaitem.py b/openlp/plugins/bibles/lib/mediaitem.py index 26f45b6b3..c162447b2 100644 --- a/openlp/plugins/bibles/lib/mediaitem.py +++ b/openlp/plugins/bibles/lib/mediaitem.py @@ -30,7 +30,8 @@ from PyQt4 import QtCore, QtGui from openlp.core.lib import MediaManagerItem, Receiver, BaseListWithDnD, \ ItemCapabilities, translate -from openlp.core.lib.ui import critical_error_message_box, media_item_combo_box +from openlp.core.lib.ui import add_widget_completer, media_item_combo_box, \ + critical_error_message_box from openlp.plugins.bibles.forms import BibleImportForm from openlp.plugins.bibles.lib import get_reference_match @@ -379,9 +380,7 @@ class BibleMediaItem(MediaManagerItem): book_data = bibles[bible].get_books() books = [book.name for book in book_data] books.sort() - completer = QtGui.QCompleter(books) - completer.setCaseSensitivity(QtCore.Qt.CaseInsensitive) - self.quickSearchEdit.setCompleter(completer) + add_widget_completer(books, self.quickSearchEdit) def onAdvancedVersionComboBox(self): self.initialiseBible( diff --git a/openlp/plugins/bibles/lib/opensong.py b/openlp/plugins/bibles/lib/opensong.py index 12a6562bc..9a0fd110d 100644 --- a/openlp/plugins/bibles/lib/opensong.py +++ b/openlp/plugins/bibles/lib/opensong.py @@ -38,7 +38,6 @@ class OpenSongBible(BibleDB): """ OpenSong Bible format importer class. """ - def __init__(self, parent, **kwargs): """ Constructor to create and set up an instance of the OpenSongBible @@ -81,14 +80,13 @@ class OpenSongBible(BibleDB): db_book.id, int(chapter.attrib[u'n'].split()[-1]), int(verse.attrib[u'n']), - unicode(verse.text) - ) - Receiver.send_message(u'openlp_process_events') + unicode(verse.text)) self.wizard.incrementProgressBar(unicode(translate( 'BiblesPlugin.Opensong', 'Importing %s %s...', 'Importing ...')) % (db_book.name, int(chapter.attrib[u'n'].split()[-1]))) - self.session.commit() + self.session.commit() + Receiver.send_message(u'openlp_process_events') except (IOError, AttributeError): log.exception(u'Loading bible from OpenSong file failed') success = False diff --git a/openlp/plugins/custom/forms/editcustomdialog.py b/openlp/plugins/custom/forms/editcustomdialog.py index 75717c082..b7887aa90 100644 --- a/openlp/plugins/custom/forms/editcustomdialog.py +++ b/openlp/plugins/custom/forms/editcustomdialog.py @@ -27,7 +27,8 @@ from PyQt4 import QtCore, QtGui from openlp.core.lib import build_icon, translate -from openlp.core.lib.ui import save_cancel_button_box +from openlp.core.lib.ui import create_save_cancel_button_box, \ + create_delete_push_button, create_up_down_push_button_set class Ui_CustomEditDialog(object): def setupUi(self, customEditDialog): @@ -59,22 +60,21 @@ class Ui_CustomEditDialog(object): self.addButton.setObjectName(u'addButton') self.buttonLayout.addWidget(self.addButton) self.editButton = QtGui.QPushButton(customEditDialog) + self.editButton.setEnabled(False) self.editButton.setObjectName(u'editButton') self.buttonLayout.addWidget(self.editButton) self.editAllButton = QtGui.QPushButton(customEditDialog) self.editAllButton.setObjectName(u'editAllButton') self.buttonLayout.addWidget(self.editAllButton) - self.deleteButton = QtGui.QPushButton(customEditDialog) - self.deleteButton.setObjectName(u'deleteButton') + self.deleteButton = create_delete_push_button(customEditDialog) + self.deleteButton.setEnabled(False) self.buttonLayout.addWidget(self.deleteButton) self.buttonLayout.addStretch() - self.upButton = QtGui.QPushButton(customEditDialog) - self.upButton.setIcon(build_icon(u':/services/service_up.png')) - self.upButton.setObjectName(u'upButton') + self.upButton, self.downButton = create_up_down_push_button_set( + customEditDialog) + self.upButton.setEnabled(False) + self.downButton.setEnabled(False) self.buttonLayout.addWidget(self.upButton) - self.downButton = QtGui.QPushButton(customEditDialog) - self.downButton.setIcon(build_icon(u':/services/service_down.png')) - self.downButton.setObjectName(u'downButton') self.buttonLayout.addWidget(self.downButton) self.centralLayout.addLayout(self.buttonLayout) self.dialogLayout.addLayout(self.centralLayout) @@ -94,7 +94,10 @@ class Ui_CustomEditDialog(object): self.creditLabel.setBuddy(self.creditEdit) self.bottomFormLayout.addRow(self.creditLabel, self.creditEdit) self.dialogLayout.addLayout(self.bottomFormLayout) - self.buttonBox = save_cancel_button_box(customEditDialog) + self.buttonBox = create_save_cancel_button_box(customEditDialog) + self.previewButton = QtGui.QPushButton() + self.buttonBox.addButton( + self.previewButton, QtGui.QDialogButtonBox.ActionRole) self.dialogLayout.addWidget(self.buttonBox) self.retranslateUi(customEditDialog) QtCore.QMetaObject.connectSlotsByName(customEditDialog) @@ -102,12 +105,6 @@ class Ui_CustomEditDialog(object): def retranslateUi(self, customEditDialog): customEditDialog.setWindowTitle( translate('CustomPlugin.EditCustomForm', 'Edit Custom Slides')) - self.upButton.setToolTip( - translate('CustomPlugin.EditCustomForm', 'Move slide up one ' - 'position.')) - self.downButton.setToolTip( - translate('CustomPlugin.EditCustomForm', 'Move slide down one ' - 'position.')) self.titleLabel.setText( translate('CustomPlugin.EditCustomForm', '&Title:')) self.addButton.setText( @@ -125,12 +122,9 @@ class Ui_CustomEditDialog(object): self.editAllButton.setToolTip( translate('CustomPlugin.EditCustomForm', 'Edit all the slides at ' 'once.')) - self.deleteButton.setText( - translate('CustomPlugin.EditCustomForm', '&Delete')) - self.deleteButton.setToolTip( - translate('CustomPlugin.EditCustomForm', 'Delete the selected ' - 'slide.')) self.themeLabel.setText( translate('CustomPlugin.EditCustomForm', 'The&me:')) self.creditLabel.setText( translate('CustomPlugin.EditCustomForm', '&Credits:')) + self.previewButton.setText( + translate('CustomPlugin.EditCustomForm', 'Save && Preview')) diff --git a/openlp/plugins/custom/forms/editcustomform.py b/openlp/plugins/custom/forms/editcustomform.py index e8dfa20aa..b667cd529 100644 --- a/openlp/plugins/custom/forms/editcustomform.py +++ b/openlp/plugins/custom/forms/editcustomform.py @@ -48,52 +48,22 @@ class EditCustomForm(QtGui.QDialog, Ui_CustomEditDialog): """ QtGui.QDialog.__init__(self, parent) self.setupUi(self) + # Create other objects and forms. + self.manager = manager + self.editSlideForm = EditCustomSlideForm(self) # Connecting signals and slots - self.previewButton = QtGui.QPushButton() - self.previewButton.setText( - translate('CustomPlugin.EditCustomForm', 'Save && Preview')) - self.buttonBox.addButton( - self.previewButton, QtGui.QDialogButtonBox.ActionRole) - QtCore.QObject.connect(self.buttonBox, - QtCore.SIGNAL(u'clicked(QAbstractButton*)'), self.onPreview) + QtCore.QObject.connect(self.previewButton, + QtCore.SIGNAL(u'pressed()'), self.onPreviewButtonPressed) QtCore.QObject.connect(self.addButton, QtCore.SIGNAL(u'pressed()'), self.onAddButtonPressed) QtCore.QObject.connect(self.editButton, QtCore.SIGNAL(u'pressed()'), self.onEditButtonPressed) QtCore.QObject.connect(self.editAllButton, QtCore.SIGNAL(u'pressed()'), self.onEditAllButtonPressed) - QtCore.QObject.connect(self.deleteButton, - QtCore.SIGNAL(u'pressed()'), self.onDeleteButtonPressed) - QtCore.QObject.connect(self.upButton, - QtCore.SIGNAL(u'pressed()'), self.onUpButtonPressed) - QtCore.QObject.connect(self.downButton, - QtCore.SIGNAL(u'pressed()'), self.onDownButtonPressed) - QtCore.QObject.connect(self.slideListView, - QtCore.SIGNAL(u'itemClicked(QListWidgetItem*)'), - self.onSlideListViewPressed) QtCore.QObject.connect(Receiver.get_receiver(), QtCore.SIGNAL(u'theme_update_list'), self.loadThemes) - # Create other objects and forms. - self.manager = manager - self.editSlideForm = EditCustomSlideForm(self) - self.initialise() - - def onPreview(self, button): - log.debug(u'onPreview') - if button.text() == unicode(translate('CustomPlugin.EditCustomForm', - 'Save && Preview')) and self.saveCustom(): - Receiver.send_message(u'custom_preview') - - def initialise(self): - self.addButton.setEnabled(True) - self.deleteButton.setEnabled(False) - self.editButton.setEnabled(False) - self.editAllButton.setEnabled(True) - self.titleEdit.setText(u'') - self.creditEdit.setText(u'') - self.slideListView.clear() - # Make sure we have a new item. - self.customSlide = CustomSlide() + QtCore.QObject.connect(self.slideListView, + QtCore.SIGNAL(u'currentRowChanged(int)'), self.onCurrentRowChanged) def loadThemes(self, themelist): self.themeComboBox.clear() @@ -112,9 +82,13 @@ class EditCustomForm(QtGui.QDialog, Ui_CustomEditDialog): States whether the custom is edited while being previewed in the preview panel. """ - self.customSlide = CustomSlide() - self.initialise() - if id != 0: + self.slideListView.clear() + if id == 0: + self.customSlide = CustomSlide() + self.titleEdit.setText(u'') + self.creditEdit.setText(u'') + self.themeComboBox.setCurrentIndex(0) + else: self.customSlide = self.manager.get_object(CustomSlide, id) self.titleEdit.setText(self.customSlide.title) self.creditEdit.setText(self.customSlide.credits) @@ -128,9 +102,6 @@ class EditCustomForm(QtGui.QDialog, Ui_CustomEditDialog): if id == -1: id = 0 self.themeComboBox.setCurrentIndex(id) - else: - self.themeComboBox.setCurrentIndex(0) - self.editAllButton.setEnabled(False) # If not preview hide the preview button. self.previewButton.setVisible(False) if preview: @@ -150,9 +121,7 @@ class EditCustomForm(QtGui.QDialog, Ui_CustomEditDialog): """ Saves the custom. """ - valid, message = self._validate() - if not valid: - critical_error_message_box(message=message) + if not self._validate(): return False sxml = CustomXMLBuilder() sxml.new_document() @@ -168,14 +137,14 @@ class EditCustomForm(QtGui.QDialog, Ui_CustomEditDialog): self.customSlide.theme_name = unicode(self.themeComboBox.currentText()) return self.manager.save_object(self.customSlide) - def onUpButtonPressed(self): + def onUpButtonClicked(self): selectedRow = self.slideListView.currentRow() if selectedRow != 0: qw = self.slideListView.takeItem(selectedRow) self.slideListView.insertItem(selectedRow - 1, qw) self.slideListView.setCurrentRow(selectedRow - 1) - def onDownButtonPressed(self): + def onDownButtonClicked(self): selectedRow = self.slideListView.currentRow() # zero base arrays if selectedRow != self.slideListView.count() - 1: @@ -183,16 +152,11 @@ class EditCustomForm(QtGui.QDialog, Ui_CustomEditDialog): self.slideListView.insertItem(selectedRow + 1, qw) self.slideListView.setCurrentRow(selectedRow + 1) - def onSlideListViewPressed(self, item): - self.deleteButton.setEnabled(True) - self.editButton.setEnabled(True) - def onAddButtonPressed(self): self.editSlideForm.setText(u'') if self.editSlideForm.exec_(): for slide in self.editSlideForm.getText(): self.slideListView.addItem(slide) - self.editAllButton.setEnabled(True) def onEditButtonPressed(self): self.editSlideForm.setText(self.slideListView.currentItem().text()) @@ -203,16 +167,23 @@ class EditCustomForm(QtGui.QDialog, Ui_CustomEditDialog): """ Edits all slides. """ - if self.slideListView.count() > 0: - slide_list = u'' - for row in range(0, self.slideListView.count()): - item = self.slideListView.item(row) - slide_list += item.text() - if row != self.slideListView.count() - 1: - slide_list += u'\n[---]\n' - self.editSlideForm.setText(slide_list) - if self.editSlideForm.exec_(): - self.updateSlideList(self.editSlideForm.getText(), True) + slide_list = u'' + for row in range(0, self.slideListView.count()): + item = self.slideListView.item(row) + slide_list += item.text() + if row != self.slideListView.count() - 1: + slide_list += u'\n[---]\n' + self.editSlideForm.setText(slide_list) + if self.editSlideForm.exec_(): + self.updateSlideList(self.editSlideForm.getText(), True) + + def onPreviewButtonPressed(self): + """ + Save the custom item and preview it. + """ + log.debug(u'onPreview') + if self.saveCustom(): + Receiver.send_message(u'custom_preview') def updateSlideList(self, slides, edit_all=False): """ @@ -243,14 +214,41 @@ class EditCustomForm(QtGui.QDialog, Ui_CustomEditDialog): self.slideListView.addItem(slide) self.slideListView.repaint() - def onDeleteButtonPressed(self): + def onDeleteButtonClicked(self): + """ + Removes the current row from the list. + """ self.slideListView.takeItem(self.slideListView.currentRow()) - self.editButton.setEnabled(True) - self.editAllButton.setEnabled(True) - if self.slideListView.count() == 0: + if self.slideListView.currentRow() == 0: + self.upButton.setEnabled(False) + if self.slideListView.currentRow() == self.slideListView.count(): + self.downButton.setEnabled(False) + + def onCurrentRowChanged(self, row): + """ + Called when the *slideListView*'s current row has been changed. This + enables or disables buttons which require an slide to act on. + + ``row`` + The row (int). If there is no current row, the value is -1. + """ + if row == -1: self.deleteButton.setEnabled(False) self.editButton.setEnabled(False) - self.editAllButton.setEnabled(False) + self.upButton.setEnabled(False) + self.downButton.setEnabled(False) + else: + self.deleteButton.setEnabled(True) + self.editButton.setEnabled(True) + # Decide if the up/down buttons should be enabled or not. + if self.slideListView.count() - 1 == row: + self.downButton.setEnabled(False) + else: + self.downButton.setEnabled(True) + if row == 0: + self.upButton.setEnabled(False) + else: + self.upButton.setEnabled(True) def _validate(self): """ @@ -259,10 +257,14 @@ class EditCustomForm(QtGui.QDialog, Ui_CustomEditDialog): # We must have a title. if len(self.titleEdit.displayText()) == 0: self.titleEdit.setFocus() - return False, translate('CustomPlugin.EditCustomForm', - 'You need to type in a title.') + critical_error_message_box( + message=translate('CustomPlugin.EditCustomForm', + 'You need to type in a title.')) + return False # We must have at least one slide. if self.slideListView.count() == 0: - return False, translate('CustomPlugin.EditCustomForm', - 'You need to add at least one slide') - return True, u'' + critical_error_message_box( + message=translate('CustomPlugin.EditCustomForm', + 'You need to add at least one slide')) + return False + return True diff --git a/openlp/plugins/custom/forms/editcustomslidedialog.py b/openlp/plugins/custom/forms/editcustomslidedialog.py index 93bff68b8..24c40d6e6 100644 --- a/openlp/plugins/custom/forms/editcustomslidedialog.py +++ b/openlp/plugins/custom/forms/editcustomslidedialog.py @@ -27,7 +27,7 @@ from PyQt4 import QtCore, QtGui from openlp.core.lib import translate, SpellTextEdit -from openlp.core.lib.ui import save_cancel_button_box +from openlp.core.lib.ui import create_save_cancel_button_box class Ui_CustomSlideEditDialog(object): def setupUi(self, customSlideEditDialog): @@ -37,7 +37,7 @@ class Ui_CustomSlideEditDialog(object): self.slideTextEdit = SpellTextEdit(self) self.slideTextEdit.setObjectName(u'slideTextEdit') self.dialogLayout.addWidget(self.slideTextEdit) - self.buttonBox = save_cancel_button_box(customSlideEditDialog) + self.buttonBox = create_save_cancel_button_box(customSlideEditDialog) self.splitButton = QtGui.QPushButton(customSlideEditDialog) self.splitButton.setObjectName(u'splitButton') self.buttonBox.addButton(self.splitButton, diff --git a/openlp/plugins/custom/lib/mediaitem.py b/openlp/plugins/custom/lib/mediaitem.py index ec915b0a9..908c4e18d 100644 --- a/openlp/plugins/custom/lib/mediaitem.py +++ b/openlp/plugins/custom/lib/mediaitem.py @@ -69,7 +69,7 @@ class CustomMediaItem(MediaManagerItem): QtCore.SIGNAL(u'custom_preview'), self.onPreviewClick) def initialise(self): - self.loadCustomListView(self.manager.get_all_objects( + self.loadList(self.manager.get_all_objects( CustomSlide, order_by_ref=CustomSlide.title)) # Called to redisplay the custom list screen edith from a search # or from the exit of the Custom edit dialog. If remote editing is @@ -80,7 +80,7 @@ class CustomMediaItem(MediaManagerItem): self.onPreviewClick() self.onRemoteEditClear() - def loadCustomListView(self, list): + def loadList(self, list): self.listView.clear() for customSlide in list: custom_name = QtGui.QListWidgetItem(customSlide.title) diff --git a/openlp/plugins/images/lib/mediaitem.py b/openlp/plugins/images/lib/mediaitem.py index cbefc7171..71027881c 100644 --- a/openlp/plugins/images/lib/mediaitem.py +++ b/openlp/plugins/images/lib/mediaitem.py @@ -123,19 +123,19 @@ class ImageMediaItem(MediaManagerItem): self.settingsSection, self.getFileList()) def loadList(self, list): - for file in list: - filename = os.path.split(unicode(file))[1] + for imageFile in list: + filename = os.path.split(unicode(imageFile))[1] thumb = os.path.join(self.servicePath, filename) if os.path.exists(thumb): - if self.validate(file, thumb): + if self.validate(imageFile, thumb): icon = build_icon(thumb) else: icon = build_icon(u':/general/general_delete.png') else: - icon = self.iconFromFile(file, thumb) + icon = self.iconFromFile(imageFile, thumb) item_name = QtGui.QListWidgetItem(filename) item_name.setIcon(icon) - item_name.setData(QtCore.Qt.UserRole, QtCore.QVariant(file)) + item_name.setData(QtCore.Qt.UserRole, QtCore.QVariant(imageFile)) self.listView.addItem(item_name) def generateSlideData(self, service_item, item=None, xmlVersion=False): diff --git a/openlp/plugins/presentations/presentationplugin.py b/openlp/plugins/presentations/presentationplugin.py index 8afed6022..17417df58 100644 --- a/openlp/plugins/presentations/presentationplugin.py +++ b/openlp/plugins/presentations/presentationplugin.py @@ -74,7 +74,11 @@ class PresentationPlugin(Plugin): self.insertToolboxItem() for controller in self.controllers: if self.controllers[controller].enabled(): - self.controllers[controller].start_process() + try: + self.controllers[controller].start_process() + except: + log.exception(u'Failed to start controller process') + self.controllers[controller].available = False self.mediaItem.buildFileMaskString() def finalise(self): diff --git a/openlp/plugins/songs/forms/authorsdialog.py b/openlp/plugins/songs/forms/authorsdialog.py index 3fd3c5fef..0b3f791d1 100644 --- a/openlp/plugins/songs/forms/authorsdialog.py +++ b/openlp/plugins/songs/forms/authorsdialog.py @@ -27,7 +27,7 @@ from PyQt4 import QtCore, QtGui from openlp.core.lib import translate -from openlp.core.lib.ui import save_cancel_button_box +from openlp.core.lib.ui import create_save_cancel_button_box class Ui_AuthorsDialog(object): def setupUi(self, authorsDialog): @@ -56,7 +56,8 @@ class Ui_AuthorsDialog(object): self.displayLabel.setBuddy(self.displayEdit) self.authorLayout.addRow(self.displayLabel, self.displayEdit) self.dialogLayout.addLayout(self.authorLayout) - self.dialogLayout.addWidget(save_cancel_button_box(authorsDialog)) + self.dialogLayout.addWidget( + create_save_cancel_button_box(authorsDialog)) self.retranslateUi(authorsDialog) authorsDialog.setMaximumHeight(authorsDialog.sizeHint().height()) QtCore.QMetaObject.connectSlotsByName(authorsDialog) diff --git a/openlp/plugins/songs/forms/editsongdialog.py b/openlp/plugins/songs/forms/editsongdialog.py index c44b42d46..e9be62830 100644 --- a/openlp/plugins/songs/forms/editsongdialog.py +++ b/openlp/plugins/songs/forms/editsongdialog.py @@ -27,7 +27,7 @@ from PyQt4 import QtCore, QtGui from openlp.core.lib import build_icon, translate -from openlp.core.lib.ui import save_cancel_button_box +from openlp.core.lib.ui import create_save_cancel_button_box class Ui_EditSongDialog(object): def setupUi(self, editSongDialog): @@ -241,7 +241,7 @@ class Ui_EditSongDialog(object): self.themeTabLayout.addWidget(self.commentsGroupBox) self.songTabWidget.addTab(self.themeTab, u'') self.dialogLayout.addWidget(self.songTabWidget) - self.buttonBox = save_cancel_button_box(editSongDialog) + self.buttonBox = create_save_cancel_button_box(editSongDialog) self.dialogLayout.addWidget(self.buttonBox) self.retranslateUi(editSongDialog) QtCore.QMetaObject.connectSlotsByName(editSongDialog) diff --git a/openlp/plugins/songs/forms/editsongform.py b/openlp/plugins/songs/forms/editsongform.py index bf94503ff..8536d38b8 100644 --- a/openlp/plugins/songs/forms/editsongform.py +++ b/openlp/plugins/songs/forms/editsongform.py @@ -30,7 +30,7 @@ import re from PyQt4 import QtCore, QtGui from openlp.core.lib import Receiver, translate -from openlp.core.lib.ui import critical_error_message_box +from openlp.core.lib.ui import add_widget_completer, critical_error_message_box from openlp.plugins.songs.forms import EditVerseForm from openlp.plugins.songs.lib import SongXML, VerseType from openlp.plugins.songs.lib.db import Book, Song, Author, Topic @@ -129,37 +129,26 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog): self.authorsComboBox.setItemData( row, QtCore.QVariant(author.id)) self.authors.append(author.display_name) - completer = QtGui.QCompleter(self.authors) - completer.setCaseSensitivity(QtCore.Qt.CaseInsensitive) - self.authorsComboBox.setCompleter(completer) + add_widget_completer(self.authors, self.authorsComboBox) def loadTopics(self): - topics = self.manager.get_all_objects(Topic, order_by_ref=Topic.name) - self.topicsComboBox.clear() - self.topicsComboBox.addItem(u'') self.topics = [] - for topic in topics: - row = self.topicsComboBox.count() - self.topicsComboBox.addItem(topic.name) - self.topics.append(topic.name) - self.topicsComboBox.setItemData(row, QtCore.QVariant(topic.id)) - completer = QtGui.QCompleter(self.topics) - completer.setCaseSensitivity(QtCore.Qt.CaseInsensitive) - self.topicsComboBox.setCompleter(completer) + self.__loadObjects(Topic, self.topicsComboBox, self.topics) def loadBooks(self): - books = self.manager.get_all_objects(Book, order_by_ref=Book.name) - self.songBookComboBox.clear() - self.songBookComboBox.addItem(u'') self.books = [] - for book in books: - row = self.songBookComboBox.count() - self.songBookComboBox.addItem(book.name) - self.books.append(book.name) - self.songBookComboBox.setItemData(row, QtCore.QVariant(book.id)) - completer = QtGui.QCompleter(self.books) - completer.setCaseSensitivity(QtCore.Qt.CaseInsensitive) - self.songBookComboBox.setCompleter(completer) + self.__loadObjects(Book, self.songBookComboBox, self.books) + + def __loadObjects(self, cls, combo, cache): + objects = self.manager.get_all_objects(cls, order_by_ref=cls.name) + combo.clear() + combo.addItem(u'') + for object in objects: + row = combo.count() + combo.addItem(object.name) + cache.append(object.name) + combo.setItemData(row, QtCore.QVariant(object.id)) + add_widget_completer(cache, combo) def loadThemes(self, theme_list): self.themeComboBox.clear() @@ -168,9 +157,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog): for theme in theme_list: self.themeComboBox.addItem(theme) self.themes.append(theme) - completer = QtGui.QCompleter(self.themes) - completer.setCaseSensitivity(QtCore.Qt.CaseInsensitive) - self.themeComboBox.setCompleter(completer) + add_widget_completer(self.themes, self.themeComboBox) def newSong(self): log.debug(u'New Song') diff --git a/openlp/plugins/songs/forms/editversedialog.py b/openlp/plugins/songs/forms/editversedialog.py index deaf952e2..b4bc4551f 100644 --- a/openlp/plugins/songs/forms/editversedialog.py +++ b/openlp/plugins/songs/forms/editversedialog.py @@ -27,7 +27,7 @@ from PyQt4 import QtCore, QtGui from openlp.core.lib import build_icon, translate, SpellTextEdit -from openlp.core.lib.ui import save_cancel_button_box +from openlp.core.lib.ui import create_save_cancel_button_box from openlp.plugins.songs.lib import VerseType class Ui_EditVerseDialog(object): @@ -60,7 +60,8 @@ class Ui_EditVerseDialog(object): self.verseTypeLayout.addWidget(self.insertButton) self.verseTypeLayout.addStretch() self.dialogLayout.addLayout(self.verseTypeLayout) - self.dialogLayout.addWidget(save_cancel_button_box(editVerseDialog)) + self.dialogLayout.addWidget( + create_save_cancel_button_box(editVerseDialog)) self.retranslateUi(editVerseDialog) QtCore.QMetaObject.connectSlotsByName(editVerseDialog) diff --git a/openlp/plugins/songs/forms/songbookdialog.py b/openlp/plugins/songs/forms/songbookdialog.py index fcd6bd364..89b8941b5 100644 --- a/openlp/plugins/songs/forms/songbookdialog.py +++ b/openlp/plugins/songs/forms/songbookdialog.py @@ -27,7 +27,7 @@ from PyQt4 import QtCore, QtGui from openlp.core.lib import translate -from openlp.core.lib.ui import save_cancel_button_box +from openlp.core.lib.ui import create_save_cancel_button_box class Ui_SongBookDialog(object): def setupUi(self, songBookDialog): @@ -50,7 +50,8 @@ class Ui_SongBookDialog(object): self.publisherLabel.setBuddy(self.publisherEdit) self.bookLayout.addRow(self.publisherLabel, self.publisherEdit) self.dialogLayout.addLayout(self.bookLayout) - self.dialogLayout.addWidget(save_cancel_button_box(songBookDialog)) + self.dialogLayout.addWidget( + create_save_cancel_button_box(songBookDialog)) self.retranslateUi(songBookDialog) songBookDialog.setMaximumHeight(songBookDialog.sizeHint().height()) QtCore.QMetaObject.connectSlotsByName(songBookDialog) diff --git a/openlp/plugins/songs/forms/songimportform.py b/openlp/plugins/songs/forms/songimportform.py index f1e1f82ed..a9af7f6b2 100644 --- a/openlp/plugins/songs/forms/songimportform.py +++ b/openlp/plugins/songs/forms/songimportform.py @@ -162,6 +162,8 @@ class SongImportForm(OpenLPWizard): self.formatLayout.setItem(1, QtGui.QFormLayout.LabelRole, self.formatSpacer) self.sourceLayout.addLayout(self.formatLayout) + self.stackSpacer = QtGui.QSpacerItem(10, 0, QtGui.QSizePolicy.Fixed, + QtGui.QSizePolicy.Expanding) self.formatStack = QtGui.QStackedLayout() self.formatStack.setObjectName(u'FormatStack') # OpenLP 2.0 @@ -807,10 +809,8 @@ class SongImportForm(OpenLPWizard): browseButton.setIcon(self.openIcon) browseButton.setObjectName(obj_prefix + u'BrowseButton') fileLayout.addWidget(browseButton) - formSpacer = QtGui.QSpacerItem(10, 0, QtGui.QSizePolicy.Fixed, - QtGui.QSizePolicy.Expanding) importLayout.addLayout(fileLayout) - importLayout.addSpacerItem(formSpacer) + importLayout.addSpacerItem(self.stackSpacer) else: fileListWidget = QtGui.QListWidget(importWidget) fileListWidget.setSelectionMode( @@ -859,6 +859,7 @@ class SongImportForm(OpenLPWizard): disabledLabel.setWordWrap(True) disabledLabel.setObjectName(obj_prefix + u'DisabledLabel') disabledLayout.addWidget(disabledLabel) + disabledLayout.addSpacerItem(self.stackSpacer) layout.addWidget(disabledWidget) importWidget = QtGui.QWidget(page) importWidget.setObjectName(obj_prefix + u'ImportWidget') diff --git a/openlp/plugins/songs/forms/songmaintenanceform.py b/openlp/plugins/songs/forms/songmaintenanceform.py index 737d63204..1eb63fbf4 100644 --- a/openlp/plugins/songs/forms/songmaintenanceform.py +++ b/openlp/plugins/songs/forms/songmaintenanceform.py @@ -372,7 +372,6 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog): Utility method to merge two objects to leave one in the database. """ Receiver.send_message(u'cursor_busy') - Receiver.send_message(u'openlp_process_events') merge(dbObject) reset() Receiver.send_message(u'songs_load_list') @@ -484,19 +483,19 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog): def onAuthorsListRowChanged(self, row): """ - Called when the *authorsListWidget*s current row has changed. + Called when the *authorsListWidget*'s current row has changed. """ self.__rowChange(row, self.authorsEditButton, self.authorsDeleteButton) def onTopicsListRowChanged(self, row): """ - Called when the *topicsListWidget*s current row has changed. + Called when the *topicsListWidget*'s current row has changed. """ self.__rowChange(row, self.topicsEditButton, self.topicsDeleteButton) def onBooksListRowChanged(self, row): """ - Called when the *booksListWidget*s current row has changed. + Called when the *booksListWidget*'s current row has changed. """ self.__rowChange(row, self.booksEditButton, self.booksDeleteButton) diff --git a/openlp/plugins/songs/forms/topicsdialog.py b/openlp/plugins/songs/forms/topicsdialog.py index f2c9fdeba..596597034 100644 --- a/openlp/plugins/songs/forms/topicsdialog.py +++ b/openlp/plugins/songs/forms/topicsdialog.py @@ -27,7 +27,7 @@ from PyQt4 import QtCore, QtGui from openlp.core.lib import translate -from openlp.core.lib.ui import save_cancel_button_box +from openlp.core.lib.ui import create_save_cancel_button_box class Ui_TopicsDialog(object): def setupUi(self, topicsDialog): @@ -44,7 +44,8 @@ class Ui_TopicsDialog(object): self.nameLabel.setBuddy(self.nameEdit) self.nameLayout.addRow(self.nameLabel, self.nameEdit) self.dialogLayout.addLayout(self.nameLayout) - self.dialogLayout.addWidget(save_cancel_button_box(topicsDialog)) + self.dialogLayout.addWidget( + create_save_cancel_button_box(topicsDialog)) self.retranslateUi(topicsDialog) topicsDialog.setMaximumHeight(topicsDialog.sizeHint().height()) QtCore.QMetaObject.connectSlotsByName(topicsDialog) diff --git a/openlp/plugins/songs/lib/db.py b/openlp/plugins/songs/lib/db.py index 838172893..2bbf818b3 100644 --- a/openlp/plugins/songs/lib/db.py +++ b/openlp/plugins/songs/lib/db.py @@ -39,6 +39,7 @@ class Author(BaseModel): """ pass + class Book(BaseModel): """ Book model @@ -47,30 +48,115 @@ class Book(BaseModel): return u'' % ( str(self.id), self.name, self.publisher) + class MediaFile(BaseModel): """ MediaFile model """ pass + class Song(BaseModel): """ Song model """ pass + class Topic(BaseModel): """ Topic model """ pass + def init_schema(url): + """ - Setup the songs database connection and initialise the database schema + Setup the songs database connection and initialise the database schema. ``url`` The database to setup + + The song database contains the following tables: + + * authors + * authors_songs + * media_files + * media_files_songs + * song_books + * songs + * songs_topics + * topics + + **authors** Table + This table holds the names of all the authors. It has the following + columns: + + * id + * first_name + * last_name + * display_name + + **authors_songs Table** + This is a bridging table between the *authors* and *songs* tables, which + serves to create a many-to-many relationship between the two tables. It + has the following columns: + + * author_id + * song_id + + **media_files Table** + * id + * file_name + * type + + **media_files_songs Table** + * media_file_id + * song_id + + **song_books Table** + The *song_books* table holds a list of books that a congregation gets + their songs from, or old hymnals now no longer used. This table has the + following columns: + + * id + * name + * publisher + + **songs Table** + This table contains the songs, and each song has a list of attributes. + The *songs* table has the following columns: + + * id + * song_book_id + * title + * alternate_title + * lyrics + * verse_order + * copyright + * comments + * ccli_number + * song_number + * theme_name + * search_title + * search_lyrics + + **songs_topics Table** + This is a bridging table between the *songs* and *topics* tables, which + serves to create a many-to-many relationship between the two tables. It + has the following columns: + + * song_id + * topic_id + + **topics Table** + The topics table holds a selection of topics that songs can cover. This + is useful when a worship leader wants to select songs with a certain + theme. This table has the following columns: + + * id + * name """ session, metadata = init_db(url) diff --git a/resources/forms/exceptiondialog.ui b/resources/forms/exceptiondialog.ui index f6f15cdc7..9fd138092 100644 --- a/resources/forms/exceptiondialog.ui +++ b/resources/forms/exceptiondialog.ui @@ -13,82 +13,128 @@ Dialog - - - 8 + + + + 8 + 194 + 564 + 171 + - - 8 + + true - - - - 0 - - - 0 - - - 0 - - - - - - 64 - 64 - - - - - 64 - 64 - - - - - - - :/graphics/exception.png - - - Qt::AlignCenter - - - - - - - Oops! OpenLP hit a problem, and couldn't recover. The text in the box below contains information that might be helpful to the OpenLP developers, so please e-mail it to bugs@openlp.org, along with a detailed description of what you were doing when the problem occurred. - - - true - - - - - - - - - true - - - false - - - - - - - Qt::Horizontal - - - QDialogButtonBox::Close - - - - + + false + + + + + + 8 + 373 + 83 + 26 + + + + Qt::Horizontal + + + QDialogButtonBox::Close + + + + + + 8 + 103 + 561 + 71 + + + + + 0 + 0 + + + + + + + 10 + 170 + 301 + 17 + + + + TextLabel + + + + + + 10 + 80 + 59 + 17 + + + + TextLabel + + + + + + 0 + + + 0 + + + 0 + + + + + + 64 + 64 + + + + + 64 + 64 + + + + + + + :/graphics/exception.png + + + Qt::AlignCenter + + + + + + + Oops! OpenLP hit a problem, and couldn't recover. The text in the box below contains information that might be helpful to the OpenLP developers, so please e-mail it to bugs@openlp.org, along with a detailed description of what you were doing when the problem occurred. + + + true + + + + + diff --git a/scripts/bible-1to2-converter.py b/scripts/bible-1to2-converter.py deleted file mode 100755 index ebf246608..000000000 --- a/scripts/bible-1to2-converter.py +++ /dev/null @@ -1,308 +0,0 @@ -#!/usr/bin/env python -# -*- 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, Meinert Jordan, Andreas Preikschat, Christian # -# Richter, Philip Ridout, Maikel Stuivenberg, Martin Thompson, Jon Tibble, # -# Carsten Tinggaard, Frode Woldsund # -# --------------------------------------------------------------------------- # -# This program is free software; you can redistribute it and/or modify it # -# under the terms of the GNU General Public License as published by the Free # -# Software Foundation; version 2 of the License. # -# # -# This program is distributed in the hope that it will be useful, but WITHOUT # -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or # -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for # -# more details. # -# # -# You should have received a copy of the GNU General Public License along # -# with this program; if not, write to the Free Software Foundation, Inc., 59 # -# Temple Place, Suite 330, Boston, MA 02111-1307 USA # -############################################################################### - -import sys -import os -import sqlite -import sqlite3 - -from optparse import OptionParser -from traceback import format_tb as get_traceback - -# Some global options to be used throughout the import process -verbose = False -debug = False -old_cursor = None -new_cursor = None - -# SQL create statments -create_statements = [ - (u'table "book"', u"""CREATE TABLE book ( - id INTEGER NOT NULL, - testament_id INTEGER, - name VARCHAR(30), - abbreviation VARCHAR(5), - PRIMARY KEY (id), - FOREIGN KEY(testament_id) REFERENCES testament (id) -)"""), - (u'table "metadata"', u"""CREATE TABLE metadata ( - "key" VARCHAR(255) NOT NULL, - value VARCHAR(255), - PRIMARY KEY ("key") -)"""), - (u'table "testament"', u"""CREATE TABLE testament ( - id INTEGER NOT NULL, - name VARCHAR(30), - PRIMARY KEY (id) -)"""), - (u'table "verse"', u"""CREATE TABLE verse ( - id INTEGER NOT NULL, - book_id INTEGER, - chapter INTEGER, - verse INTEGER, - text TEXT, - PRIMARY KEY (id), - FOREIGN KEY(book_id) REFERENCES book (id) -)"""), - (u'index "idx_abbrev"', - u"""CREATE INDEX idx_abbrev ON book (abbreviation, id)"""), - (u'index "idx_chapter_verse_book', - u"""CREATE INDEX idx_chapter_verse_book ON verse (chapter, verse, book_id, id)"""), - (u'index "idx_chapter_verse_text"', - u"""CREATE INDEX idx_chapter_verse_text ON verse (text, verse, book_id, id)"""), - (u'index "idx_name"', - u"""CREATE INDEX idx_name ON book (name, id)""") -] - -def display_sql(sql, params): - prepared_params = [] - for param in params: - if isinstance(param, basestring): - prepared_params.append(u'"%s"' % param) - elif isinstance(param, (int, long)): - prepared_params.append(u'%d' % param) - elif isinstance(param, (float, complex)): - prepared_params.append(u'%f' % param) - else: - prepared_params.append(u'"%s"' % str(param)) - for prepared_param in prepared_params: - sql = sql.replace(u'?', prepared_param, 1) - return sql - -def create_database(): - global new_cursor, create_statements - if debug or verbose: - print 'Creating new database:' - else: - print 'Creating new database...', - for statement_type, sql_create in create_statements: - if debug: - print '... ', sql_create.replace('\n', ' ').replace(' ', ' ') - elif verbose: - print '... creating %s...' % statement_type, - new_cursor.execute(sql_create) - if verbose and not debug: - print 'done.' - if not verbose and not debug: - print 'done.' - -def import_bible(): - global old_cursor, new_cursor, debug, verbose - if debug or verbose: - print 'Importing metadata:' - else: - print 'Importing metadata...', - if debug: - print '... SELECT "key", "value" FROM metadata' - elif verbose: - print '... fetching metadata from old database...', - old_cursor.execute(u'SELECT "key", "value" FROM metadata') - rows = old_cursor.fetchall() - if not debug and verbose: - print 'done.' - for row in rows: - key = unicode(row[0], u'cp1252') - value = unicode(row[1], u'cp1252') - if key == u'Permission': - key = u'Permissions' - sql_insert = u'INSERT INTO metadata '\ - '("key", "value") '\ - 'VALUES (?, ?)' - sql_params = (key, value) - if debug: - print '...', display_sql(sql_insert, sql_params) - elif verbose: - print '... importing "%s"' % key - new_cursor.execute(sql_insert, sql_params) - if not verbose and not debug: - print 'done.' - if debug or verbose: - print 'Importing testaments:' - else: - print 'Importing testaments...', - if debug: - print '... SELECT id, name FROM testament' - elif verbose: - print '... fetching testaments from old database...', - old_cursor.execute(u'SELECT id, name FROM testament') - rows = old_cursor.fetchall() - if not debug and verbose: - print 'done.' - for row in rows: - id = int(row[0]) - name = unicode(row[1], u'cp1252') - sql_insert = u'INSERT INTO testament '\ - '(id, name) '\ - 'VALUES (?, ?)' - sql_params = (id, name) - if debug: - print '...', display_sql(sql_insert, sql_params) - elif verbose: - print '... importing "%s"' % name - new_cursor.execute(sql_insert, sql_params) - if not verbose and not debug: - print 'done.' - if debug or verbose: - print 'Importing books:' - else: - print 'Importing books...', - if debug: - print '... SELECT id, testament_id, name, abbreviation FROM book' - elif verbose: - print '... fetching books from old database...', - old_cursor.execute(u'SELECT id, testament_id, name, abbreviation FROM book') - rows = old_cursor.fetchall() - if not debug and verbose: - print 'done.' - book_map = {} - for row in rows: - testament_id = int(row[1]) - name = unicode(row[2], u'cp1252') - abbreviation = unicode(row[3], u'cp1252') - sql_insert = u'INSERT INTO book '\ - '(id, testament_id, name, abbreviation) '\ - 'VALUES (NULL, ?, ?, ?)' - sql_params = (testament_id, name, abbreviation) - if debug: - print '...', display_sql(sql_insert, sql_params) - elif verbose: - print '... importing "%s"' % name - new_cursor.execute(sql_insert, sql_params) - book_map[row[0]] = new_cursor.lastrowid - if debug: - print ' >>> (old) books.id =', row[0], ' (new) books.id =', book_map[row[0]] - if not verbose and not debug: - print 'done.' - if debug or verbose: - print 'Importing verses:' - else: - print 'Importing verses...', - if debug: - print '... SELECT id, book_id, chapter, verse, text || \'\' AS text FROM verse...', - elif verbose: - print '... fetching verses from old database...', - old_cursor.execute(u'SELECT id, book_id, chapter, verse, text || \'\' AS text FROM verse') - rows = old_cursor.fetchall() - if debug or verbose: - print 'done.' - for row in rows: - book_id = int(row[1]) - chapter = int(row[2]) - verse = int(row[3]) - text = unicode(row[4], u'cp1252') - sql_insert = u'INSERT INTO verse '\ - '(id, book_id, chapter, verse, text) '\ - 'VALUES (NULL, ?, ?, ?, ?)' - sql_params = (book_map[book_id], chapter, verse, text) - if debug: - print '...', display_sql(sql_insert, sql_params) - elif verbose: - print '... importing "%s..."' % text[:17] - new_cursor.execute(sql_insert, sql_params) - if not verbose and not debug: - print 'done.' - -def main(old_db, new_db): - global old_cursor, new_cursor, debug - old_connection = None - new_connection = None - try: - old_connection = sqlite.connect(old_db) - except: - if debug: - errormsg = '\n' + ''.join(get_traceback(sys.exc_info()[2]))\ - + str(sys.exc_info()[1]) - else: - errormsg = sys.exc_info()[1] - print 'There was a problem connecting to the old database:', errormsg - return 1 - try: - new_connection = sqlite3.connect(new_db) - except: - if debug: - errormsg = '\n' + ''.join(get_traceback(sys.exc_info()[2]))\ - + str(sys.exc_info()[1]) - else: - errormsg = sys.exc_info()[1] - print 'There was a problem creating the new database:', errormsg - return 1 - old_cursor = old_connection.cursor() - new_cursor = new_connection.cursor() - try: - create_database() - except: - if debug: - errormsg = '\n' + ''.join(get_traceback(sys.exc_info()[2]))\ - + str(sys.exc_info()[1]) - else: - errormsg = sys.exc_info()[1] - print 'There was a problem creating the database:', errormsg - return 1 - try: - import_bible() - new_connection.commit() - except: - new_connection.rollback() - if debug: - errormsg = '\n' + ''.join(get_traceback(sys.exc_info()[2]))\ - + str(sys.exc_info()[1]) - else: - errormsg = sys.exc_info()[1] - print 'There was a problem importing songs:', errormsg - return 1 - print 'Import complete.' - -if __name__ == u'__main__': - option_parser = OptionParser(usage='Usage: %prog [options] OLDDATABASE NEWDATABASE') - option_parser.add_option('-o', '--overwrite', dest='overwrite', default=False, - action=u'store_true', help='Overwrite database file if it already exists.') - option_parser.add_option('-v', '--verbose', dest='verbose', default=False, - action=u'store_true', help='Outputs additional progress data.') - option_parser.add_option('-d', '--debug', dest='debug', default=False, - action=u'store_true', help='Outputs raw SQL statements (overrides verbose).') - options, arguments = option_parser.parse_args() - if len(arguments) < 2: - if len(arguments) == 0: - option_parser.error('Please specify an old database and a new database.') - else: - option_parser.error('Please specify a new database.') - old_db = os.path.abspath(arguments[0]) - new_db = os.path.abspath(arguments[1]) - if not os.path.isfile(old_db): - option_parser.error('Old database file ("%s") is not a file.' % old_db) - if not os.path.exists(old_db): - option_parser.error('Old database file ("%s") does not exist.' % old_db) - if os.path.exists(new_db): - if not options.overwrite: - option_parser.error('New database file ("%s") exists. If you want to overwrite it, specify the --overwrite option.' % new_db) - else: - if not os.path.isfile(new_db): - option_parser.error('New database file ("%s") is not a file.' % new_db) - os.unlink(new_db) - verbose = options.verbose - debug = options.debug - main(old_db, new_db) diff --git a/scripts/openlp-1to2-converter.py b/scripts/openlp-1to2-converter.py deleted file mode 100755 index bd554aa70..000000000 --- a/scripts/openlp-1to2-converter.py +++ /dev/null @@ -1,323 +0,0 @@ -#!/usr/bin/env python -# -*- 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, Meinert Jordan, Andreas Preikschat, Christian # -# Richter, Philip Ridout, Maikel Stuivenberg, Martin Thompson, Jon Tibble, # -# Carsten Tinggaard, Frode Woldsund # -# --------------------------------------------------------------------------- # -# This program is free software; you can redistribute it and/or modify it # -# under the terms of the GNU General Public License as published by the Free # -# Software Foundation; version 2 of the License. # -# # -# This program is distributed in the hope that it will be useful, but WITHOUT # -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or # -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for # -# more details. # -# # -# You should have received a copy of the GNU General Public License along # -# with this program; if not, write to the Free Software Foundation, Inc., 59 # -# Temple Place, Suite 330, Boston, MA 02111-1307 USA # -############################################################################### - -import sys -import os -import sqlite -import sqlite3 -import re -from optparse import OptionParser -from traceback import format_tb as get_traceback - -# Some global options to be used throughout the import process -dirty_chars = re.compile(r'\W ', re.UNICODE) -verbose = False -debug = False -old_cursor = None -new_cursor = None - -# SQL create statments -create_statements = [ - (u'table "authors"', u"""CREATE TABLE authors ( - id INTEGER NOT NULL, - first_name VARCHAR(128), - last_name VARCHAR(128), - display_name VARCHAR(255) NOT NULL, - PRIMARY KEY (id) -)"""), - (u'table "song_books"', u"""CREATE TABLE song_books ( - id INTEGER NOT NULL, - name VARCHAR(128) NOT NULL, - publisher VARCHAR(128), - PRIMARY KEY (id) -)"""), - (u'table "songs"', u"""CREATE TABLE songs ( - id INTEGER NOT NULL, - song_book_id INTEGER, - title VARCHAR(255) NOT NULL, - alternate_title VARCHAR(255), - lyrics TEXT NOT NULL, - verse_order VARCHAR(128), - copyright VARCHAR(255), - comments TEXT, - ccli_number VARCHAR(64), - song_number VARCHAR(64), - theme_name VARCHAR(128), - search_title VARCHAR(255) NOT NULL, - search_lyrics TEXT NOT NULL, - PRIMARY KEY (id), - FOREIGN KEY(song_book_id) REFERENCES song_books (id) -)"""), - (u'table "topics"', u"""CREATE TABLE topics ( - id INTEGER NOT NULL, - name VARCHAR(128) NOT NULL, - PRIMARY KEY (id) -)"""), - (u'index "ix_songs_search_lyrics"', - u"""CREATE INDEX ix_songs_search_lyrics ON songs (search_lyrics)"""), - (u'index "ix_songs_search_title', - u"""CREATE INDEX ix_songs_search_title ON songs (search_title)"""), - (u'table "authors_songs"', u"""CREATE TABLE authors_songs ( - author_id INTEGER NOT NULL, - song_id INTEGER NOT NULL, - PRIMARY KEY (author_id, song_id), - FOREIGN KEY(author_id) REFERENCES authors (id), - FOREIGN KEY(song_id) REFERENCES songs (id) -)"""), - (u'table "songs_topics"', u"""CREATE TABLE songs_topics ( - song_id INTEGER NOT NULL, - topic_id INTEGER NOT NULL, - PRIMARY KEY (song_id, topic_id), - FOREIGN KEY(song_id) REFERENCES songs (id), - FOREIGN KEY(topic_id) REFERENCES topics (id) -)""") -] - -def prepare_string(dirty): - return dirty_chars.sub(u'', dirty.replace(u'\r\n', u' ').replace(u'\n', u' ')) - -def display_sql(sql, params): - prepared_params = [] - for param in params: - if isinstance(param, basestring): - prepared_params.append(u'"%s"' % param) - elif isinstance(param, (int, long)): - prepared_params.append(u'%d' % param) - elif isinstance(param, (float, complex)): - prepared_params.append(u'%f' % param) - else: - prepared_params.append(u'"%s"' % str(param)) - for prepared_param in prepared_params: - sql = sql.replace(u'?', prepared_param, 1) - return sql - -def create_database(): - global new_cursor, create_statements - if debug or verbose: - print 'Creating new database:' - else: - print 'Creating new database...', - for statement_type, sql_create in create_statements: - if debug: - print '... ', sql_create.replace('\n', ' ').replace(' ', ' ') - elif verbose: - print '... creating %s...' % statement_type, - new_cursor.execute(sql_create) - if verbose and not debug: - print 'done.' - if not verbose and not debug: - print 'done.' - -def import_songs(): - global old_cursor, new_cursor, debug, verbose - if debug or verbose: - print 'Importing authors:' - else: - print 'Importing authors...', - if debug: - print '... SELECT authorid AS id, authorname AS displayname FROM authors' - elif verbose: - print '... fetching authors from old database...', - old_cursor.execute(u'SELECT authorid AS id, authorname AS displayname FROM authors') - rows = old_cursor.fetchall() - if not debug and verbose: - print 'done.' - author_map = {} - for row in rows: - display_name = unicode(row[1], u'cp1252') - names = display_name.split(u' ') - first_name = names[0] - last_name = u' '.join(names[1:]) - if last_name is None: - last_name = u'' - sql_insert = u'INSERT INTO authors '\ - '(id, first_name, last_name, display_name) '\ - 'VALUES (NULL, ?, ?, ?)' - sql_params = (first_name, last_name, display_name) - if debug: - print '...', display_sql(sql_insert, sql_params) - elif verbose: - print '... importing "%s"' % display_name - new_cursor.execute(sql_insert, sql_params) - author_map[row[0]] = new_cursor.lastrowid - if debug: - print ' >>> authors.authorid =', row[0], 'authors.id =', author_map[row[0]] - if not verbose and not debug: - print 'done.' - if debug or verbose: - print 'Importing songs:' - else: - print 'Importing songs...', - if debug: - print '... SELECT songid AS id, songtitle AS title, lyrics || \'\' AS lyrics, copyrightinfo AS copyright FROM songs...', - elif verbose: - print '... fetching songs from old database...', - old_cursor.execute(u'SELECT songid AS id, songtitle AS title, lyrics || \'\' AS lyrics, copyrightinfo AS copyright FROM songs') - rows = old_cursor.fetchall() - if debug or verbose: - print 'done.' - song_map = {} - xml_lyrics_template = u'%s' - xml_verse_template = u'' - for row in rows: - clean_title = unicode(row[1], u'cp1252') - clean_lyrics = unicode(row[2], u'cp1252').replace(u'\r\n', u'\n') - clean_copyright = unicode(row[3], u'cp1252') - verse_order = u'' - text_lyrics = clean_lyrics.split(u'\n\n') - xml_verse = u'' - verses = [] - for line, verse in enumerate(text_lyrics): - if not verse: - continue - xml_verse += (xml_verse_template % (line + 1, verse)) - verses.append(u'V%d' % (line + 1)) - verse_order = u' '.join(verses) - xml_lyrics = xml_lyrics_template % xml_verse - search_title = prepare_string(clean_title) - search_lyrics = prepare_string(clean_lyrics) - sql_insert = u'INSERT INTO songs '\ - '(id, song_book_id, title, lyrics, verse_order, copyright, search_title, search_lyrics) '\ - 'VALUES (NULL, 0, ?, ?, ?, ?, ?, ?)' - sql_params = (clean_title, xml_lyrics, verse_order, clean_copyright, search_title, search_lyrics) - if debug: - print '...', display_sql(sql_insert, (sql_params[0], u'%s...' % clean_lyrics[:7], sql_params[2], sql_params[3], sql_params[4], u'%s...' % search_lyrics[:7])) - elif verbose: - print '... importing "%s"' % clean_title - new_cursor.execute(sql_insert, sql_params) - song_map[row[0]] = new_cursor.lastrowid - if debug: - print ' >>> songs.songid =', row[0], 'songs.id =', song_map[row[0]] - if not verbose and not debug: - print 'done.' - if debug or verbose: - print 'Importing song-to-author mapping:' - else: - print 'Importing song-to-author mapping...', - if debug: - print '... SELECT authorid AS author_id, songid AS song_id FROM songauthors' - elif verbose: - print '... fetching song-to-author mapping from old database...', - old_cursor.execute(u'SELECT authorid AS author_id, songid AS song_id FROM songauthors') - rows = old_cursor.fetchall() - if not debug and verbose: - print 'done.' - for row in rows: - sql_insert = u'INSERT INTO authors_songs '\ - '(author_id, song_id) '\ - 'VALUES (?, ?)' - sql_params = (author_map[row[0]], song_map[row[1]]) - if debug: - print '... ', display_sql(sql_insert, sql_params) - elif verbose: - print '... Author %d (was %d) => Song %d (was %d)'\ - % (int(row[0]), author_map[row[0]], - int(row[1]), song_map[row[1]]) - new_cursor.execute(sql_insert, sql_params) - if not verbose and not debug: - print 'done.' - -def main(old_db, new_db): - global old_cursor, new_cursor, debug - old_connection = None - new_connection = None - try: - old_connection = sqlite.connect(old_db) - except: - if debug: - errormsg = '\n' + ''.join(get_traceback(sys.exc_info()[2]))\ - + str(sys.exc_info()[1]) - else: - errormsg = sys.exc_info()[1] - print 'There was a problem connecting to the old database:', errormsg - return 1 - try: - new_connection = sqlite3.connect(new_db) - except: - if debug: - errormsg = '\n' + ''.join(get_traceback(sys.exc_info()[2]))\ - + str(sys.exc_info()[1]) - else: - errormsg = sys.exc_info()[1] - print 'There was a problem creating the new database:', errormsg - return 1 - old_cursor = old_connection.cursor() - new_cursor = new_connection.cursor() - try: - create_database() - except: - if debug: - errormsg = '\n' + ''.join(get_traceback(sys.exc_info()[2]))\ - + str(sys.exc_info()[1]) - else: - errormsg = sys.exc_info()[1] - print 'There was a problem creating the database:', errormsg - return 1 - try: - import_songs() - new_connection.commit() - except: - new_connection.rollback() - if debug: - errormsg = '\n' + ''.join(get_traceback(sys.exc_info()[2]))\ - + str(sys.exc_info()[1]) - else: - errormsg = sys.exc_info()[1] - print 'There was a problem importing songs:', errormsg - return 1 - print 'Import complete.' - -if __name__ == u'__main__': - option_parser = OptionParser(usage='Usage: %prog [options] OLDDATABASE NEWDATABASE') - option_parser.add_option('-o', '--overwrite', dest='overwrite', default=False, - action=u'store_true', help='Overwrite database file if it already exists.') - option_parser.add_option('-v', '--verbose', dest='verbose', default=False, - action=u'store_true', help='Outputs additional progress data.') - option_parser.add_option('-d', '--debug', dest='debug', default=False, - action=u'store_true', help='Outputs raw SQL statements (overrides verbose).') - options, arguments = option_parser.parse_args() - if len(arguments) < 2: - if len(arguments) == 0: - option_parser.error('Please specify an old database and a new database.') - else: - option_parser.error('Please specify a new database.') - old_db = os.path.abspath(arguments[0]) - new_db = os.path.abspath(arguments[1]) - if not os.path.isfile(old_db): - option_parser.error('Old database file ("%s") is not a file.' % old_db) - if not os.path.exists(old_db): - option_parser.error('Old database file ("%s") does not exist.' % old_db) - if os.path.exists(new_db): - if not options.overwrite: - option_parser.error('New database file ("%s") exists. If you want to overwrite it, specify the --overwrite option.' % new_db) - else: - if not os.path.isfile(new_db): - option_parser.error('New database file ("%s") is not a file.' % new_db) - os.unlink(new_db) - verbose = options.verbose - debug = options.debug - main(old_db, new_db) diff --git a/setup.py b/setup.py index a435c5496..205688f1e 100755 --- a/setup.py +++ b/setup.py @@ -69,8 +69,7 @@ OpenLP (previously openlp.org) is free church presentation software, or lyrics p url='http://openlp.org/', license='GNU General Public License', packages=find_packages(exclude=['ez_setup', 'examples', 'tests']), - scripts=['openlp.pyw', 'scripts/openlp-1to2-converter.py', - 'scripts/bible-1to2-converter.py','scripts/openlp-remoteclient.py'], + scripts=['openlp.pyw', 'scripts/openlp-remoteclient.py'], include_package_data=True, zip_safe=False, install_requires=[