From 670c06db60d090b796658340c928896d987e05f0 Mon Sep 17 00:00:00 2001 From: Philip Ridout Date: Wed, 27 Feb 2019 21:12:16 +0000 Subject: [PATCH 1/8] Add proxy settings button to FTW, ftw refactors, a few fixes! --- openlp/core/app.py | 2 +- openlp/core/common/httputils.py | 24 ++- openlp/core/threading.py | 4 +- openlp/core/ui/firsttimeform.py | 169 ++++++++---------- openlp/core/ui/firsttimewizard.py | 119 ++++++------ openlp/core/ui/mainwindow.py | 3 +- openlp/core/widgets/widgets.py | 28 +++ openlp/plugins/songs/lib/importers/openlp.py | 2 +- openlp/plugins/songs/reporting.py | 7 - run_openlp.py | 0 setup.py | 0 .../openlp_core/common/test_httputils.py | 2 +- .../openlp_core/ui/test_firsttimeform.py | 138 ++++++++++++-- 13 files changed, 310 insertions(+), 188 deletions(-) mode change 100755 => 100644 run_openlp.py mode change 100755 => 100644 setup.py diff --git a/openlp/core/app.py b/openlp/core/app.py index 7dafaaf3c..22d0f8195 100644 --- a/openlp/core/app.py +++ b/openlp/core/app.py @@ -101,7 +101,7 @@ class OpenLP(QtWidgets.QApplication): ftw.initialize(screens) if ftw.exec() == QtWidgets.QDialog.Accepted: Settings().setValue('core/has run wizard', True) - elif ftw.was_cancelled: + else: QtCore.QCoreApplication.exit() sys.exit() # Correct stylesheet bugs diff --git a/openlp/core/common/httputils.py b/openlp/core/common/httputils.py index c9a555a55..1231cb7be 100644 --- a/openlp/core/common/httputils.py +++ b/openlp/core/common/httputils.py @@ -158,16 +158,20 @@ def get_web_page(url, headers=None, update_openlp=False, proxy=None): return response.text -def get_url_file_size(url): +def get_url_file_size(url, proxy=None): """ Get the size of a file. :param url: The URL of the file we want to download. + :param dict | ProxyMode | None proxy: ProxyMode enum or a dictionary containing the proxy servers, with their types + as the key e.g. {'http': 'http://proxyserver:port', 'https': 'https://proxyserver:port'} """ retries = 0 + if not isinstance(proxy, dict): + proxy = get_proxy_settings(mode=proxy) while True: try: - response = requests.head(url, timeout=float(CONNECTION_TIMEOUT), allow_redirects=True) + response = requests.head(url, proxies=proxy, timeout=float(CONNECTION_TIMEOUT), allow_redirects=True) return int(response.headers['Content-Length']) except OSError: if retries > CONNECTION_RETRIES: @@ -178,7 +182,7 @@ def get_url_file_size(url): continue -def download_file(update_object, url, file_path, sha256=None): +def download_file(update_object, url, file_path, sha256=None, proxy=None): """" Download a file given a URL. The file is retrieved in chunks, giving the ability to cancel the download at any point. Returns False on download error. @@ -187,15 +191,19 @@ def download_file(update_object, url, file_path, sha256=None): :param url: URL to download :param file_path: Destination file :param sha256: The check sum value to be checked against the download value + :param dict | ProxyMode | None proxy: ProxyMode enum or a dictionary containing the proxy servers, with their types + as the key e.g. {'http': 'http://proxyserver:port', 'https': 'https://proxyserver:port'} """ block_count = 0 block_size = 4096 retries = 0 + if not isinstance(proxy, dict): + proxy = get_proxy_settings(mode=proxy) log.debug('url_get_file: %s', url) while retries < CONNECTION_RETRIES: try: with file_path.open('wb') as saved_file: - response = requests.get(url, timeout=float(CONNECTION_TIMEOUT), stream=True) + response = requests.get(url, proxies=proxy, timeout=float(CONNECTION_TIMEOUT), stream=True) if sha256: hasher = hashlib.sha256() # Download until finished or canceled. @@ -244,21 +252,21 @@ class DownloadWorker(ThreadWorker): """ self._base_url = base_url self._file_name = file_name - self._download_cancelled = False + self.was_cancelled = False super().__init__() def start(self): """ Download the url to the temporary directory """ - if self._download_cancelled: + if self.was_cancelled: self.quit.emit() return try: dest_path = Path(gettempdir()) / 'openlp' / self._file_name url = '{url}{name}'.format(url=self._base_url, name=self._file_name) is_success = download_file(self, url, dest_path) - if is_success and not self._download_cancelled: + if is_success and not self.was_cancelled: self.download_succeeded.emit(dest_path) else: self.download_failed.emit() @@ -273,4 +281,4 @@ class DownloadWorker(ThreadWorker): """ A slot to allow the download to be cancelled from outside of the thread """ - self._download_cancelled = True + self.was_cancelled = True diff --git a/openlp/core/threading.py b/openlp/core/threading.py index 502598609..fb51674dd 100644 --- a/openlp/core/threading.py +++ b/openlp/core/threading.py @@ -76,11 +76,11 @@ def get_thread_worker(thread_name): Get the worker by the thread name :param str thread_name: The name of the thread - :returns: The worker for this thread name + :returns: The worker for this thread name, or None """ thread_info = Registry().get('application').worker_threads.get(thread_name) if not thread_info: - raise KeyError('No thread named "{}" exists'.format(thread_name)) + return return thread_info.get('worker') diff --git a/openlp/core/ui/firsttimeform.py b/openlp/core/ui/firsttimeform.py index d5f48e2d7..0826a2081 100644 --- a/openlp/core/ui/firsttimeform.py +++ b/openlp/core/ui/firsttimeform.py @@ -46,6 +46,7 @@ from openlp.core.lib.ui import critical_error_message_box from openlp.core.threading import get_thread_worker, is_thread_finished, run_thread from openlp.core.ui.firsttimewizard import FirstTimePage, UiFirstTimeWizard from openlp.core.ui.icons import UiIcons +from openlp.core.widgets.widgets import ProxyDialog log = logging.getLogger(__name__) @@ -91,7 +92,7 @@ class ThemeListWidgetItem(QtWidgets.QListWidgetItem): class FirstTimeForm(QtWidgets.QWizard, UiFirstTimeWizard, RegistryProperties): """ - This is the Theme Import Wizard, which allows easy creation and editing of OpenLP themes. + This is the FirstTimeWizard, designed to help new users to get up and running quickly. """ log.info('ThemeWizardForm loaded') @@ -103,6 +104,7 @@ class FirstTimeForm(QtWidgets.QWizard, UiFirstTimeWizard, RegistryProperties): self.web_access = True self.web = '' self.setup_ui(self) + self.customButtonClicked.connect(self._on_custom_button_clicked) self.themes_list_widget.itemSelectionChanged.connect(self.on_themes_list_widget_selection_changed) self.themes_deselect_all_button.clicked.connect(self.themes_list_widget.clearSelection) self.themes_select_all_button.clicked.connect(self.themes_list_widget.selectAll) @@ -111,13 +113,13 @@ class FirstTimeForm(QtWidgets.QWizard, UiFirstTimeWizard, RegistryProperties): """ Returns the id of the next FirstTimePage to go to based on enabled plugins """ - if FirstTimePage.ScreenConfig < self.currentId() < FirstTimePage.Songs and self.songs_check_box.isChecked(): + if FirstTimePage.Download < self.currentId() < FirstTimePage.Songs and self.songs_check_box.isChecked(): # If the songs plugin is enabled then go to the songs page return FirstTimePage.Songs - elif FirstTimePage.ScreenConfig < self.currentId() < FirstTimePage.Bibles and self.bible_check_box.isChecked(): + elif FirstTimePage.Download < 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 - elif FirstTimePage.ScreenConfig < self.currentId() < FirstTimePage.Themes: + elif FirstTimePage.Download < 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 else: @@ -133,9 +135,7 @@ class FirstTimeForm(QtWidgets.QWizard, UiFirstTimeWizard, RegistryProperties): if not self.web_access: return FirstTimePage.NoInternet else: - return FirstTimePage.Plugins - elif self.currentId() == FirstTimePage.Plugins: - return self.get_next_page_id() + return FirstTimePage.Songs elif self.currentId() == FirstTimePage.Progress: return -1 elif self.currentId() == FirstTimePage.NoInternet: @@ -147,7 +147,7 @@ class FirstTimeForm(QtWidgets.QWizard, UiFirstTimeWizard, RegistryProperties): Run the wizard. """ self.set_defaults() - return QtWidgets.QWizard.exec(self) + return super().exec() def initialize(self, screens): """ @@ -227,17 +227,13 @@ class FirstTimeForm(QtWidgets.QWizard, UiFirstTimeWizard, RegistryProperties): """ self.restart() self.web = 'https://get.openlp.org/ftw/' - 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.no_internet_cancel_button.clicked.connect(self.on_no_internet_cancel_button_clicked) self.currentIdChanged.connect(self.on_current_id_changed) Registry().register_function('config_screen_changed', self.screen_selection_widget.load) - self.no_internet_finish_button.setVisible(False) - self.no_internet_cancel_button.setVisible(False) # Check if this is a re-run of the wizard. self.has_run_wizard = Settings().value('core/has run wizard') create_paths(Path(gettempdir(), 'openlp')) self.theme_combo_box.clear() + self.button(QtWidgets.QWizard.CustomButton1).setVisible(False) if self.has_run_wizard: 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()) @@ -260,57 +256,85 @@ class FirstTimeForm(QtWidgets.QWizard, UiFirstTimeWizard, RegistryProperties): """ Detects Page changes and updates as appropriate. """ - # Keep track of the page we are at. Triggering "Cancel" causes page_id to be a -1. + back_button = self.button(QtWidgets.QWizard.BackButton) + cancel_button = self.button(QtWidgets.QWizard.CancelButton) + internet_settings_button = self.button(QtWidgets.QWizard.CustomButton1) + next_button = self.button(QtWidgets.QWizard.NextButton) + back_button.setVisible(True) + next_button.setVisible(True) + internet_settings_button.setVisible(False) self.application.process_events() - if page_id != -1: - self.last_id = page_id - if page_id == FirstTimePage.Download: - self.back_button.setVisible(False) - self.next_button.setVisible(False) - # Set the no internet page text. - if self.has_run_wizard: - self.no_internet_label.setText(self.no_internet_text) - else: - self.no_internet_label.setText(self.no_internet_text + self.cancel_wizard_text) + if page_id == FirstTimePage.SampleOption: + internet_settings_button.setVisible(True) + elif page_id == FirstTimePage.Download: + back_button.setVisible(False) + next_button.setVisible(False) 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.NoInternet: - self.back_button.setVisible(False) - self.next_button.setVisible(False) - self.cancel_button.setVisible(False) - self.no_internet_finish_button.setVisible(True) - if self.has_run_wizard: - self.no_internet_cancel_button.setVisible(False) - else: - self.no_internet_cancel_button.setVisible(True) - elif page_id == FirstTimePage.Plugins: - self.back_button.setVisible(False) + next_button.setVisible(False) + cancel_button.setVisible(False) + internet_settings_button.setVisible(True) elif page_id == FirstTimePage.Progress: + back_button.setVisible(False) + next_button.setVisible(False) self.application.set_busy_cursor() self._pre_wizard() self._perform_wizard() self._post_wizard() self.application.set_normal_cursor() - def on_cancel_button_clicked(self): + def accept(self): """ - Process the triggering of the cancel button. + Called when the user clicks 'Finish'. Reimplement it to to save the plugin status + + :rtype: None + """ + self._set_plugin_status(self.songs_check_box, 'songs/status') + self._set_plugin_status(self.bible_check_box, 'bibles/status') + self._set_plugin_status(self.presentation_check_box, 'presentations/status') + self._set_plugin_status(self.image_check_box, 'images/status') + self._set_plugin_status(self.media_check_box, 'media/status') + self._set_plugin_status(self.custom_check_box, 'custom/status') + self._set_plugin_status(self.song_usage_check_box, 'songusage/status') + self._set_plugin_status(self.alert_check_box, 'alerts/status') + self.screen_selection_widget.save() + if self.theme_combo_box.currentIndex() != -1: + Settings().setValue('themes/global theme', self.theme_combo_box.currentText()) + super().accept() + + def reject(self): + """ + Called when the user clicks the cancel button. Reimplement it to clean up the threads. + + :rtype: None """ self.was_cancelled = True - if self.thumbnail_download_threads: # TODO: Use main thread list - for thread_name in self.thumbnail_download_threads: - worker = get_thread_worker(thread_name) - if worker: - worker.cancel_download() + for thread_name in self.thumbnail_download_threads: + worker = get_thread_worker(thread_name) + if worker: + worker.cancel_download() # Was the thread created. if self.thumbnail_download_threads: while any([not is_thread_finished(thread_name) for thread_name in self.thumbnail_download_threads]): time.sleep(0.1) self.application.set_normal_cursor() + super().reject() + + def _on_custom_button_clicked(self, which): + """ + Slot to handle the a click on one of the wizards custom buttons. + + :param int QtWidgets.QWizard which: The button pressed + :rtype: None + """ + # Internet settings button + if which == QtWidgets.QWizard.CustomButton1: + proxy_dialog = ProxyDialog(self) + proxy_dialog.retranslate_ui() + proxy_dialog.exec() def on_themes_list_widget_selection_changed(self): """ @@ -330,23 +354,6 @@ class FirstTimeForm(QtWidgets.QWizard, UiFirstTimeWizard, RegistryProperties): elif not item.isSelected() and cbox_index != -1: self.theme_combo_box.removeItem(cbox_index) - 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._perform_wizard() - self.application.set_normal_cursor() - Settings().setValue('core/has run wizard', True) - self.close() - - def on_no_internet_cancel_button_clicked(self): - """ - Process the triggering of the "Cancel" button on the No Internet page. - """ - self.was_cancelled = True - self.close() - def update_progress(self, count, block_size): """ Calculate and display the download progress. This method is called by download_file(). @@ -373,7 +380,7 @@ class FirstTimeForm(QtWidgets.QWizard, UiFirstTimeWizard, RegistryProperties): Prepare the UI for the process. """ self.max_progress = 0 - self.finish_button.setVisible(False) + self.button(QtWidgets.QWizard.FinishButton).setEnabled(False) self.application.process_events() try: # Loop through the songs list and increase for each selected item @@ -428,58 +435,36 @@ class FirstTimeForm(QtWidgets.QWizard, UiFirstTimeWizard, RegistryProperties): """ Clean up the UI after the process has finished. """ + complete_str = '' if self.max_progress: self.progress_bar.setValue(self.progress_bar.maximum()) if self.has_run_wizard: text = translate('OpenLP.FirstTimeWizard', - 'Download complete. Click the {button} button to return to OpenLP.' - ).format(button=clean_button_text(self.buttonText(QtWidgets.QWizard.FinishButton))) - self.progress_label.setText(text) + 'Download complete. Click the \'{finish_button}\' button to return to OpenLP.') else: text = translate('OpenLP.FirstTimeWizard', - 'Download complete. Click the {button} button to start OpenLP.' - ).format(button=clean_button_text(self.buttonText(QtWidgets.QWizard.FinishButton))) - self.progress_label.setText(text) + 'Download complete. Click the \'{finish_button}\' button to start OpenLP.') else: if self.has_run_wizard: - text = translate('OpenLP.FirstTimeWizard', - 'Click the {button} button to return to OpenLP.' - ).format(button=clean_button_text(self.buttonText(QtWidgets.QWizard.FinishButton))) - self.progress_label.setText(text) + text = translate('OpenLP.FirstTimeWizard', 'Click the \'{finish_button}\' button to return to OpenLP.') else: - text = translate('OpenLP.FirstTimeWizard', - 'Click the {button} button to start OpenLP.' - ).format(button=clean_button_text(self.buttonText(QtWidgets.QWizard.FinishButton))) - self.progress_label.setText(text) - self.finish_button.setVisible(True) - self.finish_button.setEnabled(True) - self.cancel_button.setVisible(False) - self.next_button.setVisible(False) + text = translate('OpenLP.FirstTimeWizard', 'Click the \'{finish_button}\' button to start OpenLP.') + self.progress_label.setText(text.format(finish_button=self.finish_button_text)) + self.button(QtWidgets.QWizard.FinishButton).setEnabled(True) + self.button(QtWidgets.QWizard.CancelButton).setVisible(False) self.application.process_events() def _perform_wizard(self): """ Run the tasks in the wizard. """ - # Set plugin states - self._increment_progress_bar(translate('OpenLP.FirstTimeWizard', 'Enabling selected plugins...')) - self._set_plugin_status(self.songs_check_box, 'songs/status') - self._set_plugin_status(self.bible_check_box, 'bibles/status') - self._set_plugin_status(self.presentation_check_box, 'presentations/status') - self._set_plugin_status(self.image_check_box, 'images/status') - self._set_plugin_status(self.media_check_box, 'media/status') - self._set_plugin_status(self.custom_check_box, 'custom/status') - self._set_plugin_status(self.song_usage_check_box, 'songusage/status') - self._set_plugin_status(self.alert_check_box, 'alerts/status') + if self.web_access: if not self._download_selected(): critical_error_message_box(translate('OpenLP.FirstTimeWizard', 'Download Error'), translate('OpenLP.FirstTimeWizard', 'There was a connection problem while ' 'downloading, so further downloads will be skipped. Try to re-run ' 'the First Time Wizard later.')) - self.screen_selection_widget.save() - if self.theme_combo_box.currentIndex() != -1: - Settings().setValue('themes/global theme', self.theme_combo_box.currentText()) def _download_selected(self): """ diff --git a/openlp/core/ui/firsttimewizard.py b/openlp/core/ui/firsttimewizard.py index 37b9389a7..f93ce02f3 100644 --- a/openlp/core/ui/firsttimewizard.py +++ b/openlp/core/ui/firsttimewizard.py @@ -39,14 +39,15 @@ class FirstTimePage(object): An enumeration class with each of the pages of the wizard. """ Welcome = 0 - ScreenConfig = 1 - Download = 2 - NoInternet = 3 - Plugins = 4 - Songs = 5 - Bibles = 6 - Themes = 7 - Progress = 8 + Plugins = 1 + ScreenConfig = 2 + SampleOption = 3 + Download = 4 + NoInternet = 5 + Songs = 6 + Bibles = 7 + Themes = 8 + Progress = 9 class ThemeListWidget(QtWidgets.QListWidget): @@ -97,20 +98,13 @@ class UiFirstTimeWizard(object): first_time_wizard.resize(550, 386) first_time_wizard.setModal(True) first_time_wizard.setOptions(QtWidgets.QWizard.IndependentPages | QtWidgets.QWizard.NoBackButtonOnStartPage | - QtWidgets.QWizard.NoBackButtonOnLastPage | QtWidgets.QWizard.HaveCustomButton1 | - QtWidgets.QWizard.HaveCustomButton2) + QtWidgets.QWizard.NoBackButtonOnLastPage | QtWidgets.QWizard.HaveCustomButton1) if is_macosx(): # pragma: nocover first_time_wizard.setPixmap(QtWidgets.QWizard.BackgroundPixmap, QtGui.QPixmap(':/wizards/openlp-osx-wizard.png')) first_time_wizard.resize(634, 386) else: first_time_wizard.setWizardStyle(QtWidgets.QWizard.ModernStyle) - self.finish_button = self.button(QtWidgets.QWizard.FinishButton) - self.no_internet_finish_button = self.button(QtWidgets.QWizard.CustomButton1) - self.cancel_button = self.button(QtWidgets.QWizard.CancelButton) - self.no_internet_cancel_button = self.button(QtWidgets.QWizard.CustomButton2) - self.next_button = self.button(QtWidgets.QWizard.NextButton) - self.back_button = self.button(QtWidgets.QWizard.BackButton) add_welcome_page(first_time_wizard, ':/wizards/wizard_firsttime.bmp') # The screen config page self.screen_page = QtWidgets.QWizardPage() @@ -121,6 +115,18 @@ class UiFirstTimeWizard(object): self.screen_selection_widget.load() self.screen_page_layout.addRow(self.screen_selection_widget) first_time_wizard.setPage(FirstTimePage.ScreenConfig, self.screen_page) + # Download Samples page + self.resource_page = QtWidgets.QWizardPage() + self.resource_page.setObjectName('resource_page') + self.resource_page.setFinalPage(True) + self.resource_layout = QtWidgets.QVBoxLayout(self.resource_page) + self.resource_layout.setContentsMargins(50, 20, 50, 20) + self.resource_layout.setObjectName('resource_layout') + self.resource_label = QtWidgets.QLabel(self.resource_page) + self.resource_label.setObjectName('resource_label') + self.resource_label.setWordWrap(True) + self.resource_layout.addWidget(self.resource_label) + first_time_wizard.setPage(FirstTimePage.SampleOption, self.resource_page) # The download page self.download_page = QtWidgets.QWizardPage() self.download_page.setObjectName('download_page') @@ -134,6 +140,7 @@ class UiFirstTimeWizard(object): # The "you don't have an internet connection" page. self.no_internet_page = QtWidgets.QWizardPage() self.no_internet_page.setObjectName('no_internet_page') + self.no_internet_page.setFinalPage(True) self.no_internet_layout = QtWidgets.QVBoxLayout(self.no_internet_page) self.no_internet_layout.setContentsMargins(50, 30, 50, 40) self.no_internet_layout.setObjectName('no_internet_layout') @@ -242,27 +249,32 @@ class UiFirstTimeWizard(object): self.progress_bar.setObjectName('progress_bar') self.progress_layout.addWidget(self.progress_bar) first_time_wizard.setPage(FirstTimePage.Progress, self.progress_page) - self.retranslate_ui(first_time_wizard) + self.retranslate_ui() - def retranslate_ui(self, first_time_wizard): + def retranslate_ui(self): """ Translate the UI on the fly :param first_time_wizard: The wizard form """ - first_time_wizard.setWindowTitle(translate('OpenLP.FirstTimeWizard', 'First Time Wizard')) + self.finish_button_text = clean_button_text(self.buttonText(QtWidgets.QWizard.FinishButton)) + back_button_text = clean_button_text(self.buttonText(QtWidgets.QWizard.BackButton)) + next_button_text = clean_button_text(self.buttonText(QtWidgets.QWizard.NextButton)) + + self.setWindowTitle(translate('OpenLP.FirstTimeWizard', 'First Time Wizard')) text = translate('OpenLP.FirstTimeWizard', 'Welcome to the First Time Wizard') - first_time_wizard.title_label.setText('{text}' - ''.format(text=text)) - button = clean_button_text(first_time_wizard.buttonText(QtWidgets.QWizard.NextButton)) - first_time_wizard.information_label.setText( + self.title_label.setText('{text}'.format(text=text)) + self.information_label.setText( translate('OpenLP.FirstTimeWizard', 'This wizard will help you to configure OpenLP for initial use. ' - 'Click the {button} button below to start.').format(button=button)) + 'Click the \'{next_button}\' button below to start.' + ).format(next_button=next_button_text)) + self.setButtonText( + QtWidgets.QWizard.CustomButton1, translate('OpenLP.FirstTimeWizard', 'Internet Settings')) 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.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', 'Select parts of the program you wish to use')) self.plugin_page.setSubTitle(translate('OpenLP.FirstTimeWizard', 'You can also change these settings after the Wizard.')) @@ -270,11 +282,10 @@ class UiFirstTimeWizard(object): self.screen_page.setSubTitle(translate('OpenLP.FirstTimeWizard', 'Choose the main display screen for OpenLP.')) self.songs_check_box.setText(translate('OpenLP.FirstTimeWizard', 'Songs')) - self.custom_check_box.setText(translate('OpenLP.FirstTimeWizard', - 'Custom Slides – Easier to manage than songs and they have their own' - ' list of slides')) - self.bible_check_box.setText(translate('OpenLP.FirstTimeWizard', - 'Bibles – Import and show Bibles')) + self.custom_check_box.setText( + translate('OpenLP.FirstTimeWizard', + 'Custom Slides – Easier to manage than songs and they have their own list of slides')) + self.bible_check_box.setText(translate('OpenLP.FirstTimeWizard', 'Bibles – Import and show Bibles')) self.image_check_box.setText(translate('OpenLP.FirstTimeWizard', 'Images – Show images or replace background with them')) self.presentation_check_box.setText(translate('OpenLP.FirstTimeWizard', @@ -283,22 +294,25 @@ class UiFirstTimeWizard(object): self.song_usage_check_box.setText(translate('OpenLP.FirstTimeWizard', 'Song Usage Monitor')) self.alert_check_box.setText(translate('OpenLP.FirstTimeWizard', 'Alerts – Display informative messages while showing other slides')) + self.resource_page.setTitle(translate('OpenLP.FirstTimeWizard', 'Resource Data')) + self.resource_page.setSubTitle(translate('OpenLP.FirstTimeWizard', 'Can OpenLP download some resource data?')) + self.resource_label.setText( + translate('OpenLP.FirstTimeWizard', + 'OpenLP has collected some resources that we have permission to distribute.\n\n' + 'If you would like to download some of these resources click the \'{next_button}\' button, ' + 'otherwise click the \'{finish_button}\' button.' + ).format(next_button=next_button_text, finish_button=self.finish_button_text)) 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.')) - button = clean_button_text(first_time_wizard.buttonText(QtWidgets.QWizard.FinishButton)) - 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 {button} 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.' - ).format(button=button) - button = clean_button_text(first_time_wizard.buttonText(QtWidgets.QWizard.CancelButton)) - self.cancel_wizard_text = translate('OpenLP.FirstTimeWizard', - '\n\nTo cancel the First Time Wizard completely (and not start OpenLP), ' - 'click the {button} button now.').format(button=button) + self.no_internet_page.setSubTitle(translate('OpenLP.FirstTimeWizard', 'Cannot connect to the internet.')) + self.no_internet_label.setText( + translate('OpenLP.FirstTimeWizard', + 'OpenLP could not connect to the internet to get information about the sample data available.\n\n' + 'Please check your internet connection. If your church uses a proxy server click the ' + '\'Internet Settings\' button below and enter the server details there.\n\nClick the ' + '\'{back_button}\' button to try again.\n\nIf you click the \'{finish_button}\' ' + 'button you can download the data at a later time by selecting \'Re-run First Time Wizard\' ' + 'from the \'Tools\' menu in OpenLP.' + ).format(back_button=back_button_text, finish_button=self.finish_button_text)) 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')) @@ -310,13 +324,8 @@ class UiFirstTimeWizard(object): self.themes_select_all_button.setToolTip(translate('OpenLP.FirstTimeWizard', 'Select all')) self.themes_deselect_all_button.setToolTip(translate('OpenLP.FirstTimeWizard', 'Deselect all')) 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...')) - first_time_wizard.setButtonText(QtWidgets.QWizard.CustomButton1, - clean_button_text(first_time_wizard.buttonText(QtWidgets.QWizard.FinishButton))) - first_time_wizard.setButtonText(QtWidgets.QWizard.CustomButton2, - clean_button_text(first_time_wizard.buttonText(QtWidgets.QWizard.CancelButton))) + self.progress_page.setSubTitle( + translate('OpenLP.FirstTimeWizard', 'Please wait while resources are downloaded and OpenLP is configured.')) def on_projectors_check_box_clicked(self): # When clicking projectors_check box, change the visibility setting for Projectors panel. diff --git a/openlp/core/ui/mainwindow.py b/openlp/core/ui/mainwindow.py index dabfca79e..29b131378 100644 --- a/openlp/core/ui/mainwindow.py +++ b/openlp/core/ui/mainwindow.py @@ -681,8 +681,7 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow, LogMixin, RegistryPropert return first_run_wizard = FirstTimeForm(self) first_run_wizard.initialize(ScreenList()) - first_run_wizard.exec() - if first_run_wizard.was_cancelled: + if first_run_wizard.exec() == QtWidgets.QDialog.Rejected: return self.application.set_busy_cursor() self.first_time() diff --git a/openlp/core/widgets/widgets.py b/openlp/core/widgets/widgets.py index c7697927c..1a193fe08 100644 --- a/openlp/core/widgets/widgets.py +++ b/openlp/core/widgets/widgets.py @@ -150,6 +150,34 @@ class ProxyWidget(QtWidgets.QGroupBox): settings.setValue('advanced/proxy password', self.password_edit.text()) +class ProxyDialog(QtWidgets.QDialog): + """ + A basic dialog to show proxy settingd + """ + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.layout = QtWidgets.QVBoxLayout(self) + self.proxy_widget = ProxyWidget(self) + self.layout.addWidget(self.proxy_widget) + self.button_box = \ + QtWidgets.QDialogButtonBox(QtWidgets.QDialogButtonBox.Ok | QtWidgets.QDialogButtonBox.Cancel, self) + self.layout.addWidget(self.button_box) + self.button_box.accepted.connect(self.accept) + self.button_box.rejected.connect(self.reject) + + def accept(self): + """ + Reimplement the the accept slot so that the ProxyWidget settings can be saved. + :rtype: None + """ + self.proxy_widget.save() + super().accept() + + def retranslate_ui(self): + self.proxy_widget.retranslate_ui() + self.setWindowTitle(translate('OpenLP.ProxyDialog', 'Proxy Server Settings')) + + class ScreenButton(QtWidgets.QPushButton): """ A special button class that holds the screen information about it diff --git a/openlp/plugins/songs/lib/importers/openlp.py b/openlp/plugins/songs/lib/importers/openlp.py index 361406bfd..f6ea3ac0a 100644 --- a/openlp/plugins/songs/lib/importers/openlp.py +++ b/openlp/plugins/songs/lib/importers/openlp.py @@ -106,7 +106,7 @@ class OpenLPSongImport(SongImport): pass # Check the file type - if not isinstance(self.import_source, str) or not self.import_source.endswith('.sqlite'): + if self.import_source.suffix != '.sqlite': self.log_error(self.import_source, translate('SongsPlugin.OpenLPSongImport', 'Not a valid OpenLP 2 song database.')) return diff --git a/openlp/plugins/songs/reporting.py b/openlp/plugins/songs/reporting.py index 985b9bc12..35521dbe9 100644 --- a/openlp/plugins/songs/reporting.py +++ b/openlp/plugins/songs/reporting.py @@ -49,13 +49,6 @@ def report_song_list(): Path(translate('SongPlugin.ReportSongList', 'song_extract.csv')), translate('SongPlugin.ReportSongList', 'CSV format (*.csv)')) - if report_file_path is None: - main_window.error_message( - translate('SongPlugin.ReportSongList', 'Output Path Not Selected'), - translate('SongPlugin.ReportSongList', 'You have not set a valid output location for your report. \n' - 'Please select an existing path on your computer.') - ) - return report_file_path.with_suffix('.csv') Registry().get('application').set_busy_cursor() try: diff --git a/run_openlp.py b/run_openlp.py old mode 100755 new mode 100644 diff --git a/setup.py b/setup.py old mode 100755 new mode 100644 diff --git a/tests/functional/openlp_core/common/test_httputils.py b/tests/functional/openlp_core/common/test_httputils.py index bb16e6800..f059d893c 100644 --- a/tests/functional/openlp_core/common/test_httputils.py +++ b/tests/functional/openlp_core/common/test_httputils.py @@ -224,7 +224,7 @@ class TestHttpUtils(TestCase, TestMixin): file_size = get_url_file_size(fake_url) # THEN: The correct methods are called with the correct arguments and a web page is returned - mocked_requests.head.assert_called_once_with(fake_url, allow_redirects=True, timeout=30.0) + mocked_requests.head.assert_called_once_with(fake_url, allow_redirects=True, proxies=None, timeout=30.0) assert file_size == 100 @patch('openlp.core.common.httputils.requests') diff --git a/tests/functional/openlp_core/ui/test_firsttimeform.py b/tests/functional/openlp_core/ui/test_firsttimeform.py index 220e1fff6..e5d0ce550 100644 --- a/tests/functional/openlp_core/ui/test_firsttimeform.py +++ b/tests/functional/openlp_core/ui/test_firsttimeform.py @@ -27,6 +27,8 @@ import tempfile from unittest import TestCase from unittest.mock import MagicMock, call, patch, DEFAULT +from PyQt5 import QtWidgets + from openlp.core.common.path import Path from openlp.core.common.registry import Registry from openlp.core.ui.firsttimeform import FirstTimeForm, ThemeListWidgetItem @@ -120,10 +122,23 @@ class TestFirstTimeForm(TestCase, TestMixin): # THEN: The screens should be set up, and the default values initialised assert expected_screens == frw.screens, 'The screens should be correct' assert frw.web_access is True, 'The default value of self.web_access should be True' - assert frw.was_cancelled is False, 'The default value of self.was_cancelled should be False' assert [] == frw.thumbnail_download_threads, 'The list of threads should be empty' assert frw.has_run_wizard is False, 'has_run_wizard should be False' + @patch('openlp.core.ui.firsttimewizard.QtWidgets.QWizard.exec') + def test_exec(self, mocked_qwizard_exec): + + # GIVEN: An instance of FirstTimeForm + frw = FirstTimeForm(None) + with patch.object(frw, 'set_defaults') as mocked_set_defaults: + + # WHEN: exec is called + frw.exec() + + # THEN: The wizard should be reset and the exec methon on the super class should have been called + mocked_set_defaults.assert_called_once() + mocked_qwizard_exec.assert_called_once() + def test_set_defaults(self): """ Test that the default values are set when set_defaults() is run @@ -134,8 +149,6 @@ class TestFirstTimeForm(TestCase, TestMixin): mocked_settings = MagicMock() mocked_settings.value.side_effect = lambda key: {'core/has run wizard': False}[key] with patch.object(frw, 'restart') as mocked_restart, \ - patch.object(frw, 'cancel_button') as mocked_cancel_button, \ - patch.object(frw, 'no_internet_finish_button') as mocked_no_internet_finish_btn, \ patch.object(frw, 'currentIdChanged') as mocked_currentIdChanged, \ patch.object(frw, 'theme_combo_box') as mocked_theme_combo_box, \ patch.object(frw, 'songs_check_box') as mocked_songs_check_box, \ @@ -153,12 +166,8 @@ class TestFirstTimeForm(TestCase, TestMixin): # THEN: The default values should have been set mocked_restart.assert_called_once() assert 'https://get.openlp.org/ftw/' == frw.web, 'The default URL should be set' - mocked_cancel_button.clicked.connect.assert_called_once_with(frw.on_cancel_button_clicked) - mocked_no_internet_finish_btn.clicked.connect.assert_called_once_with( - frw.on_no_internet_finish_button_clicked) mocked_currentIdChanged.connect.assert_called_once_with(frw.on_current_id_changed) mocked_register_function.assert_called_once_with('config_screen_changed', frw.screen_selection_widget.load) - mocked_no_internet_finish_btn.setVisible.assert_called_once_with(False) mocked_settings.value.assert_has_calls([call('core/has run wizard')]) mocked_gettempdir.assert_called_once() mocked_create_paths.assert_called_once_with(Path('temp', 'openlp')) @@ -177,8 +186,6 @@ class TestFirstTimeForm(TestCase, TestMixin): mocked_settings.value.side_effect = \ lambda key: {'core/has run wizard': True, 'themes/global theme': 'Default Theme'}[key] with patch.object(frw, 'restart') as mocked_restart, \ - patch.object(frw, 'cancel_button') as mocked_cancel_button, \ - patch.object(frw, 'no_internet_finish_button') as mocked_no_internet_finish_btn, \ patch.object(frw, 'currentIdChanged') as mocked_currentIdChanged, \ patch.object(frw, 'theme_combo_box', **{'findText.return_value': 3}) as mocked_theme_combo_box, \ patch.multiple(frw, songs_check_box=DEFAULT, bible_check_box=DEFAULT, presentation_check_box=DEFAULT, @@ -200,12 +207,8 @@ class TestFirstTimeForm(TestCase, TestMixin): # THEN: The default values should have been set mocked_restart.assert_called_once() assert 'https://get.openlp.org/ftw/' == frw.web, 'The default URL should be set' - mocked_cancel_button.clicked.connect.assert_called_once_with(frw.on_cancel_button_clicked) - mocked_no_internet_finish_btn.clicked.connect.assert_called_once_with( - frw.on_no_internet_finish_button_clicked) mocked_currentIdChanged.connect.assert_called_once_with(frw.on_current_id_changed) mocked_register_function.assert_called_once_with('config_screen_changed', frw.screen_selection_widget.load) - mocked_no_internet_finish_btn.setVisible.assert_called_once_with(False) mocked_settings.value.assert_has_calls([call('core/has run wizard'), call('themes/global theme')]) mocked_gettempdir.assert_called_once() mocked_create_paths.assert_called_once_with(Path('temp', 'openlp')) @@ -219,12 +222,79 @@ class TestFirstTimeForm(TestCase, TestMixin): mocked_theme_combo_box.findText.assert_called_once_with('Default Theme') mocked_theme_combo_box.setCurrentIndex(3) + @patch('openlp.core.ui.firsttimewizard.QtWidgets.QWizard.accept') + @patch('openlp.core.ui.firsttimewizard.Settings') + def test_accept_method(self, mocked_settings, mocked_qwizard_accept): + """ + Test the FirstTimeForm.accept method + """ + # GIVEN: An instance of FirstTimeForm + frw = FirstTimeForm(None) + with patch.object(frw, '_set_plugin_status') as mocked_set_plugin_status, \ + patch.multiple(frw, songs_check_box=DEFAULT, bible_check_box=DEFAULT, presentation_check_box=DEFAULT, + image_check_box=DEFAULT, media_check_box=DEFAULT, custom_check_box=DEFAULT, + song_usage_check_box=DEFAULT, alert_check_box=DEFAULT) as mocked_check_boxes, \ + patch.object(frw, 'screen_selection_widget') as mocked_screen_selection_widget: + + # WHEN: Calling accept + frw.accept() + + # THEN: The selected plugins should be enabled, the screen selection saved and the super method called + mocked_set_plugin_status.assert_has_calls([ + call(mocked_check_boxes['songs_check_box'], 'songs/status'), + call(mocked_check_boxes['bible_check_box'], 'bibles/status'), + call(mocked_check_boxes['presentation_check_box'], 'presentations/status'), + call(mocked_check_boxes['image_check_box'], 'images/status'), + call(mocked_check_boxes['media_check_box'], 'media/status'), + call(mocked_check_boxes['custom_check_box'], 'custom/status'), + call(mocked_check_boxes['song_usage_check_box'], 'songusage/status'), + call(mocked_check_boxes['alert_check_box'], 'alerts/status')]) + mocked_screen_selection_widget.save.assert_called_once() + mocked_qwizard_accept.assert_called_once() + + @patch('openlp.core.ui.firsttimewizard.Settings') + def test_accept_method_theme_not_selected(self, mocked_settings): + """ + Test the FirstTimeForm.accept method when there is no default theme selected + """ + # GIVEN: An instance of FirstTimeForm + frw = FirstTimeForm(None) + with patch.object(frw, '_set_plugin_status'), \ + patch.object(frw, 'screen_selection_widget'), \ + patch.object(frw, 'theme_combo_box', **{'currentIndex.return_value': '-1'}): + + # WHEN: Calling accept and the currentIndex method of the theme_combo_box returns -1 + frw.accept() + + # THEN: OpenLP should not try to save a theme name + mocked_settings.setValue.assert_not_called() + + @patch('openlp.core.ui.firsttimeform.Settings') + def test_accept_method_theme_selected(self, mocked_settings): + """ + Test the FirstTimeForm.accept method when a default theme is selected + """ + # GIVEN: An instance of FirstTimeForm + frw = FirstTimeForm(None) + with patch.object(frw, '_set_plugin_status'), \ + patch.object(frw, 'screen_selection_widget'), \ + patch.object( + frw, 'theme_combo_box', **{'currentIndex.return_value': 0, 'currentText.return_value': 'Test Item'}): + + # WHEN: Calling accept and the currentIndex method of the theme_combo_box returns 0 + frw.accept() + + # THEN: The 'currentItem' in the combobox should have been set as the default theme. + mocked_settings().setValue.assert_called_once_with('themes/global theme', 'Test Item') + + @patch('openlp.core.ui.firsttimewizard.QtWidgets.QWizard.reject') @patch('openlp.core.ui.firsttimeform.time') @patch('openlp.core.ui.firsttimeform.get_thread_worker') @patch('openlp.core.ui.firsttimeform.is_thread_finished') - def test_on_cancel_button_clicked(self, mocked_is_thread_finished, mocked_get_thread_worker, mocked_time): + def test_reject_method( + self, mocked_is_thread_finished, mocked_get_thread_worker, mocked_time, mocked_qwizard_reject): """ - Test that the cancel button click slot shuts down the threads correctly + Test that the reject method shuts down the threads correctly """ # GIVEN: A FRW, some mocked threads and workers (that isn't quite done) and other mocked stuff mocked_worker = MagicMock() @@ -235,17 +305,47 @@ class TestFirstTimeForm(TestCase, TestMixin): frw.thumbnail_download_threads = ['test_thread'] with patch.object(frw.application, 'set_normal_cursor') as mocked_set_normal_cursor: - # WHEN: on_cancel_button_clicked() is called - frw.on_cancel_button_clicked() + # WHEN: the reject method is called + frw.reject() # THEN: The right things should be called in the right order - assert frw.was_cancelled is True, 'The was_cancelled property should have been set to True' mocked_get_thread_worker.assert_called_once_with('test_thread') mocked_worker.cancel_download.assert_called_once() mocked_is_thread_finished.assert_called_with('test_thread') assert mocked_is_thread_finished.call_count == 2, 'isRunning() should have been called twice' mocked_time.sleep.assert_called_once_with(0.1) - mocked_set_normal_cursor.assert_called_once_with() + mocked_set_normal_cursor.assert_called_once() + mocked_qwizard_reject.assert_called_once() + + @patch('openlp.core.ui.firsttimeform.ProxyDialog') + def test_on_custom_button_clicked(self, mocked_proxy_dialog): + """ + Test _on_custom_button when it is called whe the 'internet settings' (CustomButton1) button is not clicked. + """ + # GIVEN: An instance of the FirstTimeForm + frw = FirstTimeForm(None) + + # WHEN: Calling _on_custom_button_clicked with a different button to the 'internet settings button. + frw._on_custom_button_clicked(QtWidgets.QWizard.CustomButton2) + + # THEN: The ProxyDialog should not be shown. + mocked_proxy_dialog.assert_not_called() + + @patch('openlp.core.ui.firsttimeform.ProxyDialog') + def test_on_custom_button_clicked_internet_settings(self, mocked_proxy_dialog): + """ + Test _on_custom_button when it is called when the 'internet settings' (CustomButton1) button is clicked. + """ + # GIVEN: An instance of the FirstTimeForm + frw = FirstTimeForm(None) + + # WHEN: Calling _on_custom_button_clicked with the constant for the 'internet settings' button (CustomButton1) + frw._on_custom_button_clicked(QtWidgets.QWizard.CustomButton1) + + # THEN: The ProxyDialog should be shown. + mocked_proxy_dialog.assert_called_with(frw) + mocked_proxy_dialog().retranslate_ui.assert_called_once() + mocked_proxy_dialog().exec.assert_called_once() @patch('openlp.core.ui.firsttimeform.critical_error_message_box') def test__parse_config_invalid_config(self, mocked_critical_error_message_box): From f3485513f1aa2eb9719b7b6f8456ebf5485b52ff Mon Sep 17 00:00:00 2001 From: Philip Ridout Date: Wed, 27 Feb 2019 22:14:08 +0000 Subject: [PATCH 2/8] Test and PEP8 fixes --- tests/functional/openlp_core/test_threading.py | 14 +++++--------- .../openlp_plugins/songs/test_openlpimporter.py | 12 ++++++------ 2 files changed, 11 insertions(+), 15 deletions(-) diff --git a/tests/functional/openlp_core/test_threading.py b/tests/functional/openlp_core/test_threading.py index 6926b1730..e493725d0 100644 --- a/tests/functional/openlp_core/test_threading.py +++ b/tests/functional/openlp_core/test_threading.py @@ -133,15 +133,11 @@ def test_get_thread_worker_mising(MockRegistry): # GIVEN: A mocked thread worker MockRegistry.return_value.get.return_value.worker_threads = {} - try: - # WHEN: get_thread_worker() is called - get_thread_worker('test_thread') - assert False, 'A KeyError should have been raised' - except KeyError: - # THEN: The mocked worker is returned - pass - except Exception: - assert False, 'A KeyError should have been raised' + # WHEN: get_thread_worker() is called + result = get_thread_worker('test_thread') + + # THEN: None should have been returned + assert result is None @patch('openlp.core.threading.Registry') diff --git a/tests/functional/openlp_plugins/songs/test_openlpimporter.py b/tests/functional/openlp_plugins/songs/test_openlpimporter.py index e76c08d3b..c67e18bbd 100644 --- a/tests/functional/openlp_plugins/songs/test_openlpimporter.py +++ b/tests/functional/openlp_plugins/songs/test_openlpimporter.py @@ -22,6 +22,7 @@ """ This module contains tests for the OpenLP song importer. """ +from pathlib import Path from unittest import TestCase from unittest.mock import MagicMock, patch @@ -66,10 +67,9 @@ class TestOpenLPImport(TestCase): importer.stop_import_flag = True # WHEN: Import source is not a list - for source in ['not a list', 0]: - importer.import_source = source + importer.import_source = Path() - # THEN: do_import should return none and the progress bar maximum should not be set. - assert importer.do_import() is None, 'do_import should return None when import_source is not a list' - assert mocked_import_wizard.progress_bar.setMaximum.called is False, \ - 'setMaximum on import_wizard.progress_bar should not have been called' + # THEN: do_import should return none and the progress bar maximum should not be set. + assert importer.do_import() is None, 'do_import should return None when import_source is not a list' + assert mocked_import_wizard.progress_bar.setMaximum.called is False, \ + 'setMaximum on import_wizard.progress_bar should not have been called' From fc8255d6582f48c219083d0e4fcbfb9458489bee Mon Sep 17 00:00:00 2001 From: Philip Ridout Date: Fri, 8 Mar 2019 21:00:16 +0000 Subject: [PATCH 3/8] PEP fixes --- openlp/core/display/render.py | 4 ++-- .../openlp_core/ui/test_firsttimeform.py | 18 +++++++++--------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/openlp/core/display/render.py b/openlp/core/display/render.py index 35f144ee8..0358c74aa 100644 --- a/openlp/core/display/render.py +++ b/openlp/core/display/render.py @@ -43,8 +43,8 @@ from openlp.core.lib.formattingtags import FormattingTags log = logging.getLogger(__name__) SLIM_CHARS = 'fiíIÍjlĺľrtť.,;/ ()|"\'!:\\' -CHORD_LINE_MATCH = re.compile(r'\[(.*?)\]([\u0080-\uFFFF,\w]*)' # noqa - '([\u0080-\uFFFF,\w,\s,\.,\,,\!,\?,\;,\:,\|,\",\',\-,\_]*)(\Z)?') +CHORD_LINE_MATCH = re.compile(r'\[(.*?)\]([\u0080-\uFFFF,\w]*)' + r'([\u0080-\uFFFF,\w,\s,\.,\,,\!,\?,\;,\:,\|,\",\',\-,\_]*)(\Z)?') CHORD_TEMPLATE = '{chord}' FIRST_CHORD_TEMPLATE = '{chord}' CHORD_LINE_TEMPLATE = '{chord}{tail}{whitespace}{remainder}' diff --git a/tests/functional/openlp_core/ui/test_firsttimeform.py b/tests/functional/openlp_core/ui/test_firsttimeform.py index e5d0ce550..59070f8fd 100644 --- a/tests/functional/openlp_core/ui/test_firsttimeform.py +++ b/tests/functional/openlp_core/ui/test_firsttimeform.py @@ -47,6 +47,7 @@ class TestThemeListWidgetItem(TestCase): """ Test the :class:`ThemeListWidgetItem` class """ + def setUp(self): self.sample_theme_data = {'file_name': 'BlueBurst.otz', 'sha256': 'sha_256_hash', 'thumbnail': 'BlueBurst.png', 'title': 'Blue Burst'} @@ -61,7 +62,7 @@ class TestThemeListWidgetItem(TestCase): """ Test that the theme data is loaded correctly in to a ThemeListWidgetItem object when instantiated """ - # GIVEN: A sample theme dictanary object + # GIVEN: A sample theme dictionary object # WHEN: Creating an instance of `ThemeListWidgetItem` instance = ThemeListWidgetItem('url', self.sample_theme_data, MagicMock()) @@ -76,7 +77,7 @@ class TestThemeListWidgetItem(TestCase): """ Test that the `DownloadWorker` worker is set up correctly and that the thread is started. """ - # GIVEN: A sample theme dictanary object + # GIVEN: A sample theme dictionary object mocked_ftw = MagicMock(spec=FirstTimeForm) mocked_ftw.thumbnail_download_threads = [] @@ -259,8 +260,7 @@ class TestFirstTimeForm(TestCase, TestMixin): """ # GIVEN: An instance of FirstTimeForm frw = FirstTimeForm(None) - with patch.object(frw, '_set_plugin_status'), \ - patch.object(frw, 'screen_selection_widget'), \ + with patch.object(frw, '_set_plugin_status'), patch.object(frw, 'screen_selection_widget'), \ patch.object(frw, 'theme_combo_box', **{'currentIndex.return_value': '-1'}): # WHEN: Calling accept and the currentIndex method of the theme_combo_box returns -1 @@ -277,9 +277,9 @@ class TestFirstTimeForm(TestCase, TestMixin): # GIVEN: An instance of FirstTimeForm frw = FirstTimeForm(None) with patch.object(frw, '_set_plugin_status'), \ - patch.object(frw, 'screen_selection_widget'), \ - patch.object( - frw, 'theme_combo_box', **{'currentIndex.return_value': 0, 'currentText.return_value': 'Test Item'}): + patch.object(frw, 'screen_selection_widget'), \ + patch.object( + frw, 'theme_combo_box', **{'currentIndex.return_value': 0, 'currentText.return_value': 'Test Item'}): # WHEN: Calling accept and the currentIndex method of the theme_combo_box returns 0 frw.accept() @@ -379,8 +379,8 @@ class TestFirstTimeForm(TestCase, TestMixin): # THEN: the critical_error_message_box should have been called mocked_message_box.critical.assert_called_once_with( - first_time_form, 'Network Error', 'There was a network error attempting to connect to retrieve ' - 'initial configuration information', 'OK') + first_time_form, 'Network Error', + 'There was a network error attempting to connect to retrieve initial configuration information', 'OK') @patch('openlp.core.ui.firsttimewizard.Settings') def test_on_projectors_check_box_checked(self, MockSettings): From 11aa69c9ac38a6e51549dceb5d39a296eee759ab Mon Sep 17 00:00:00 2001 From: Philip Ridout Date: Fri, 8 Mar 2019 21:25:16 +0000 Subject: [PATCH 4/8] PEP fixes --- openlp/core/common/httputils.py | 2 +- openlp/core/ui/firsttimeform.py | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/openlp/core/common/httputils.py b/openlp/core/common/httputils.py index 1231cb7be..ba4326f6e 100644 --- a/openlp/core/common/httputils.py +++ b/openlp/core/common/httputils.py @@ -203,7 +203,7 @@ def download_file(update_object, url, file_path, sha256=None, proxy=None): while retries < CONNECTION_RETRIES: try: with file_path.open('wb') as saved_file: - response = requests.get(url, proxies=proxy, timeout=float(CONNECTION_TIMEOUT), stream=True) + response = requests.get(url, proxies=proxy, timeout=float(CONNECTION_TIMEOUT), stream=True) if sha256: hasher = hashlib.sha256() # Download until finished or canceled. diff --git a/openlp/core/ui/firsttimeform.py b/openlp/core/ui/firsttimeform.py index 0826a2081..094403135 100644 --- a/openlp/core/ui/firsttimeform.py +++ b/openlp/core/ui/firsttimeform.py @@ -32,7 +32,7 @@ from tempfile import gettempdir from PyQt5 import QtCore, QtWidgets -from openlp.core.common import clean_button_text, trace_error_handler +from openlp.core.common import trace_error_handler from openlp.core.common.applocation import AppLocation from openlp.core.common.httputils import DownloadWorker, download_file, get_url_file_size, get_web_page from openlp.core.common.i18n import translate @@ -435,7 +435,6 @@ class FirstTimeForm(QtWidgets.QWizard, UiFirstTimeWizard, RegistryProperties): """ Clean up the UI after the process has finished. """ - complete_str = '' if self.max_progress: self.progress_bar.setValue(self.progress_bar.maximum()) if self.has_run_wizard: From 1523d9ffa6119d23735c9db955e275cb0d539d00 Mon Sep 17 00:00:00 2001 From: Philip Ridout Date: Fri, 8 Mar 2019 22:51:27 +0000 Subject: [PATCH 5/8] mac test fix? --- tests/functional/openlp_core/ui/test_firsttimeform.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/functional/openlp_core/ui/test_firsttimeform.py b/tests/functional/openlp_core/ui/test_firsttimeform.py index 59070f8fd..d4ddcae46 100644 --- a/tests/functional/openlp_core/ui/test_firsttimeform.py +++ b/tests/functional/openlp_core/ui/test_firsttimeform.py @@ -261,6 +261,7 @@ class TestFirstTimeForm(TestCase, TestMixin): # GIVEN: An instance of FirstTimeForm frw = FirstTimeForm(None) with patch.object(frw, '_set_plugin_status'), patch.object(frw, 'screen_selection_widget'), \ + patch.object(frw, 'setup_ui'), \ patch.object(frw, 'theme_combo_box', **{'currentIndex.return_value': '-1'}): # WHEN: Calling accept and the currentIndex method of the theme_combo_box returns -1 From 87216707d3e45fc714851ca68512fae705e4bd77 Mon Sep 17 00:00:00 2001 From: Philip Ridout Date: Sat, 9 Mar 2019 06:58:52 +0000 Subject: [PATCH 6/8] Test fixes --- openlp/core/ui/firsttimeform.py | 7 +++++ openlp/core/ui/firsttimewizard.py | 7 ----- .../openlp_core/common/test_httputils.py | 12 +++------ .../openlp_core/ui/test_firsttimeform.py | 26 +++++++++---------- 4 files changed, 24 insertions(+), 28 deletions(-) diff --git a/openlp/core/ui/firsttimeform.py b/openlp/core/ui/firsttimeform.py index 094403135..3af8f66b7 100644 --- a/openlp/core/ui/firsttimeform.py +++ b/openlp/core/ui/firsttimeform.py @@ -336,6 +336,13 @@ class FirstTimeForm(QtWidgets.QWizard, UiFirstTimeWizard, RegistryProperties): proxy_dialog.retranslate_ui() proxy_dialog.exec() + def on_projectors_check_box_clicked(self): + # When clicking projectors_check box, change the visibility setting for Projectors panel. + if Settings().value('projector/show after wizard'): + Settings().setValue('projector/show after wizard', False) + else: + Settings().setValue('projector/show after wizard', True) + def on_themes_list_widget_selection_changed(self): """ Update the `theme_combo_box` with the selected items diff --git a/openlp/core/ui/firsttimewizard.py b/openlp/core/ui/firsttimewizard.py index f93ce02f3..d80fb6013 100644 --- a/openlp/core/ui/firsttimewizard.py +++ b/openlp/core/ui/firsttimewizard.py @@ -326,10 +326,3 @@ class UiFirstTimeWizard(object): 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.')) - - def on_projectors_check_box_clicked(self): - # When clicking projectors_check box, change the visibility setting for Projectors panel. - if Settings().value('projector/show after wizard'): - Settings().setValue('projector/show after wizard', False) - else: - Settings().setValue('projector/show after wizard', True) diff --git a/tests/functional/openlp_core/common/test_httputils.py b/tests/functional/openlp_core/common/test_httputils.py index f059d893c..ba1e36441 100644 --- a/tests/functional/openlp_core/common/test_httputils.py +++ b/tests/functional/openlp_core/common/test_httputils.py @@ -249,34 +249,30 @@ class TestGetProxySettings(TestCase, TestMixin): self.addCleanup(self.destroy_settings) @patch('openlp.core.common.httputils.Settings') - def test_mode_arg_specified(self, MockSettings): + def test_mode_arg_specified(self, mocked_settings): """ Test that the argument is used rather than reading the 'advanced/proxy mode' setting """ # GIVEN: Mocked settings - mocked_settings = MagicMock() - MockSettings.return_value = mocked_settings # WHEN: Calling `get_proxy_settings` with the mode arg specified get_proxy_settings(mode=ProxyMode.NO_PROXY) # THEN: The mode arg should have been used rather than looking it up in the settings - mocked_settings.value.assert_not_called() + mocked_settings().value.assert_not_called() @patch('openlp.core.common.httputils.Settings') - def test_mode_incorrect_arg_specified(self, MockSettings): + def test_mode_incorrect_arg_specified(self, mocked_settings): """ Test that the system settings are used when the mode arg specieied is invalid """ # GIVEN: Mocked settings - mocked_settings = MagicMock() - MockSettings.return_value = mocked_settings # WHEN: Calling `get_proxy_settings` with an invalid mode arg specified result = get_proxy_settings(mode='qwerty') # THEN: An None should be returned - mocked_settings.value.assert_not_called() + mocked_settings().value.assert_not_called() assert result is None def test_no_proxy_mode(self): diff --git a/tests/functional/openlp_core/ui/test_firsttimeform.py b/tests/functional/openlp_core/ui/test_firsttimeform.py index d4ddcae46..36ed165a6 100644 --- a/tests/functional/openlp_core/ui/test_firsttimeform.py +++ b/tests/functional/openlp_core/ui/test_firsttimeform.py @@ -126,7 +126,7 @@ class TestFirstTimeForm(TestCase, TestMixin): assert [] == frw.thumbnail_download_threads, 'The list of threads should be empty' assert frw.has_run_wizard is False, 'has_run_wizard should be False' - @patch('openlp.core.ui.firsttimewizard.QtWidgets.QWizard.exec') + @patch('openlp.core.ui.firsttimeform.QtWidgets.QWizard.exec') def test_exec(self, mocked_qwizard_exec): # GIVEN: An instance of FirstTimeForm @@ -223,9 +223,10 @@ class TestFirstTimeForm(TestCase, TestMixin): mocked_theme_combo_box.findText.assert_called_once_with('Default Theme') mocked_theme_combo_box.setCurrentIndex(3) - @patch('openlp.core.ui.firsttimewizard.QtWidgets.QWizard.accept') - @patch('openlp.core.ui.firsttimewizard.Settings') - def test_accept_method(self, mocked_settings, mocked_qwizard_accept): + + @patch('openlp.core.ui.firsttimeform.Settings') + @patch('openlp.core.ui.firsttimeform.QtWidgets.QWizard.accept') + def test_accept_method(self, mocked_qwizard_accept, *args): """ Test the FirstTimeForm.accept method """ @@ -253,7 +254,7 @@ class TestFirstTimeForm(TestCase, TestMixin): mocked_screen_selection_widget.save.assert_called_once() mocked_qwizard_accept.assert_called_once() - @patch('openlp.core.ui.firsttimewizard.Settings') + @patch('openlp.core.ui.firsttimeform.Settings') def test_accept_method_theme_not_selected(self, mocked_settings): """ Test the FirstTimeForm.accept method when there is no default theme selected @@ -261,14 +262,13 @@ class TestFirstTimeForm(TestCase, TestMixin): # GIVEN: An instance of FirstTimeForm frw = FirstTimeForm(None) with patch.object(frw, '_set_plugin_status'), patch.object(frw, 'screen_selection_widget'), \ - patch.object(frw, 'setup_ui'), \ - patch.object(frw, 'theme_combo_box', **{'currentIndex.return_value': '-1'}): + patch.object(frw, 'theme_combo_box', **{'currentIndex.return_value': -1}): # WHEN: Calling accept and the currentIndex method of the theme_combo_box returns -1 frw.accept() # THEN: OpenLP should not try to save a theme name - mocked_settings.setValue.assert_not_called() + mocked_settings().setValue.assert_not_called() @patch('openlp.core.ui.firsttimeform.Settings') def test_accept_method_theme_selected(self, mocked_settings): @@ -288,7 +288,7 @@ class TestFirstTimeForm(TestCase, TestMixin): # THEN: The 'currentItem' in the combobox should have been set as the default theme. mocked_settings().setValue.assert_called_once_with('themes/global theme', 'Test Item') - @patch('openlp.core.ui.firsttimewizard.QtWidgets.QWizard.reject') + @patch('openlp.core.ui.firsttimeform.QtWidgets.QWizard.reject') @patch('openlp.core.ui.firsttimeform.time') @patch('openlp.core.ui.firsttimeform.get_thread_worker') @patch('openlp.core.ui.firsttimeform.is_thread_finished') @@ -383,7 +383,7 @@ class TestFirstTimeForm(TestCase, TestMixin): first_time_form, 'Network Error', 'There was a network error attempting to connect to retrieve initial configuration information', 'OK') - @patch('openlp.core.ui.firsttimewizard.Settings') + @patch('openlp.core.ui.firsttimeform.Settings') def test_on_projectors_check_box_checked(self, MockSettings): """ Test that the projector panel is shown when the checkbox in the first time wizard is checked @@ -398,10 +398,10 @@ class TestFirstTimeForm(TestCase, TestMixin): frw.on_projectors_check_box_clicked() # THEN: The visibility of the projects panel should have been set - mocked_settings.value.assert_called_once_with('projector/show after wizard') - mocked_settings.setValue.assert_called_once_with('projector/show after wizard', False) + mocked_settings().value.assert_called_once_with('projector/show after wizard') + mocked_settings().setValue.assert_called_once_with('projector/show after wizard', False) - @patch('openlp.core.ui.firsttimewizard.Settings') + @patch('openlp.core.ui.firsttimeform.Settings') def test_on_projectors_check_box_unchecked(self, MockSettings): """ Test that the projector panel is shown when the checkbox in the first time wizard is checked From 0578ab8908417491fa119e99b5ae63a95c4d7878 Mon Sep 17 00:00:00 2001 From: Philip Ridout Date: Sat, 9 Mar 2019 07:06:00 +0000 Subject: [PATCH 7/8] Test fix --- tests/functional/openlp_core/ui/test_firsttimeform.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/functional/openlp_core/ui/test_firsttimeform.py b/tests/functional/openlp_core/ui/test_firsttimeform.py index 36ed165a6..0a5fe5a0f 100644 --- a/tests/functional/openlp_core/ui/test_firsttimeform.py +++ b/tests/functional/openlp_core/ui/test_firsttimeform.py @@ -398,8 +398,8 @@ class TestFirstTimeForm(TestCase, TestMixin): frw.on_projectors_check_box_clicked() # THEN: The visibility of the projects panel should have been set - mocked_settings().value.assert_called_once_with('projector/show after wizard') - mocked_settings().setValue.assert_called_once_with('projector/show after wizard', False) + mocked_settings.value.assert_called_once_with('projector/show after wizard') + mocked_settings.setValue.assert_called_once_with('projector/show after wizard', False) @patch('openlp.core.ui.firsttimeform.Settings') def test_on_projectors_check_box_unchecked(self, MockSettings): From 056b902cf9549969e193d5ba59bf16473a9f9baa Mon Sep 17 00:00:00 2001 From: Philip Ridout Date: Fri, 15 Mar 2019 20:47:47 +0000 Subject: [PATCH 8/8] PEP8 --- openlp/core/ui/firsttimewizard.py | 1 - tests/functional/openlp_core/ui/test_firsttimeform.py | 1 - 2 files changed, 2 deletions(-) diff --git a/openlp/core/ui/firsttimewizard.py b/openlp/core/ui/firsttimewizard.py index d80fb6013..b3aa042a4 100644 --- a/openlp/core/ui/firsttimewizard.py +++ b/openlp/core/ui/firsttimewizard.py @@ -26,7 +26,6 @@ from PyQt5 import QtCore, QtGui, QtWidgets from openlp.core.common import clean_button_text, is_macosx from openlp.core.common.i18n import translate -from openlp.core.common.settings import Settings from openlp.core.lib.ui import add_welcome_page from openlp.core.ui.icons import UiIcons diff --git a/tests/functional/openlp_core/ui/test_firsttimeform.py b/tests/functional/openlp_core/ui/test_firsttimeform.py index 0a5fe5a0f..554ae30b7 100644 --- a/tests/functional/openlp_core/ui/test_firsttimeform.py +++ b/tests/functional/openlp_core/ui/test_firsttimeform.py @@ -223,7 +223,6 @@ class TestFirstTimeForm(TestCase, TestMixin): mocked_theme_combo_box.findText.assert_called_once_with('Default Theme') mocked_theme_combo_box.setCurrentIndex(3) - @patch('openlp.core.ui.firsttimeform.Settings') @patch('openlp.core.ui.firsttimeform.QtWidgets.QWizard.accept') def test_accept_method(self, mocked_qwizard_accept, *args):