forked from openlp/openlp
Head
This commit is contained in:
commit
38eec999b7
|
@ -38,6 +38,8 @@ from sqlalchemy import Table, MetaData, Column, types, create_engine
|
|||
from sqlalchemy.exc import SQLAlchemyError, InvalidRequestError, DBAPIError, OperationalError
|
||||
from sqlalchemy.orm import scoped_session, sessionmaker, mapper
|
||||
from sqlalchemy.pool import NullPool
|
||||
from alembic.migration import MigrationContext
|
||||
from alembic.operations import Operations
|
||||
|
||||
from openlp.core.lib import translate, Settings
|
||||
from openlp.core.lib.ui import critical_error_message_box
|
||||
|
@ -65,6 +67,17 @@ def init_db(url, auto_flush=True, auto_commit=False):
|
|||
return session, metadata
|
||||
|
||||
|
||||
def get_upgrade_op(session):
|
||||
"""
|
||||
Create a migration context and an operations object for performing upgrades.
|
||||
|
||||
``session``
|
||||
The SQLAlchemy session object.
|
||||
"""
|
||||
context = MigrationContext.configure(session.bind.connect())
|
||||
return Operations(context)
|
||||
|
||||
|
||||
def upgrade_db(url, upgrade):
|
||||
"""
|
||||
Upgrade a database.
|
||||
|
@ -82,13 +95,7 @@ def upgrade_db(url, upgrade):
|
|||
Provides a class for the metadata table.
|
||||
"""
|
||||
pass
|
||||
load_changes = False
|
||||
tables = []
|
||||
try:
|
||||
tables = upgrade.upgrade_setup(metadata)
|
||||
load_changes = True
|
||||
except (SQLAlchemyError, DBAPIError):
|
||||
pass
|
||||
|
||||
metadata_table = Table(u'metadata', metadata,
|
||||
Column(u'key', types.Unicode(64), primary_key=True),
|
||||
Column(u'value', types.UnicodeText(), default=None)
|
||||
|
@ -105,22 +112,22 @@ def upgrade_db(url, upgrade):
|
|||
if version > upgrade.__version__:
|
||||
return version, upgrade.__version__
|
||||
version += 1
|
||||
if load_changes:
|
||||
try:
|
||||
while hasattr(upgrade, u'upgrade_%d' % version):
|
||||
log.debug(u'Running upgrade_%d', version)
|
||||
try:
|
||||
upgrade_func = getattr(upgrade, u'upgrade_%d' % version)
|
||||
upgrade_func(session, metadata, tables)
|
||||
upgrade_func(session, metadata)
|
||||
session.commit()
|
||||
# Update the version number AFTER a commit so that we are sure the previous transaction happened
|
||||
version_meta.value = unicode(version)
|
||||
session.commit()
|
||||
version += 1
|
||||
except (SQLAlchemyError, DBAPIError):
|
||||
log.exception(u'Could not run database upgrade script '
|
||||
'"upgrade_%s", upgrade process has been halted.', version)
|
||||
log.exception(u'Could not run database upgrade script "upgrade_%s", upgrade process has been halted.',
|
||||
version)
|
||||
break
|
||||
else:
|
||||
except (SQLAlchemyError, DBAPIError):
|
||||
version_meta = Metadata.populate(key=u'version', value=int(upgrade.__version__))
|
||||
session.commit()
|
||||
return int(version_meta.value), upgrade.__version__
|
||||
|
|
|
@ -107,7 +107,7 @@ class Renderer(object):
|
|||
del self._theme_dimensions[old_theme_name]
|
||||
if theme_name in self._theme_dimensions:
|
||||
del self._theme_dimensions[theme_name]
|
||||
if not only_delete:
|
||||
if not only_delete and theme_name:
|
||||
self._set_theme(theme_name)
|
||||
|
||||
def _set_theme(self, theme_name):
|
||||
|
|
|
@ -485,6 +485,12 @@ class ServiceItem(object):
|
|||
"""
|
||||
return self.unique_identifier != other.unique_identifier
|
||||
|
||||
def __hash__(self):
|
||||
"""
|
||||
Return the hash for the service item.
|
||||
"""
|
||||
return self.unique_identifier
|
||||
|
||||
def is_media(self):
|
||||
"""
|
||||
Confirms if the ServiceItem is media
|
||||
|
|
|
@ -244,6 +244,7 @@ class Settings(QtCore.QSettings):
|
|||
u'shortcuts/printServiceItem': [QtGui.QKeySequence(u'Ctrl+P')],
|
||||
u'shortcuts/songExportItem': [],
|
||||
u'shortcuts/songUsageStatus': [QtGui.QKeySequence(QtCore.Qt.Key_F4)],
|
||||
u'shortcuts/searchShortcut': [QtGui.QKeySequence(u'Ctrl+F')],
|
||||
u'shortcuts/settingsShortcutsItem': [],
|
||||
u'shortcuts/settingsImportItem': [],
|
||||
u'shortcuts/settingsPluginListItem': [QtGui.QKeySequence(u'Alt+F7')],
|
||||
|
|
|
@ -291,7 +291,7 @@ class Ui_AboutDialog(object):
|
|||
'but WITHOUT ANY WARRANTY; without even the implied warranty of '
|
||||
'MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See below '
|
||||
'for more details.')
|
||||
gpltext = ('GNU GENERAL PUBLIC LICENSE\n'
|
||||
gpl_text = ('GNU GENERAL PUBLIC LICENSE\n'
|
||||
'Version 2, June 1991\n'
|
||||
'\n'
|
||||
'Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 '
|
||||
|
@ -662,7 +662,7 @@ class Ui_AboutDialog(object):
|
|||
'linking proprietary applications with the library. If this is '
|
||||
'what you want to do, use the GNU Lesser General Public License '
|
||||
'instead of this License.')
|
||||
self.license_text_edit.setPlainText(u'%s\n\n%s\n\n%s\n\n\n%s' % (copyright_note, licence, disclaimer, gpltext))
|
||||
self.license_text_edit.setPlainText(u'%s\n\n%s\n\n%s\n\n\n%s' % (copyright_note, licence, disclaimer, gpl_text))
|
||||
self.about_notebook.setTabText(self.about_notebook.indexOf(self.license_tab),
|
||||
translate('OpenLP.AboutForm', 'License'))
|
||||
self.volunteer_button.setText(translate('OpenLP.AboutForm', 'Volunteer'))
|
||||
|
|
|
@ -34,8 +34,8 @@ import re
|
|||
import os
|
||||
import platform
|
||||
|
||||
import bs4
|
||||
import sqlalchemy
|
||||
from bs4 import BeautifulSoup
|
||||
from lxml import etree
|
||||
from PyQt4 import Qt, QtCore, QtGui, QtWebKit
|
||||
|
||||
|
@ -145,7 +145,7 @@ class ExceptionForm(QtGui.QDialog, Ui_ExceptionDialog):
|
|||
u'QtWebkit: %s\n' % WEBKIT_VERSION + \
|
||||
u'SQLAlchemy: %s\n' % sqlalchemy.__version__ + \
|
||||
u'SQLAlchemy Migrate: %s\n' % MIGRATE_VERSION + \
|
||||
u'BeautifulSoup: %s\n' % BeautifulSoup.__version__ + \
|
||||
u'BeautifulSoup: %s\n' % bs4.__version__ + \
|
||||
u'lxml: %s\n' % etree.__version__ + \
|
||||
u'Chardet: %s\n' % CHARDET_VERSION + \
|
||||
u'PyEnchant: %s\n' % ENCHANT_VERSION + \
|
||||
|
|
|
@ -68,7 +68,7 @@ class ThemeScreenshotThread(QtCore.QThread):
|
|||
screenshot = config.get(u'theme_%s' % theme, u'screenshot')
|
||||
urllib.urlretrieve(u'%s%s' % (self.parent().web, screenshot),
|
||||
os.path.join(unicode(gettempdir(), get_filesystem_encoding()), u'openlp', screenshot))
|
||||
item = QtGui.QListWidgetItem(title, self.parent().themesListWidget)
|
||||
item = QtGui.QListWidgetItem(title, self.parent().themes_list_widget)
|
||||
item.setData(QtCore.Qt.UserRole, filename)
|
||||
item.setCheckState(QtCore.Qt.Unchecked)
|
||||
item.setFlags(item.flags() | QtCore.Qt.ItemIsUserCheckable)
|
||||
|
@ -76,8 +76,7 @@ class ThemeScreenshotThread(QtCore.QThread):
|
|||
|
||||
class FirstTimeForm(QtGui.QWizard, Ui_FirstTimeWizard):
|
||||
"""
|
||||
This is the Theme Import Wizard, which allows easy creation and editing of
|
||||
OpenLP themes.
|
||||
This is the Theme Import Wizard, which allows easy creation and editing of OpenLP themes.
|
||||
"""
|
||||
log.info(u'ThemeWizardForm loaded')
|
||||
|
||||
|
@ -97,10 +96,11 @@ class FirstTimeForm(QtGui.QWizard, Ui_FirstTimeWizard):
|
|||
self.config.readfp(io.BytesIO(files))
|
||||
self.update_screen_list_combo()
|
||||
self.was_download_cancelled = False
|
||||
self.theme_screenshot_thread = None
|
||||
self.downloading = translate('OpenLP.FirstTimeWizard', 'Downloading %s...')
|
||||
self.cancelButton.clicked.connect(self.onCancelButtonClicked)
|
||||
self.noInternetFinishButton.clicked.connect(self.onNoInternetFinishButtonClicked)
|
||||
self.currentIdChanged.connect(self.onCurrentIdChanged)
|
||||
self.cancel_button.clicked.connect(self.on_cancel_button_clicked)
|
||||
self.no_internet_finish_button.clicked.connect(self.on_no_internet_finish_button_clicked)
|
||||
self.currentIdChanged.connect(self.on_current_id_changed)
|
||||
Registry().register_function(u'config_screen_changed', self.update_screen_list_combo)
|
||||
|
||||
def exec_(self):
|
||||
|
@ -116,9 +116,9 @@ class FirstTimeForm(QtGui.QWizard, Ui_FirstTimeWizard):
|
|||
"""
|
||||
self.restart()
|
||||
check_directory_exists(os.path.join(unicode(gettempdir(), get_filesystem_encoding()), u'openlp'))
|
||||
self.noInternetFinishButton.setVisible(False)
|
||||
self.no_internet_finish_button.setVisible(False)
|
||||
# Check if this is a re-run of the wizard.
|
||||
self.hasRunWizard = Settings().value(u'core/has run wizard')
|
||||
self.has_run_wizard = Settings().value(u'core/has run wizard')
|
||||
# Sort out internet access for downloads
|
||||
if self.web_access:
|
||||
songs = self.config.get(u'songs', u'languages')
|
||||
|
@ -126,7 +126,7 @@ class FirstTimeForm(QtGui.QWizard, Ui_FirstTimeWizard):
|
|||
for song in songs:
|
||||
title = unicode(self.config.get(u'songs_%s' % song, u'title'), u'utf8')
|
||||
filename = unicode(self.config.get(u'songs_%s' % song, u'filename'), u'utf8')
|
||||
item = QtGui.QListWidgetItem(title, self.songsListWidget)
|
||||
item = QtGui.QListWidgetItem(title, self.songs_list_widget)
|
||||
item.setData(QtCore.Qt.UserRole, filename)
|
||||
item.setCheckState(QtCore.Qt.Unchecked)
|
||||
item.setFlags(item.flags() | QtCore.Qt.ItemIsUserCheckable)
|
||||
|
@ -134,7 +134,7 @@ class FirstTimeForm(QtGui.QWizard, Ui_FirstTimeWizard):
|
|||
bible_languages = bible_languages.split(u',')
|
||||
for lang in bible_languages:
|
||||
language = unicode(self.config.get(u'bibles_%s' % lang, u'title'), u'utf8')
|
||||
langItem = QtGui.QTreeWidgetItem(self.biblesTreeWidget, [language])
|
||||
langItem = QtGui.QTreeWidgetItem(self.bibles_tree_widget, [language])
|
||||
bibles = self.config.get(u'bibles_%s' % lang, u'translations')
|
||||
bibles = bibles.split(u',')
|
||||
for bible in bibles:
|
||||
|
@ -144,10 +144,10 @@ class FirstTimeForm(QtGui.QWizard, Ui_FirstTimeWizard):
|
|||
item.setData(0, QtCore.Qt.UserRole, filename)
|
||||
item.setCheckState(0, QtCore.Qt.Unchecked)
|
||||
item.setFlags(item.flags() | QtCore.Qt.ItemIsUserCheckable)
|
||||
self.biblesTreeWidget.expandAll()
|
||||
self.bibles_tree_widget.expandAll()
|
||||
# Download the theme screenshots.
|
||||
self.themeScreenshotThread = ThemeScreenshotThread(self)
|
||||
self.themeScreenshotThread.start()
|
||||
self.theme_screenshot_thread = ThemeScreenshotThread(self)
|
||||
self.theme_screenshot_thread.start()
|
||||
self.application.set_normal_cursor()
|
||||
|
||||
def nextId(self):
|
||||
|
@ -166,61 +166,60 @@ class FirstTimeForm(QtGui.QWizard, Ui_FirstTimeWizard):
|
|||
return FirstTimePage.Progress
|
||||
elif self.currentId() == FirstTimePage.Themes:
|
||||
self.application.set_busy_cursor()
|
||||
while not self.themeScreenshotThread.isFinished():
|
||||
while not self.theme_screenshot_thread.isFinished():
|
||||
time.sleep(0.1)
|
||||
self.application.process_events()
|
||||
# Build the screenshot icons, as this can not be done in the thread.
|
||||
self._buildThemeScreenshots()
|
||||
self._build_theme_screenshots()
|
||||
self.application.set_normal_cursor()
|
||||
return FirstTimePage.Defaults
|
||||
else:
|
||||
return self.currentId() + 1
|
||||
|
||||
def onCurrentIdChanged(self, pageId):
|
||||
def on_current_id_changed(self, page_id):
|
||||
"""
|
||||
Detects Page changes and updates as appropriate.
|
||||
"""
|
||||
# Keep track of the page we are at. Triggering "Cancel" causes pageId
|
||||
# to be a -1.
|
||||
# Keep track of the page we are at. Triggering "Cancel" causes page_id to be a -1.
|
||||
self.application.process_events()
|
||||
if pageId != -1:
|
||||
self.lastId = pageId
|
||||
if pageId == FirstTimePage.Plugins:
|
||||
if page_id != -1:
|
||||
self.last_id = page_id
|
||||
if page_id == FirstTimePage.Plugins:
|
||||
# Set the no internet page text.
|
||||
if self.hasRunWizard:
|
||||
self.noInternetLabel.setText(self.noInternetText)
|
||||
if self.has_run_wizard:
|
||||
self.no_internet_label.setText(self.no_internet_text)
|
||||
else:
|
||||
self.noInternetLabel.setText(self.noInternetText + self.cancelWizardText)
|
||||
elif pageId == FirstTimePage.Defaults:
|
||||
self.themeComboBox.clear()
|
||||
for iter in xrange(self.themesListWidget.count()):
|
||||
item = self.themesListWidget.item(iter)
|
||||
self.no_internet_label.setText(self.no_internet_text + self.cancelWizardText)
|
||||
elif page_id == FirstTimePage.Defaults:
|
||||
self.theme_combo_box.clear()
|
||||
for iter in xrange(self.themes_list_widget.count()):
|
||||
item = self.themes_list_widget.item(iter)
|
||||
if item.checkState() == QtCore.Qt.Checked:
|
||||
self.themeComboBox.addItem(item.text())
|
||||
if self.hasRunWizard:
|
||||
self.theme_combo_box.addItem(item.text())
|
||||
if self.has_run_wizard:
|
||||
# Add any existing themes to list.
|
||||
for theme in self.theme_manager.get_themes():
|
||||
index = self.themeComboBox.findText(theme)
|
||||
index = self.theme_combo_box.findText(theme)
|
||||
if index == -1:
|
||||
self.themeComboBox.addItem(theme)
|
||||
self.theme_combo_box.addItem(theme)
|
||||
default_theme = Settings().value(u'themes/global theme')
|
||||
# Pre-select the current default theme.
|
||||
index = self.themeComboBox.findText(default_theme)
|
||||
self.themeComboBox.setCurrentIndex(index)
|
||||
elif pageId == FirstTimePage.NoInternet:
|
||||
self.backButton.setVisible(False)
|
||||
self.nextButton.setVisible(False)
|
||||
self.noInternetFinishButton.setVisible(True)
|
||||
if self.hasRunWizard:
|
||||
self.cancelButton.setVisible(False)
|
||||
elif pageId == FirstTimePage.Progress:
|
||||
index = self.theme_combo_box.findText(default_theme)
|
||||
self.theme_combo_box.setCurrentIndex(index)
|
||||
elif page_id == FirstTimePage.NoInternet:
|
||||
self.back_button.setVisible(False)
|
||||
self.next_button.setVisible(False)
|
||||
self.no_internet_finish_button.setVisible(True)
|
||||
if self.has_run_wizard:
|
||||
self.cancel_button.setVisible(False)
|
||||
elif page_id == FirstTimePage.Progress:
|
||||
self.application.set_busy_cursor()
|
||||
self.repaint()
|
||||
self.application.process_events()
|
||||
# Try to give the wizard a chance to redraw itself
|
||||
time.sleep(0.2)
|
||||
self._preWizard()
|
||||
self._performWizard()
|
||||
self._pre_wizard()
|
||||
self._perform_wizard()
|
||||
self._post_wizard()
|
||||
self.application.set_normal_cursor()
|
||||
|
||||
|
@ -229,70 +228,72 @@ class FirstTimeForm(QtGui.QWizard, Ui_FirstTimeWizard):
|
|||
The user changed screen resolution or enabled/disabled more screens, so
|
||||
we need to update the combo box.
|
||||
"""
|
||||
self.displayComboBox.clear()
|
||||
self.displayComboBox.addItems(self.screens.get_screen_list())
|
||||
self.displayComboBox.setCurrentIndex(self.displayComboBox.count() - 1)
|
||||
self.display_combo_box.clear()
|
||||
self.display_combo_box.addItems(self.screens.get_screen_list())
|
||||
self.display_combo_box.setCurrentIndex(self.display_combo_box.count() - 1)
|
||||
|
||||
def onCancelButtonClicked(self):
|
||||
def on_cancel_button_clicked(self):
|
||||
"""
|
||||
Process the triggering of the cancel button.
|
||||
"""
|
||||
if self.lastId == FirstTimePage.NoInternet or (self.lastId <= FirstTimePage.Plugins and not self.hasRunWizard):
|
||||
if self.last_id == FirstTimePage.NoInternet or (self.last_id <= FirstTimePage.Plugins and not self.has_run_wizard):
|
||||
QtCore.QCoreApplication.exit()
|
||||
sys.exit()
|
||||
self.was_download_cancelled = True
|
||||
while self.themeScreenshotThread.isRunning():
|
||||
time.sleep(0.1)
|
||||
# Was the thread created.
|
||||
if self.theme_screenshot_thread:
|
||||
while self.theme_screenshot_thread.isRunning():
|
||||
time.sleep(0.1)
|
||||
self.application.set_normal_cursor()
|
||||
|
||||
def onNoInternetFinishButtonClicked(self):
|
||||
def on_no_internet_finish_button_clicked(self):
|
||||
"""
|
||||
Process the triggering of the "Finish" button on the No Internet page.
|
||||
"""
|
||||
self.application.set_busy_cursor()
|
||||
self._performWizard()
|
||||
self._perform_wizard()
|
||||
self.application.set_normal_cursor()
|
||||
Settings().setValue(u'core/has run wizard', True)
|
||||
self.close()
|
||||
|
||||
def urlGetFile(self, url, fpath):
|
||||
def url_get_file(self, url, f_path):
|
||||
""""
|
||||
Download a file given a URL. The file is retrieved in chunks, giving
|
||||
the ability to cancel the download at any point.
|
||||
Download a file given a URL. The file is retrieved in chunks, giving the ability to cancel the download at any
|
||||
point.
|
||||
"""
|
||||
block_count = 0
|
||||
block_size = 4096
|
||||
urlfile = urllib2.urlopen(url)
|
||||
filename = open(fpath, "wb")
|
||||
url_file = urllib2.urlopen(url)
|
||||
filename = open(f_path, "wb")
|
||||
# Download until finished or canceled.
|
||||
while not self.was_download_cancelled:
|
||||
data = urlfile.read(block_size)
|
||||
data = url_file.read(block_size)
|
||||
if not data:
|
||||
break
|
||||
filename.write(data)
|
||||
block_count += 1
|
||||
self._downloadProgress(block_count, block_size)
|
||||
self._download_progress(block_count, block_size)
|
||||
filename.close()
|
||||
# Delete file if cancelled, it may be a partial file.
|
||||
if self.was_download_cancelled:
|
||||
os.remove(fpath)
|
||||
os.remove(f_path)
|
||||
|
||||
def _buildThemeScreenshots(self):
|
||||
def _build_theme_screenshots(self):
|
||||
"""
|
||||
This method builds the theme screenshots' icons for all items in the
|
||||
``self.themesListWidget``.
|
||||
``self.themes_list_widget``.
|
||||
"""
|
||||
themes = self.config.get(u'themes', u'files')
|
||||
themes = themes.split(u',')
|
||||
for theme in themes:
|
||||
filename = self.config.get(u'theme_%s' % theme, u'filename')
|
||||
screenshot = self.config.get(u'theme_%s' % theme, u'screenshot')
|
||||
for index in xrange(self.themesListWidget.count()):
|
||||
item = self.themesListWidget.item(index)
|
||||
for index in xrange(self.themes_list_widget.count()):
|
||||
item = self.themes_list_widget.item(index)
|
||||
if item.data(QtCore.Qt.UserRole) == filename:
|
||||
break
|
||||
item.setIcon(build_icon(os.path.join(unicode(gettempdir(),
|
||||
get_filesystem_encoding()), u'openlp', screenshot)))
|
||||
get_filesystem_encoding()), u'openlp', screenshot)))
|
||||
|
||||
def _getFileSize(self, url):
|
||||
"""
|
||||
|
@ -305,15 +306,15 @@ class FirstTimeForm(QtGui.QWizard, Ui_FirstTimeWizard):
|
|||
meta = site.info()
|
||||
return int(meta.getheaders("Content-Length")[0])
|
||||
|
||||
def _downloadProgress(self, count, block_size):
|
||||
def _download_progress(self, count, block_size):
|
||||
"""
|
||||
Calculate and display the download progress.
|
||||
"""
|
||||
increment = (count * block_size) - self.previous_size
|
||||
self._incrementProgressBar(None, increment)
|
||||
self._increment_progress_bar(None, increment)
|
||||
self.previous_size = count * block_size
|
||||
|
||||
def _incrementProgressBar(self, status_text, increment=1):
|
||||
def _increment_progress_bar(self, status_text, increment=1):
|
||||
"""
|
||||
Update the wizard progress page.
|
||||
|
||||
|
@ -324,28 +325,28 @@ class FirstTimeForm(QtGui.QWizard, Ui_FirstTimeWizard):
|
|||
The value to increment the progress bar by.
|
||||
"""
|
||||
if status_text:
|
||||
self.progressLabel.setText(status_text)
|
||||
self.progress_label.setText(status_text)
|
||||
if increment > 0:
|
||||
self.progressBar.setValue(self.progressBar.value() + increment)
|
||||
self.progress_bar.setValue(self.progress_bar.value() + increment)
|
||||
self.application.process_events()
|
||||
|
||||
def _preWizard(self):
|
||||
def _pre_wizard(self):
|
||||
"""
|
||||
Prepare the UI for the process.
|
||||
"""
|
||||
self.max_progress = 0
|
||||
self.finishButton.setVisible(False)
|
||||
self.finish_button.setVisible(False)
|
||||
self.application.process_events()
|
||||
# Loop through the songs list and increase for each selected item
|
||||
for i in xrange(self.songsListWidget.count()):
|
||||
for i in xrange(self.songs_list_widget.count()):
|
||||
self.application.process_events()
|
||||
item = self.songsListWidget.item(i)
|
||||
item = self.songs_list_widget.item(i)
|
||||
if item.checkState() == QtCore.Qt.Checked:
|
||||
filename = item.data(QtCore.Qt.UserRole)
|
||||
size = self._getFileSize(u'%s%s' % (self.web, filename))
|
||||
self.max_progress += size
|
||||
# Loop through the Bibles list and increase for each selected item
|
||||
iterator = QtGui.QTreeWidgetItemIterator(self.biblesTreeWidget)
|
||||
iterator = QtGui.QTreeWidgetItemIterator(self.bibles_tree_widget)
|
||||
while iterator.value():
|
||||
self.application.process_events()
|
||||
item = iterator.value()
|
||||
|
@ -355,9 +356,9 @@ class FirstTimeForm(QtGui.QWizard, Ui_FirstTimeWizard):
|
|||
self.max_progress += size
|
||||
iterator += 1
|
||||
# Loop through the themes list and increase for each selected item
|
||||
for i in xrange(self.themesListWidget.count()):
|
||||
for i in xrange(self.themes_list_widget.count()):
|
||||
self.application.process_events()
|
||||
item = self.themesListWidget.item(i)
|
||||
item = self.themes_list_widget.item(i)
|
||||
if item.checkState() == QtCore.Qt.Checked:
|
||||
filename = item.data(QtCore.Qt.UserRole)
|
||||
size = self._getFileSize(u'%s%s' % (self.web, filename))
|
||||
|
@ -365,16 +366,16 @@ class FirstTimeForm(QtGui.QWizard, Ui_FirstTimeWizard):
|
|||
if self.max_progress:
|
||||
# Add on 2 for plugins status setting plus a "finished" point.
|
||||
self.max_progress += 2
|
||||
self.progressBar.setValue(0)
|
||||
self.progressBar.setMinimum(0)
|
||||
self.progressBar.setMaximum(self.max_progress)
|
||||
self.progressPage.setTitle(translate('OpenLP.FirstTimeWizard', 'Setting Up And Downloading'))
|
||||
self.progressPage.setSubTitle(
|
||||
self.progress_bar.setValue(0)
|
||||
self.progress_bar.setMinimum(0)
|
||||
self.progress_bar.setMaximum(self.max_progress)
|
||||
self.progress_page.setTitle(translate('OpenLP.FirstTimeWizard', 'Setting Up And Downloading'))
|
||||
self.progress_page.setSubTitle(
|
||||
translate('OpenLP.FirstTimeWizard', 'Please wait while OpenLP is set up and your data is downloaded.'))
|
||||
else:
|
||||
self.progressBar.setVisible(False)
|
||||
self.progressPage.setTitle(translate('OpenLP.FirstTimeWizard', 'Setting Up'))
|
||||
self.progressPage.setSubTitle(u'Setup complete.')
|
||||
self.progress_bar.setVisible(False)
|
||||
self.progress_page.setTitle(translate('OpenLP.FirstTimeWizard', 'Setting Up'))
|
||||
self.progress_page.setSubTitle(u'Setup complete.')
|
||||
self.repaint()
|
||||
self.application.process_events()
|
||||
# Try to give the wizard a chance to repaint itself
|
||||
|
@ -385,44 +386,44 @@ class FirstTimeForm(QtGui.QWizard, Ui_FirstTimeWizard):
|
|||
Clean up the UI after the process has finished.
|
||||
"""
|
||||
if self.max_progress:
|
||||
self.progressBar.setValue(self.progressBar.maximum())
|
||||
if self.hasRunWizard:
|
||||
self.progressLabel.setText(translate('OpenLP.FirstTimeWizard',
|
||||
self.progress_bar.setValue(self.progress_bar.maximum())
|
||||
if self.has_run_wizard:
|
||||
self.progress_label.setText(translate('OpenLP.FirstTimeWizard',
|
||||
'Download complete. Click the finish button to return to OpenLP.'))
|
||||
else:
|
||||
self.progressLabel.setText(translate('OpenLP.FirstTimeWizard',
|
||||
self.progress_label.setText(translate('OpenLP.FirstTimeWizard',
|
||||
'Download complete. Click the finish button to start OpenLP.'))
|
||||
else:
|
||||
if self.hasRunWizard:
|
||||
self.progressLabel.setText(translate('OpenLP.FirstTimeWizard',
|
||||
if self.has_run_wizard:
|
||||
self.progress_label.setText(translate('OpenLP.FirstTimeWizard',
|
||||
'Click the finish button to return to OpenLP.'))
|
||||
else:
|
||||
self.progressLabel.setText(translate('OpenLP.FirstTimeWizard',
|
||||
self.progress_label.setText(translate('OpenLP.FirstTimeWizard',
|
||||
'Click the finish button to start OpenLP.'))
|
||||
self.finishButton.setVisible(True)
|
||||
self.finishButton.setEnabled(True)
|
||||
self.cancelButton.setVisible(False)
|
||||
self.nextButton.setVisible(False)
|
||||
self.finish_button.setVisible(True)
|
||||
self.finish_button.setEnabled(True)
|
||||
self.cancel_button.setVisible(False)
|
||||
self.next_button.setVisible(False)
|
||||
self.application.process_events()
|
||||
|
||||
def _performWizard(self):
|
||||
def _perform_wizard(self):
|
||||
"""
|
||||
Run the tasks in the wizard.
|
||||
"""
|
||||
# Set plugin states
|
||||
self._incrementProgressBar(translate('OpenLP.FirstTimeWizard', 'Enabling selected plugins...'))
|
||||
self._setPluginStatus(self.songsCheckBox, u'songs/status')
|
||||
self._setPluginStatus(self.bibleCheckBox, u'bibles/status')
|
||||
self._increment_progress_bar(translate('OpenLP.FirstTimeWizard', 'Enabling selected plugins...'))
|
||||
self._set_plugin_status(self.songs_check_box, u'songs/status')
|
||||
self._set_plugin_status(self.bible_check_box, u'bibles/status')
|
||||
# TODO Presentation plugin is not yet working on Mac OS X.
|
||||
# For now just ignore it.
|
||||
if sys.platform != 'darwin':
|
||||
self._setPluginStatus(self.presentationCheckBox, u'presentations/status')
|
||||
self._setPluginStatus(self.imageCheckBox, u'images/status')
|
||||
self._setPluginStatus(self.mediaCheckBox, u'media/status')
|
||||
self._setPluginStatus(self.remoteCheckBox, u'remotes/status')
|
||||
self._setPluginStatus(self.customCheckBox, u'custom/status')
|
||||
self._setPluginStatus(self.songUsageCheckBox, u'songusage/status')
|
||||
self._setPluginStatus(self.alertCheckBox, u'alerts/status')
|
||||
self._set_plugin_status(self.presentation_check_box, u'presentations/status')
|
||||
self._set_plugin_status(self.image_check_box, u'images/status')
|
||||
self._set_plugin_status(self.media_check_box, u'media/status')
|
||||
self._set_plugin_status(self.remote_check_box, u'remotes/status')
|
||||
self._set_plugin_status(self.custom_check_box, u'custom/status')
|
||||
self._set_plugin_status(self.song_usage_check_box, u'songusage/status')
|
||||
self._set_plugin_status(self.alert_check_box, u'alerts/status')
|
||||
if self.web_access:
|
||||
# Build directories for downloads
|
||||
songs_destination = os.path.join(
|
||||
|
@ -430,42 +431,42 @@ class FirstTimeForm(QtGui.QWizard, Ui_FirstTimeWizard):
|
|||
bibles_destination = AppLocation.get_section_data_path(u'bibles')
|
||||
themes_destination = AppLocation.get_section_data_path(u'themes')
|
||||
# Download songs
|
||||
for i in xrange(self.songsListWidget.count()):
|
||||
item = self.songsListWidget.item(i)
|
||||
for i in xrange(self.songs_list_widget.count()):
|
||||
item = self.songs_list_widget.item(i)
|
||||
if item.checkState() == QtCore.Qt.Checked:
|
||||
filename = item.data(QtCore.Qt.UserRole)
|
||||
self._incrementProgressBar(self.downloading % filename, 0)
|
||||
self._increment_progress_bar(self.downloading % filename, 0)
|
||||
self.previous_size = 0
|
||||
destination = os.path.join(songs_destination, unicode(filename))
|
||||
self.urlGetFile(u'%s%s' % (self.web, filename), destination)
|
||||
self.url_get_file(u'%s%s' % (self.web, filename), destination)
|
||||
# Download Bibles
|
||||
bibles_iterator = QtGui.QTreeWidgetItemIterator(
|
||||
self.biblesTreeWidget)
|
||||
self.bibles_tree_widget)
|
||||
while bibles_iterator.value():
|
||||
item = bibles_iterator.value()
|
||||
if item.parent() and item.checkState(0) == QtCore.Qt.Checked:
|
||||
bible = item.data(0, QtCore.Qt.UserRole)
|
||||
self._incrementProgressBar(self.downloading % bible, 0)
|
||||
self._increment_progress_bar(self.downloading % bible, 0)
|
||||
self.previous_size = 0
|
||||
self.urlGetFile(u'%s%s' % (self.web, bible), os.path.join(bibles_destination, bible))
|
||||
self.url_get_file(u'%s%s' % (self.web, bible), os.path.join(bibles_destination, bible))
|
||||
bibles_iterator += 1
|
||||
# Download themes
|
||||
for i in xrange(self.themesListWidget.count()):
|
||||
item = self.themesListWidget.item(i)
|
||||
for i in xrange(self.themes_list_widget.count()):
|
||||
item = self.themes_list_widget.item(i)
|
||||
if item.checkState() == QtCore.Qt.Checked:
|
||||
theme = item.data(QtCore.Qt.UserRole)
|
||||
self._incrementProgressBar(self.downloading % theme, 0)
|
||||
self._increment_progress_bar(self.downloading % theme, 0)
|
||||
self.previous_size = 0
|
||||
self.urlGetFile(u'%s%s' % (self.web, theme), os.path.join(themes_destination, theme))
|
||||
self.url_get_file(u'%s%s' % (self.web, theme), os.path.join(themes_destination, theme))
|
||||
# Set Default Display
|
||||
if self.displayComboBox.currentIndex() != -1:
|
||||
Settings().setValue(u'core/monitor', self.displayComboBox.currentIndex())
|
||||
self.screens.set_current_display(self.displayComboBox.currentIndex())
|
||||
if self.display_combo_box.currentIndex() != -1:
|
||||
Settings().setValue(u'core/monitor', self.display_combo_box.currentIndex())
|
||||
self.screens.set_current_display(self.display_combo_box.currentIndex())
|
||||
# Set Global Theme
|
||||
if self.themeComboBox.currentIndex() != -1:
|
||||
Settings().setValue(u'themes/global theme', self.themeComboBox.currentText())
|
||||
if self.theme_combo_box.currentIndex() != -1:
|
||||
Settings().setValue(u'themes/global theme', self.theme_combo_box.currentText())
|
||||
|
||||
def _setPluginStatus(self, field, tag):
|
||||
def _set_plugin_status(self, field, tag):
|
||||
"""
|
||||
Set the status of a plugin.
|
||||
"""
|
||||
|
|
|
@ -55,200 +55,199 @@ class Ui_FirstTimeWizard(object):
|
|||
"""
|
||||
The UI widgets for the first time wizard.
|
||||
"""
|
||||
def setupUi(self, FirstTimeWizard):
|
||||
def setupUi(self, first_time_wizard):
|
||||
"""
|
||||
Set up the UI.
|
||||
"""
|
||||
FirstTimeWizard.setObjectName(u'FirstTimeWizard')
|
||||
FirstTimeWizard.resize(550, 386)
|
||||
FirstTimeWizard.setModal(True)
|
||||
FirstTimeWizard.setWizardStyle(QtGui.QWizard.ModernStyle)
|
||||
FirstTimeWizard.setOptions(QtGui.QWizard.IndependentPages | QtGui.QWizard.NoBackButtonOnStartPage |
|
||||
first_time_wizard.setObjectName(u'first_time_wizard')
|
||||
first_time_wizard.resize(550, 386)
|
||||
first_time_wizard.setModal(True)
|
||||
first_time_wizard.setWizardStyle(QtGui.QWizard.ModernStyle)
|
||||
first_time_wizard.setOptions(QtGui.QWizard.IndependentPages | QtGui.QWizard.NoBackButtonOnStartPage |
|
||||
QtGui.QWizard.NoBackButtonOnLastPage | QtGui.QWizard.HaveCustomButton1)
|
||||
self.finishButton = self.button(QtGui.QWizard.FinishButton)
|
||||
self.noInternetFinishButton = self.button(QtGui.QWizard.CustomButton1)
|
||||
self.cancelButton = self.button(QtGui.QWizard.CancelButton)
|
||||
self.nextButton = self.button(QtGui.QWizard.NextButton)
|
||||
self.backButton = self.button(QtGui.QWizard.BackButton)
|
||||
add_welcome_page(FirstTimeWizard, u':/wizards/wizard_firsttime.bmp')
|
||||
self.finish_button = self.button(QtGui.QWizard.FinishButton)
|
||||
self.no_internet_finish_button = self.button(QtGui.QWizard.CustomButton1)
|
||||
self.cancel_button = self.button(QtGui.QWizard.CancelButton)
|
||||
self.next_button = self.button(QtGui.QWizard.NextButton)
|
||||
self.back_button = self.button(QtGui.QWizard.BackButton)
|
||||
add_welcome_page(first_time_wizard, u':/wizards/wizard_firsttime.bmp')
|
||||
# The plugins page
|
||||
self.pluginPage = QtGui.QWizardPage()
|
||||
self.pluginPage.setObjectName(u'pluginPage')
|
||||
self.pluginLayout = QtGui.QVBoxLayout(self.pluginPage)
|
||||
self.pluginLayout.setContentsMargins(40, 15, 40, 0)
|
||||
self.pluginLayout.setObjectName(u'pluginLayout')
|
||||
self.songsCheckBox = QtGui.QCheckBox(self.pluginPage)
|
||||
self.songsCheckBox.setChecked(True)
|
||||
self.songsCheckBox.setObjectName(u'songsCheckBox')
|
||||
self.pluginLayout.addWidget(self.songsCheckBox)
|
||||
self.customCheckBox = QtGui.QCheckBox(self.pluginPage)
|
||||
self.customCheckBox.setChecked(True)
|
||||
self.customCheckBox.setObjectName(u'customCheckBox')
|
||||
self.pluginLayout.addWidget(self.customCheckBox)
|
||||
self.bibleCheckBox = QtGui.QCheckBox(self.pluginPage)
|
||||
self.bibleCheckBox.setChecked(True)
|
||||
self.bibleCheckBox.setObjectName(u'bibleCheckBox')
|
||||
self.pluginLayout.addWidget(self.bibleCheckBox)
|
||||
self.imageCheckBox = QtGui.QCheckBox(self.pluginPage)
|
||||
self.imageCheckBox.setChecked(True)
|
||||
self.imageCheckBox.setObjectName(u'imageCheckBox')
|
||||
self.pluginLayout.addWidget(self.imageCheckBox)
|
||||
self.plugin_page = QtGui.QWizardPage()
|
||||
self.plugin_page.setObjectName(u'plugin_page')
|
||||
self.plugin_layout = QtGui.QVBoxLayout(self.plugin_page)
|
||||
self.plugin_layout.setContentsMargins(40, 15, 40, 0)
|
||||
self.plugin_layout.setObjectName(u'plugin_layout')
|
||||
self.songs_check_box = QtGui.QCheckBox(self.plugin_page)
|
||||
self.songs_check_box.setChecked(True)
|
||||
self.songs_check_box.setObjectName(u'songs_check_box')
|
||||
self.plugin_layout.addWidget(self.songs_check_box)
|
||||
self.custom_check_box = QtGui.QCheckBox(self.plugin_page)
|
||||
self.custom_check_box.setChecked(True)
|
||||
self.custom_check_box.setObjectName(u'custom_check_box')
|
||||
self.plugin_layout.addWidget(self.custom_check_box)
|
||||
self.bible_check_box = QtGui.QCheckBox(self.plugin_page)
|
||||
self.bible_check_box.setChecked(True)
|
||||
self.bible_check_box.setObjectName(u'bible_check_box')
|
||||
self.plugin_layout.addWidget(self.bible_check_box)
|
||||
self.image_check_box = QtGui.QCheckBox(self.plugin_page)
|
||||
self.image_check_box.setChecked(True)
|
||||
self.image_check_box.setObjectName(u'image_check_box')
|
||||
self.plugin_layout.addWidget(self.image_check_box)
|
||||
# TODO Presentation plugin is not yet working on Mac OS X.
|
||||
# For now just ignore it.
|
||||
if sys.platform != 'darwin':
|
||||
self.presentationCheckBox = QtGui.QCheckBox(self.pluginPage)
|
||||
self.presentationCheckBox.setChecked(True)
|
||||
self.presentationCheckBox.setObjectName(u'presentationCheckBox')
|
||||
self.pluginLayout.addWidget(self.presentationCheckBox)
|
||||
self.mediaCheckBox = QtGui.QCheckBox(self.pluginPage)
|
||||
self.mediaCheckBox.setChecked(True)
|
||||
self.mediaCheckBox.setObjectName(u'mediaCheckBox')
|
||||
self.pluginLayout.addWidget(self.mediaCheckBox)
|
||||
self.remoteCheckBox = QtGui.QCheckBox(self.pluginPage)
|
||||
self.remoteCheckBox.setObjectName(u'remoteCheckBox')
|
||||
self.pluginLayout.addWidget(self.remoteCheckBox)
|
||||
self.songUsageCheckBox = QtGui.QCheckBox(self.pluginPage)
|
||||
self.songUsageCheckBox.setChecked(True)
|
||||
self.songUsageCheckBox.setObjectName(u'songUsageCheckBox')
|
||||
self.pluginLayout.addWidget(self.songUsageCheckBox)
|
||||
self.alertCheckBox = QtGui.QCheckBox(self.pluginPage)
|
||||
self.alertCheckBox.setChecked(True)
|
||||
self.alertCheckBox.setObjectName(u'alertCheckBox')
|
||||
self.pluginLayout.addWidget(self.alertCheckBox)
|
||||
FirstTimeWizard.setPage(FirstTimePage.Plugins, self.pluginPage)
|
||||
self.presentation_check_box = QtGui.QCheckBox(self.plugin_page)
|
||||
self.presentation_check_box.setChecked(True)
|
||||
self.presentation_check_box.setObjectName(u'presentation_check_box')
|
||||
self.plugin_layout.addWidget(self.presentation_check_box)
|
||||
self.media_check_box = QtGui.QCheckBox(self.plugin_page)
|
||||
self.media_check_box.setChecked(True)
|
||||
self.media_check_box.setObjectName(u'media_check_box')
|
||||
self.plugin_layout.addWidget(self.media_check_box)
|
||||
self.remote_check_box = QtGui.QCheckBox(self.plugin_page)
|
||||
self.remote_check_box.setObjectName(u'remote_check_box')
|
||||
self.plugin_layout.addWidget(self.remote_check_box)
|
||||
self.song_usage_check_box = QtGui.QCheckBox(self.plugin_page)
|
||||
self.song_usage_check_box.setChecked(True)
|
||||
self.song_usage_check_box.setObjectName(u'song_usage_check_box')
|
||||
self.plugin_layout.addWidget(self.song_usage_check_box)
|
||||
self.alert_check_box = QtGui.QCheckBox(self.plugin_page)
|
||||
self.alert_check_box.setChecked(True)
|
||||
self.alert_check_box.setObjectName(u'alert_check_box')
|
||||
self.plugin_layout.addWidget(self.alert_check_box)
|
||||
first_time_wizard.setPage(FirstTimePage.Plugins, self.plugin_page)
|
||||
# The "you don't have an internet connection" page.
|
||||
self.noInternetPage = QtGui.QWizardPage()
|
||||
self.noInternetPage.setObjectName(u'noInternetPage')
|
||||
self.noInternetLayout = QtGui.QVBoxLayout(self.noInternetPage)
|
||||
self.noInternetLayout.setContentsMargins(50, 30, 50, 40)
|
||||
self.noInternetLayout.setObjectName(u'noInternetLayout')
|
||||
self.noInternetLabel = QtGui.QLabel(self.noInternetPage)
|
||||
self.noInternetLabel.setWordWrap(True)
|
||||
self.noInternetLabel.setObjectName(u'noInternetLabel')
|
||||
self.noInternetLayout.addWidget(self.noInternetLabel)
|
||||
FirstTimeWizard.setPage(FirstTimePage.NoInternet, self.noInternetPage)
|
||||
self.no_internet_page = QtGui.QWizardPage()
|
||||
self.no_internet_page.setObjectName(u'no_internet_page')
|
||||
self.no_internet_layout = QtGui.QVBoxLayout(self.no_internet_page)
|
||||
self.no_internet_layout.setContentsMargins(50, 30, 50, 40)
|
||||
self.no_internet_layout.setObjectName(u'no_internet_layout')
|
||||
self.no_internet_label = QtGui.QLabel(self.no_internet_page)
|
||||
self.no_internet_label.setWordWrap(True)
|
||||
self.no_internet_label.setObjectName(u'no_internet_label')
|
||||
self.no_internet_layout.addWidget(self.no_internet_label)
|
||||
first_time_wizard.setPage(FirstTimePage.NoInternet, self.no_internet_page)
|
||||
# The song samples page
|
||||
self.songsPage = QtGui.QWizardPage()
|
||||
self.songsPage.setObjectName(u'songsPage')
|
||||
self.songsLayout = QtGui.QVBoxLayout(self.songsPage)
|
||||
self.songsLayout.setContentsMargins(50, 20, 50, 20)
|
||||
self.songsLayout.setObjectName(u'songsLayout')
|
||||
self.songsListWidget = QtGui.QListWidget(self.songsPage)
|
||||
self.songsListWidget.setAlternatingRowColors(True)
|
||||
self.songsListWidget.setObjectName(u'songsListWidget')
|
||||
self.songsLayout.addWidget(self.songsListWidget)
|
||||
FirstTimeWizard.setPage(FirstTimePage.Songs, self.songsPage)
|
||||
self.songs_page = QtGui.QWizardPage()
|
||||
self.songs_page.setObjectName(u'songs_page')
|
||||
self.songs_layout = QtGui.QVBoxLayout(self.songs_page)
|
||||
self.songs_layout.setContentsMargins(50, 20, 50, 20)
|
||||
self.songs_layout.setObjectName(u'songs_layout')
|
||||
self.songs_list_widget = QtGui.QListWidget(self.songs_page)
|
||||
self.songs_list_widget.setAlternatingRowColors(True)
|
||||
self.songs_list_widget.setObjectName(u'songs_list_widget')
|
||||
self.songs_layout.addWidget(self.songs_list_widget)
|
||||
first_time_wizard.setPage(FirstTimePage.Songs, self.songs_page)
|
||||
# The Bible samples page
|
||||
self.biblesPage = QtGui.QWizardPage()
|
||||
self.biblesPage.setObjectName(u'biblesPage')
|
||||
self.biblesLayout = QtGui.QVBoxLayout(self.biblesPage)
|
||||
self.biblesLayout.setContentsMargins(50, 20, 50, 20)
|
||||
self.biblesLayout.setObjectName(u'biblesLayout')
|
||||
self.biblesTreeWidget = QtGui.QTreeWidget(self.biblesPage)
|
||||
self.biblesTreeWidget.setAlternatingRowColors(True)
|
||||
self.biblesTreeWidget.header().setVisible(False)
|
||||
self.biblesTreeWidget.setObjectName(u'biblesTreeWidget')
|
||||
self.biblesLayout.addWidget(self.biblesTreeWidget)
|
||||
FirstTimeWizard.setPage(FirstTimePage.Bibles, self.biblesPage)
|
||||
self.bibles_page = QtGui.QWizardPage()
|
||||
self.bibles_page.setObjectName(u'bibles_page')
|
||||
self.bibles_layout = QtGui.QVBoxLayout(self.bibles_page)
|
||||
self.bibles_layout.setContentsMargins(50, 20, 50, 20)
|
||||
self.bibles_layout.setObjectName(u'bibles_layout')
|
||||
self.bibles_tree_widget = QtGui.QTreeWidget(self.bibles_page)
|
||||
self.bibles_tree_widget.setAlternatingRowColors(True)
|
||||
self.bibles_tree_widget.header().setVisible(False)
|
||||
self.bibles_tree_widget.setObjectName(u'bibles_tree_widget')
|
||||
self.bibles_layout.addWidget(self.bibles_tree_widget)
|
||||
first_time_wizard.setPage(FirstTimePage.Bibles, self.bibles_page)
|
||||
# The theme samples page
|
||||
self.themesPage = QtGui.QWizardPage()
|
||||
self.themesPage.setObjectName(u'themesPage')
|
||||
self.themesLayout = QtGui.QVBoxLayout(self.themesPage)
|
||||
self.themesLayout.setContentsMargins(20, 50, 20, 60)
|
||||
self.themesLayout.setObjectName(u'themesLayout')
|
||||
self.themesListWidget = QtGui.QListWidget(self.themesPage)
|
||||
self.themesListWidget.setViewMode(QtGui.QListView.IconMode)
|
||||
self.themesListWidget.setMovement(QtGui.QListView.Static)
|
||||
self.themesListWidget.setFlow(QtGui.QListView.LeftToRight)
|
||||
self.themesListWidget.setSpacing(4)
|
||||
self.themesListWidget.setUniformItemSizes(True)
|
||||
self.themesListWidget.setIconSize(QtCore.QSize(133, 100))
|
||||
self.themesListWidget.setWrapping(False)
|
||||
self.themesListWidget.setObjectName(u'themesListWidget')
|
||||
self.themesLayout.addWidget(self.themesListWidget)
|
||||
FirstTimeWizard.setPage(FirstTimePage.Themes, self.themesPage)
|
||||
self.themes_page = QtGui.QWizardPage()
|
||||
self.themes_page.setObjectName(u'themes_page')
|
||||
self.themes_layout = QtGui.QVBoxLayout(self.themes_page)
|
||||
self.themes_layout.setContentsMargins(20, 50, 20, 60)
|
||||
self.themes_layout.setObjectName(u'themes_layout')
|
||||
self.themes_list_widget = QtGui.QListWidget(self.themes_page)
|
||||
self.themes_list_widget.setViewMode(QtGui.QListView.IconMode)
|
||||
self.themes_list_widget.setMovement(QtGui.QListView.Static)
|
||||
self.themes_list_widget.setFlow(QtGui.QListView.LeftToRight)
|
||||
self.themes_list_widget.setSpacing(4)
|
||||
self.themes_list_widget.setUniformItemSizes(True)
|
||||
self.themes_list_widget.setIconSize(QtCore.QSize(133, 100))
|
||||
self.themes_list_widget.setWrapping(False)
|
||||
self.themes_list_widget.setObjectName(u'themes_list_widget')
|
||||
self.themes_layout.addWidget(self.themes_list_widget)
|
||||
first_time_wizard.setPage(FirstTimePage.Themes, self.themes_page)
|
||||
# the default settings page
|
||||
self.defaultsPage = QtGui.QWizardPage()
|
||||
self.defaultsPage.setObjectName(u'defaultsPage')
|
||||
self.defaultsLayout = QtGui.QFormLayout(self.defaultsPage)
|
||||
self.defaultsLayout.setContentsMargins(50, 20, 50, 20)
|
||||
self.defaultsLayout.setObjectName(u'defaultsLayout')
|
||||
self.displayLabel = QtGui.QLabel(self.defaultsPage)
|
||||
self.displayLabel.setObjectName(u'displayLabel')
|
||||
self.displayComboBox = QtGui.QComboBox(self.defaultsPage)
|
||||
self.displayComboBox.setEditable(False)
|
||||
self.displayComboBox.setInsertPolicy(QtGui.QComboBox.NoInsert)
|
||||
self.displayComboBox.setObjectName(u'displayComboBox')
|
||||
self.defaultsLayout.addRow(self.displayLabel, self.displayComboBox)
|
||||
self.themeLabel = QtGui.QLabel(self.defaultsPage)
|
||||
self.themeLabel.setObjectName(u'themeLabel')
|
||||
self.themeComboBox = QtGui.QComboBox(self.defaultsPage)
|
||||
self.themeComboBox.setEditable(False)
|
||||
self.themeComboBox.setInsertPolicy(QtGui.QComboBox.NoInsert)
|
||||
self.themeComboBox.setSizeAdjustPolicy(QtGui.QComboBox.AdjustToContents)
|
||||
self.themeComboBox.setObjectName(u'themeComboBox')
|
||||
self.defaultsLayout.addRow(self.themeLabel, self.themeComboBox)
|
||||
FirstTimeWizard.setPage(FirstTimePage.Defaults, self.defaultsPage)
|
||||
self.defaults_page = QtGui.QWizardPage()
|
||||
self.defaults_page.setObjectName(u'defaults_page')
|
||||
self.defaults_layout = QtGui.QFormLayout(self.defaults_page)
|
||||
self.defaults_layout.setContentsMargins(50, 20, 50, 20)
|
||||
self.defaults_layout.setObjectName(u'defaults_layout')
|
||||
self.display_label = QtGui.QLabel(self.defaults_page)
|
||||
self.display_label.setObjectName(u'display_label')
|
||||
self.display_combo_box = QtGui.QComboBox(self.defaults_page)
|
||||
self.display_combo_box.setEditable(False)
|
||||
self.display_combo_box.setInsertPolicy(QtGui.QComboBox.NoInsert)
|
||||
self.display_combo_box.setObjectName(u'display_combo_box')
|
||||
self.defaults_layout.addRow(self.display_label, self.display_combo_box)
|
||||
self.theme_label = QtGui.QLabel(self.defaults_page)
|
||||
self.theme_label.setObjectName(u'theme_label')
|
||||
self.theme_combo_box = QtGui.QComboBox(self.defaults_page)
|
||||
self.theme_combo_box.setEditable(False)
|
||||
self.theme_combo_box.setInsertPolicy(QtGui.QComboBox.NoInsert)
|
||||
self.theme_combo_box.setSizeAdjustPolicy(QtGui.QComboBox.AdjustToContents)
|
||||
self.theme_combo_box.setObjectName(u'theme_combo_box')
|
||||
self.defaults_layout.addRow(self.theme_label, self.theme_combo_box)
|
||||
first_time_wizard.setPage(FirstTimePage.Defaults, self.defaults_page)
|
||||
# Progress page
|
||||
self.progressPage = QtGui.QWizardPage()
|
||||
self.progressPage.setObjectName(u'progressPage')
|
||||
self.progressLayout = QtGui.QVBoxLayout(self.progressPage)
|
||||
self.progressLayout.setMargin(48)
|
||||
self.progressLayout.setObjectName(u'progressLayout')
|
||||
self.progressLabel = QtGui.QLabel(self.progressPage)
|
||||
self.progressLabel.setObjectName(u'progressLabel')
|
||||
self.progressLayout.addWidget(self.progressLabel)
|
||||
self.progressBar = QtGui.QProgressBar(self.progressPage)
|
||||
self.progressBar.setObjectName(u'progressBar')
|
||||
self.progressLayout.addWidget(self.progressBar)
|
||||
FirstTimeWizard.setPage(FirstTimePage.Progress, self.progressPage)
|
||||
self.retranslateUi(FirstTimeWizard)
|
||||
self.progress_page = QtGui.QWizardPage()
|
||||
self.progress_page.setObjectName(u'progress_page')
|
||||
self.progress_layout = QtGui.QVBoxLayout(self.progress_page)
|
||||
self.progress_layout.setMargin(48)
|
||||
self.progress_layout.setObjectName(u'progress_layout')
|
||||
self.progress_label = QtGui.QLabel(self.progress_page)
|
||||
self.progress_label.setObjectName(u'progress_label')
|
||||
self.progress_layout.addWidget(self.progress_label)
|
||||
self.progress_bar = QtGui.QProgressBar(self.progress_page)
|
||||
self.progress_bar.setObjectName(u'progress_bar')
|
||||
self.progress_layout.addWidget(self.progress_bar)
|
||||
first_time_wizard.setPage(FirstTimePage.Progress, self.progress_page)
|
||||
self.retranslateUi(first_time_wizard)
|
||||
|
||||
def retranslateUi(self, FirstTimeWizard):
|
||||
def retranslateUi(self, first_time_wizard):
|
||||
"""
|
||||
Translate the UI on the fly
|
||||
"""
|
||||
FirstTimeWizard.setWindowTitle(translate('OpenLP.FirstTimeWizard', 'First Time Wizard'))
|
||||
first_time_wizard.setWindowTitle(translate('OpenLP.FirstTimeWizard', 'First Time Wizard'))
|
||||
self.title_label.setText(u'<span style="font-size:14pt; font-weight:600;">%s</span>' %
|
||||
translate('OpenLP.FirstTimeWizard', 'Welcome to the First Time Wizard'))
|
||||
self.information_label.setText(translate('OpenLP.FirstTimeWizard',
|
||||
'This wizard will help you to configure OpenLP for initial use. Click the next button below to start.'))
|
||||
self.pluginPage.setTitle(translate('OpenLP.FirstTimeWizard', 'Activate required Plugins'))
|
||||
self.pluginPage.setSubTitle(translate('OpenLP.FirstTimeWizard', 'Select the Plugins you wish to use. '))
|
||||
self.songsCheckBox.setText(translate('OpenLP.FirstTimeWizard', 'Songs'))
|
||||
self.customCheckBox.setText(translate('OpenLP.FirstTimeWizard', 'Custom Slides'))
|
||||
self.bibleCheckBox.setText(translate('OpenLP.FirstTimeWizard', 'Bible'))
|
||||
self.imageCheckBox.setText(translate('OpenLP.FirstTimeWizard', 'Images'))
|
||||
self.plugin_page.setTitle(translate('OpenLP.FirstTimeWizard', 'Activate required Plugins'))
|
||||
self.plugin_page.setSubTitle(translate('OpenLP.FirstTimeWizard', 'Select the Plugins you wish to use. '))
|
||||
self.songs_check_box.setText(translate('OpenLP.FirstTimeWizard', 'Songs'))
|
||||
self.custom_check_box.setText(translate('OpenLP.FirstTimeWizard', 'Custom Slides'))
|
||||
self.bible_check_box.setText(translate('OpenLP.FirstTimeWizard', 'Bible'))
|
||||
self.image_check_box.setText(translate('OpenLP.FirstTimeWizard', 'Images'))
|
||||
# TODO Presentation plugin is not yet working on Mac OS X.
|
||||
# For now just ignore it.
|
||||
if sys.platform != 'darwin':
|
||||
self.presentationCheckBox.setText(translate('OpenLP.FirstTimeWizard', 'Presentations'))
|
||||
self.mediaCheckBox.setText(translate('OpenLP.FirstTimeWizard', 'Media (Audio and Video)'))
|
||||
self.remoteCheckBox.setText(translate('OpenLP.FirstTimeWizard', 'Allow remote access'))
|
||||
self.songUsageCheckBox.setText(translate('OpenLP.FirstTimeWizard', 'Monitor Song Usage'))
|
||||
self.alertCheckBox.setText(translate('OpenLP.FirstTimeWizard', 'Allow Alerts'))
|
||||
self.noInternetPage.setTitle(translate('OpenLP.FirstTimeWizard', 'No Internet Connection'))
|
||||
self.noInternetPage.setSubTitle(translate('OpenLP.FirstTimeWizard', 'Unable to detect an Internet connection.'))
|
||||
self.noInternetText = translate('OpenLP.FirstTimeWizard',
|
||||
'No Internet connection was found. The First Time Wizard needs an '
|
||||
'Internet connection in order to be able to download sample '
|
||||
'songs, Bibles and themes. Click the Finish button now to start '
|
||||
'OpenLP with initial settings and no sample data.\n\nTo re-run the '
|
||||
'First Time Wizard and import this sample data at a later time, '
|
||||
'check your Internet connection and re-run this wizard by '
|
||||
'selecting "Tools/Re-run First Time Wizard" from OpenLP.')
|
||||
self.presentation_check_box.setText(translate('OpenLP.FirstTimeWizard', 'Presentations'))
|
||||
self.media_check_box.setText(translate('OpenLP.FirstTimeWizard', 'Media (Audio and Video)'))
|
||||
self.remote_check_box.setText(translate('OpenLP.FirstTimeWizard', 'Allow remote access'))
|
||||
self.song_usage_check_box.setText(translate('OpenLP.FirstTimeWizard', 'Monitor Song Usage'))
|
||||
self.alert_check_box.setText(translate('OpenLP.FirstTimeWizard', 'Allow Alerts'))
|
||||
self.no_internet_page.setTitle(translate('OpenLP.FirstTimeWizard', 'No Internet Connection'))
|
||||
self.no_internet_page.setSubTitle(
|
||||
translate('OpenLP.FirstTimeWizard', 'Unable to detect an Internet connection.'))
|
||||
self.no_internet_text = translate('OpenLP.FirstTimeWizard',
|
||||
'No Internet connection was found. The First Time Wizard needs an Internet connection in order to be able '
|
||||
'to download sample songs, Bibles and themes. Click the Finish button now to start OpenLP with initial '
|
||||
'settings and no sample data.\n\nTo re-run the First Time Wizard and import this sample data at a later '
|
||||
'time, check your Internet connection and re-run this wizard by selecting "Tools/Re-run First Time Wizard" '
|
||||
'from OpenLP.')
|
||||
self.cancelWizardText = translate('OpenLP.FirstTimeWizard',
|
||||
'\n\nTo cancel the First Time Wizard completely (and not start OpenLP), click the Cancel button now.')
|
||||
self.songsPage.setTitle(translate('OpenLP.FirstTimeWizard', 'Sample Songs'))
|
||||
self.songsPage.setSubTitle(translate('OpenLP.FirstTimeWizard', 'Select and download public domain songs.'))
|
||||
self.biblesPage.setTitle(translate('OpenLP.FirstTimeWizard', 'Sample Bibles'))
|
||||
self.biblesPage.setSubTitle(translate('OpenLP.FirstTimeWizard', 'Select and download free Bibles.'))
|
||||
self.themesPage.setTitle(translate('OpenLP.FirstTimeWizard', 'Sample Themes'))
|
||||
self.themesPage.setSubTitle(translate('OpenLP.FirstTimeWizard', 'Select and download sample themes.'))
|
||||
self.defaultsPage.setTitle(translate('OpenLP.FirstTimeWizard', 'Default Settings'))
|
||||
self.defaultsPage.setSubTitle(translate('OpenLP.FirstTimeWizard',
|
||||
self.songs_page.setTitle(translate('OpenLP.FirstTimeWizard', 'Sample Songs'))
|
||||
self.songs_page.setSubTitle(translate('OpenLP.FirstTimeWizard', 'Select and download public domain songs.'))
|
||||
self.bibles_page.setTitle(translate('OpenLP.FirstTimeWizard', 'Sample Bibles'))
|
||||
self.bibles_page.setSubTitle(translate('OpenLP.FirstTimeWizard', 'Select and download free Bibles.'))
|
||||
self.themes_page.setTitle(translate('OpenLP.FirstTimeWizard', 'Sample Themes'))
|
||||
self.themes_page.setSubTitle(translate('OpenLP.FirstTimeWizard', 'Select and download sample themes.'))
|
||||
self.defaults_page.setTitle(translate('OpenLP.FirstTimeWizard', 'Default Settings'))
|
||||
self.defaults_page.setSubTitle(translate('OpenLP.FirstTimeWizard',
|
||||
'Set up default settings to be used by OpenLP.'))
|
||||
self.displayLabel.setText(translate('OpenLP.FirstTimeWizard', 'Default output display:'))
|
||||
self.themeLabel.setText(translate('OpenLP.FirstTimeWizard', 'Select default theme:'))
|
||||
self.progressLabel.setText(translate('OpenLP.FirstTimeWizard', 'Starting configuration process...'))
|
||||
FirstTimeWizard.setButtonText(QtGui.QWizard.CustomButton1, translate('OpenLP.FirstTimeWizard', 'Finish'))
|
||||
self.display_label.setText(translate('OpenLP.FirstTimeWizard', 'Default output display:'))
|
||||
self.theme_label.setText(translate('OpenLP.FirstTimeWizard', 'Select default theme:'))
|
||||
self.progress_label.setText(translate('OpenLP.FirstTimeWizard', 'Starting configuration process...'))
|
||||
first_time_wizard.setButtonText(QtGui.QWizard.CustomButton1, translate('OpenLP.FirstTimeWizard', 'Finish'))
|
||||
|
|
|
@ -328,6 +328,9 @@ class MainDisplay(Display):
|
|||
self.display_image(self.service_item.bg_image_bytes)
|
||||
else:
|
||||
self.display_image(None)
|
||||
# Update the preview frame.
|
||||
if self.is_live:
|
||||
self.live_controller.update_preview()
|
||||
# clear the cache
|
||||
self.override = {}
|
||||
|
||||
|
|
|
@ -100,10 +100,10 @@ class Ui_MainWindow(object):
|
|||
self.main_contentLayout.setMargin(0)
|
||||
self.main_contentLayout.setObjectName(u'main_contentLayout')
|
||||
main_window.setCentralWidget(self.main_content)
|
||||
self.controlSplitter = QtGui.QSplitter(self.main_content)
|
||||
self.controlSplitter.setOrientation(QtCore.Qt.Horizontal)
|
||||
self.controlSplitter.setObjectName(u'controlSplitter')
|
||||
self.main_contentLayout.addWidget(self.controlSplitter)
|
||||
self.control_splitter = QtGui.QSplitter(self.main_content)
|
||||
self.control_splitter.setOrientation(QtCore.Qt.Horizontal)
|
||||
self.control_splitter.setObjectName(u'control_splitter')
|
||||
self.main_contentLayout.addWidget(self.control_splitter)
|
||||
# Create slide controllers
|
||||
self.preview_controller = SlideController(self)
|
||||
self.live_controller = SlideController(self, True)
|
||||
|
@ -113,9 +113,9 @@ class Ui_MainWindow(object):
|
|||
panel_locked = Settings().value(u'user interface/lock panel')
|
||||
self.live_controller.panel.setVisible(live_visible)
|
||||
# Create menu
|
||||
self.menuBar = QtGui.QMenuBar(main_window)
|
||||
self.menuBar.setObjectName(u'menuBar')
|
||||
self.file_menu = QtGui.QMenu(self.menuBar)
|
||||
self.menu_bar = QtGui.QMenuBar(main_window)
|
||||
self.menu_bar.setObjectName(u'menu_bar')
|
||||
self.file_menu = QtGui.QMenu(self.menu_bar)
|
||||
self.file_menu.setObjectName(u'fileMenu')
|
||||
self.recent_files_menu = QtGui.QMenu(self.file_menu)
|
||||
self.recent_files_menu.setObjectName(u'recentFilesMenu')
|
||||
|
@ -124,22 +124,22 @@ class Ui_MainWindow(object):
|
|||
self.file_export_menu = QtGui.QMenu(self.file_menu)
|
||||
self.file_export_menu.setObjectName(u'file_export_menu')
|
||||
# View Menu
|
||||
self.view_menu = QtGui.QMenu(self.menuBar)
|
||||
self.view_menu = QtGui.QMenu(self.menu_bar)
|
||||
self.view_menu.setObjectName(u'viewMenu')
|
||||
self.view_modeMenu = QtGui.QMenu(self.view_menu)
|
||||
self.view_modeMenu.setObjectName(u'viewModeMenu')
|
||||
# Tools Menu
|
||||
self.tools_menu = QtGui.QMenu(self.menuBar)
|
||||
self.tools_menu = QtGui.QMenu(self.menu_bar)
|
||||
self.tools_menu.setObjectName(u'tools_menu')
|
||||
# Settings Menu
|
||||
self.settings_menu = QtGui.QMenu(self.menuBar)
|
||||
self.settings_menu = QtGui.QMenu(self.menu_bar)
|
||||
self.settings_menu.setObjectName(u'settingsMenu')
|
||||
self.settings_language_menu = QtGui.QMenu(self.settings_menu)
|
||||
self.settings_language_menu.setObjectName(u'settingsLanguageMenu')
|
||||
# Help Menu
|
||||
self.help_menu = QtGui.QMenu(self.menuBar)
|
||||
self.help_menu = QtGui.QMenu(self.menu_bar)
|
||||
self.help_menu.setObjectName(u'helpMenu')
|
||||
main_window.setMenuBar(self.menuBar)
|
||||
main_window.setMenuBar(self.menu_bar)
|
||||
self.status_bar = QtGui.QStatusBar(main_window)
|
||||
self.status_bar.setObjectName(u'status_bar')
|
||||
main_window.setStatusBar(self.status_bar)
|
||||
|
@ -237,7 +237,7 @@ class Ui_MainWindow(object):
|
|||
self.view_live_panel = create_action(main_window, u'viewLivePanel',
|
||||
can_shortcuts=True, checked=live_visible,
|
||||
category=UiStrings().View, triggers=self.set_live_panel_visibility)
|
||||
self.lockPanel = create_action(main_window, u'lockPanel',
|
||||
self.lock_panel = create_action(main_window, u'lockPanel',
|
||||
can_shortcuts=True, checked=panel_locked,
|
||||
category=UiStrings().View,
|
||||
triggers=self.set_lock_panel)
|
||||
|
@ -310,6 +310,10 @@ class Ui_MainWindow(object):
|
|||
can_shortcuts=True,
|
||||
category=UiStrings().Help, triggers=self.on_online_help_clicked)
|
||||
self.web_site_item = create_action(main_window, u'webSiteItem', can_shortcuts=True, category=UiStrings().Help)
|
||||
# Shortcuts not connected to buttons or menu entires.
|
||||
self.search_shortcut_action = create_action(main_window,
|
||||
u'searchShortcut', can_shortcuts=True, category=translate('OpenLP.MainWindow', 'General'),
|
||||
triggers=self.on_search_shortcut_triggered)
|
||||
add_actions(self.file_import_menu, (self.settings_import_item, None, self.import_theme_item,
|
||||
self.import_language_item))
|
||||
add_actions(self.file_export_menu, (self.settings_export_item, None, self.export_theme_item,
|
||||
|
@ -321,7 +325,7 @@ class Ui_MainWindow(object):
|
|||
add_actions(self.view_modeMenu, (self.mode_default_Item, self.mode_setup_item, self.mode_live_item))
|
||||
add_actions(self.view_menu, (self.view_modeMenu.menuAction(), None, self.view_media_manager_item,
|
||||
self.view_service_manager_item, self.view_theme_manager_item, None, self.view_preview_panel,
|
||||
self.view_live_panel, None, self.lockPanel))
|
||||
self.view_live_panel, None, self.lock_panel))
|
||||
# i18n add Language Actions
|
||||
add_actions(self.settings_language_menu, (self.auto_language_item, None))
|
||||
add_actions(self.settings_language_menu, self.language_group.actions())
|
||||
|
@ -342,8 +346,9 @@ class Ui_MainWindow(object):
|
|||
self.about_item))
|
||||
else:
|
||||
add_actions(self.help_menu, (self.on_line_help_item, None, self.web_site_item, self.about_item))
|
||||
add_actions(self.menuBar, (self.file_menu.menuAction(), self.view_menu.menuAction(),
|
||||
add_actions(self.menu_bar, (self.file_menu.menuAction(), self.view_menu.menuAction(),
|
||||
self.tools_menu.menuAction(), self.settings_menu.menuAction(), self.help_menu.menuAction()))
|
||||
add_actions(self, [self.search_shortcut_action])
|
||||
# Initialise the translation
|
||||
self.retranslateUi(main_window)
|
||||
self.media_tool_box.setCurrentIndex(0)
|
||||
|
@ -356,12 +361,11 @@ class Ui_MainWindow(object):
|
|||
self.set_lock_panel(panel_locked)
|
||||
self.settingsImported = False
|
||||
|
||||
def retranslateUi(self, mainWindow):
|
||||
def retranslateUi(self, main_window):
|
||||
"""
|
||||
Set up the translation system
|
||||
"""
|
||||
mainWindow.mainTitle = UiStrings().OLPV2x
|
||||
mainWindow.setWindowTitle(mainWindow.mainTitle)
|
||||
main_window.setWindowTitle(UiStrings().OLPV2x)
|
||||
self.file_menu.setTitle(translate('OpenLP.MainWindow', '&File'))
|
||||
self.file_import_menu.setTitle(translate('OpenLP.MainWindow', '&Import'))
|
||||
self.file_export_menu.setTitle(translate('OpenLP.MainWindow', '&Export'))
|
||||
|
@ -423,8 +427,8 @@ class Ui_MainWindow(object):
|
|||
translate('OpenLP.MainWindow', 'Toggle the visibility of the preview panel.'))
|
||||
self.view_live_panel.setText(translate('OpenLP.MainWindow', '&Live Panel'))
|
||||
self.view_live_panel.setToolTip(translate('OpenLP.MainWindow', 'Toggle Live Panel'))
|
||||
self.lockPanel.setText(translate('OpenLP.MainWindow', 'L&ock Panels'))
|
||||
self.lockPanel.setStatusTip(translate('OpenLP.MainWindow', 'Prevent the panels being moved.'))
|
||||
self.lock_panel.setText(translate('OpenLP.MainWindow', 'L&ock Panels'))
|
||||
self.lock_panel.setStatusTip(translate('OpenLP.MainWindow', 'Prevent the panels being moved.'))
|
||||
self.view_live_panel.setStatusTip(translate('OpenLP.MainWindow', 'Toggle the visibility of the live panel.'))
|
||||
self.settingsPluginListItem.setText(translate('OpenLP.MainWindow', '&Plugin List'))
|
||||
self.settingsPluginListItem.setStatusTip(translate('OpenLP.MainWindow', 'List the Plugins'))
|
||||
|
@ -433,6 +437,9 @@ class Ui_MainWindow(object):
|
|||
if os.name == u'nt':
|
||||
self.offlineHelpItem.setText(translate('OpenLP.MainWindow', '&User Guide'))
|
||||
self.on_line_help_item.setText(translate('OpenLP.MainWindow', '&Online Help'))
|
||||
self.search_shortcut_action.setText(UiStrings().Search)
|
||||
self.search_shortcut_action.setToolTip(
|
||||
translate('OpenLP.MainWindow', 'Jump to the search box of the current active plugin.'))
|
||||
self.web_site_item.setText(translate('OpenLP.MainWindow', '&Web Site'))
|
||||
for item in self.language_group.actions():
|
||||
item.setText(item.objectName())
|
||||
|
@ -467,8 +474,7 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
|
|||
|
||||
def __init__(self):
|
||||
"""
|
||||
This constructor sets up the interface, the various managers, and the
|
||||
plugins.
|
||||
This constructor sets up the interface, the various managers, and the plugins.
|
||||
"""
|
||||
QtGui.QMainWindow.__init__(self)
|
||||
Registry().register(u'main_window', self)
|
||||
|
@ -491,7 +497,6 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
|
|||
self.new_data_path = None
|
||||
self.copy_data = False
|
||||
Settings().set_up_default_values()
|
||||
self.service_not_saved = False
|
||||
self.about_form = AboutForm(self)
|
||||
self.media_controller = MediaController()
|
||||
self.settings_form = SettingsForm(self)
|
||||
|
@ -536,22 +541,31 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
|
|||
Registry().register_function(u'theme_update_global', self.default_theme_changed)
|
||||
Registry().register_function(u'openlp_version_check', self.version_notice)
|
||||
Registry().register_function(u'config_screen_changed', self.screen_changed)
|
||||
Registry().register_function(u'bootstrap_post_set_up', self.restore_current_media_manager_item)
|
||||
self.renderer = Renderer()
|
||||
log.info(u'Load data from Settings')
|
||||
if Settings().value(u'advanced/save current plugin'):
|
||||
savedPlugin = Settings().value(u'advanced/current media plugin')
|
||||
if savedPlugin != -1:
|
||||
self.media_tool_box.setCurrentIndex(savedPlugin)
|
||||
# Reset the cursor
|
||||
self.application.set_normal_cursor()
|
||||
|
||||
def setAutoLanguage(self, value):
|
||||
def restore_current_media_manager_item(self):
|
||||
"""
|
||||
Set the language to automatic.
|
||||
Called on start up to restore the last active media plugin.
|
||||
"""
|
||||
self.language_group.setDisabled(value)
|
||||
LanguageManager.auto_language = value
|
||||
LanguageManager.set_language(self.language_group.checkedAction())
|
||||
log.info(u'Load data from Settings')
|
||||
if Settings().value(u'advanced/save current plugin'):
|
||||
saved_plugin_id = Settings().value(u'advanced/current media plugin')
|
||||
if saved_plugin_id != -1:
|
||||
self.media_tool_box.setCurrentIndex(saved_plugin_id)
|
||||
|
||||
def on_search_shortcut_triggered(self):
|
||||
"""
|
||||
Called when the search shotcut has been pressed.
|
||||
"""
|
||||
# Make sure the media_dock is visible.
|
||||
if not self.media_manager_dock.isVisible():
|
||||
self.media_manager_dock.setVisible(True)
|
||||
widget = self.media_tool_box.currentWidget()
|
||||
if widget:
|
||||
widget.on_focus()
|
||||
|
||||
def on_media_tool_box_changed(self, index):
|
||||
"""
|
||||
|
@ -966,8 +980,7 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
|
|||
"""
|
||||
self.setViewMode(False, True, False, False, True, u'live')
|
||||
|
||||
def setViewMode(self, media=True, service=True, theme=True, preview=True,
|
||||
live=True, mode=u''):
|
||||
def setViewMode(self, media=True, service=True, theme=True, preview=True, live=True, mode=u''):
|
||||
"""
|
||||
Set OpenLP to a different view mode.
|
||||
"""
|
||||
|
@ -982,8 +995,7 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
|
|||
|
||||
def screen_changed(self):
|
||||
"""
|
||||
The screen has changed so we have to update components such as the
|
||||
renderer.
|
||||
The screen has changed so we have to update components such as the renderer.
|
||||
"""
|
||||
log.debug(u'screen_changed')
|
||||
self.application.set_busy_cursor()
|
||||
|
@ -1068,43 +1080,20 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
|
|||
# Needed for Windows to stop crashes on exit
|
||||
Registry().remove(u'application')
|
||||
|
||||
def service_changed(self, reset=False, serviceName=None):
|
||||
def set_service_modified(self, modified, file_name):
|
||||
"""
|
||||
Hook to change the main window title when the service changes
|
||||
|
||||
``reset``
|
||||
Shows if the service has been cleared or saved
|
||||
|
||||
``serviceName``
|
||||
The name of the service (if it has one)
|
||||
"""
|
||||
if not serviceName:
|
||||
service_name = u'(unsaved service)'
|
||||
else:
|
||||
service_name = serviceName
|
||||
if reset:
|
||||
self.service_not_saved = False
|
||||
title = u'%s - %s' % (self.mainTitle, service_name)
|
||||
else:
|
||||
self.service_not_saved = True
|
||||
title = u'%s - %s*' % (self.mainTitle, service_name)
|
||||
self.setWindowTitle(title)
|
||||
|
||||
def set_service_modified(self, modified, fileName):
|
||||
"""
|
||||
This method is called from the ServiceManager to set the title of the
|
||||
main window.
|
||||
This method is called from the ServiceManager to set the title of the main window.
|
||||
|
||||
``modified``
|
||||
Whether or not this service has been modified.
|
||||
|
||||
``fileName``
|
||||
``file_name``
|
||||
The file name of the service file.
|
||||
"""
|
||||
if modified:
|
||||
title = u'%s - %s*' % (self.mainTitle, fileName)
|
||||
title = u'%s - %s*' % (UiStrings().OLPV2x, file_name)
|
||||
else:
|
||||
title = u'%s - %s' % (self.mainTitle, fileName)
|
||||
title = u'%s - %s' % (UiStrings().OLPV2x, file_name)
|
||||
self.setWindowTitle(title)
|
||||
|
||||
def show_status_message(self, message):
|
||||
|
@ -1140,8 +1129,7 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
|
|||
|
||||
def set_preview_panel_visibility(self, visible):
|
||||
"""
|
||||
Sets the visibility of the preview panel including saving the setting
|
||||
and updating the menu.
|
||||
Sets the visibility of the preview panel including saving the setting and updating the menu.
|
||||
|
||||
``visible``
|
||||
A bool giving the state to set the panel to
|
||||
|
@ -1178,8 +1166,7 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
|
|||
|
||||
def set_live_panel_visibility(self, visible):
|
||||
"""
|
||||
Sets the visibility of the live panel including saving the setting and
|
||||
updating the menu.
|
||||
Sets the visibility of the live panel including saving the setting and updating the menu.
|
||||
|
||||
``visible``
|
||||
A bool giving the state to set the panel to
|
||||
|
@ -1208,7 +1195,7 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
|
|||
self.restoreState(settings.value(u'main window state'))
|
||||
self.live_controller.splitter.restoreState(settings.value(u'live splitter geometry'))
|
||||
self.preview_controller.splitter.restoreState(settings.value(u'preview splitter geometry'))
|
||||
self.controlSplitter.restoreState(settings.value(u'main window splitter geometry'))
|
||||
self.control_splitter.restoreState(settings.value(u'main window splitter geometry'))
|
||||
settings.endGroup()
|
||||
|
||||
def save_settings(self):
|
||||
|
@ -1229,13 +1216,12 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
|
|||
settings.setValue(u'main window geometry', self.saveGeometry())
|
||||
settings.setValue(u'live splitter geometry', self.live_controller.splitter.saveState())
|
||||
settings.setValue(u'preview splitter geometry', self.preview_controller.splitter.saveState())
|
||||
settings.setValue(u'main window splitter geometry', self.controlSplitter.saveState())
|
||||
settings.setValue(u'main window splitter geometry', self.control_splitter.saveState())
|
||||
settings.endGroup()
|
||||
|
||||
def update_recent_files_menu(self):
|
||||
"""
|
||||
Updates the recent file menu with the latest list of service files
|
||||
accessed.
|
||||
Updates the recent file menu with the latest list of service files accessed.
|
||||
"""
|
||||
recent_file_count = Settings().value(u'advanced/recent file count')
|
||||
existing_recent_files = [recentFile for recentFile in self.recent_files
|
||||
|
|
|
@ -44,113 +44,57 @@ VIDEO_CSS = u"""
|
|||
z-index:3;
|
||||
background-color: %(bgcolor)s;
|
||||
}
|
||||
#video1 {
|
||||
background-color: %(bgcolor)s;
|
||||
z-index:4;
|
||||
}
|
||||
#video2 {
|
||||
#video {
|
||||
background-color: %(bgcolor)s;
|
||||
z-index:4;
|
||||
}
|
||||
"""
|
||||
|
||||
VIDEO_JS = u"""
|
||||
var video_timer = null;
|
||||
var current_video = '1';
|
||||
function show_video(state, path, volume, loop, variable_value){
|
||||
// Sometimes video.currentTime stops slightly short of video.duration and video.ended is intermittent!
|
||||
|
||||
function show_video(state, path, volume, loop, varVal){
|
||||
// Note, the preferred method for looping would be to use the
|
||||
// video tag loop attribute.
|
||||
// But QtWebKit doesn't support this. Neither does it support the
|
||||
// onended event, hence the setInterval()
|
||||
// In addition, setting the currentTime attribute to zero to restart
|
||||
// the video raises an INDEX_SIZE_ERROR: DOM Exception 1
|
||||
// To complicate it further, sometimes vid.currentTime stops
|
||||
// slightly short of vid.duration and vid.ended is intermittent!
|
||||
//
|
||||
// Note, currently the background may go black between loops. Not
|
||||
// desirable. Need to investigate using two <video>'s, and hiding/
|
||||
// preloading one, and toggle between the two when looping.
|
||||
|
||||
if(current_video=='1'){
|
||||
var vid = document.getElementById('video1');
|
||||
var vid2 = document.getElementById('video2');
|
||||
} else {
|
||||
var vid = document.getElementById('video2');
|
||||
var vid2 = document.getElementById('video1');
|
||||
}
|
||||
var video = document.getElementById('video');
|
||||
if(volume != null){
|
||||
vid.volume = volume;
|
||||
vid2.volume = volume;
|
||||
video.volume = volume;
|
||||
}
|
||||
switch(state){
|
||||
case 'init':
|
||||
vid.src = 'file:///' + path;
|
||||
vid2.src = 'file:///' + path;
|
||||
if(loop == null) loop = false;
|
||||
vid.looping = loop;
|
||||
vid2.looping = loop;
|
||||
vid.load();
|
||||
break;
|
||||
case 'load':
|
||||
vid2.style.visibility = 'hidden';
|
||||
vid2.load();
|
||||
video.src = 'file:///' + path;
|
||||
if(loop == true) {
|
||||
video.loop = true;
|
||||
}
|
||||
video.load();
|
||||
break;
|
||||
case 'play':
|
||||
vid.play();
|
||||
if(vid.looping){
|
||||
video_timer = setInterval(
|
||||
function() {
|
||||
show_video('poll');
|
||||
}, 200);
|
||||
}
|
||||
video.play();
|
||||
break;
|
||||
case 'pause':
|
||||
if(video_timer!=null){
|
||||
clearInterval(video_timer);
|
||||
video_timer = null;
|
||||
}
|
||||
vid.pause();
|
||||
video.pause();
|
||||
break;
|
||||
case 'stop':
|
||||
show_video('pause');
|
||||
vid.currentTime = 0;
|
||||
break;
|
||||
case 'poll':
|
||||
if(vid.ended||vid.currentTime+0.2>vid.duration)
|
||||
show_video('swap');
|
||||
break;
|
||||
case 'swap':
|
||||
show_video('pause');
|
||||
if(current_video=='1')
|
||||
current_video = '2';
|
||||
else
|
||||
current_video = '1';
|
||||
show_video('load');
|
||||
show_video('play');
|
||||
show_video('setVisible',null,null,null,'visible');
|
||||
video.currentTime = 0;
|
||||
break;
|
||||
case 'close':
|
||||
show_video('stop');
|
||||
vid.src = '';
|
||||
vid2.src = '';
|
||||
video.src = '';
|
||||
break;
|
||||
case 'length':
|
||||
return vid.duration;
|
||||
case 'currentTime':
|
||||
return vid.currentTime;
|
||||
return video.duration;
|
||||
case 'current_time':
|
||||
return video.currentTime;
|
||||
case 'seek':
|
||||
// doesnt work currently
|
||||
vid.currentTime = varVal;
|
||||
video.currentTime = variable_value;
|
||||
break;
|
||||
case 'isEnded':
|
||||
return vid.ended;
|
||||
return video.ended;
|
||||
case 'setVisible':
|
||||
vid.style.visibility = varVal;
|
||||
video.style.visibility = variable_value;
|
||||
break;
|
||||
case 'setBackBoard':
|
||||
var back = document.getElementById('videobackboard');
|
||||
back.style.visibility = varVal;
|
||||
back.style.visibility = variable_value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -158,10 +102,7 @@ VIDEO_JS = u"""
|
|||
|
||||
VIDEO_HTML = u"""
|
||||
<div id="videobackboard" class="size" style="visibility:hidden"></div>
|
||||
<video id="video1" class="size" style="visibility:hidden" autobuffer preload>
|
||||
</video>
|
||||
<video id="video2" class="size" style="visibility:hidden" autobuffer preload>
|
||||
</video>
|
||||
<video id="video" class="size" style="visibility:hidden" autobuffer preload></video>
|
||||
"""
|
||||
|
||||
FLASH_CSS = u"""
|
||||
|
@ -173,25 +114,21 @@ FLASH_CSS = u"""
|
|||
FLASH_JS = u"""
|
||||
function getFlashMovieObject(movieName)
|
||||
{
|
||||
if (window.document[movieName])
|
||||
{
|
||||
if (window.document[movieName]){
|
||||
return window.document[movieName];
|
||||
}
|
||||
if (document.embeds && document.embeds[movieName])
|
||||
if (document.embeds && document.embeds[movieName]){
|
||||
return document.embeds[movieName];
|
||||
}
|
||||
}
|
||||
|
||||
function show_flash(state, path, volume, varVal){
|
||||
function show_flash(state, path, volume, variable_value){
|
||||
var text = document.getElementById('flash');
|
||||
var flashMovie = getFlashMovieObject("OpenLPFlashMovie");
|
||||
var src = "src = 'file:///" + path + "'";
|
||||
var view_parm = " wmode='opaque'" +
|
||||
" width='100%%'" +
|
||||
" height='100%%'";
|
||||
var swf_parm = " name='OpenLPFlashMovie'" +
|
||||
" autostart='true' loop='false' play='true'" +
|
||||
" hidden='false' swliveconnect='true' allowscriptaccess='always'" +
|
||||
" volume='" + volume + "'";
|
||||
var view_parm = " wmode='opaque'" + " width='100%%'" + " height='100%%'";
|
||||
var swf_parm = " name='OpenLPFlashMovie'" + " autostart='true' loop='false' play='true'" +
|
||||
" hidden='false' swliveconnect='true' allowscriptaccess='always'" + " volume='" + volume + "'";
|
||||
|
||||
switch(state){
|
||||
case 'load':
|
||||
|
@ -217,15 +154,16 @@ FLASH_JS = u"""
|
|||
break;
|
||||
case 'length':
|
||||
return flashMovie.TotalFrames();
|
||||
case 'currentTime':
|
||||
case 'current_time':
|
||||
return flashMovie.CurrentFrame();
|
||||
case 'seek':
|
||||
// flashMovie.GotoFrame(varVal);
|
||||
// flashMovie.GotoFrame(variable_value);
|
||||
break;
|
||||
case 'isEnded':
|
||||
return false;//TODO check flash end
|
||||
//TODO check flash end
|
||||
return false;
|
||||
case 'setVisible':
|
||||
text.style.visibility = varVal;
|
||||
text.style.visibility = variable_value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -338,7 +276,7 @@ class WebkitPlayer(MediaPlayer):
|
|||
controller.media_info.is_flash = True
|
||||
js = u'show_flash("load","%s");' % (path.replace(u'\\', u'\\\\'))
|
||||
else:
|
||||
js = u'show_video("init", "%s", %s, %s);' % (path.replace(u'\\', u'\\\\'), str(vol), loop)
|
||||
js = u'show_video("load", "%s", %s, %s);' % (path.replace(u'\\', u'\\\\'), str(vol), loop)
|
||||
display.frame.evaluateJavaScript(js)
|
||||
return True
|
||||
|
||||
|
@ -447,25 +385,25 @@ class WebkitPlayer(MediaPlayer):
|
|||
"""
|
||||
controller = display.controller
|
||||
if controller.media_info.is_flash:
|
||||
currentTime = display.frame.evaluateJavaScript(u'show_flash("currentTime");')
|
||||
current_time = display.frame.evaluateJavaScript(u'show_flash("current_time");')
|
||||
length = display.frame.evaluateJavaScript(u'show_flash("length");')
|
||||
else:
|
||||
if display.frame.evaluateJavaScript(u'show_video("isEnded");'):
|
||||
self.stop(display)
|
||||
currentTime = display.frame.evaluateJavaScript(u'show_video("currentTime");')
|
||||
current_time = display.frame.evaluateJavaScript(u'show_video("current_time");')
|
||||
# check if conversion was ok and value is not 'NaN'
|
||||
if currentTime and currentTime != float('inf'):
|
||||
currentTime = int(currentTime * 1000)
|
||||
if current_time and current_time != float('inf'):
|
||||
current_time = int(current_time * 1000)
|
||||
length = display.frame.evaluateJavaScript(u'show_video("length");')
|
||||
# check if conversion was ok and value is not 'NaN'
|
||||
if length and length != float('inf'):
|
||||
length = int(length * 1000)
|
||||
if currentTime > 0:
|
||||
if current_time:
|
||||
controller.media_info.length = length
|
||||
controller.seek_slider.setMaximum(length)
|
||||
if not controller.seek_slider.isSliderDown():
|
||||
controller.seek_slider.blockSignals(True)
|
||||
controller.seek_slider.setSliderPosition(currentTime)
|
||||
controller.seek_slider.setSliderPosition(current_time)
|
||||
controller.seek_slider.blockSignals(False)
|
||||
|
||||
def get_info(self):
|
||||
|
|
|
@ -35,6 +35,7 @@ import logging
|
|||
import os
|
||||
import shutil
|
||||
import zipfile
|
||||
import json
|
||||
from tempfile import mkstemp
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
|
@ -458,7 +459,7 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
|||
path_file_name = unicode(self.file_name())
|
||||
path, file_name = os.path.split(path_file_name)
|
||||
base_name = os.path.splitext(file_name)[0]
|
||||
service_file_name = '%s.osd' % base_name
|
||||
service_file_name = '%s.osj' % base_name
|
||||
log.debug(u'ServiceManager.save_file - %s', path_file_name)
|
||||
Settings().setValue(self.main_window.service_manager_settings_section + u'/last directory', path)
|
||||
service = []
|
||||
|
@ -512,7 +513,7 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
|||
file_size = os.path.getsize(file_item)
|
||||
total_size += file_size
|
||||
log.debug(u'ServiceManager.save_file - ZIP contents size is %i bytes' % total_size)
|
||||
service_content = cPickle.dumps(service)
|
||||
service_content = json.dumps(service)
|
||||
# Usual Zip file cannot exceed 2GiB, file with Zip64 cannot be extracted using unzip in UNIX.
|
||||
allow_zip_64 = (total_size > 2147483648 + len(service_content))
|
||||
log.debug(u'ServiceManager.save_file - allowZip64 is %s' % allow_zip_64)
|
||||
|
@ -572,7 +573,7 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
|||
path_file_name = unicode(self.file_name())
|
||||
path, file_name = os.path.split(path_file_name)
|
||||
base_name = os.path.splitext(file_name)[0]
|
||||
service_file_name = '%s.osd' % base_name
|
||||
service_file_name = '%s.osj' % base_name
|
||||
log.debug(u'ServiceManager.save_file - %s', path_file_name)
|
||||
Settings().setValue(self.main_window.service_manager_settings_section + u'/last directory', path)
|
||||
service = []
|
||||
|
@ -585,7 +586,7 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
|||
#TODO: check for file item on save.
|
||||
service.append({u'serviceitem': service_item})
|
||||
self.main_window.increment_progress_bar()
|
||||
service_content = cPickle.dumps(service)
|
||||
service_content = json.dumps(service)
|
||||
zip_file = None
|
||||
success = True
|
||||
self.main_window.increment_progress_bar()
|
||||
|
@ -698,11 +699,14 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
|||
log.debug(u'Extract file: %s', osfile)
|
||||
zip_info.filename = osfile
|
||||
zip_file.extract(zip_info, self.servicePath)
|
||||
if osfile.endswith(u'osd'):
|
||||
if osfile.endswith(u'osj') or osfile.endswith(u'osd'):
|
||||
p_file = os.path.join(self.servicePath, osfile)
|
||||
if 'p_file' in locals():
|
||||
file_to = open(p_file, u'r')
|
||||
items = cPickle.load(file_to)
|
||||
if p_file.endswith(u'osj'):
|
||||
items = json.load(file_to)
|
||||
else:
|
||||
items = cPickle.load(file_to)
|
||||
file_to.close()
|
||||
self.new_file()
|
||||
self.set_file_name(file_name)
|
||||
|
|
|
@ -129,11 +129,18 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog):
|
|||
continue
|
||||
item = QtGui.QTreeWidgetItem([category.name])
|
||||
for action in category.actions:
|
||||
actionText = REMOVE_AMPERSAND.sub('', action.text())
|
||||
actionItem = QtGui.QTreeWidgetItem([actionText])
|
||||
actionItem.setIcon(0, action.icon())
|
||||
actionItem.setData(0, QtCore.Qt.UserRole, action)
|
||||
item.addChild(actionItem)
|
||||
action_text = REMOVE_AMPERSAND.sub('', action.text())
|
||||
action_item = QtGui.QTreeWidgetItem([action_text])
|
||||
action_item.setIcon(0, action.icon())
|
||||
action_item.setData(0, QtCore.Qt.UserRole, action)
|
||||
tool_tip_text = action.toolTip()
|
||||
# Only display tool tips if they are helpful.
|
||||
if tool_tip_text != action_text:
|
||||
# Display the tool tip in all three colums.
|
||||
action_item.setToolTip(0, tool_tip_text)
|
||||
action_item.setToolTip(1, tool_tip_text)
|
||||
action_item.setToolTip(2, tool_tip_text)
|
||||
item.addChild(action_item)
|
||||
self.treeWidget.addTopLevelItem(item)
|
||||
item.setExpanded(True)
|
||||
self.refreshShortcutList()
|
||||
|
|
|
@ -121,7 +121,7 @@ class SlideController(DisplayController):
|
|||
self.service_item = None
|
||||
self.slide_limits = None
|
||||
self.update_slide_limits()
|
||||
self.panel = QtGui.QWidget(parent.controlSplitter)
|
||||
self.panel = QtGui.QWidget(parent.control_splitter)
|
||||
self.slideList = {}
|
||||
self.slide_count = 0
|
||||
self.slide_image = None
|
||||
|
@ -566,8 +566,7 @@ class SlideController(DisplayController):
|
|||
max_width = self.preview_frame.width() - self.grid.margin() * 2
|
||||
self.slide_preview.setFixedSize(QtCore.QSize(max_width, max_width / self.ratio))
|
||||
self.preview_display.setFixedSize(QtCore.QSize(max_width, max_width / self.ratio))
|
||||
self.preview_display.screen = {
|
||||
u'size': self.preview_display.geometry()}
|
||||
self.preview_display.screen = {u'size': self.preview_display.geometry()}
|
||||
self.on_controller_size_changed(self.controller.width())
|
||||
|
||||
def on_controller_size_changed(self, width):
|
||||
|
@ -592,7 +591,7 @@ class SlideController(DisplayController):
|
|||
"""
|
||||
request = self.sender().text()
|
||||
slide_no = self.slideList[request]
|
||||
width = self.main_window.controlSplitter.sizes()[self.split]
|
||||
width = self.main_window.control_splitter.sizes()[self.split]
|
||||
self.preview_widget.replace_service_item(self.service_item, width, slide_no)
|
||||
self.slide_selected()
|
||||
|
||||
|
@ -697,9 +696,8 @@ class SlideController(DisplayController):
|
|||
|
||||
def add_service_manager_item(self, item, slide_no):
|
||||
"""
|
||||
Method to install the service item into the controller and
|
||||
request the correct toolbar for the plugin.
|
||||
Called by ServiceManager
|
||||
Method to install the service item into the controller and request the correct toolbar for the plugin. Called by
|
||||
:class:`~openlp.core.ui.ServiceManager`
|
||||
"""
|
||||
log.debug(u'add_service_manager_item live = %s' % self.is_live)
|
||||
# If no valid slide number is specified we take the first one, but we remember the initial value to see if we
|
||||
|
@ -724,8 +722,7 @@ class SlideController(DisplayController):
|
|||
|
||||
def _process_item(self, service_item, slideno):
|
||||
"""
|
||||
Loads a ServiceItem into the system from ServiceManager
|
||||
Display the slide number passed
|
||||
Loads a ServiceItem into the system from ServiceManager. Display the slide number passed.
|
||||
"""
|
||||
log.debug(u'processManagerItem live = %s' % self.is_live)
|
||||
self.on_stop_loop()
|
||||
|
@ -734,7 +731,8 @@ class SlideController(DisplayController):
|
|||
self.service_item = copy.copy(service_item)
|
||||
if old_item and self.is_live and old_item.is_capable(ItemCapabilities.ProvidesOwnDisplay):
|
||||
self._reset_blank()
|
||||
Registry().execute(u'%s_start' % service_item.name.lower(), [service_item, self.is_live, self.hide_mode(), slideno])
|
||||
Registry().execute(
|
||||
u'%s_start' % service_item.name.lower(), [service_item, self.is_live, self.hide_mode(), slideno])
|
||||
self.slideList = {}
|
||||
if self.is_live:
|
||||
self.song_menu.menu().clear()
|
||||
|
@ -759,7 +757,7 @@ class SlideController(DisplayController):
|
|||
self.display.audio_player.play()
|
||||
self.set_audio_items_visibility(True)
|
||||
row = 0
|
||||
width = self.main_window.controlSplitter.sizes()[self.split]
|
||||
width = self.main_window.control_splitter.sizes()[self.split]
|
||||
for framenumber, frame in enumerate(self.service_item.get_frames()):
|
||||
if self.service_item.is_text():
|
||||
if frame[u'verseTag']:
|
||||
|
|
|
@ -53,27 +53,25 @@ log = logging.getLogger(__name__)
|
|||
|
||||
class AppLocation(object):
|
||||
"""
|
||||
The :class:`AppLocation` class is a static class which retrieves a
|
||||
directory based on the directory type.
|
||||
The :class:`AppLocation` class is a static class which retrieves a directory based on the directory type.
|
||||
"""
|
||||
AppDir = 1
|
||||
ConfigDir = 2
|
||||
DataDir = 3
|
||||
PluginsDir = 4
|
||||
VersionDir = 5
|
||||
CacheDir = 6
|
||||
LanguageDir = 7
|
||||
DataDir = 2
|
||||
PluginsDir = 3
|
||||
VersionDir = 4
|
||||
CacheDir = 5
|
||||
LanguageDir = 6
|
||||
|
||||
# Base path where data/config/cache dir is located
|
||||
BaseDir = None
|
||||
|
||||
@staticmethod
|
||||
def get_directory(dir_type=1):
|
||||
def get_directory(dir_type=AppDir):
|
||||
"""
|
||||
Return the appropriate directory according to the directory type.
|
||||
|
||||
``dir_type``
|
||||
The directory type you want, for instance the data directory.
|
||||
The directory type you want, for instance the data directory. Default *AppLocation.AppDir*
|
||||
"""
|
||||
if dir_type == AppLocation.AppDir:
|
||||
return _get_frozen_path(os.path.abspath(os.path.split(sys.argv[0])[0]), os.path.split(openlp.__file__)[0])
|
||||
|
@ -161,16 +159,13 @@ def _get_os_dir_path(dir_type):
|
|||
return os.path.join(unicode(os.getenv(u'HOME'), encoding), u'Library', u'Application Support', u'openlp')
|
||||
else:
|
||||
if dir_type == AppLocation.LanguageDir:
|
||||
prefixes = [u'/usr/local', u'/usr']
|
||||
for prefix in prefixes:
|
||||
for prefix in [u'/usr/local', u'/usr']:
|
||||
directory = os.path.join(prefix, u'share', u'openlp')
|
||||
if os.path.exists(directory):
|
||||
return directory
|
||||
return os.path.join(u'/usr', u'share', u'openlp')
|
||||
if XDG_BASE_AVAILABLE:
|
||||
if dir_type == AppLocation.ConfigDir:
|
||||
return os.path.join(unicode(BaseDirectory.xdg_config_home, encoding), u'openlp')
|
||||
elif dir_type == AppLocation.DataDir:
|
||||
if dir_type == AppLocation.DataDir:
|
||||
return os.path.join(unicode(BaseDirectory.xdg_data_home, encoding), u'openlp')
|
||||
elif dir_type == AppLocation.CacheDir:
|
||||
return os.path.join(unicode(BaseDirectory.xdg_cache_home, encoding), u'openlp')
|
||||
|
|
|
@ -37,28 +37,13 @@ __version__ = 1
|
|||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def upgrade_setup(metadata):
|
||||
"""
|
||||
Set up the latest revision all tables, with reflection, needed for the
|
||||
upgrade process. If you want to drop a table, you need to remove it from
|
||||
here, and add it to your upgrade function.
|
||||
"""
|
||||
# Don't define the "metadata" table, as the upgrade mechanism already
|
||||
# defines it.
|
||||
tables = {
|
||||
u'book': Table(u'book', metadata, autoload=True),
|
||||
u'verse': Table(u'verse', metadata, autoload=True)
|
||||
}
|
||||
return tables
|
||||
|
||||
|
||||
def upgrade_1(session, metadata, tables):
|
||||
def upgrade_1(session, metadata):
|
||||
"""
|
||||
Version 1 upgrade.
|
||||
|
||||
This upgrade renames a number of keys to a single naming convention.
|
||||
"""
|
||||
metadata_table = metadata.tables[u'metadata']
|
||||
metadata_table = Table(u'metadata', metadata, autoload=True)
|
||||
# Copy "Version" to "name" ("version" used by upgrade system)
|
||||
# TODO: Clean up in a subsequent release of OpenLP (like 2.0 final)
|
||||
session.execute(insert(metadata_table).values(
|
||||
|
|
|
@ -100,8 +100,7 @@ class EditCustomForm(QtGui.QDialog, Ui_CustomEditDialog):
|
|||
self.credit_edit.setText(self.custom_slide.credits)
|
||||
custom_XML = CustomXMLParser(self.custom_slide.text)
|
||||
slide_list = custom_XML.get_verses()
|
||||
for slide in slide_list:
|
||||
self.slide_list_view.addItem(slide[1])
|
||||
self.slide_list_view.addItems([slide[1] for slide in slide_list])
|
||||
theme = self.custom_slide.theme_name
|
||||
find_and_set_in_combo_box(self.theme_combo_box, theme)
|
||||
self.title_edit.setFocus()
|
||||
|
|
|
@ -41,14 +41,19 @@ class CustomSlide(BaseModel):
|
|||
"""
|
||||
CustomSlide model
|
||||
"""
|
||||
# By default sort the customs by its title considering language specific
|
||||
# characters.
|
||||
# By default sort the customs by its title considering language specific characters.
|
||||
def __lt__(self, other):
|
||||
return get_locale_key(self.title) < get_locale_key(other.title)
|
||||
|
||||
def __eq__(self, other):
|
||||
return get_locale_key(self.title) == get_locale_key(other.title)
|
||||
|
||||
def __hash__(self):
|
||||
"""
|
||||
Return the hash for a custom slide.
|
||||
"""
|
||||
return self.id
|
||||
|
||||
|
||||
def init_schema(url):
|
||||
"""
|
||||
|
|
|
@ -174,13 +174,13 @@ class PPTViewer(QtGui.QWidget):
|
|||
int(self.widthEdit.text()), int(self.heightEdit.text()))
|
||||
filename = str(self.pptEdit.text().replace(u'/', u'\\'))
|
||||
folder = str(self.folderEdit.text().replace(u'/', u'\\'))
|
||||
print filename, folder
|
||||
print(filename, folder)
|
||||
self.pptid = self.pptdll.OpenPPT(filename, None, rect, folder)
|
||||
print u'id: ' + unicode(self.pptid)
|
||||
print(u'id: ' + unicode(self.pptid))
|
||||
if oldid >= 0:
|
||||
self.pptdll.ClosePPT(oldid);
|
||||
slides = self.pptdll.GetSlideCount(self.pptid)
|
||||
print u'slidecount: ' + unicode(slides)
|
||||
print(u'slidecount: ' + unicode(slides))
|
||||
self.total.setNum(self.pptdll.GetSlideCount(self.pptid))
|
||||
self.updateCurrSlide()
|
||||
|
||||
|
@ -188,14 +188,14 @@ class PPTViewer(QtGui.QWidget):
|
|||
if self.pptid < 0:
|
||||
return
|
||||
slide = unicode(self.pptdll.GetCurrentSlide(self.pptid))
|
||||
print u'currslide: ' + slide
|
||||
print(u'currslide: ' + slide)
|
||||
self.slideEdit.setText(slide)
|
||||
app.processEvents()
|
||||
|
||||
def gotoClick(self):
|
||||
if self.pptid < 0:
|
||||
return
|
||||
print self.slideEdit.text()
|
||||
print(self.slideEdit.text())
|
||||
self.pptdll.GotoSlide(self.pptid, int(self.slideEdit.text()))
|
||||
self.updateCurrSlide()
|
||||
app.processEvents()
|
||||
|
@ -207,7 +207,7 @@ class PPTViewer(QtGui.QWidget):
|
|||
if __name__ == '__main__':
|
||||
pptdll = cdll.LoadLibrary(r'pptviewlib.dll')
|
||||
pptdll.SetDebug(1)
|
||||
print u'Begin...'
|
||||
print(u'Begin...')
|
||||
app = QtGui.QApplication(sys.argv)
|
||||
window = PPTViewer()
|
||||
window.pptdll = pptdll
|
||||
|
|
|
@ -275,7 +275,6 @@ class Ui_EditSongDialog(object):
|
|||
self.bottom_layout.setObjectName(u'bottom_layout')
|
||||
self.warning_label = QtGui.QLabel(edit_song_dialog)
|
||||
self.warning_label.setObjectName(u'warning_label')
|
||||
self.warning_label.setVisible(False)
|
||||
self.bottom_layout.addWidget(self.warning_label)
|
||||
self.button_box = create_button_box(edit_song_dialog, u'button_box', [u'cancel', u'save'])
|
||||
self.bottom_layout.addWidget(self.button_box)
|
||||
|
@ -323,8 +322,10 @@ class Ui_EditSongDialog(object):
|
|||
self.from_media_button.setText(translate('SongsPlugin.EditSongForm', 'Add &Media'))
|
||||
self.audio_remove_button.setText(translate('SongsPlugin.EditSongForm', '&Remove'))
|
||||
self.audio_remove_all_button.setText(translate('SongsPlugin.EditSongForm', 'Remove &All'))
|
||||
self.warning_label.setText(
|
||||
translate('SongsPlugin.EditSongForm', '<strong>Warning:</strong> Not all of the verses are in use.'))
|
||||
self.not_all_verses_used_warning = \
|
||||
translate('SongsPlugin.EditSongForm', '<strong>Warning:</strong> Not all of the verses are in use.')
|
||||
self.no_verse_order_entered_warning = \
|
||||
translate('SongsPlugin.EditSongForm', '<strong>Warning:</strong> You have not entered a verse order.')
|
||||
|
||||
|
||||
def create_combo_box(parent, name):
|
||||
|
|
|
@ -456,6 +456,8 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
|
|||
self.title_edit.setFocus()
|
||||
# Hide or show the preview button.
|
||||
self.preview_button.setVisible(preview)
|
||||
# Check if all verse tags are used.
|
||||
self.on_verse_order_text_changed(self.verse_order_edit.text())
|
||||
|
||||
def tag_rows(self):
|
||||
"""
|
||||
|
@ -683,21 +685,33 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
|
|||
self.verse_edit_button.setEnabled(False)
|
||||
self.verse_delete_button.setEnabled(False)
|
||||
|
||||
|
||||
def on_verse_order_text_changed(self, text):
|
||||
verses = []
|
||||
verse_names = []
|
||||
order = self._extract_verse_order(text)
|
||||
"""
|
||||
Checks if the verse order is complete or missing. Shows a error message according to the state of the verse
|
||||
order.
|
||||
|
||||
``text``
|
||||
The text of the verse order edit (ignored).
|
||||
"""
|
||||
# Extract all verses which were used in the order.
|
||||
verses_in_order = self._extract_verse_order(self.verse_order_edit.text())
|
||||
# Find the verses which were not used in the order.
|
||||
verses_not_used = []
|
||||
for index in range(self.verse_list_widget.rowCount()):
|
||||
verse = self.verse_list_widget.item(index, 0)
|
||||
verse = verse.data(QtCore.Qt.UserRole)
|
||||
if verse not in verse_names:
|
||||
verses.append(verse)
|
||||
verse_names.append(u'%s%s' % (VerseType.translated_tag(verse[0]), verse[1:]))
|
||||
verses_not_used = []
|
||||
for verse in verses:
|
||||
if not verse in order:
|
||||
if verse not in verses_in_order:
|
||||
verses_not_used.append(verse)
|
||||
self.warning_label.setVisible(len(verses_not_used) > 0)
|
||||
# Set the label text.
|
||||
label_text = u''
|
||||
# No verse order was entered.
|
||||
if not verses_in_order:
|
||||
label_text = self.no_verse_order_entered_warning
|
||||
# The verse order does not contain all verses.
|
||||
elif verses_not_used:
|
||||
label_text = self.not_all_verses_used_warning
|
||||
self.warning_label.setText(label_text)
|
||||
|
||||
def on_copyright_insert_button_triggered(self):
|
||||
text = self.copyright_edit.text()
|
||||
|
|
|
@ -74,7 +74,13 @@ if os.name == u'nt':
|
|||
HAS_MEDIASHOUT = True
|
||||
except ImportError:
|
||||
log.exception('Error importing %s', 'MediaShoutImport')
|
||||
|
||||
HAS_WORSHIPCENTERPRO = False
|
||||
if os.name == u'nt':
|
||||
try:
|
||||
from worshipcenterproimport import WorshipCenterProImport
|
||||
HAS_WORSHIPCENTERPRO = True
|
||||
except ImportError:
|
||||
log.exception('Error importing %s', 'WorshipCenterProImport')
|
||||
|
||||
class SongFormatSelect(object):
|
||||
"""
|
||||
|
@ -157,7 +163,8 @@ class SongFormat(object):
|
|||
SongsOfFellowship = 14
|
||||
SundayPlus = 15
|
||||
WordsOfWorship = 16
|
||||
ZionWorx = 17
|
||||
WorshipCenterPro = 17
|
||||
ZionWorx = 18
|
||||
|
||||
# Set optional attribute defaults
|
||||
__defaults__ = {
|
||||
|
@ -300,6 +307,16 @@ class SongFormat(object):
|
|||
u'filter': u'%s (*.wsg *.wow-song)' %
|
||||
translate('SongsPlugin.ImportWizardForm', 'Words Of Worship Song Files')
|
||||
},
|
||||
WorshipCenterPro: {
|
||||
u'name': u'WorshipCenter Pro',
|
||||
u'prefix': u'worshipCenterPro',
|
||||
u'canDisable': True,
|
||||
u'selectMode': SongFormatSelect.SingleFile,
|
||||
u'filter': u'%s (*.mdb)' % translate('SongsPlugin.ImportWizardForm', 'WorshipCenter Pro Song Files'),
|
||||
u'disabledLabelText': translate('SongsPlugin.ImportWizardForm',
|
||||
'The WorshipCenter Pro importer is only supported on Windows. It has been disabled due to a missing '
|
||||
'Python module. If you want to use this importer, you will need to install the "pyodbc" module.')
|
||||
},
|
||||
ZionWorx: {
|
||||
u'class': ZionWorxImport,
|
||||
u'name': u'ZionWorx',
|
||||
|
@ -336,6 +353,7 @@ class SongFormat(object):
|
|||
SongFormat.SongsOfFellowship,
|
||||
SongFormat.SundayPlus,
|
||||
SongFormat.WordsOfWorship,
|
||||
SongFormat.WorshipCenterPro,
|
||||
SongFormat.ZionWorx
|
||||
]
|
||||
|
||||
|
@ -385,5 +403,9 @@ if HAS_OOO:
|
|||
SongFormat.set(SongFormat.MediaShout, u'availability', HAS_MEDIASHOUT)
|
||||
if HAS_MEDIASHOUT:
|
||||
SongFormat.set(SongFormat.MediaShout, u'class', MediaShoutImport)
|
||||
SongFormat.set(SongFormat.WorshipCenterPro, u'availability', HAS_WORSHIPCENTERPRO)
|
||||
if HAS_WORSHIPCENTERPRO:
|
||||
SongFormat.set(SongFormat.WorshipCenterPro, u'class', WorshipCenterProImport)
|
||||
|
||||
|
||||
__all__ = [u'SongFormat', u'SongFormatSelect']
|
||||
|
|
|
@ -1,131 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
|
||||
|
||||
###############################################################################
|
||||
# OpenLP - Open Source Lyrics Projection #
|
||||
# --------------------------------------------------------------------------- #
|
||||
# Copyright (c) 2008-2013 Raoul Snyman #
|
||||
# Portions copyright (c) 2008-2013 Tim Bentley, Gerald Britton, Jonathan #
|
||||
# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, #
|
||||
# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. #
|
||||
# Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru, #
|
||||
# Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, #
|
||||
# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Dave Warnock, #
|
||||
# Frode Woldsund, Martin Zibricky, Patrick Zimmermann #
|
||||
# --------------------------------------------------------------------------- #
|
||||
# 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 #
|
||||
###############################################################################
|
||||
|
||||
from openlp.plugins.songs.lib.opensongimport import OpenSongImport
|
||||
from openlp.core.lib.db import Manager
|
||||
from openlp.plugins.songs.lib.db import init_schema
|
||||
|
||||
import logging
|
||||
LOG_FILENAME = 'test.log'
|
||||
logging.basicConfig(filename=LOG_FILENAME,level=logging.INFO)
|
||||
|
||||
# Stubs to replace the UI functions for raw testing
|
||||
class wizard_stub:
|
||||
def __init__(self):
|
||||
self.progressBar=progbar_stub()
|
||||
def incrementProgressBar(self, str):
|
||||
pass
|
||||
class progbar_stub:
|
||||
def __init__(self):
|
||||
pass
|
||||
def setMaximum(self, arg):
|
||||
pass
|
||||
|
||||
def test():
|
||||
manager = Manager(u'songs', init_schema)
|
||||
o = OpenSongImport(manager, filenames=[u'test.opensong'])
|
||||
o.import_wizard = wizard_stub()
|
||||
o.commit = False
|
||||
o.do_import()
|
||||
o.print_song()
|
||||
assert o.copyright == u'2010 Martin Thompson'
|
||||
assert o.authors == [u'MartiÑ Thómpson', u'Martin2 Thómpson']
|
||||
assert o.title == u'Martins Test'
|
||||
assert o.alternate_title == u''
|
||||
assert o.song_number == u'1'
|
||||
assert [u'C1', u'Chorus 1'] in o.verses
|
||||
assert [u'C2', u'Chorus 2'] in o.verses
|
||||
assert not [u'C3', u'Chorus 3'] in o.verses
|
||||
assert [u'B1', u'Bridge 1\nBridge 1 line 2'] in o.verses
|
||||
assert [u'V1', u'v1 Line 1\nV1 Line 2'] in o.verses
|
||||
assert [u'V2', u'v2 Line 1\nV2 Line 2'] in o.verses
|
||||
assert [u'V3A', u'V3 Line 1\nV3 Line 2'] in o.verses
|
||||
assert [u'RAP1', u'Rap 1 Line 1\nRap 1 Line 2'] in o.verses
|
||||
assert [u'RAP2', u'Rap 2 Line 1\nRap 2 Line 2'] in o.verses
|
||||
assert [u'RAP3', u'Rap 3 Line 1\nRap 3 Line 2'] in o.verses
|
||||
assert [u'X1', u'Unreferenced verse line 1'] in o.verses
|
||||
assert o.verse_order_list == [u'V1', u'C1', u'V2', u'C2', u'V3A', u'B1', u'V1', u'T1', u'RAP1', u'RAP2', u'RAP3']
|
||||
assert o.ccli_number == u'Blah'
|
||||
assert o.topics == [u'TestTheme', u'TestAltTheme']
|
||||
|
||||
o.filenames = [u'test.opensong.zip']
|
||||
o.set_defaults()
|
||||
o.do_import()
|
||||
o.print_song()
|
||||
assert o.copyright == u'2010 Martin Thompson'
|
||||
assert o.authors == [u'MartiÑ Thómpson']
|
||||
assert o.title == u'Martins Test'
|
||||
assert o.alternate_title == u''
|
||||
assert o.song_number == u'1'
|
||||
assert [u'B1', u'Bridge 1\nBridge 1 line 2'] in o.verses
|
||||
assert [u'C1', u'Chorus 1'] in o.verses
|
||||
assert [u'C2', u'Chorus 2'] in o.verses
|
||||
assert not [u'C3', u'Chorus 3'] in o.verses
|
||||
assert [u'V1', u'v1 Line 1\nV1 Line 2'] in o.verses
|
||||
assert [u'V2', u'v2 Line 1\nV2 Line 2'] in o.verses
|
||||
print o.verse_order_list
|
||||
assert o.verse_order_list == [u'V1', u'C1', u'V2', u'C2', u'V3', u'B1', u'V1']
|
||||
|
||||
o.filenames = [u'test2.opensong']
|
||||
o.set_defaults()
|
||||
o.do_import()
|
||||
o.print_song()
|
||||
assert o.copyright == u'2010 Martin Thompson'
|
||||
assert o.authors == [u'Martin Thompson']
|
||||
assert o.title == u'Martins 2nd Test'
|
||||
assert o.alternate_title == u''
|
||||
assert o.song_number == u'2'
|
||||
print o.verses
|
||||
assert [u'B1', u'Bridge 1\nBridge 1 line 2'] in o.verses
|
||||
assert [u'C1', u'Chorus 1'] in o.verses
|
||||
assert [u'C2', u'Chorus 2'] in o.verses
|
||||
assert not [u'C3', u'Chorus 3'] in o.verses
|
||||
assert [u'V1', u'v1 Line 1\nV1 Line 2'] in o.verses
|
||||
assert [u'V2', u'v2 Line 1\nV2 Line 2'] in o.verses
|
||||
print o.verse_order_list
|
||||
assert o.verse_order_list == [u'V1', u'V2', u'B1', u'C1', u'C2']
|
||||
|
||||
o.filenames = [u'test3.opensong']
|
||||
o.set_defaults()
|
||||
o.do_import()
|
||||
o.print_song()
|
||||
assert o.copyright == u'2010'
|
||||
assert o.authors == [u'Martin Thompson']
|
||||
assert o.title == u'Test single verse'
|
||||
assert o.alternate_title == u''
|
||||
assert o.ccli_number == u'123456'
|
||||
assert o.verse_order_list == [u'V1']
|
||||
assert o.topics == [u'Worship: Declaration']
|
||||
print o.verses[0]
|
||||
assert [u'V1', u'Line 1\nLine 2'] in o.verses
|
||||
|
||||
print "Tests passed"
|
||||
|
||||
if __name__ == "__main__":
|
||||
test()
|
|
@ -31,31 +31,15 @@ The :mod:`upgrade` module provides a way for the database and schema that is the
|
|||
backend for the Songs plugin
|
||||
"""
|
||||
|
||||
from sqlalchemy import Column, Table, types
|
||||
from sqlalchemy.sql.expression import func
|
||||
from migrate.changeset.constraint import ForeignKeyConstraint
|
||||
from sqlalchemy import Column, types
|
||||
from sqlalchemy.sql.expression import func, false, null, text
|
||||
|
||||
from openlp.core.lib.db import get_upgrade_op
|
||||
|
||||
__version__ = 3
|
||||
|
||||
def upgrade_setup(metadata):
|
||||
"""
|
||||
Set up the latest revision all tables, with reflection, needed for the
|
||||
upgrade process. If you want to drop a table, you need to remove it from
|
||||
here, and add it to your upgrade function.
|
||||
"""
|
||||
tables = {
|
||||
u'authors': Table(u'authors', metadata, autoload=True),
|
||||
u'media_files': Table(u'media_files', metadata, autoload=True),
|
||||
u'song_books': Table(u'song_books', metadata, autoload=True),
|
||||
u'songs': Table(u'songs', metadata, autoload=True),
|
||||
u'topics': Table(u'topics', metadata, autoload=True),
|
||||
u'authors_songs': Table(u'authors_songs', metadata, autoload=True),
|
||||
u'songs_topics': Table(u'songs_topics', metadata, autoload=True)
|
||||
}
|
||||
return tables
|
||||
|
||||
|
||||
def upgrade_1(session, metadata, tables):
|
||||
def upgrade_1(session, metadata):
|
||||
"""
|
||||
Version 1 upgrade.
|
||||
|
||||
|
@ -67,30 +51,35 @@ def upgrade_1(session, metadata, tables):
|
|||
added to the media_files table, and a weight column so that the media
|
||||
files can be ordered.
|
||||
"""
|
||||
Table(u'media_files_songs', metadata, autoload=True).drop(checkfirst=True)
|
||||
Column(u'song_id', types.Integer(), default=None).create(table=tables[u'media_files'])
|
||||
Column(u'weight', types.Integer(), default=0).create(table=tables[u'media_files'])
|
||||
op = get_upgrade_op(session)
|
||||
op.drop_table(u'media_files_songs')
|
||||
op.add_column(u'media_files', Column(u'song_id', types.Integer(), server_default=null()))
|
||||
op.add_column(u'media_files', Column(u'weight', types.Integer(), server_default=text(u'0')))
|
||||
if metadata.bind.url.get_dialect().name != 'sqlite':
|
||||
# SQLite doesn't support ALTER TABLE ADD CONSTRAINT
|
||||
ForeignKeyConstraint([u'song_id'], [u'songs.id'],
|
||||
table=tables[u'media_files']).create()
|
||||
op.create_foreign_key(u'fk_media_files_song_id', u'media_files', u'songs', [u'song_id', u'id'])
|
||||
|
||||
|
||||
def upgrade_2(session, metadata, tables):
|
||||
def upgrade_2(session, metadata):
|
||||
"""
|
||||
Version 2 upgrade.
|
||||
|
||||
This upgrade adds a create_date and last_modified date to the songs table
|
||||
"""
|
||||
Column(u'create_date', types.DateTime(), default=func.now()).create(table=tables[u'songs'])
|
||||
Column(u'last_modified', types.DateTime(), default=func.now()).create(table=tables[u'songs'])
|
||||
op = get_upgrade_op(session)
|
||||
op.add_column(u'songs', Column(u'create_date', types.DateTime(), default=func.now()))
|
||||
op.add_column(u'songs', Column(u'last_modified', types.DateTime(), default=func.now()))
|
||||
|
||||
|
||||
def upgrade_3(session, metadata, tables):
|
||||
def upgrade_3(session, metadata):
|
||||
"""
|
||||
Version 3 upgrade.
|
||||
|
||||
This upgrade adds a temporary song flag to the songs table
|
||||
"""
|
||||
Column(u'temporary', types.Boolean(), default=False).create(table=tables[u'songs'])
|
||||
op = get_upgrade_op(session)
|
||||
if metadata.bind.url.get_dialect().name == 'sqlite':
|
||||
op.add_column(u'songs', Column(u'temporary', types.Boolean(create_constraint=False), server_default=false()))
|
||||
else:
|
||||
op.add_column(u'songs', Column(u'temporary', types.Boolean(), server_default=false()))
|
||||
|
||||
|
|
|
@ -0,0 +1,85 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
|
||||
|
||||
###############################################################################
|
||||
# OpenLP - Open Source Lyrics Projection #
|
||||
# --------------------------------------------------------------------------- #
|
||||
# Copyright (c) 2008-2013 Raoul Snyman #
|
||||
# Portions copyright (c) 2008-2013 Tim Bentley, Gerald Britton, Jonathan #
|
||||
# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, #
|
||||
# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. #
|
||||
# Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru, #
|
||||
# Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, #
|
||||
# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Dave Warnock, #
|
||||
# Frode Woldsund, Martin Zibricky, Patrick Zimmermann #
|
||||
# --------------------------------------------------------------------------- #
|
||||
# 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 #
|
||||
###############################################################################
|
||||
"""
|
||||
The :mod:`worshipcenterpro` module provides the functionality for importing
|
||||
a WorshipCenter Pro database into the OpenLP database.
|
||||
"""
|
||||
import logging
|
||||
|
||||
import pyodbc
|
||||
|
||||
from openlp.core.lib import translate
|
||||
from openlp.plugins.songs.lib.songimport import SongImport
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class WorshipCenterProImport(SongImport):
|
||||
"""
|
||||
The :class:`WorshipCenterProImport` class provides the ability to import the
|
||||
WorshipCenter Pro Access Database
|
||||
"""
|
||||
def __init__(self, manager, **kwargs):
|
||||
"""
|
||||
Initialise the WorshipCenter Pro importer.
|
||||
"""
|
||||
SongImport.__init__(self, manager, **kwargs)
|
||||
|
||||
def doImport(self):
|
||||
"""
|
||||
Receive a single file to import.
|
||||
"""
|
||||
try:
|
||||
conn = pyodbc.connect(u'DRIVER={Microsoft Access Driver (*.mdb)};DBQ=%s' % self.import_source)
|
||||
except (pyodbc.DatabaseError, pyodbc.IntegrityError, pyodbc.InternalError, pyodbc.OperationalError), e:
|
||||
log.warn(u'Unable to connect the WorshipCenter Pro database %s. %s', self.import_source, unicode(e))
|
||||
# Unfortunately no specific exception type
|
||||
self.logError(self.import_source,
|
||||
translate('SongsPlugin.WorshipCenterProImport', 'Unable to connect the WorshipCenter Pro database.'))
|
||||
return
|
||||
cursor = conn.cursor()
|
||||
cursor.execute(u'SELECT ID, Field, Value FROM __SONGDATA')
|
||||
records = cursor.fetchall()
|
||||
songs = {}
|
||||
for record in records:
|
||||
id = record.ID
|
||||
if not songs.has_key(id):
|
||||
songs[id] = {}
|
||||
songs[id][record.Field] = record.Value
|
||||
self.import_wizard.progress_bar.setMaximum(len(songs))
|
||||
for song in songs:
|
||||
if self.stop_import_flag:
|
||||
break
|
||||
self.setDefaults()
|
||||
self.title = songs[song][u'TITLE']
|
||||
lyrics = songs[song][u'LYRICS'].strip(u'&crlf;&crlf;')
|
||||
for verse in lyrics.split(u'&crlf;&crlf;'):
|
||||
verse = verse.replace(u'&crlf;', u'\n')
|
||||
self.addVerse(verse)
|
||||
self.finish()
|
|
@ -30,30 +30,19 @@
|
|||
The :mod:`upgrade` module provides a way for the database and schema that is the
|
||||
backend for the SongsUsage plugin
|
||||
"""
|
||||
from openlp.core.lib.db import get_upgrade_op
|
||||
|
||||
from sqlalchemy import Column, Table, types
|
||||
from sqlalchemy import Column, types
|
||||
|
||||
__version__ = 1
|
||||
|
||||
def upgrade_setup(metadata):
|
||||
"""
|
||||
Set up the latest revision all tables, with reflection, needed for the
|
||||
upgrade process. If you want to drop a table, you need to remove it from
|
||||
here, and add it to your upgrade function.
|
||||
"""
|
||||
tables = {
|
||||
u'songusage_data': Table(u'songusage_data', metadata, autoload=True)
|
||||
}
|
||||
return tables
|
||||
|
||||
|
||||
def upgrade_1(session, metadata, tables):
|
||||
def upgrade_1(session, metadata):
|
||||
"""
|
||||
Version 1 upgrade.
|
||||
|
||||
This upgrade adds two new fields to the songusage database
|
||||
"""
|
||||
Column(u'plugin_name', types.Unicode(20), default=u'') \
|
||||
.create(table=tables[u'songusage_data'], populate_default=True)
|
||||
Column(u'source', types.Unicode(10), default=u'') \
|
||||
.create(table=tables[u'songusage_data'], populate_default=True)
|
||||
op = get_upgrade_op(session)
|
||||
op.add_column(u'songusage_data', Column(u'plugin_name', types.Unicode(20), server_default=u''))
|
||||
op.add_column(u'songusage_data', Column(u'source', types.Unicode(10), server_default=u''))
|
||||
|
|
|
@ -62,6 +62,7 @@ WIN32_MODULES = [
|
|||
'win32com',
|
||||
'win32ui',
|
||||
'pywintypes',
|
||||
'pyodbc',
|
||||
]
|
||||
|
||||
MODULES = [
|
||||
|
@ -85,6 +86,7 @@ MODULES = [
|
|||
'migrate',
|
||||
'uno',
|
||||
'icu',
|
||||
'bs4',
|
||||
]
|
||||
|
||||
|
||||
|
|
2
setup.py
2
setup.py
|
@ -175,6 +175,8 @@ OpenLP (previously openlp.org) is free church presentation software, or lyrics p
|
|||
zip_safe=False,
|
||||
install_requires=[
|
||||
# -*- Extra requirements: -*-
|
||||
'sqlalchemy',
|
||||
'alembic'
|
||||
],
|
||||
entry_points="""
|
||||
# -*- Entry points: -*-
|
||||
|
|
|
@ -0,0 +1,84 @@
|
|||
"""
|
||||
Package to test the openlp.core.lib package.
|
||||
"""
|
||||
from unittest import TestCase
|
||||
|
||||
from mock import MagicMock, patch
|
||||
from sqlalchemy.pool import NullPool
|
||||
from sqlalchemy.orm.scoping import ScopedSession
|
||||
from sqlalchemy import MetaData
|
||||
|
||||
from openlp.core.lib.db import init_db, get_upgrade_op
|
||||
|
||||
|
||||
class TestDB(TestCase):
|
||||
"""
|
||||
A test case for all the tests for the :mod:`~openlp.core.lib.db` module.
|
||||
"""
|
||||
def init_db_calls_correct_functions_test(self):
|
||||
"""
|
||||
Test that the init_db function makes the correct function calls
|
||||
"""
|
||||
# GIVEN: Mocked out SQLAlchemy calls and return objects, and an in-memory SQLite database URL
|
||||
with patch(u'openlp.core.lib.db.create_engine') as mocked_create_engine, \
|
||||
patch(u'openlp.core.lib.db.MetaData') as MockedMetaData, \
|
||||
patch(u'openlp.core.lib.db.sessionmaker') as mocked_sessionmaker, \
|
||||
patch(u'openlp.core.lib.db.scoped_session') as mocked_scoped_session:
|
||||
mocked_engine = MagicMock()
|
||||
mocked_metadata = MagicMock()
|
||||
mocked_sessionmaker_object = MagicMock()
|
||||
mocked_scoped_session_object = MagicMock()
|
||||
mocked_create_engine.return_value = mocked_engine
|
||||
MockedMetaData.return_value = mocked_metadata
|
||||
mocked_sessionmaker.return_value = mocked_sessionmaker_object
|
||||
mocked_scoped_session.return_value = mocked_scoped_session_object
|
||||
db_url = u'sqlite://'
|
||||
|
||||
# WHEN: We try to initialise the db
|
||||
session, metadata = init_db(db_url)
|
||||
|
||||
# THEN: We should see the correct function calls
|
||||
mocked_create_engine.assert_called_with(db_url, poolclass=NullPool)
|
||||
MockedMetaData.assert_called_with(bind=mocked_engine)
|
||||
mocked_sessionmaker.assert_called_with(autoflush=True, autocommit=False, bind=mocked_engine)
|
||||
mocked_scoped_session.assert_called_with(mocked_sessionmaker_object)
|
||||
self.assertIs(session, mocked_scoped_session_object, u'The ``session`` object should be the mock')
|
||||
self.assertIs(metadata, mocked_metadata, u'The ``metadata`` object should be the mock')
|
||||
|
||||
def init_db_defaults_test(self):
|
||||
"""
|
||||
Test that initialising an in-memory SQLite database via ``init_db`` uses the defaults
|
||||
"""
|
||||
# GIVEN: An in-memory SQLite URL
|
||||
db_url = u'sqlite://'
|
||||
|
||||
# WHEN: The database is initialised through init_db
|
||||
session, metadata = init_db(db_url)
|
||||
|
||||
# THEN: Valid session and metadata objects should be returned
|
||||
self.assertIsInstance(session, ScopedSession, u'The ``session`` object should be a ``ScopedSession`` instance')
|
||||
self.assertIsInstance(metadata, MetaData, u'The ``metadata`` object should be a ``MetaData`` instance')
|
||||
|
||||
def get_upgrade_op_test(self):
|
||||
"""
|
||||
Test that the ``get_upgrade_op`` function creates a MigrationContext and an Operations object
|
||||
"""
|
||||
# GIVEN: Mocked out alembic classes and a mocked out SQLAlchemy session object
|
||||
with patch(u'openlp.core.lib.db.MigrationContext') as MockedMigrationContext, \
|
||||
patch(u'openlp.core.lib.db.Operations') as MockedOperations:
|
||||
mocked_context = MagicMock()
|
||||
mocked_op = MagicMock()
|
||||
mocked_connection = MagicMock()
|
||||
MockedMigrationContext.configure.return_value = mocked_context
|
||||
MockedOperations.return_value = mocked_op
|
||||
mocked_session = MagicMock()
|
||||
mocked_session.bind.connect.return_value = mocked_connection
|
||||
|
||||
# WHEN: get_upgrade_op is executed with the mocked session object
|
||||
op = get_upgrade_op(mocked_session)
|
||||
|
||||
# THEN: The op object should be mocked_op, and the correction function calls should have been made
|
||||
self.assertIs(op, mocked_op, u'The return value should be the mocked object')
|
||||
mocked_session.bind.connect.assert_called_with()
|
||||
MockedMigrationContext.configure.assert_called_with(mocked_connection)
|
||||
MockedOperations.assert_called_with(mocked_context)
|
|
@ -16,6 +16,7 @@ from openlp.core.lib import str_to_bool, create_thumb, translate, check_director
|
|||
TEST_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), u'..', u'..', u'resources'))
|
||||
|
||||
|
||||
|
||||
class TestLib(TestCase):
|
||||
|
||||
def str_to_bool_with_bool_test(self):
|
||||
|
|
|
@ -0,0 +1,261 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
Package to test the openlp.core.lib package.
|
||||
"""
|
||||
import os
|
||||
import io
|
||||
import cPickle
|
||||
import json
|
||||
import tempfile
|
||||
from unittest import TestCase
|
||||
from mock import MagicMock, patch
|
||||
|
||||
from openlp.core.lib import ItemCapabilities, ServiceItem, Registry
|
||||
from lxml import objectify, etree
|
||||
|
||||
VERSE = u'The Lord said to {r}Noah{/r}: \n'\
|
||||
'There\'s gonna be a {su}floody{/su}, {sb}floody{/sb}\n'\
|
||||
'The Lord said to {g}Noah{/g}:\n'\
|
||||
'There\'s gonna be a {st}floody{/st}, {it}floody{/it}\n'\
|
||||
'Get those children out of the muddy, muddy \n'\
|
||||
'{r}C{/r}{b}h{/b}{bl}i{/bl}{y}l{/y}{g}d{/g}{pk}'\
|
||||
'r{/pk}{o}e{/o}{pp}n{/pp} of the Lord\n'
|
||||
FOOTER = [u'Arky Arky (Unknown)', u'Public Domain', u'CCLI 123456']
|
||||
|
||||
TEST_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), u'..', u'..', u'resources'))
|
||||
|
||||
|
||||
class TestServiceItem(TestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""
|
||||
Set up the Registry
|
||||
"""
|
||||
Registry.create()
|
||||
mocked_renderer = MagicMock()
|
||||
mocked_renderer.format_slide.return_value = [VERSE]
|
||||
Registry().register(u'renderer', mocked_renderer)
|
||||
Registry().register(u'image_manager', MagicMock())
|
||||
|
||||
def serviceitem_basic_test(self):
|
||||
"""
|
||||
Test the Service Item - basic test
|
||||
"""
|
||||
# GIVEN: A new service item
|
||||
|
||||
# WHEN: A service item is created (without a plugin)
|
||||
service_item = ServiceItem(None)
|
||||
|
||||
# THEN: We should get back a valid service item
|
||||
assert service_item.is_valid is True, u'The new service item should be valid'
|
||||
assert service_item.missing_frames() is True, u'There should not be any frames in the service item'
|
||||
|
||||
def serviceitem_load_custom_from_service_test(self):
|
||||
"""
|
||||
Test the Service Item - adding a custom slide from a saved service
|
||||
"""
|
||||
# GIVEN: A new service item and a mocked add icon function
|
||||
service_item = ServiceItem(None)
|
||||
service_item.add_icon = MagicMock()
|
||||
|
||||
# WHEN: adding a custom from a saved Service
|
||||
line = self.convert_file_service_item(u'serviceitem_custom_1.osj')
|
||||
service_item.set_from_service(line)
|
||||
|
||||
# THEN: We should get back a valid service item
|
||||
assert service_item.is_valid is True, u'The new service item should be valid'
|
||||
assert len(service_item._display_frames) == 0, u'The service item should have no display frames'
|
||||
assert len(service_item.capabilities) == 5, u'There should be 5 default custom item capabilities'
|
||||
service_item.render(True)
|
||||
assert service_item.get_display_title() == u'Test Custom', u'The title should be "Test Custom"'
|
||||
assert service_item.get_frames()[0][u'text'] == VERSE[:-1], \
|
||||
u'The returned text matches the input, except the last line feed'
|
||||
assert service_item.get_rendered_frame(1) == VERSE.split(u'\n', 1)[0], u'The first line has been returned'
|
||||
assert service_item.get_frame_title(0) == u'Slide 1', u'"Slide 1" has been returned as the title'
|
||||
assert service_item.get_frame_title(1) == u'Slide 2', u'"Slide 2" has been returned as the title'
|
||||
assert service_item.get_frame_title(2) == u'', u'Blank has been returned as the title of slide 3'
|
||||
|
||||
def serviceitem_load_image_from_service_test(self):
|
||||
"""
|
||||
Test the Service Item - adding an image from a saved service
|
||||
"""
|
||||
# GIVEN: A new service item and a mocked add icon function
|
||||
image_name = u'image_1.jpg'
|
||||
test_file = os.path.join(TEST_PATH, image_name)
|
||||
frame_array = {u'path': test_file, u'title': image_name}
|
||||
|
||||
service_item = ServiceItem(None)
|
||||
service_item.add_icon = MagicMock()
|
||||
|
||||
# WHEN: adding an image from a saved Service and mocked exists
|
||||
line = self.convert_file_service_item(u'serviceitem_image_1.osj')
|
||||
with patch(u'openlp.core.ui.servicemanager.os.path.exists') as mocked_exists:
|
||||
mocked_exists.return_value = True
|
||||
service_item.set_from_service(line, TEST_PATH)
|
||||
|
||||
# THEN: We should get back a valid service item
|
||||
assert service_item.is_valid is True, u'The new service item should be valid'
|
||||
assert service_item.get_rendered_frame(0) == test_file, u'The first frame should match the path to the image'
|
||||
assert service_item.get_frames()[0] == frame_array, u'The return should match frame array1'
|
||||
assert service_item.get_frame_path(0) == test_file, u'The frame path should match the full path to the image'
|
||||
assert service_item.get_frame_title(0) == image_name, u'The frame title should match the image name'
|
||||
assert service_item.get_display_title() == image_name, u'The display title should match the first image name'
|
||||
assert service_item.is_image() is True, u'This service item should be of an "image" type'
|
||||
assert service_item.is_capable(ItemCapabilities.CanMaintain) is True, \
|
||||
u'This service item should be able to be Maintained'
|
||||
assert service_item.is_capable(ItemCapabilities.CanPreview) is True, \
|
||||
u'This service item should be able to be be Previewed'
|
||||
assert service_item.is_capable(ItemCapabilities.CanLoop) is True, \
|
||||
u'This service item should be able to be run in a can be made to Loop'
|
||||
assert service_item.is_capable(ItemCapabilities.CanAppend) is True, \
|
||||
u'This service item should be able to have new items added to it'
|
||||
|
||||
def serviceitem_load_image_from_local_service_test(self):
|
||||
"""
|
||||
Test the Service Item - adding an image from a saved local service
|
||||
"""
|
||||
# GIVEN: A new service item and a mocked add icon function
|
||||
image_name1 = u'image_1.jpg'
|
||||
image_name2 = u'image_2.jpg'
|
||||
test_file1 = os.path.join(u'/home/openlp', image_name1)
|
||||
test_file2 = os.path.join(u'/home/openlp', image_name2)
|
||||
frame_array1 = {u'path': test_file1, u'title': image_name1}
|
||||
frame_array2 = {u'path': test_file2, u'title': image_name2}
|
||||
|
||||
service_item = ServiceItem(None)
|
||||
service_item.add_icon = MagicMock()
|
||||
|
||||
service_item2 = ServiceItem(None)
|
||||
service_item2.add_icon = MagicMock()
|
||||
|
||||
# WHEN: adding an image from a saved Service and mocked exists
|
||||
line = self.convert_file_service_item(u'serviceitem_image_2.osj')
|
||||
line2 = self.convert_file_service_item(u'serviceitem_image_2.osj', 1)
|
||||
|
||||
with patch(u'openlp.core.ui.servicemanager.os.path.exists') as mocked_exists:
|
||||
mocked_exists.return_value = True
|
||||
service_item2.set_from_service(line2)
|
||||
service_item.set_from_service(line)
|
||||
|
||||
|
||||
# THEN: We should get back a valid service item
|
||||
|
||||
# This test is copied from service_item.py, but is changed since to conform to
|
||||
# new layout of service item. The layout use in serviceitem_image_2.osd is actually invalid now.
|
||||
assert service_item.is_valid is True, u'The first service item should be valid'
|
||||
assert service_item2.is_valid is True, u'The second service item should be valid'
|
||||
assert service_item.get_rendered_frame(0) == test_file1, u'The first frame should match the path to the image'
|
||||
assert service_item2.get_rendered_frame(0) == test_file2, u'The Second frame should match the path to the image'
|
||||
assert service_item.get_frames()[0] == frame_array1, u'The return should match the frame array1'
|
||||
assert service_item2.get_frames()[0] == frame_array2, u'The return should match the frame array2'
|
||||
assert service_item.get_frame_path(0) == test_file1, u'The frame path should match the full path to the image'
|
||||
assert service_item2.get_frame_path(0) == test_file2, u'The frame path should match the full path to the image'
|
||||
assert service_item.get_frame_title(0) == image_name1, u'The 1st frame title should match the image name'
|
||||
assert service_item2.get_frame_title(0) == image_name2, u'The 2nd frame title should match the image name'
|
||||
assert service_item.title.lower() == service_item.name, \
|
||||
u'The plugin name should match the display title, as there are > 1 Images'
|
||||
assert service_item.is_image() is True, u'This service item should be of an "image" type'
|
||||
assert service_item.is_capable(ItemCapabilities.CanMaintain) is True, \
|
||||
u'This service item should be able to be Maintained'
|
||||
assert service_item.is_capable(ItemCapabilities.CanPreview) is True, \
|
||||
u'This service item should be able to be be Previewed'
|
||||
assert service_item.is_capable(ItemCapabilities.CanLoop) is True, \
|
||||
u'This service item should be able to be run in a can be made to Loop'
|
||||
assert service_item.is_capable(ItemCapabilities.CanAppend) is True, \
|
||||
u'This service item should be able to have new items added to it'
|
||||
|
||||
def serviceitem_convert_osd2osj_test(self):
|
||||
"""
|
||||
Test the Service Item - load a osd to service_item, convert to json,
|
||||
load again to service_item and compare the old and new service_item.
|
||||
"""
|
||||
# GIVEN: A valid osd (python pickle format) service in file
|
||||
service_file = os.path.join(TEST_PATH, u'serviceitem_osd2osj.osd')
|
||||
osd_service_items = []
|
||||
try:
|
||||
open_file = open(service_file, u'r')
|
||||
osd_service_items = cPickle.load(open_file)
|
||||
except IOError:
|
||||
osd_service_items = []
|
||||
finally:
|
||||
open_file.close()
|
||||
|
||||
# WHEN: Dumping loaded osd service to json format, and save to file and reloading to service
|
||||
json_service_content = json.dumps(osd_service_items)
|
||||
open_file = None
|
||||
open_filename = u''
|
||||
try:
|
||||
(open_filep, open_filename) = tempfile.mkstemp()
|
||||
open_file = open(open_filename, u'w')
|
||||
open_file.write(json_service_content)
|
||||
open_file.close()
|
||||
open_file = open(open_filename, u'r')
|
||||
json_service_content = open_file.read()
|
||||
except IOError:
|
||||
json_service_content = u''
|
||||
finally:
|
||||
open_file.close()
|
||||
os.remove(open_filename)
|
||||
osj_service_items = json.loads(json_service_content)
|
||||
|
||||
# THEN: The service loaded from osj (json format) should be the same as the service loaded from the original osd (python pickle format)
|
||||
|
||||
# Loop over every item and compare the osj with osd version
|
||||
for osd_item, osj_item in zip(osd_service_items, osj_service_items):
|
||||
# Create service item objects
|
||||
service_item_osd = ServiceItem()
|
||||
service_item_osd.add_icon = MagicMock()
|
||||
|
||||
service_item_osj = ServiceItem()
|
||||
service_item_osj.add_icon = MagicMock()
|
||||
|
||||
with patch(u'openlp.core.ui.servicemanager.os.path.exists') as mocked_exists:
|
||||
mocked_exists.return_value = True
|
||||
service_item_osd.set_from_service(osd_item, TEST_PATH)
|
||||
service_item_osj.set_from_service(osj_item, TEST_PATH)
|
||||
|
||||
# Check that the exported/imported attributes are the same
|
||||
assert service_item_osj.is_valid is True, u'The osj service item should be valid'
|
||||
assert service_item_osd.is_valid is True, u'The osd service item should be valid'
|
||||
assert service_item_osj.name == service_item_osd.name , u'The osd and the osj attribute name should be the same!'
|
||||
assert service_item_osj.theme == service_item_osd.theme , u'The osd and the osj attribute theme should be the same!'
|
||||
assert service_item_osj.title == service_item_osd.title , u'The osd and the osj attribute title should be the same!'
|
||||
assert service_item_osj.icon == service_item_osd.icon , u'The osd and the osj attribute icon should be the same!'
|
||||
assert service_item_osj.raw_footer == service_item_osd.raw_footer , u'The osd and the osj attribute raw_footer should be the same!'
|
||||
assert service_item_osj.service_item_type == service_item_osd.service_item_type , u'The osd and the osj attribute service_item_type should be the same!'
|
||||
assert service_item_osj.audit == service_item_osd.audit , u'The osd and the osj attribute audit should be the same!'
|
||||
assert service_item_osj.notes == service_item_osd.notes , u'The osd and the osj attribute notes should be the same!'
|
||||
assert service_item_osj.from_plugin == service_item_osd.from_plugin , u'The osd and the osj attribute from_plugin should be the same!'
|
||||
assert service_item_osj.capabilities == service_item_osd.capabilities , u'The osd and the osj attribute capabilities should be the same!'
|
||||
assert service_item_osj.search_string == service_item_osd.search_string , u'The osd and the osj attribute search_string should be the same!'
|
||||
assert service_item_osj.data_string == service_item_osd.data_string , u'The osd and the osj attribute data_string should be the same!'
|
||||
# Notice that xml_version from osd needs to be utf-8 decoded, since unicode-characters
|
||||
# is written as byte-codes by pickle, while json can escape unicode-characters
|
||||
if service_item_osd.xml_version:
|
||||
assert service_item_osj.xml_version == service_item_osd.xml_version.decode(u'utf-8') , u'The osd and the osj attribute xml_version should be the same!'
|
||||
assert service_item_osj.auto_play_slides_once == service_item_osd.auto_play_slides_once , u'The osd and the osj attribute auto_play_slides_once should be the same!'
|
||||
assert service_item_osj.auto_play_slides_loop == service_item_osd.auto_play_slides_loop , u'The osd and the osj attribute auto_play_slides_loop should be the same!'
|
||||
assert service_item_osj.timed_slide_interval == service_item_osd.timed_slide_interval , u'The osd and the osj attribute timed_slide_interval should be the same!'
|
||||
assert service_item_osj.start_time == service_item_osd.start_time , u'The osd and the osj attribute start_time should be the same!'
|
||||
assert service_item_osj.end_time == service_item_osd.end_time , u'The osd and the osj attribute end_time should be the same!'
|
||||
assert service_item_osj.media_length == service_item_osd.media_length , u'The osd and the osj attribute media_length should be the same!'
|
||||
assert service_item_osj.background_audio == service_item_osd.background_audio , u'The osd and the osj attribute background_audio should be the same!'
|
||||
assert service_item_osj.theme_overwritten == service_item_osd.theme_overwritten , u'The osd and the osj attribute theme_overwritten should be the same!'
|
||||
assert service_item_osj.will_auto_start == service_item_osd.will_auto_start , u'The osd and the osj attribute will_auto_start should be the same!'
|
||||
assert service_item_osj.processor == service_item_osd.processor , u'The osd and the osj attribute processor should be the same!'
|
||||
|
||||
|
||||
|
||||
def convert_file_service_item(self, name, row=0):
|
||||
service_file = os.path.join(TEST_PATH, name)
|
||||
try:
|
||||
open_file = open(service_file, u'r')
|
||||
items = json.load(open_file)
|
||||
first_line = items[row]
|
||||
except IOError:
|
||||
first_line = u''
|
||||
finally:
|
||||
open_file.close()
|
||||
return first_line
|
||||
|
|
@ -7,7 +7,7 @@ from unittest import TestCase
|
|||
|
||||
from mock import patch, MagicMock
|
||||
|
||||
from PyQt4 import QtGui
|
||||
from PyQt4 import QtCore, QtGui
|
||||
|
||||
from openlp.core.lib import Registry, ServiceItem, Settings
|
||||
|
||||
|
@ -32,6 +32,7 @@ class TestMediaItem(TestCase):
|
|||
fd, self.ini_file = mkstemp(u'.ini')
|
||||
Settings().set_filename(self.ini_file)
|
||||
self.application = QtGui.QApplication.instance()
|
||||
QtCore.QLocale.setDefault(QtCore.QLocale('en_GB'))
|
||||
|
||||
def tearDown(self):
|
||||
"""
|
||||
|
|
|
@ -0,0 +1,192 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
|
||||
|
||||
"""
|
||||
This module contains tests for the WorshipCenter Pro song importer.
|
||||
"""
|
||||
|
||||
from unittest import TestCase
|
||||
from mock import patch, MagicMock
|
||||
import pyodbc
|
||||
|
||||
from openlp.plugins.songs.lib.worshipcenterproimport import WorshipCenterProImport
|
||||
|
||||
class TestRecord(object):
|
||||
"""
|
||||
Microsoft Access Driver is not available on non Microsoft Systems for this reason the :class:`TestRecord` is used
|
||||
to simulate a recordset that would be returned by pyobdc.
|
||||
"""
|
||||
def __init__(self, id, field, value):
|
||||
# The case of the following instance variables is important as it needs to be the same as the ones in use in the
|
||||
# WorshipCenter Pro database.
|
||||
self.ID = id
|
||||
self.Field = field
|
||||
self.Value = value
|
||||
|
||||
class WorshipCenterProImportLogger(WorshipCenterProImport):
|
||||
"""
|
||||
This class logs changes in the title instance variable
|
||||
"""
|
||||
_title_assignment_list = []
|
||||
|
||||
def __init__(self, manager):
|
||||
WorshipCenterProImport.__init__(self, manager)
|
||||
|
||||
@property
|
||||
def title(self):
|
||||
return self._title_assignment_list[-1]
|
||||
|
||||
@title.setter
|
||||
def title(self, title):
|
||||
self._title_assignment_list.append(title)
|
||||
|
||||
|
||||
RECORDSET_TEST_DATA = [TestRecord(1, u'TITLE', u'Amazing Grace'),
|
||||
TestRecord(1, u'LYRICS',
|
||||
u'Amazing grace! How&crlf;sweet the sound&crlf;That saved a wretch like me!&crlf;'
|
||||
u'I once was lost,&crlf;but now am found;&crlf;Was blind, but now I see.&crlf;&crlf;'
|
||||
u'\'Twas grace that&crlf;taught my heart to fear,&crlf;And grace my fears relieved;&crlf;'
|
||||
u'How precious did&crlf;that grace appear&crlf;The hour I first believed.&crlf;&crlf;'
|
||||
u'Through many dangers,&crlf;toils and snares,&crlf;I have already come;&crlf;'
|
||||
u'\'Tis grace hath brought&crlf;me safe thus far,&crlf;'
|
||||
u'And grace will lead me home.&crlf;&crlf;The Lord has&crlf;promised good to me,&crlf;'
|
||||
u'His Word my hope secures;&crlf;He will my Shield&crlf;and Portion be,&crlf;'
|
||||
u'As long as life endures.&crlf;&crlf;Yea, when this flesh&crlf;and heart shall fail,&crlf;'
|
||||
u'And mortal life shall cease,&crlf;I shall possess,&crlf;within the veil,&crlf;'
|
||||
u'A life of joy and peace.&crlf;&crlf;The earth shall soon&crlf;dissolve like snow,&crlf;'
|
||||
u'The sun forbear to shine;&crlf;But God, Who called&crlf;me here below,&crlf;'
|
||||
u'Shall be forever mine.&crlf;&crlf;When we\'ve been there&crlf;ten thousand years,&crlf;'
|
||||
u'Bright shining as the sun,&crlf;We\'ve no less days to&crlf;sing God\'s praise&crlf;'
|
||||
u'Than when we\'d first begun.&crlf;&crlf;'),
|
||||
TestRecord(2, u'TITLE', u'Beautiful Garden Of Prayer, The'),
|
||||
TestRecord(2, u'LYRICS',
|
||||
u'There\'s a garden where&crlf;Jesus is waiting,&crlf;'
|
||||
u'There\'s a place that&crlf;is wondrously fair,&crlf;For it glows with the&crlf;'
|
||||
u'light of His presence.&crlf;\'Tis the beautiful&crlf;garden of prayer.&crlf;&crlf;'
|
||||
u'Oh, the beautiful garden,&crlf;the garden of prayer!&crlf;Oh, the beautiful&crlf;'
|
||||
u'garden of prayer!&crlf;There my Savior awaits,&crlf;and He opens the gates&crlf;'
|
||||
u'To the beautiful&crlf;garden of prayer.&crlf;&crlf;There\'s a garden where&crlf;'
|
||||
u'Jesus is waiting,&crlf;And I go with my&crlf;burden and care,&crlf;'
|
||||
u'Just to learn from His&crlf;lips words of comfort&crlf;In the beautiful&crlf;'
|
||||
u'garden of prayer.&crlf;&crlf;There\'s a garden where&crlf;Jesus is waiting,&crlf;'
|
||||
u'And He bids you to come,&crlf;meet Him there;&crlf;Just to bow and&crlf;'
|
||||
u'receive a new blessing&crlf;In the beautiful&crlf;garden of prayer.&crlf;&crlf;')]
|
||||
SONG_TEST_DATA = [{u'title': u'Amazing Grace',
|
||||
u'verses': [
|
||||
(u'Amazing grace! How\nsweet the sound\nThat saved a wretch like me!\nI once was lost,\n'
|
||||
u'but now am found;\nWas blind, but now I see.'),
|
||||
(u'\'Twas grace that\ntaught my heart to fear,\nAnd grace my fears relieved;\nHow precious did\n'
|
||||
u'that grace appear\nThe hour I first believed.'),
|
||||
(u'Through many dangers,\ntoils and snares,\nI have already come;\n\'Tis grace hath brought\n'
|
||||
u'me safe thus far,\nAnd grace will lead me home.'),
|
||||
(u'The Lord has\npromised good to me,\nHis Word my hope secures;\n'
|
||||
u'He will my Shield\nand Portion be,\nAs long as life endures.'),
|
||||
(u'Yea, when this flesh\nand heart shall fail,\nAnd mortal life shall cease,\nI shall possess,\n'
|
||||
u'within the veil,\nA life of joy and peace.'),
|
||||
(u'The earth shall soon\ndissolve like snow,\nThe sun forbear to shine;\nBut God, Who called\n'
|
||||
u'me here below,\nShall be forever mine.'),
|
||||
(u'When we\'ve been there\nten thousand years,\nBright shining as the sun,\n'
|
||||
u'We\'ve no less days to\nsing God\'s praise\nThan when we\'d first begun.')]},
|
||||
{u'title': u'Beautiful Garden Of Prayer, The',
|
||||
u'verses': [
|
||||
(u'There\'s a garden where\nJesus is waiting,\nThere\'s a place that\nis wondrously fair,\n'
|
||||
u'For it glows with the\nlight of His presence.\n\'Tis the beautiful\ngarden of prayer.'),
|
||||
(u'Oh, the beautiful garden,\nthe garden of prayer!\nOh, the beautiful\ngarden of prayer!\n'
|
||||
u'There my Savior awaits,\nand He opens the gates\nTo the beautiful\ngarden of prayer.'),
|
||||
(u'There\'s a garden where\nJesus is waiting,\nAnd I go with my\nburden and care,\n'
|
||||
u'Just to learn from His\nlips words of comfort\nIn the beautiful\ngarden of prayer.'),
|
||||
(u'There\'s a garden where\nJesus is waiting,\nAnd He bids you to come,\nmeet Him there;\n'
|
||||
u'Just to bow and\nreceive a new blessing\nIn the beautiful\ngarden of prayer.')]}]
|
||||
|
||||
class TestWorshipCenterProSongImport(TestCase):
|
||||
"""
|
||||
Test the functions in the :mod:`worshipcenterproimport` module.
|
||||
"""
|
||||
def create_importer_test(self):
|
||||
"""
|
||||
Test creating an instance of the WorshipCenter Pro file importer
|
||||
"""
|
||||
# GIVEN: A mocked out SongImport class, and a mocked out "manager"
|
||||
with patch(u'openlp.plugins.songs.lib.worshipcenterproimport.SongImport'):
|
||||
mocked_manager = MagicMock()
|
||||
|
||||
# WHEN: An importer object is created
|
||||
importer = WorshipCenterProImport(mocked_manager)
|
||||
|
||||
# THEN: The importer object should not be None
|
||||
self.assertIsNotNone(importer, u'Import should not be none')
|
||||
|
||||
def pyodbc_exception_test(self):
|
||||
"""
|
||||
Test that exceptions raised by pyodbc are handled
|
||||
"""
|
||||
# GIVEN: A mocked out SongImport class, a mocked out pyodbc module, a mocked out translate method,
|
||||
# a mocked "manager" and a mocked out logError method.
|
||||
with patch(u'openlp.plugins.songs.lib.worshipcenterproimport.SongImport'), \
|
||||
patch(u'openlp.plugins.songs.lib.worshipcenterproimport.pyodbc.connect') as mocked_pyodbc_connect, \
|
||||
patch(u'openlp.plugins.songs.lib.worshipcenterproimport.translate') as mocked_translate:
|
||||
mocked_manager = MagicMock()
|
||||
mocked_log_error = MagicMock()
|
||||
mocked_translate.return_value = u'Translated Text'
|
||||
importer = WorshipCenterProImport(mocked_manager)
|
||||
importer.logError = mocked_log_error
|
||||
importer.import_source = u'import_source'
|
||||
pyodbc_errors = [pyodbc.DatabaseError, pyodbc.IntegrityError, pyodbc.InternalError, pyodbc.OperationalError]
|
||||
mocked_pyodbc_connect.side_effect = pyodbc_errors
|
||||
|
||||
# WHEN: Calling the doImport method
|
||||
for effect in pyodbc_errors:
|
||||
return_value = importer.doImport()
|
||||
|
||||
# THEN: doImport should return None, and pyodbc, translate & logError are called with known calls
|
||||
self.assertIsNone(return_value, u'doImport should return None when pyodbc raises an exception.')
|
||||
mocked_pyodbc_connect.assert_called_with( u'DRIVER={Microsoft Access Driver (*.mdb)};DBQ=import_source')
|
||||
mocked_translate.assert_called_with('SongsPlugin.WorshipCenterProImport',
|
||||
'Unable to connect the WorshipCenter Pro database.')
|
||||
mocked_log_error.assert_called_with(u'import_source', u'Translated Text')
|
||||
|
||||
def song_import_test(self):
|
||||
"""
|
||||
Test that a simulated WorshipCenter Pro recordset is imported correctly
|
||||
"""
|
||||
# GIVEN: A mocked out SongImport class, a mocked out pyodbc module with a simulated recordset, a mocked out
|
||||
# translate method, a mocked "manager", addVerse method & mocked_finish method.
|
||||
with patch(u'openlp.plugins.songs.lib.worshipcenterproimport.SongImport'), \
|
||||
patch(u'openlp.plugins.songs.lib.worshipcenterproimport.pyodbc') as mocked_pyodbc, \
|
||||
patch(u'openlp.plugins.songs.lib.worshipcenterproimport.translate') as mocked_translate:
|
||||
mocked_manager = MagicMock()
|
||||
mocked_import_wizard = MagicMock()
|
||||
mocked_add_verse = MagicMock()
|
||||
mocked_finish = MagicMock()
|
||||
mocked_pyodbc.connect().cursor().fetchall.return_value = RECORDSET_TEST_DATA
|
||||
mocked_translate.return_value = u'Translated Text'
|
||||
importer = WorshipCenterProImportLogger(mocked_manager)
|
||||
importer.import_source = u'import_source'
|
||||
importer.import_wizard = mocked_import_wizard
|
||||
importer.addVerse = mocked_add_verse
|
||||
importer.stop_import_flag = False
|
||||
importer.finish = mocked_finish
|
||||
|
||||
# WHEN: Calling the doImport method
|
||||
return_value = importer.doImport()
|
||||
|
||||
|
||||
# THEN: doImport should return None, and pyodbc, import_wizard, importer.title and addVerse are called with
|
||||
# known calls
|
||||
self.assertIsNone(return_value, u'doImport should return None when pyodbc raises an exception.')
|
||||
mocked_pyodbc.connect.assert_called_with(u'DRIVER={Microsoft Access Driver (*.mdb)};DBQ=import_source')
|
||||
mocked_pyodbc.connect().cursor.assert_any_call()
|
||||
mocked_pyodbc.connect().cursor().execute.assert_called_with(u'SELECT ID, Field, Value FROM __SONGDATA')
|
||||
mocked_pyodbc.connect().cursor().fetchall.assert_any_call()
|
||||
mocked_import_wizard.progress_bar.setMaximum.assert_called_with(2)
|
||||
add_verse_call_count = 0
|
||||
for song_data in SONG_TEST_DATA:
|
||||
title_value = song_data[u'title']
|
||||
self.assertIn(title_value, importer._title_assignment_list,
|
||||
u'title should have been set to %s' % title_value)
|
||||
verse_calls = song_data[u'verses']
|
||||
add_verse_call_count += len(verse_calls)
|
||||
for call in verse_calls:
|
||||
mocked_add_verse.assert_any_call(call)
|
||||
self.assertEqual(mocked_add_verse.call_count, add_verse_call_count,
|
||||
u'Incorrect number of calls made to addVerse')
|
|
@ -0,0 +1,60 @@
|
|||
"""
|
||||
Package to test the openlp.core.ui.mainwindow package.
|
||||
"""
|
||||
from unittest import TestCase
|
||||
from mock import MagicMock, patch
|
||||
|
||||
from PyQt4 import QtGui
|
||||
|
||||
from openlp.core.lib import Registry
|
||||
from openlp.core.ui.mainwindow import MainWindow
|
||||
|
||||
|
||||
class TestMainWindow(TestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""
|
||||
Create the UI
|
||||
"""
|
||||
Registry.create()
|
||||
self.registry = Registry()
|
||||
self.app = QtGui.QApplication([])
|
||||
# Mock cursor busy/normal methods.
|
||||
self.app.set_busy_cursor = MagicMock()
|
||||
self.app.set_normal_cursor = MagicMock()
|
||||
self.app.args =[]
|
||||
Registry().register(u'application', self.app)
|
||||
# Mock classes and methods used by mainwindow.
|
||||
with patch(u'openlp.core.ui.mainwindow.SettingsForm') as mocked_settings_form, \
|
||||
patch(u'openlp.core.ui.mainwindow.ImageManager') as mocked_image_manager, \
|
||||
patch(u'openlp.core.ui.mainwindow.SlideController') as mocked_slide_controller, \
|
||||
patch(u'openlp.core.ui.mainwindow.OpenLPDockWidget') as mocked_dock_widget, \
|
||||
patch(u'openlp.core.ui.mainwindow.QtGui.QToolBox') as mocked_q_tool_box_class, \
|
||||
patch(u'openlp.core.ui.mainwindow.QtGui.QMainWindow.addDockWidget') as mocked_add_dock_method, \
|
||||
patch(u'openlp.core.ui.mainwindow.ServiceManager') as mocked_service_manager, \
|
||||
patch(u'openlp.core.ui.mainwindow.ThemeManager') as mocked_theme_manager, \
|
||||
patch(u'openlp.core.ui.mainwindow.Renderer') as mocked_renderer:
|
||||
self.main_window = MainWindow()
|
||||
|
||||
def tearDown(self):
|
||||
"""
|
||||
Delete all the C++ objects at the end so that we don't have a segfault
|
||||
"""
|
||||
del self.main_window
|
||||
del self.app
|
||||
|
||||
def restore_current_media_manager_item_test(self):
|
||||
"""
|
||||
Regression test for bug #1152509.
|
||||
"""
|
||||
# GIVEN: Mocked Settings().value method.
|
||||
with patch(u'openlp.core.ui.mainwindow.Settings.value') as mocked_value:
|
||||
# save current plugin: True; current media plugin: 2
|
||||
mocked_value.side_effect = [True, 2]
|
||||
|
||||
# WHEN: Call the restore method.
|
||||
Registry().execute(u'bootstrap_post_set_up')
|
||||
|
||||
# THEN: The current widget should have been set.
|
||||
self.main_window.media_tool_box.setCurrentIndex.assert_called_with(2)
|
||||
|
|
@ -44,4 +44,67 @@ class TestEditSongForm(TestCase):
|
|||
self.assertFalse(self.form.topic_remove_button.isEnabled(), u'The topic remove button should not be enabled')
|
||||
|
||||
def is_verse_edit_form_executed_test(self):
|
||||
pass
|
||||
pass
|
||||
|
||||
def verse_order_no_warning_test(self):
|
||||
"""
|
||||
Test if the verse order warning is not shown
|
||||
"""
|
||||
# GIVEN: Mocked methods.
|
||||
given_verse_order = u'V1 V2'
|
||||
self.form.verse_list_widget.rowCount = MagicMock(return_value=2)
|
||||
# Mock out the verse.
|
||||
first_verse = MagicMock()
|
||||
first_verse.data = MagicMock(return_value=u'V1')
|
||||
second_verse = MagicMock()
|
||||
second_verse.data = MagicMock(return_value= u'V2')
|
||||
self.form.verse_list_widget.item = MagicMock(side_effect=[first_verse, second_verse])
|
||||
self.form._extract_verse_order = MagicMock(return_value=given_verse_order.split())
|
||||
|
||||
# WHEN: Call the method.
|
||||
self.form.on_verse_order_text_changed(given_verse_order)
|
||||
|
||||
# THEN: No text should be shown.
|
||||
assert self.form.warning_label.text() == u'', u'There should be no warning.'
|
||||
|
||||
def verse_order_incomplete_warning_test(self):
|
||||
"""
|
||||
Test if the verse-order-incomple warning is shown
|
||||
"""
|
||||
# GIVEN: Mocked methods.
|
||||
given_verse_order = u'V1'
|
||||
self.form.verse_list_widget.rowCount = MagicMock(return_value=2)
|
||||
# Mock out the verse.
|
||||
first_verse = MagicMock()
|
||||
first_verse.data = MagicMock(return_value=u'V1')
|
||||
second_verse = MagicMock()
|
||||
second_verse.data = MagicMock(return_value= u'V2')
|
||||
self.form.verse_list_widget.item = MagicMock(side_effect=[first_verse, second_verse])
|
||||
self.form._extract_verse_order = MagicMock(return_value=[given_verse_order])
|
||||
|
||||
# WHEN: Call the method.
|
||||
self.form.on_verse_order_text_changed(given_verse_order)
|
||||
|
||||
# THEN: The verse-order-incomplete text should be shown.
|
||||
assert self.form.warning_label.text() == self.form.not_all_verses_used_warning, \
|
||||
u'The verse-order-incomplete warning should be shown.'
|
||||
|
||||
def bug_1170435_test(self):
|
||||
"""
|
||||
Regression test for bug 1170435 (test if "no verse order" message is shown)
|
||||
"""
|
||||
# GIVEN: Mocked methods.
|
||||
given_verse_order = u''
|
||||
self.form.verse_list_widget.rowCount = MagicMock(return_value=1)
|
||||
# Mock out the verse. (We want a verse type to be returned).
|
||||
mocked_verse = MagicMock()
|
||||
mocked_verse.data = MagicMock(return_value=u'V1')
|
||||
self.form.verse_list_widget.item = MagicMock(return_value=mocked_verse)
|
||||
self.form._extract_verse_order = MagicMock(return_value=[])
|
||||
self.form.verse_order_edit.text = MagicMock(return_value=given_verse_order)
|
||||
# WHEN: Call the method.
|
||||
self.form.on_verse_order_text_changed(given_verse_order)
|
||||
|
||||
# THEN: The no-verse-order message should be shown.
|
||||
assert self.form.warning_label.text() == self.form.no_verse_order_entered_warning, \
|
||||
u'The no-verse-order message should be shown.'
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
[{"serviceitem": {"header": {"xml_version": null, "auto_play_slides_loop": false, "auto_play_slides_once": false, "will_auto_start": false, "title": "Test Custom", "capabilities": [2, 1, 5, 13, 8], "theme": null, "background_audio": [], "icon": ":/plugins/plugin_custom.png", "type": 1, "start_time": 0, "from_plugin": false, "media_length": 0, "data": "", "timed_slide_interval": 0, "audit": "", "search": "", "name": "custom", "footer": ["Test Custom Credits"], "notes": "", "plugin": "custom", "theme_overwritten": false, "end_time": 0, "processor": null}, "data": [{"verseTag": null, "raw_slide": "Slide 1", "title": "Slide 1"}, {"verseTag": null, "raw_slide": "Slide 2", "title": "Slide 2"}]}}]
|
|
@ -0,0 +1 @@
|
|||
[{"serviceitem": {"header": {"xml_version": null, "auto_play_slides_loop": false, "auto_play_slides_once": false, "will_auto_start": false, "title": "Images", "capabilities": [3, 1, 5, 6], "theme": -1, "background_audio": [], "icon": ":/plugins/plugin_images.png", "type": 2, "start_time": 0, "from_plugin": false, "media_length": 0, "data": "", "timed_slide_interval": 0, "audit": "", "search": "", "name": "images", "footer": [], "notes": "", "plugin": "images", "theme_overwritten": false, "end_time": 0, "processor": null}, "data": ["image_1.jpg"]}}]
|
|
@ -0,0 +1 @@
|
|||
[{"serviceitem": {"header": {"xml_version": null, "auto_play_slides_loop": false, "auto_play_slides_once": false, "will_auto_start": false, "title": "Images", "capabilities": [3, 1, 5, 6], "theme": -1, "background_audio": [], "icon": ":/plugins/plugin_images.png", "type": 2, "start_time": 0, "from_plugin": false, "media_length": 0, "data": "", "timed_slide_interval": 0, "audit": "", "search": "", "name": "images", "footer": [], "notes": "", "plugin": "images", "theme_overwritten": false, "end_time": 0, "processor": null}, "data": [{"path": "/home/openlp/image_1.jpg", "title": "image_1.jpg"}]}}, {"serviceitem": {"header": {"xml_version": null, "auto_play_slides_loop": false, "auto_play_slides_once": false, "will_auto_start": false, "title": "Images", "capabilities": [3, 1, 5, 6], "theme": -1, "background_audio": [], "icon": ":/plugins/plugin_images.png", "type": 2, "start_time": 0, "from_plugin": false, "media_length": 0, "data": "", "timed_slide_interval": 0, "audit": "", "search": "", "name": "images", "footer": [], "notes": "", "plugin": "images", "theme_overwritten": false, "end_time": 0, "processor": null}, "data": [{"path": "/home/openlp/image_2.jpg", "title": "image_2.jpg"}]}}]
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue