This commit is contained in:
Tim Bentley 2014-12-15 22:03:02 +00:00
commit 8c209fbb86
10 changed files with 467 additions and 159 deletions

View File

@ -122,6 +122,9 @@ class OpenLP(OpenLPMixin, QtGui.QApplication):
ftw.initialize(screens) ftw.initialize(screens)
if ftw.exec_() == QtGui.QDialog.Accepted: if ftw.exec_() == QtGui.QDialog.Accepted:
Settings().setValue('core/has run wizard', True) Settings().setValue('core/has run wizard', True)
elif ftw.was_cancelled:
QtCore.QCoreApplication.exit()
sys.exit()
# Correct stylesheet bugs # Correct stylesheet bugs
application_stylesheet = '' application_stylesheet = ''
if not Settings().value('advanced/alternate rows'): if not Settings().value('advanced/alternate rows'):

View File

@ -31,7 +31,6 @@ This module contains the first time wizard.
""" """
import logging import logging
import os import os
import sys
import time import time
import urllib.request import urllib.request
import urllib.parse import urllib.parse
@ -51,30 +50,48 @@ from .firsttimewizard import UiFirstTimeWizard, FirstTimePage
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
class ThemeScreenshotThread(QtCore.QThread): class ThemeScreenshotWorker(QtCore.QObject):
""" """
This thread downloads the theme screenshots. This thread downloads a theme's screenshot
""" """
screenshot_downloaded = QtCore.pyqtSignal(str, str)
finished = QtCore.pyqtSignal()
def __init__(self, themes_url, title, filename, screenshot):
"""
Set up the worker object
"""
self.was_download_cancelled = False
self.themes_url = themes_url
self.title = title
self.filename = filename
self.screenshot = screenshot
super(ThemeScreenshotWorker, self).__init__()
def run(self): def run(self):
""" """
Overridden method to run the thread. Overridden method to run the thread.
""" """
themes = self.parent().config.get('themes', 'files') if self.was_download_cancelled:
themes = themes.split(',') return
config = self.parent().config try:
for theme in themes: urllib.request.urlretrieve('%s%s' % (self.themes_url, self.screenshot),
# Stop if the wizard has been cancelled. os.path.join(gettempdir(), 'openlp', self.screenshot))
if self.parent().was_download_cancelled: # Signal that the screenshot has been downloaded
return self.screenshot_downloaded.emit(self.title, self.filename)
title = config.get('theme_%s' % theme, 'title') except:
filename = config.get('theme_%s' % theme, 'filename') log.exception('Unable to download screenshot')
screenshot = config.get('theme_%s' % theme, 'screenshot') finally:
urllib.request.urlretrieve('%s%s' % (self.parent().themes_url, screenshot), self.finished.emit()
os.path.join(gettempdir(), 'openlp', screenshot))
item = QtGui.QListWidgetItem(title, self.parent().themes_list_widget) @QtCore.pyqtSlot(bool)
item.setData(QtCore.Qt.UserRole, filename) def set_download_canceled(self, toggle):
item.setCheckState(QtCore.Qt.Unchecked) """
item.setFlags(item.flags() | QtCore.Qt.ItemIsUserCheckable) Externally set if the download was canceled
:param toggle: Set if the download was canceled or not
"""
self.was_download_cancelled = toggle
class FirstTimeForm(QtGui.QWizard, UiFirstTimeWizard, RegistryProperties): class FirstTimeForm(QtGui.QWizard, UiFirstTimeWizard, RegistryProperties):
@ -88,22 +105,25 @@ class FirstTimeForm(QtGui.QWizard, UiFirstTimeWizard, RegistryProperties):
Create and set up the first time wizard. Create and set up the first time wizard.
""" """
super(FirstTimeForm, self).__init__(parent) super(FirstTimeForm, self).__init__(parent)
self.web_access = True
self.web = ''
self.setup_ui(self) self.setup_ui(self)
def get_next_page_id(self): def get_next_page_id(self):
""" """
Returns the id of the next FirstTimePage to go to based on enabled plugins Returns the id of the next FirstTimePage to go to based on enabled plugins
""" """
# The songs plugin is enabled
if FirstTimePage.Welcome < self.currentId() < FirstTimePage.Songs and self.songs_check_box.isChecked(): if FirstTimePage.Welcome < self.currentId() < FirstTimePage.Songs and self.songs_check_box.isChecked():
print('Go for songs! %r' % self.songs_check_box.isChecked()) # If the songs plugin is enabled then go to the songs page
return FirstTimePage.Songs return FirstTimePage.Songs
# The Bibles plugin is enabled
elif FirstTimePage.Welcome < self.currentId() < FirstTimePage.Bibles and self.bible_check_box.isChecked(): elif FirstTimePage.Welcome < self.currentId() < FirstTimePage.Bibles and self.bible_check_box.isChecked():
# Otherwise, if the Bibles plugin is enabled then go to the Bibles page
return FirstTimePage.Bibles return FirstTimePage.Bibles
elif FirstTimePage.Welcome < self.currentId() < FirstTimePage.Themes: elif FirstTimePage.Welcome < self.currentId() < FirstTimePage.Themes:
# Otherwise, if the current page is somewhere between the Welcome and the Themes pages, go to the themes
return FirstTimePage.Themes return FirstTimePage.Themes
else: else:
# If all else fails, go to the next page
return self.currentId() + 1 return self.currentId() + 1
def nextId(self): def nextId(self):
@ -111,18 +131,20 @@ class FirstTimeForm(QtGui.QWizard, UiFirstTimeWizard, RegistryProperties):
Determine the next page in the Wizard to go to. Determine the next page in the Wizard to go to.
""" """
self.application.process_events() self.application.process_events()
if self.currentId() == FirstTimePage.Plugins: if self.currentId() == FirstTimePage.Download:
if not self.web_access: if not self.web_access:
return FirstTimePage.NoInternet return FirstTimePage.NoInternet
else: else:
return self.get_next_page_id() return FirstTimePage.Plugins
elif self.currentId() == FirstTimePage.Plugins:
return self.get_next_page_id()
elif self.currentId() == FirstTimePage.Progress: elif self.currentId() == FirstTimePage.Progress:
return -1 return -1
elif self.currentId() == FirstTimePage.NoInternet: elif self.currentId() == FirstTimePage.NoInternet:
return FirstTimePage.Progress return FirstTimePage.Progress
elif self.currentId() == FirstTimePage.Themes: elif self.currentId() == FirstTimePage.Themes:
self.application.set_busy_cursor() self.application.set_busy_cursor()
while not self.theme_screenshot_thread.isFinished(): while not all([thread.isFinished() for thread in self.theme_screenshot_threads]):
time.sleep(0.1) time.sleep(0.1)
self.application.process_events() self.application.process_events()
# Build the screenshot icons, as this can not be done in the thread. # Build the screenshot icons, as this can not be done in the thread.
@ -146,11 +168,20 @@ class FirstTimeForm(QtGui.QWizard, UiFirstTimeWizard, RegistryProperties):
:param screens: The screens detected by OpenLP :param screens: The screens detected by OpenLP
""" """
self.screens = screens self.screens = screens
self.was_cancelled = False
self.theme_screenshot_threads = []
self.theme_screenshot_workers = []
self.has_run_wizard = False
def _download_index(self):
"""
Download the configuration file and kick off the theme screenshot download threads
"""
# check to see if we have web access # check to see if we have web access
self.web_access = False self.web_access = False
self.web = 'http://openlp.org/files/frw/'
self.config = ConfigParser() self.config = ConfigParser()
user_agent = 'OpenLP/' + Registry().get('application').applicationVersion() user_agent = 'OpenLP/' + Registry().get('application').applicationVersion()
self.application.process_events()
web_config = get_web_page('%s%s' % (self.web, 'download.cfg'), header=('User-Agent', user_agent)) web_config = get_web_page('%s%s' % (self.web, 'download.cfg'), header=('User-Agent', user_agent))
if web_config: if web_config:
files = web_config.read() files = web_config.read()
@ -165,24 +196,8 @@ class FirstTimeForm(QtGui.QWizard, UiFirstTimeWizard, RegistryProperties):
log.debug('A problem occured while parsing the downloaded config file') log.debug('A problem occured while parsing the downloaded config file')
trace_error_handler(log) trace_error_handler(log)
self.update_screen_list_combo() self.update_screen_list_combo()
self.was_download_cancelled = False self.application.process_events()
self.theme_screenshot_thread = None
self.has_run_wizard = False
self.downloading = translate('OpenLP.FirstTimeWizard', 'Downloading %s...') self.downloading = translate('OpenLP.FirstTimeWizard', 'Downloading %s...')
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('config_screen_changed', self.update_screen_list_combo)
def set_defaults(self):
"""
Set up display at start of theme edit.
"""
self.restart()
check_directory_exists(os.path.join(gettempdir(), 'openlp'))
self.no_internet_finish_button.setVisible(False)
# Check if this is a re-run of the wizard.
self.has_run_wizard = Settings().value('core/has run wizard')
if self.has_run_wizard: if self.has_run_wizard:
self.songs_check_box.setChecked(self.plugin_manager.get_plugin_by_name('songs').is_active()) self.songs_check_box.setChecked(self.plugin_manager.get_plugin_by_name('songs').is_active())
self.bible_check_box.setChecked(self.plugin_manager.get_plugin_by_name('bibles').is_active()) self.bible_check_box.setChecked(self.plugin_manager.get_plugin_by_name('bibles').is_active())
@ -199,6 +214,7 @@ class FirstTimeForm(QtGui.QWizard, UiFirstTimeWizard, RegistryProperties):
songs = self.config.get('songs', 'languages') songs = self.config.get('songs', 'languages')
songs = songs.split(',') songs = songs.split(',')
for song in songs: for song in songs:
self.application.process_events()
title = self.config.get('songs_%s' % song, 'title') title = self.config.get('songs_%s' % song, 'title')
filename = self.config.get('songs_%s' % song, 'filename') filename = self.config.get('songs_%s' % song, 'filename')
item = QtGui.QListWidgetItem(title, self.songs_list_widget) item = QtGui.QListWidgetItem(title, self.songs_list_widget)
@ -208,11 +224,13 @@ class FirstTimeForm(QtGui.QWizard, UiFirstTimeWizard, RegistryProperties):
bible_languages = self.config.get('bibles', 'languages') bible_languages = self.config.get('bibles', 'languages')
bible_languages = bible_languages.split(',') bible_languages = bible_languages.split(',')
for lang in bible_languages: for lang in bible_languages:
self.application.process_events()
language = self.config.get('bibles_%s' % lang, 'title') language = self.config.get('bibles_%s' % lang, 'title')
lang_item = QtGui.QTreeWidgetItem(self.bibles_tree_widget, [language]) lang_item = QtGui.QTreeWidgetItem(self.bibles_tree_widget, [language])
bibles = self.config.get('bibles_%s' % lang, 'translations') bibles = self.config.get('bibles_%s' % lang, 'translations')
bibles = bibles.split(',') bibles = bibles.split(',')
for bible in bibles: for bible in bibles:
self.application.process_events()
title = self.config.get('bible_%s' % bible, 'title') title = self.config.get('bible_%s' % bible, 'title')
filename = self.config.get('bible_%s' % bible, 'filename') filename = self.config.get('bible_%s' % bible, 'filename')
item = QtGui.QTreeWidgetItem(lang_item, [title]) item = QtGui.QTreeWidgetItem(lang_item, [title])
@ -220,9 +238,38 @@ class FirstTimeForm(QtGui.QWizard, UiFirstTimeWizard, RegistryProperties):
item.setCheckState(0, QtCore.Qt.Unchecked) item.setCheckState(0, QtCore.Qt.Unchecked)
item.setFlags(item.flags() | QtCore.Qt.ItemIsUserCheckable) item.setFlags(item.flags() | QtCore.Qt.ItemIsUserCheckable)
self.bibles_tree_widget.expandAll() self.bibles_tree_widget.expandAll()
# Download the theme screenshots. self.application.process_events()
self.theme_screenshot_thread = ThemeScreenshotThread(self) # Download the theme screenshots
self.theme_screenshot_thread.start() themes = self.config.get('themes', 'files').split(',')
for theme in themes:
self.application.process_events()
title = self.config.get('theme_%s' % theme, 'title')
filename = self.config.get('theme_%s' % theme, 'filename')
screenshot = self.config.get('theme_%s' % theme, 'screenshot')
worker = ThemeScreenshotWorker(self.themes_url, title, filename, screenshot)
self.theme_screenshot_workers.append(worker)
worker.screenshot_downloaded.connect(self.on_screenshot_downloaded)
thread = QtCore.QThread(self)
self.theme_screenshot_threads.append(thread)
thread.started.connect(worker.run)
worker.finished.connect(thread.quit)
worker.moveToThread(thread)
thread.start()
def set_defaults(self):
"""
Set up display at start of theme edit.
"""
self.restart()
self.web = 'http://openlp.org/files/frw/'
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('config_screen_changed', self.update_screen_list_combo)
self.no_internet_finish_button.setVisible(False)
# Check if this is a re-run of the wizard.
self.has_run_wizard = Settings().value('core/has run wizard')
check_directory_exists(os.path.join(gettempdir(), 'openlp'))
def update_screen_list_combo(self): def update_screen_list_combo(self):
""" """
@ -241,12 +288,20 @@ class FirstTimeForm(QtGui.QWizard, UiFirstTimeWizard, RegistryProperties):
self.application.process_events() self.application.process_events()
if page_id != -1: if page_id != -1:
self.last_id = page_id self.last_id = page_id
if page_id == FirstTimePage.Plugins: if page_id == FirstTimePage.Download:
self.back_button.setVisible(False)
self.next_button.setVisible(False)
# Set the no internet page text. # Set the no internet page text.
if self.has_run_wizard: if self.has_run_wizard:
self.no_internet_label.setText(self.no_internet_text) self.no_internet_label.setText(self.no_internet_text)
else: else:
self.no_internet_label.setText(self.no_internet_text + self.cancel_wizard_text) self.no_internet_label.setText(self.no_internet_text + self.cancel_wizard_text)
self.application.set_busy_cursor()
self._download_index()
self.application.set_normal_cursor()
self.back_button.setVisible(False)
self.next_button.setVisible(True)
self.next()
elif page_id == FirstTimePage.Defaults: elif page_id == FirstTimePage.Defaults:
self.theme_combo_box.clear() self.theme_combo_box.clear()
for index in range(self.themes_list_widget.count()): for index in range(self.themes_list_widget.count()):
@ -266,15 +321,12 @@ class FirstTimeForm(QtGui.QWizard, UiFirstTimeWizard, RegistryProperties):
elif page_id == FirstTimePage.NoInternet: elif page_id == FirstTimePage.NoInternet:
self.back_button.setVisible(False) self.back_button.setVisible(False)
self.next_button.setVisible(False) self.next_button.setVisible(False)
self.cancel_button.setVisible(False)
self.no_internet_finish_button.setVisible(True) self.no_internet_finish_button.setVisible(True)
if self.has_run_wizard: elif page_id == FirstTimePage.Plugins:
self.cancel_button.setVisible(False) self.back_button.setVisible(False)
elif page_id == FirstTimePage.Progress: elif page_id == FirstTimePage.Progress:
self.application.set_busy_cursor() 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._pre_wizard() self._pre_wizard()
self._perform_wizard() self._perform_wizard()
self._post_wizard() self._post_wizard()
@ -284,17 +336,28 @@ class FirstTimeForm(QtGui.QWizard, UiFirstTimeWizard, RegistryProperties):
""" """
Process the triggering of the cancel button. Process the triggering of the cancel button.
""" """
if self.last_id == FirstTimePage.NoInternet or \ self.was_cancelled = True
(self.last_id <= FirstTimePage.Plugins and not self.has_run_wizard): if self.theme_screenshot_workers:
QtCore.QCoreApplication.exit() for worker in self.theme_screenshot_workers:
sys.exit() worker.set_download_canceled(True)
self.was_download_cancelled = True
# Was the thread created. # Was the thread created.
if self.theme_screenshot_thread: if self.theme_screenshot_threads:
while self.theme_screenshot_thread.isRunning(): while any([thread.isRunning() for thread in self.theme_screenshot_threads]):
time.sleep(0.1) time.sleep(0.1)
self.application.set_normal_cursor() self.application.set_normal_cursor()
def on_screenshot_downloaded(self, title, filename):
"""
Add an item to the list when a theme has been downloaded
:param title: The title of the theme
:param filename: The filename of the theme
"""
item = QtGui.QListWidgetItem(title, self.themes_list_widget)
item.setData(QtCore.Qt.UserRole, filename)
item.setCheckState(QtCore.Qt.Unchecked)
item.setFlags(item.flags() | QtCore.Qt.ItemIsUserCheckable)
def on_no_internet_finish_button_clicked(self): def on_no_internet_finish_button_clicked(self):
""" """
Process the triggering of the "Finish" button on the No Internet page. Process the triggering of the "Finish" button on the No Internet page.
@ -321,7 +384,7 @@ class FirstTimeForm(QtGui.QWizard, UiFirstTimeWizard, RegistryProperties):
url_file = urllib.request.urlopen(url, timeout=CONNECTION_TIMEOUT) url_file = urllib.request.urlopen(url, timeout=CONNECTION_TIMEOUT)
filename = open(f_path, "wb") filename = open(f_path, "wb")
# Download until finished or canceled. # Download until finished or canceled.
while not self.was_download_cancelled: while not self.was_cancelled:
data = url_file.read(block_size) data = url_file.read(block_size)
if not data: if not data:
break break
@ -341,7 +404,7 @@ class FirstTimeForm(QtGui.QWizard, UiFirstTimeWizard, RegistryProperties):
continue continue
break break
# Delete file if cancelled, it may be a partial file. # Delete file if cancelled, it may be a partial file.
if self.was_download_cancelled: if self.was_cancelled:
os.remove(f_path) os.remove(f_path)
return True return True
@ -354,6 +417,7 @@ class FirstTimeForm(QtGui.QWizard, UiFirstTimeWizard, RegistryProperties):
for index, theme in enumerate(themes): for index, theme in enumerate(themes):
screenshot = self.config.get('theme_%s' % theme, 'screenshot') screenshot = self.config.get('theme_%s' % theme, 'screenshot')
item = self.themes_list_widget.item(index) item = self.themes_list_widget.item(index)
# if item:
item.setIcon(build_icon(os.path.join(gettempdir(), 'openlp', screenshot))) item.setIcon(build_icon(os.path.join(gettempdir(), 'openlp', screenshot)))
def _get_file_size(self, url): def _get_file_size(self, url):

View File

@ -41,13 +41,14 @@ class FirstTimePage(object):
An enumeration class with each of the pages of the wizard. An enumeration class with each of the pages of the wizard.
""" """
Welcome = 0 Welcome = 0
Plugins = 1 Download = 1
NoInternet = 2 NoInternet = 2
Songs = 3 Plugins = 3
Bibles = 4 Songs = 4
Themes = 5 Bibles = 5
Defaults = 6 Themes = 6
Progress = 7 Defaults = 7
Progress = 8
class UiFirstTimeWizard(object): class UiFirstTimeWizard(object):
@ -78,6 +79,27 @@ class UiFirstTimeWizard(object):
self.next_button = self.button(QtGui.QWizard.NextButton) self.next_button = self.button(QtGui.QWizard.NextButton)
self.back_button = self.button(QtGui.QWizard.BackButton) self.back_button = self.button(QtGui.QWizard.BackButton)
add_welcome_page(first_time_wizard, ':/wizards/wizard_firsttime.bmp') add_welcome_page(first_time_wizard, ':/wizards/wizard_firsttime.bmp')
# The download page
self.download_page = QtGui.QWizardPage()
self.download_page.setObjectName('download_page')
self.download_layout = QtGui.QVBoxLayout(self.download_page)
self.download_layout.setMargin(48)
self.download_layout.setObjectName('download_layout')
self.download_label = QtGui.QLabel(self.download_page)
self.download_label.setObjectName('download_label')
self.download_layout.addWidget(self.download_label)
first_time_wizard.setPage(FirstTimePage.Download, self.download_page)
# The "you don't have an internet connection" page.
self.no_internet_page = QtGui.QWizardPage()
self.no_internet_page.setObjectName('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('no_internet_layout')
self.no_internet_label = QtGui.QLabel(self.no_internet_page)
self.no_internet_label.setWordWrap(True)
self.no_internet_label.setObjectName('no_internet_label')
self.no_internet_layout.addWidget(self.no_internet_label)
first_time_wizard.setPage(FirstTimePage.NoInternet, self.no_internet_page)
# The plugins page # The plugins page
self.plugin_page = QtGui.QWizardPage() self.plugin_page = QtGui.QWizardPage()
self.plugin_page.setObjectName('plugin_page') self.plugin_page.setObjectName('plugin_page')
@ -120,17 +142,6 @@ class UiFirstTimeWizard(object):
self.alert_check_box.setObjectName('alert_check_box') self.alert_check_box.setObjectName('alert_check_box')
self.plugin_layout.addWidget(self.alert_check_box) self.plugin_layout.addWidget(self.alert_check_box)
first_time_wizard.setPage(FirstTimePage.Plugins, self.plugin_page) first_time_wizard.setPage(FirstTimePage.Plugins, self.plugin_page)
# The "you don't have an internet connection" page.
self.no_internet_page = QtGui.QWizardPage()
self.no_internet_page.setObjectName('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('no_internet_layout')
self.no_internet_label = QtGui.QLabel(self.no_internet_page)
self.no_internet_label.setWordWrap(True)
self.no_internet_label.setObjectName('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 # The song samples page
self.songs_page = QtGui.QWizardPage() self.songs_page = QtGui.QWizardPage()
self.songs_page.setObjectName('songs_page') self.songs_page.setObjectName('songs_page')
@ -221,6 +232,11 @@ class UiFirstTimeWizard(object):
translate('OpenLP.FirstTimeWizard', 'This wizard will help you to configure OpenLP for initial use. ' translate('OpenLP.FirstTimeWizard', 'This wizard will help you to configure OpenLP for initial use. '
'Click the %s button below to start.') % 'Click the %s button below to start.') %
clean_button_text(first_time_wizard.buttonText(QtGui.QWizard.NextButton))) clean_button_text(first_time_wizard.buttonText(QtGui.QWizard.NextButton)))
self.download_page.setTitle(translate('OpenLP.FirstTimeWizard', 'Downloading Resource Index'))
self.download_page.setSubTitle(translate('OpenLP.FirstTimeWizard', 'Please wait while the resource index is '
'downloaded.'))
self.download_label.setText(translate('OpenLP.FirstTimeWizard', 'Please wait while OpenLP downloads the '
'resource index file...'))
self.plugin_page.setTitle(translate('OpenLP.FirstTimeWizard', 'Activate required Plugins')) 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.plugin_page.setSubTitle(translate('OpenLP.FirstTimeWizard', 'Select the Plugins you wish to use. '))
self.songs_check_box.setText(translate('OpenLP.FirstTimeWizard', 'Songs')) self.songs_check_box.setText(translate('OpenLP.FirstTimeWizard', 'Songs'))
@ -257,5 +273,8 @@ class UiFirstTimeWizard(object):
'Set up default settings to be used by OpenLP.')) 'Set up default settings to be used by OpenLP.'))
self.display_label.setText(translate('OpenLP.FirstTimeWizard', 'Default output display:')) self.display_label.setText(translate('OpenLP.FirstTimeWizard', 'Default output display:'))
self.theme_label.setText(translate('OpenLP.FirstTimeWizard', 'Select default theme:')) self.theme_label.setText(translate('OpenLP.FirstTimeWizard', 'Select default theme:'))
self.progress_page.setTitle(translate('OpenLP.FirstTimeWizard', 'Downloading and Configuring'))
self.progress_page.setSubTitle(translate('OpenLP.FirstTimeWizard', 'Please wait while resources are downloaded '
'and OpenLP is configured.'))
self.progress_label.setText(translate('OpenLP.FirstTimeWizard', 'Starting configuration process...')) self.progress_label.setText(translate('OpenLP.FirstTimeWizard', 'Starting configuration process...'))
first_time_wizard.setButtonText(QtGui.QWizard.CustomButton1, translate('OpenLP.FirstTimeWizard', 'Finish')) first_time_wizard.setButtonText(QtGui.QWizard.CustomButton1, translate('OpenLP.FirstTimeWizard', 'Finish'))

View File

@ -690,7 +690,7 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow, RegistryProperties):
first_run_wizard = FirstTimeForm(self) first_run_wizard = FirstTimeForm(self)
first_run_wizard.initialize(ScreenList()) first_run_wizard.initialize(ScreenList())
first_run_wizard.exec_() first_run_wizard.exec_()
if first_run_wizard.was_download_cancelled: if first_run_wizard.was_cancelled:
return return
self.application.set_busy_cursor() self.application.set_busy_cursor()
self.first_time() self.first_time()

View File

@ -152,16 +152,19 @@ def set_button_tooltip(bar):
""" """
for button in bar.buttons(): for button in bar.buttons():
if bar.standardButton(button) == QDialogButtonBox.Cancel: if bar.standardButton(button) == QDialogButtonBox.Cancel:
tip = "Ignoring current changes and return to OpenLP" button.setToolTip(translate('OpenLP.SourceSelectForm',
'Ignoring current changes and return to OpenLP'))
elif bar.standardButton(button) == QDialogButtonBox.Reset: elif bar.standardButton(button) == QDialogButtonBox.Reset:
tip = "Delete all user-defined text and revert to PJLink default text" button.setToolTip(translate('OpenLP.SourceSelectForm',
'Delete all user-defined text and revert to PJLink default text'))
elif bar.standardButton(button) == QDialogButtonBox.Discard: elif bar.standardButton(button) == QDialogButtonBox.Discard:
tip = "Discard changes and reset to previous user-defined text" button.setToolTip(translate('OpenLP.SourceSelectForm',
'Discard changes and reset to previous user-defined text'))
elif bar.standardButton(button) == QDialogButtonBox.Ok: elif bar.standardButton(button) == QDialogButtonBox.Ok:
tip = "Save changes and return to OpenLP" button.setToolTip(translate('OpenLP.SourceSelectForm',
'Save changes and return to OpenLP'))
else: else:
tip = "" log.debug('No tooltip for button {}'.format(button.text()))
button.setToolTip(tip)
class FingerTabBarWidget(QTabBar): class FingerTabBarWidget(QTabBar):
@ -237,12 +240,13 @@ class SourceSelectTabs(QDialog):
""" """
log.debug('Initializing SourceSelectTabs()') log.debug('Initializing SourceSelectTabs()')
super(SourceSelectTabs, self).__init__(parent) super(SourceSelectTabs, self).__init__(parent)
self.setMinimumWidth(350)
self.projectordb = projectordb self.projectordb = projectordb
self.edit = edit self.edit = edit
if self.edit: if self.edit:
title = translate('OpenLP.SourceSelectForm', 'Select Projector Source')
else:
title = translate('OpenLP.SourceSelectForm', 'Edit Projector Source Text') title = translate('OpenLP.SourceSelectForm', 'Edit Projector Source Text')
else:
title = translate('OpenLP.SourceSelectForm', 'Select Projector Source')
self.setWindowTitle(title) self.setWindowTitle(title)
self.setObjectName('source_select_tabs') self.setObjectName('source_select_tabs')
self.setWindowIcon(build_icon(':/icon/openlp-log-32x32.png')) self.setWindowIcon(build_icon(':/icon/openlp-log-32x32.png'))
@ -286,6 +290,10 @@ class SourceSelectTabs(QDialog):
thistab = self.tabwidget.addTab(tab, PJLINK_DEFAULT_SOURCES[key]) thistab = self.tabwidget.addTab(tab, PJLINK_DEFAULT_SOURCES[key])
if buttonchecked: if buttonchecked:
self.tabwidget.setCurrentIndex(thistab) self.tabwidget.setCurrentIndex(thistab)
self.button_box = QDialogButtonBox(QtGui.QDialogButtonBox.Reset |
QtGui.QDialogButtonBox.Discard |
QtGui.QDialogButtonBox.Ok |
QtGui.QDialogButtonBox.Cancel)
else: else:
for key in keys: for key in keys:
(tab, button_count, buttonchecked) = Build_Tab(group=self.button_group, (tab, button_count, buttonchecked) = Build_Tab(group=self.button_group,
@ -297,10 +305,8 @@ class SourceSelectTabs(QDialog):
thistab = self.tabwidget.addTab(tab, PJLINK_DEFAULT_SOURCES[key]) thistab = self.tabwidget.addTab(tab, PJLINK_DEFAULT_SOURCES[key])
if buttonchecked: if buttonchecked:
self.tabwidget.setCurrentIndex(thistab) self.tabwidget.setCurrentIndex(thistab)
self.button_box = QDialogButtonBox(QtGui.QDialogButtonBox.Reset | self.button_box = QDialogButtonBox(QtGui.QDialogButtonBox.Ok |
QtGui.QDialogButtonBox.Discard | QtGui.QDialogButtonBox.Cancel)
QtGui.QDialogButtonBox.Ok |
QtGui.QDialogButtonBox.Cancel)
self.button_box.clicked.connect(self.button_clicked) self.button_box.clicked.connect(self.button_clicked)
self.layout.addWidget(self.button_box) self.layout.addWidget(self.button_box)
set_button_tooltip(self.button_box) set_button_tooltip(self.button_box)
@ -321,9 +327,9 @@ class SourceSelectTabs(QDialog):
if self.button_box.standardButton(button) == self.button_box.Cancel: if self.button_box.standardButton(button) == self.button_box.Cancel:
self.done(0) self.done(0)
elif self.button_box.standardButton(button) == self.button_box.Reset: elif self.button_box.standardButton(button) == self.button_box.Reset:
self.delete_sources()
elif self.button_box.standardButton(button) == self.button_box.Discard:
self.done(100) self.done(100)
elif self.button_box.standardButton(button) == self.button_box.Discard:
self.delete_sources()
elif self.button_box.standardButton(button) == self.button_box.Ok: elif self.button_box.standardButton(button) == self.button_box.Ok:
return self.accept_me() return self.accept_me()
else: else:
@ -331,9 +337,11 @@ class SourceSelectTabs(QDialog):
def delete_sources(self): def delete_sources(self):
msg = QtGui.QMessageBox() msg = QtGui.QMessageBox()
msg.setText('Delete entries for this projector') msg.setText(translate('OpenLP.SourceSelectForm', 'Delete entries for this projector'))
msg.setInformativeText('Are you sure you want to delete ALL user-defined ' msg.setInformativeText(translate('OpenLP.SourceSelectForm',
'source input text for this projector?') 'Are you sure you want to delete ALL user-defined '),
translate('OpenLP.SourceSelectForm',
'source input text for this projector?'))
msg.setStandardButtons(msg.Cancel | msg.Ok) msg.setStandardButtons(msg.Cancel | msg.Ok)
msg.setDefaultButton(msg.Cancel) msg.setDefaultButton(msg.Cancel)
ans = msg.exec_() ans = msg.exec_()
@ -382,7 +390,11 @@ class SourceSelectSingle(QDialog):
log.debug('Initializing SourceSelectSingle()') log.debug('Initializing SourceSelectSingle()')
self.projectordb = projectordb self.projectordb = projectordb
super(SourceSelectSingle, self).__init__(parent) super(SourceSelectSingle, self).__init__(parent)
self.setWindowTitle(translate('OpenLP.SourceSelectSingle', 'Select Projector Source')) self.edit = edit
if self.edit:
title = translate('OpenLP.SourceSelectForm', 'Edit Projector Source Text')
else:
title = translate('OpenLP.SourceSelectForm', 'Select Projector Source')
self.setObjectName('source_select_single') self.setObjectName('source_select_single')
self.setWindowIcon(build_icon(':/icon/openlp-log-32x32.png')) self.setWindowIcon(build_icon(':/icon/openlp-log-32x32.png'))
self.setModal(True) self.setModal(True)
@ -418,6 +430,10 @@ class SourceSelectSingle(QDialog):
item.setText(source_item.text) item.setText(source_item.text)
self.layout.addRow(PJLINK_DEFAULT_CODES[key], item) self.layout.addRow(PJLINK_DEFAULT_CODES[key], item)
self.button_group.append(item) self.button_group.append(item)
self.button_box = QDialogButtonBox(QtGui.QDialogButtonBox.Reset |
QtGui.QDialogButtonBox.Discard |
QtGui.QDialogButtonBox.Ok |
QtGui.QDialogButtonBox.Cancel)
else: else:
for key in keys: for key in keys:
source_text = self.projectordb.get_source_by_code(code=key, projector_id=self.projector.db_item.id) source_text = self.projectordb.get_source_by_code(code=key, projector_id=self.projector.db_item.id)
@ -427,10 +443,8 @@ class SourceSelectSingle(QDialog):
self.layout.addWidget(button) self.layout.addWidget(button)
self.button_group.addButton(button, int(key)) self.button_group.addButton(button, int(key))
button_list.append(key) button_list.append(key)
self.button_box = QDialogButtonBox(QtGui.QDialogButtonBox.Reset | self.button_box = QDialogButtonBox(QtGui.QDialogButtonBox.Ok |
QtGui.QDialogButtonBox.Discard | QtGui.QDialogButtonBox.Cancel)
QtGui.QDialogButtonBox.Ok |
QtGui.QDialogButtonBox.Cancel)
self.button_box.clicked.connect(self.button_clicked) self.button_box.clicked.connect(self.button_clicked)
self.layout.addWidget(self.button_box) self.layout.addWidget(self.button_box)
self.setMinimumHeight(key_count*25) self.setMinimumHeight(key_count*25)
@ -452,9 +466,9 @@ class SourceSelectSingle(QDialog):
if self.button_box.standardButton(button) == self.button_box.Cancel: if self.button_box.standardButton(button) == self.button_box.Cancel:
self.done(0) self.done(0)
elif self.button_box.standardButton(button) == self.button_box.Reset: elif self.button_box.standardButton(button) == self.button_box.Reset:
self.delete_sources()
elif self.button_box.standardButton(button) == self.button_box.Discard:
self.done(100) self.done(100)
elif self.button_box.standardButton(button) == self.button_box.Discard:
self.delete_sources()
elif self.button_box.standardButton(button) == self.button_box.Ok: elif self.button_box.standardButton(button) == self.button_box.Ok:
return self.accept_me() return self.accept_me()
else: else:
@ -462,9 +476,11 @@ class SourceSelectSingle(QDialog):
def delete_sources(self): def delete_sources(self):
msg = QtGui.QMessageBox() msg = QtGui.QMessageBox()
msg.setText('Delete entries for this projector') msg.setText(translate('OpenLP.SourceSelectForm', 'Delete entries for this projector'))
msg.setInformativeText('Are you sure you want to delete ALL user-defined ' msg.setInformativeText(translate('OpenLP.SourceSelectForm',
'source input text for this projector?') 'Are you sure you want to delete ALL user-defined '),
translate('OpenLP.SourceSelectForm',
'source input text for this projector?'))
msg.setStandardButtons(msg.Cancel | msg.Ok) msg.setStandardButtons(msg.Cancel | msg.Ok)
msg.setDefaultButton(msg.Cancel) msg.setDefaultButton(msg.Cancel)
ans = msg.exec_() ans = msg.exec_()

View File

@ -64,7 +64,6 @@ log = logging.getLogger(__name__ + '.__init__')
APPLICATION_VERSION = {} APPLICATION_VERSION = {}
IMAGES_FILTER = None IMAGES_FILTER = None
ICU_COLLATOR = None ICU_COLLATOR = None
UNO_CONNECTION_TYPE = 'pipe'
CONTROL_CHARS = re.compile(r'[\x00-\x1F\x7F-\x9F]', re.UNICODE) CONTROL_CHARS = re.compile(r'[\x00-\x1F\x7F-\x9F]', re.UNICODE)
INVALID_FILE_CHARS = re.compile(r'[\\/:\*\?"<>\|\+\[\]%]', re.UNICODE) INVALID_FILE_CHARS = re.compile(r'[\\/:\*\?"<>\|\+\[\]%]', re.UNICODE)
DIGITS_OR_NONDIGITS = re.compile(r'\d+|\D+', re.UNICODE) DIGITS_OR_NONDIGITS = re.compile(r'\d+|\D+', re.UNICODE)
@ -423,7 +422,7 @@ def get_web_page(url, header=None, update_openlp=False):
return page return page
def get_uno_command(): def get_uno_command(connection_type='pipe'):
""" """
Returns the UNO command to launch an openoffice.org instance. Returns the UNO command to launch an openoffice.org instance.
""" """
@ -434,21 +433,21 @@ def get_uno_command():
raise FileNotFoundError('Command not found') raise FileNotFoundError('Command not found')
OPTIONS = '--nologo --norestore --minimized --nodefault --nofirststartwizard' OPTIONS = '--nologo --norestore --minimized --nodefault --nofirststartwizard'
if UNO_CONNECTION_TYPE == 'pipe': if connection_type == 'pipe':
CONNECTION = '"--accept=pipe,name=openlp_pipe;urp;"' CONNECTION = '"--accept=pipe,name=openlp_pipe;urp;"'
else: else:
CONNECTION = '"--accept=socket,host=localhost,port=2002;urp;"' CONNECTION = '"--accept=socket,host=localhost,port=2002;urp;"'
return '%s %s %s' % (command, OPTIONS, CONNECTION) return '%s %s %s' % (command, OPTIONS, CONNECTION)
def get_uno_instance(resolver): def get_uno_instance(resolver, connection_type='pipe'):
""" """
Returns a running openoffice.org instance. Returns a running openoffice.org instance.
:param resolver: The UNO resolver to use to find a running instance. :param resolver: The UNO resolver to use to find a running instance.
""" """
log.debug('get UNO Desktop Openoffice - resolve') log.debug('get UNO Desktop Openoffice - resolve')
if UNO_CONNECTION_TYPE == 'pipe': if connection_type == 'pipe':
return resolver.resolve('uno:pipe,name=openlp_pipe;urp;StarOffice.ComponentContext') return resolver.resolve('uno:pipe,name=openlp_pipe;urp;StarOffice.ComponentContext')
else: else:
return resolver.resolve('uno:socket,host=localhost,port=2002;urp;StarOffice.ComponentContext') return resolver.resolve('uno:socket,host=localhost,port=2002;urp;StarOffice.ComponentContext')

View File

@ -30,6 +30,7 @@
Package to test the openlp.core.ui.firsttimeform package. Package to test the openlp.core.ui.firsttimeform package.
""" """
from configparser import ConfigParser from configparser import ConfigParser
import os
from unittest import TestCase from unittest import TestCase
from openlp.core.common import Registry from openlp.core.common import Registry
@ -71,55 +72,117 @@ class TestFirstTimeForm(TestCase, TestMixin):
def setUp(self): def setUp(self):
self.setup_application() self.setup_application()
self.app.setApplicationVersion('0.0') self.app.setApplicationVersion('0.0')
# Set up a fake "set_normal_cursor" method since we're not dealing with an actual OpenLP application object
self.app.set_normal_cursor = lambda: None
self.app.process_events = lambda: None
Registry.create() Registry.create()
Registry().register('application', self.app) Registry().register('application', self.app)
def basic_initialise_test(self): def initialise_test(self):
""" """
Test if we can intialise the FirstTimeForm without a config file Test if we can intialise the FirstTimeForm
""" """
# GIVEN: A mocked get_web_page, a First Time Wizard and an expected screen object # GIVEN: A First Time Wizard and an expected screen object
with patch('openlp.core.ui.firsttimeform.get_web_page') as mocked_get_web_page: frw = FirstTimeForm(None)
first_time_form = FirstTimeForm(None) expected_screens = MagicMock()
expected_screens = MagicMock()
expected_web_url = 'http://openlp.org/files/frw/'
expected_user_agent = 'OpenLP/0.0'
mocked_get_web_page.return_value = None
# WHEN: The First Time Wizard is initialised # WHEN: The First Time Wizard is initialised
first_time_form.initialize(expected_screens) frw.initialize(expected_screens)
# THEN: The First Time Form web configuration file should be accessible and parseable # THEN: The screens should be set up, and the default values initialised
self.assertEqual(expected_screens, first_time_form.screens, 'The screens should be correct') self.assertEqual(expected_screens, frw.screens, 'The screens should be correct')
self.assertEqual(expected_web_url, first_time_form.web, 'The base path of the URL should be correct') self.assertTrue(frw.web_access, 'The default value of self.web_access should be True')
self.assertIsInstance(first_time_form.config, ConfigParser, 'The config object should be a ConfigParser') self.assertFalse(frw.was_cancelled, 'The default value of self.was_cancelled should be False')
mocked_get_web_page.assert_called_with(expected_web_url + 'download.cfg', self.assertListEqual([], frw.theme_screenshot_threads, 'The list of threads should be empty')
header=('User-Agent', expected_user_agent)) self.assertListEqual([], frw.theme_screenshot_workers, 'The list of workers should be empty')
self.assertFalse(frw.has_run_wizard, 'has_run_wizard should be False')
def config_initialise_test(self): def set_defaults_test(self):
""" """
Test if we can intialise the FirstTimeForm with a config file Test that the default values are set when set_defaults() is run
""" """
# GIVEN: A mocked get_web_page, a First Time Wizard and an expected screen object # GIVEN: An initialised FRW and a whole lot of stuff mocked out
with patch('openlp.core.ui.firsttimeform.get_web_page') as mocked_get_web_page: frw = FirstTimeForm(None)
first_time_form = FirstTimeForm(None) frw.initialize(MagicMock())
expected_web_url = 'http://openlp.org/files/frw/' with patch.object(frw, 'restart') as mocked_restart, \
expected_songs_url = 'http://example.com/frw/songs/' patch.object(frw, 'cancel_button') as mocked_cancel_button, \
expected_bibles_url = 'http://example.com/frw/bibles/' patch.object(frw, 'no_internet_finish_button') as mocked_no_internet_finish_btn, \
expected_themes_url = 'http://example.com/frw/themes/' patch.object(frw, 'currentIdChanged') as mocked_currentIdChanged, \
expected_user_agent = 'OpenLP/0.0' patch.object(Registry, 'register_function') as mocked_register_function, \
mocked_get_web_page.return_value.read.return_value = FAKE_CONFIG patch('openlp.core.ui.firsttimeform.Settings') as MockedSettings, \
patch('openlp.core.ui.firsttimeform.gettempdir') as mocked_gettempdir, \
patch('openlp.core.ui.firsttimeform.check_directory_exists') as mocked_check_directory_exists, \
patch.object(frw.application, 'set_normal_cursor') as mocked_set_normal_cursor:
mocked_settings = MagicMock()
mocked_settings.value.return_value = True
MockedSettings.return_value = mocked_settings
mocked_gettempdir.return_value = 'temp'
expected_temp_path = os.path.join('temp', 'openlp')
# WHEN: The First Time Wizard is initialised # WHEN: The set_defaults() method is run
first_time_form.initialize(MagicMock()) frw.set_defaults()
# THEN: The First Time Form web configuration file should be accessible and parseable # THEN: The default values should have been set
self.assertIsInstance(first_time_form.config, ConfigParser, 'The config object should be a ConfigParser') mocked_restart.assert_called_with()
mocked_get_web_page.assert_called_with(expected_web_url + 'download.cfg', self.assertEqual('http://openlp.org/files/frw/', frw.web, 'The default URL should be set')
header=('User-Agent', expected_user_agent)) mocked_cancel_button.clicked.connect.assert_called_with(frw.on_cancel_button_clicked)
self.assertEqual(expected_songs_url, first_time_form.songs_url, 'The songs URL should be correct') mocked_no_internet_finish_btn.clicked.connect.assert_called_with(frw.on_no_internet_finish_button_clicked)
self.assertEqual(expected_bibles_url, first_time_form.bibles_url, 'The bibles URL should be correct') mocked_currentIdChanged.connect.assert_called_with(frw.on_current_id_changed)
self.assertEqual(expected_themes_url, first_time_form.themes_url, 'The themes URL should be correct') mocked_register_function.assert_called_with('config_screen_changed', frw.update_screen_list_combo)
mocked_no_internet_finish_btn.setVisible.assert_called_with(False)
mocked_settings.value.assert_called_with('core/has run wizard')
mocked_gettempdir.assert_called_with()
mocked_check_directory_exists.assert_called_with(expected_temp_path)
def update_screen_list_combo_test(self):
"""
Test that the update_screen_list_combo() method works correctly
"""
# GIVEN: A mocked Screen() object and an initialised First Run Wizard and a mocked display_combo_box
expected_screen_list = ['Screen 1', 'Screen 2']
mocked_screens = MagicMock()
mocked_screens.get_screen_list.return_value = expected_screen_list
frw = FirstTimeForm(None)
frw.initialize(mocked_screens)
with patch.object(frw, 'display_combo_box') as mocked_display_combo_box:
mocked_display_combo_box.count.return_value = 2
# WHEN: update_screen_list_combo() is called
frw.update_screen_list_combo()
# THEN: The combobox should have been updated
mocked_display_combo_box.clear.assert_called_with()
mocked_screens.get_screen_list.assert_called_with()
mocked_display_combo_box.addItems.assert_called_with(expected_screen_list)
mocked_display_combo_box.count.assert_called_with()
mocked_display_combo_box.setCurrentIndex.assert_called_with(1)
def on_cancel_button_clicked_test(self):
"""
Test that the cancel button click slot shuts down the threads correctly
"""
# GIVEN: A FRW, some mocked threads and workers (that isn't quite done) and other mocked stuff
frw = FirstTimeForm(None)
frw.initialize(MagicMock())
mocked_worker = MagicMock()
mocked_thread = MagicMock()
mocked_thread.isRunning.side_effect = [True, False]
frw.theme_screenshot_workers.append(mocked_worker)
frw.theme_screenshot_threads.append(mocked_thread)
with patch('openlp.core.ui.firsttimeform.time') as mocked_time, \
patch.object(frw.application, 'set_normal_cursor') as mocked_set_normal_cursor:
# WHEN: on_cancel_button_clicked() is called
frw.on_cancel_button_clicked()
# THEN: The right things should be called in the right order
self.assertTrue(frw.was_cancelled, 'The was_cancelled property should have been set to True')
mocked_worker.set_download_canceled.assert_called_with(True)
mocked_thread.isRunning.assert_called_with()
self.assertEqual(2, mocked_thread.isRunning.call_count, 'isRunning() should have been called twice')
mocked_time.sleep.assert_called_with(0.1)
self.assertEqual(1, mocked_time.sleep.call_count, 'sleep() should have only been called once')
mocked_set_normal_cursor.assert_called_with()
def broken_config_test(self): def broken_config_test(self):
""" """
@ -128,10 +191,11 @@ class TestFirstTimeForm(TestCase, TestMixin):
# GIVEN: A mocked get_web_page, a First Time Wizard, an expected screen object, and a mocked broken config file # GIVEN: A mocked get_web_page, a First Time Wizard, an expected screen object, and a mocked broken config file
with patch('openlp.core.ui.firsttimeform.get_web_page') as mocked_get_web_page: with patch('openlp.core.ui.firsttimeform.get_web_page') as mocked_get_web_page:
first_time_form = FirstTimeForm(None) first_time_form = FirstTimeForm(None)
first_time_form.initialize(MagicMock())
mocked_get_web_page.return_value.read.return_value = FAKE_BROKEN_CONFIG mocked_get_web_page.return_value.read.return_value = FAKE_BROKEN_CONFIG
# WHEN: The First Time Wizard is initialised # WHEN: The First Time Wizard is downloads the config file
first_time_form.initialize(MagicMock()) first_time_form._download_index()
# THEN: The First Time Form should not have web access # THEN: The First Time Form should not have web access
self.assertFalse(first_time_form.web_access, 'There should not be web access with a broken config file') self.assertFalse(first_time_form.web_access, 'There should not be web access with a broken config file')
@ -143,10 +207,11 @@ class TestFirstTimeForm(TestCase, TestMixin):
# GIVEN: A mocked get_web_page, a First Time Wizard, an expected screen object, and a mocked invalid config file # GIVEN: A mocked get_web_page, a First Time Wizard, an expected screen object, and a mocked invalid config file
with patch('openlp.core.ui.firsttimeform.get_web_page') as mocked_get_web_page: with patch('openlp.core.ui.firsttimeform.get_web_page') as mocked_get_web_page:
first_time_form = FirstTimeForm(None) first_time_form = FirstTimeForm(None)
first_time_form.initialize(MagicMock())
mocked_get_web_page.return_value.read.return_value = FAKE_INVALID_CONFIG mocked_get_web_page.return_value.read.return_value = FAKE_INVALID_CONFIG
# WHEN: The First Time Wizard is initialised # WHEN: The First Time Wizard is downloads the config file
first_time_form.initialize(MagicMock()) first_time_form._download_index()
# THEN: The First Time Form should not have web access # THEN: The First Time Form should not have web access
self.assertFalse(first_time_form.web_access, 'There should not be web access with an invalid config file') self.assertFalse(first_time_form.web_access, 'There should not be web access with an invalid config file')

View File

@ -32,7 +32,7 @@ Package to test the openlp.core.utils.actions package.
from unittest import TestCase from unittest import TestCase
from openlp.core.common.settings import Settings from openlp.core.common.settings import Settings
from openlp.core.utils import VersionThread, get_application_version from openlp.core.utils import VersionThread, get_application_version, get_uno_command
from tests.functional import MagicMock, patch from tests.functional import MagicMock, patch
from tests.helpers.testmixin import TestMixin from tests.helpers.testmixin import TestMixin
@ -62,9 +62,74 @@ class TestInitFunctions(TestMixin, TestCase):
# WHEN: We check to see if the version is different . # WHEN: We check to see if the version is different .
with patch('PyQt4.QtCore.QThread'),\ with patch('PyQt4.QtCore.QThread'),\
patch('openlp.core.utils.get_application_version') as mocked_get_application_version: patch('openlp.core.utils.get_application_version') as mocked_get_application_version:
mocked_get_application_version.return_value = \ mocked_get_application_version.return_value = {'version': '1.0.0', 'build': '', 'full': '2.0.4'}
{'version': '1.0.0', 'build': '', 'full': '2.0.4'}
version_thread = VersionThread(mocked_main_window) version_thread = VersionThread(mocked_main_window)
version_thread.run() version_thread.run()
# THEN: If the version has changed the main window is notified # THEN: If the version has changed the main window is notified
self.assertTrue(mocked_main_window.emit.called, 'The main windows should have been notified') self.assertTrue(mocked_main_window.emit.called, 'The main windows should have been notified')
def get_uno_command_libreoffice_command_exists_test(self):
"""
Test the ``get_uno_command`` function uses the libreoffice command when available.
:return:
"""
# GIVEN: A patched 'which' method which returns a path when called with 'libreoffice'
with patch('openlp.core.utils.which',
**{'side_effect': lambda command: {'libreoffice': '/usr/bin/libreoffice'}[command]}):
# WHEN: Calling get_uno_command
result = get_uno_command()
# THEN: The command 'libreoffice' should be called with the appropriate parameters
self.assertEquals(result, 'libreoffice --nologo --norestore --minimized --nodefault --nofirststartwizard'
' "--accept=pipe,name=openlp_pipe;urp;"')
def get_uno_command_only_soffice_command_exists_test(self):
"""
Test the ``get_uno_command`` function uses the soffice command when the libreoffice command is not available.
:return:
"""
# GIVEN: A patched 'which' method which returns None when called with 'libreoffice' and a path when called with
# 'soffice'
with patch('openlp.core.utils.which',
**{'side_effect': lambda command: {'libreoffice': None, 'soffice': '/usr/bin/soffice'}[command]}):
# WHEN: Calling get_uno_command
result = get_uno_command()
# THEN: The command 'soffice' should be called with the appropriate parameters
self.assertEquals(result, 'soffice --nologo --norestore --minimized --nodefault --nofirststartwizard'
' "--accept=pipe,name=openlp_pipe;urp;"')
def get_uno_command_when_no_command_exists_test(self):
"""
Test the ``get_uno_command`` function raises an FileNotFoundError when neither the libreoffice or soffice
commands are available.
:return:
"""
# GIVEN: A patched 'which' method which returns None
with patch('openlp.core.utils.which', **{'return_value': None}):
# WHEN: Calling get_uno_command
# THEN: a FileNotFoundError exception should be raised
self.assertRaises(FileNotFoundError, get_uno_command)
def get_uno_command_connection_type_test(self):
"""
Test the ``get_uno_command`` function when the connection type is anything other than pipe.
:return:
"""
# GIVEN: A patched 'which' method which returns 'libreoffice'
with patch('openlp.core.utils.which', **{'return_value': 'libreoffice'}):
# WHEN: Calling get_uno_command with a connection type other than pipe
result = get_uno_command('socket')
# THEN: The connection parameters should be set for socket
self.assertEqual(result, 'libreoffice --nologo --norestore --minimized --nodefault --nofirststartwizard'
' "--accept=socket,host=localhost,port=2002;urp;"')

View File

@ -94,11 +94,9 @@ class TestProjectorManager(TestCase, TestMixin):
self.projector_manager.bootstrap_initialise() self.projector_manager.bootstrap_initialise()
self.projector_manager.bootstrap_post_set_up() self.projector_manager.bootstrap_post_set_up()
# THEN: verify calls to retrieve saved projectors # THEN: verify calls to retrieve saved projectors and edit page initialized
self.assertEqual(1, self.projector_manager._load_projectors.call_count, self.assertEqual(1, self.projector_manager._load_projectors.call_count,
'Initialization should have called load_projectors()') 'Initialization should have called load_projectors()')
# THEN: Verify edit page is initialized
self.assertEqual(type(self.projector_manager.projector_form), ProjectorEditForm, self.assertEqual(type(self.projector_manager.projector_form), ProjectorEditForm,
'Initialization should have created a Projector Edit Form') 'Initialization should have created a Projector Edit Form')
self.assertIs(self.projector_manager.projectordb, self.assertIs(self.projector_manager.projectordb,

View File

@ -0,0 +1,79 @@
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
###############################################################################
# OpenLP - Open Source Lyrics Projection #
# --------------------------------------------------------------------------- #
# Copyright (c) 2008-2014 Raoul Snyman #
# Portions copyright (c) 2008-2014 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, Ken Roberts, 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 #
###############################################################################
"""
:mod: `tests.interfaces.openlp_core_ui.test_projectorsourceform` module
Tests for the Projector Source Select form.
"""
import logging
log = logging.getLogger(__name__)
log.debug('test_projectorsourceform loaded')
from unittest import TestCase
from tests.helpers.testmixin import TestMixin
from openlp.core.lib.projector.constants import PJLINK_DEFAULT_CODES, PJLINK_DEFAULT_SOURCES
from openlp.core.ui.projector.sourceselectform import source_group
def build_source_dict():
"""
Builds a source dictionary to verify source_group returns a valid dictionary of dictionary items
:returns: dictionary of valid PJLink source codes grouped by PJLink source group
"""
test_group = {}
for group in PJLINK_DEFAULT_SOURCES.keys():
test_group[group] = {}
for key in PJLINK_DEFAULT_CODES:
test_group[key[0]][key] = PJLINK_DEFAULT_CODES[key]
return test_group
class ProjectorSourceFormTest(TestCase, TestMixin):
"""
Test class for the Projector Source Select form module
"""
def source_dict_test(self):
"""
Test that source list dict returned from sourceselectform module is a valid dict with proper entries
"""
# GIVEN: A list of inputs
codes = []
for item in PJLINK_DEFAULT_CODES.keys():
codes.append(item)
codes.sort()
# WHEN: projector.sourceselectform.source_select() is called
check = source_group(codes, PJLINK_DEFAULT_CODES)
# THEN: return dictionary should match test dictionary
self.assertEquals(check, build_source_dict(),
"Source group dictionary should match test dictionary")