コード例 #1
0
    def test_logout(self, mocked_build_opener):
        """
        Test that when the logout method is called, it logs the user out of SongSelect
        """
        # GIVEN: A bunch of mocked out stuff and an importer object
        mocked_opener = MagicMock()
        mocked_build_opener.return_value = mocked_opener
        importer = SongSelectImport(None)

        # WHEN: The login method is called after being rigged to fail
        importer.logout()

        # THEN: The opener is called once with the logout url
        assert 1 == mocked_opener.open.call_count, 'opener should have been called once'
        mocked_opener.open.assert_called_with(LOGOUT_URL)
コード例 #2
0
ファイル: test_songselect.py プロジェクト: imkernel/openlp
    def test_logout(self, mocked_build_opener):
        """
        Test that when the logout method is called, it logs the user out of SongSelect
        """
        # GIVEN: A bunch of mocked out stuff and an importer object
        mocked_opener = MagicMock()
        mocked_build_opener.return_value = mocked_opener
        importer = SongSelectImport(None)

        # WHEN: The login method is called after being rigged to fail
        importer.logout()

        # THEN: The opener is called once with the logout url
        self.assertEqual(1, mocked_opener.open.call_count, 'opener should have been called once')
        mocked_opener.open.assert_called_with(LOGOUT_URL)
コード例 #3
0
class SongSelectForm(QtWidgets.QDialog, Ui_SongSelectDialog,
                     RegistryProperties):
    """
    The :class:`SongSelectForm` class is the SongSelect dialog.
    """
    def __init__(self, parent=None, plugin=None, db_manager=None):
        QtWidgets.QDialog.__init__(
            self, parent, QtCore.Qt.WindowSystemMenuHint
            | QtCore.Qt.WindowTitleHint | QtCore.Qt.WindowCloseButtonHint)
        self.plugin = plugin
        self.db_manager = db_manager
        self.setup_ui(self)

    def initialise(self):
        """
        Initialise the SongSelectForm
        """
        self.song_count = 0
        self.song = None
        self.set_progress_visible(False)
        self.song_select_importer = SongSelectImport(self.db_manager)
        self.save_password_checkbox.toggled.connect(
            self.on_save_password_checkbox_toggled)
        self.login_button.clicked.connect(self.on_login_button_clicked)
        self.search_button.clicked.connect(self.on_search_button_clicked)
        self.search_combobox.returnPressed.connect(
            self.on_search_button_clicked)
        self.stop_button.clicked.connect(self.on_stop_button_clicked)
        self.logout_button.clicked.connect(self.done)
        self.search_results_widget.itemDoubleClicked.connect(
            self.on_search_results_widget_double_clicked)
        self.search_results_widget.itemSelectionChanged.connect(
            self.on_search_results_widget_selection_changed)
        self.view_button.clicked.connect(self.on_view_button_clicked)
        self.back_button.clicked.connect(self.on_back_button_clicked)
        self.import_button.clicked.connect(self.on_import_button_clicked)

    def exec(self):
        """
        Execute the dialog. This method sets everything back to its initial
        values.
        """
        self.stacked_widget.setCurrentIndex(0)
        self.username_edit.setEnabled(True)
        self.password_edit.setEnabled(True)
        self.save_password_checkbox.setEnabled(True)
        self.search_combobox.clearEditText()
        self.search_combobox.clear()
        self.search_results_widget.clear()
        self.view_button.setEnabled(False)
        if self.settings.contains(self.plugin.settings_section +
                                  '/songselect password'):
            self.username_edit.setText(
                self.settings.value(self.plugin.settings_section +
                                    '/songselect username'))
            self.password_edit.setText(
                self.settings.value(self.plugin.settings_section +
                                    '/songselect password'))
            self.save_password_checkbox.setChecked(True)
        if self.settings.contains(self.plugin.settings_section +
                                  '/songselect searches'):
            self.search_combobox.addItems(
                self.settings.value(self.plugin.settings_section +
                                    '/songselect searches').split('|'))
        self.username_edit.setFocus()
        return QtWidgets.QDialog.exec(self)

    def done(self, result_code):
        """
        Log out of SongSelect.

        :param result_code: The result of the dialog.
        """
        log.debug('Closing SongSelectForm')
        if self.stacked_widget.currentIndex() > 0:
            progress_dialog = QtWidgets.QProgressDialog(
                translate('SongsPlugin.SongSelectForm', 'Logging out...'), '',
                0, 2, self)
            progress_dialog.setWindowModality(QtCore.Qt.WindowModal)
            progress_dialog.setCancelButton(None)
            progress_dialog.setValue(1)
            progress_dialog.show()
            progress_dialog.setFocus()
            self.application.process_events()
            sleep(0.5)
            self.application.process_events()
            self.song_select_importer.logout()
            self.application.process_events()
            progress_dialog.setValue(2)
        return QtWidgets.QDialog.done(self, result_code)

    def _update_login_progress(self):
        """
        Update the progress bar as the user logs in.
        """
        self.login_progress_bar.setValue(self.login_progress_bar.value() + 1)
        self.application.process_events()

    def _update_song_progress(self):
        """
        Update the progress bar as the song is being downloaded.
        """
        self.song_progress_bar.setValue(self.song_progress_bar.value() + 1)
        self.application.process_events()

    def _view_song(self, current_item):
        """
        Load a song into the song view.
        """
        if not current_item:
            return
        else:
            current_item = current_item.data(QtCore.Qt.UserRole)
        # Stop the current search, if it's running
        self.song_select_importer.stop()
        # Clear up the UI
        self.song_progress_bar.setVisible(True)
        self.import_button.setEnabled(False)
        self.back_button.setEnabled(False)
        self.title_edit.setText('')
        self.title_edit.setEnabled(False)
        self.copyright_edit.setText('')
        self.copyright_edit.setEnabled(False)
        self.ccli_edit.setText('')
        self.ccli_edit.setEnabled(False)
        self.author_list_widget.clear()
        self.author_list_widget.setEnabled(False)
        self.lyrics_table_widget.clear()
        self.lyrics_table_widget.setRowCount(0)
        self.lyrics_table_widget.setEnabled(False)
        self.stacked_widget.setCurrentIndex(2)
        song = {}
        for key, value in current_item.items():
            song[key] = value
        self.song_progress_bar.setValue(0)
        self.application.process_events()
        # Get the full song
        song = self.song_select_importer.get_song(song,
                                                  self._update_song_progress)
        if not song:
            QtWidgets.QMessageBox.critical(
                self, translate('SongsPlugin.SongSelectForm',
                                'Incomplete song'),
                translate(
                    'SongsPlugin.SongSelectForm',
                    'This song is missing some information, like the lyrics, '
                    'and cannot be imported.'),
                QtWidgets.QMessageBox.StandardButtons(
                    QtWidgets.QMessageBox.Ok), QtWidgets.QMessageBox.Ok)
            self.stacked_widget.setCurrentIndex(1)
            return
        # Update the UI
        self.title_edit.setText(song['title'])
        self.copyright_edit.setText(song['copyright'])
        self.ccli_edit.setText(song['ccli_number'])
        for author in song['authors']:
            QtWidgets.QListWidgetItem(author, self.author_list_widget)
        for counter, verse in enumerate(song['verses']):
            self.lyrics_table_widget.setRowCount(
                self.lyrics_table_widget.rowCount() + 1)
            item = QtWidgets.QTableWidgetItem(verse['lyrics'])
            item.setData(QtCore.Qt.UserRole, verse['label'])
            item.setFlags(item.flags() ^ QtCore.Qt.ItemIsEditable)
            self.lyrics_table_widget.setItem(counter, 0, item)
        self.lyrics_table_widget.setVerticalHeaderLabels(
            [verse['label'] for verse in song['verses']])
        self.lyrics_table_widget.resizeRowsToContents()
        self.title_edit.setEnabled(True)
        self.copyright_edit.setEnabled(True)
        self.ccli_edit.setEnabled(True)
        self.author_list_widget.setEnabled(True)
        self.lyrics_table_widget.setEnabled(True)
        self.lyrics_table_widget.repaint()
        self.import_button.setEnabled(True)
        self.back_button.setEnabled(True)
        self.song_progress_bar.setVisible(False)
        self.song_progress_bar.setValue(0)
        self.song = song
        self.application.process_events()

    def on_save_password_checkbox_toggled(self, checked):
        """
        Show a warning dialog when the user toggles the save checkbox on or off.

        :param checked: If the combobox is checked or not
        """
        if checked and self.login_page.isVisible():
            answer = QtWidgets.QMessageBox.question(
                self,
                translate('SongsPlugin.SongSelectForm',
                          'Save Username and Password'),
                translate(
                    'SongsPlugin.SongSelectForm',
                    'WARNING: Saving your username and password is INSECURE, your '
                    'password is stored in PLAIN TEXT. Click Yes to save your '
                    'password or No to cancel this.'),
                defaultButton=QtWidgets.QMessageBox.No)
            if answer == QtWidgets.QMessageBox.No:
                self.save_password_checkbox.setChecked(False)

    def on_login_button_clicked(self):
        """
        Log the user in to SongSelect.
        """
        self.username_edit.setEnabled(False)
        self.password_edit.setEnabled(False)
        self.save_password_checkbox.setEnabled(False)
        self.login_button.setEnabled(False)
        self.login_spacer.setVisible(False)
        self.login_progress_bar.setValue(0)
        self.login_progress_bar.setVisible(True)
        self.application.process_events()
        # Log the user in
        subscription_level = self.song_select_importer.login(
            self.username_edit.text(), self.password_edit.text(),
            self._update_login_progress)
        if not subscription_level:
            QtWidgets.QMessageBox.critical(
                self,
                translate('SongsPlugin.SongSelectForm', 'Error Logging In'),
                translate(
                    'SongsPlugin.SongSelectForm',
                    'There was a problem logging in, perhaps your username or password is incorrect?'
                ))
        else:
            if subscription_level == 'Free':
                QtWidgets.QMessageBox.information(
                    self, translate('SongsPlugin.SongSelectForm', 'Free user'),
                    translate(
                        'SongsPlugin.SongSelectForm',
                        'You logged in with a free account, '
                        'the search will be limited to songs '
                        'in the public domain.'))
            if self.save_password_checkbox.isChecked():
                self.settings.setValue(
                    self.plugin.settings_section + '/songselect username',
                    self.username_edit.text())
                self.settings.setValue(
                    self.plugin.settings_section + '/songselect password',
                    self.password_edit.text())
            else:
                self.settings.remove(self.plugin.settings_section +
                                     '/songselect username')
                self.settings.remove(self.plugin.settings_section +
                                     '/songselect password')
            self.stacked_widget.setCurrentIndex(1)
        self.login_progress_bar.setVisible(False)
        self.login_progress_bar.setValue(0)
        self.login_spacer.setVisible(True)
        self.login_button.setEnabled(True)
        self.username_edit.setEnabled(True)
        self.password_edit.setEnabled(True)
        self.save_password_checkbox.setEnabled(True)
        self.search_combobox.setFocus()
        self.application.process_events()

    def on_search_button_clicked(self):
        """
        Run a search on SongSelect.
        """
        # Set up UI components
        self.view_button.setEnabled(False)
        self.search_button.setEnabled(False)
        self.search_combobox.setEnabled(False)
        self.search_progress_bar.setMinimum(0)
        self.search_progress_bar.setMaximum(0)
        self.search_progress_bar.setValue(0)
        self.set_progress_visible(True)
        self.search_results_widget.clear()
        self.result_count_label.setText(
            translate('SongsPlugin.SongSelectForm',
                      'Found {count:d} song(s)').format(count=self.song_count))
        self.application.process_events()
        self.song_count = 0
        search_history = self.search_combobox.getItems()
        self.settings.setValue(
            self.plugin.settings_section + '/songselect searches',
            '|'.join(search_history))
        # Create thread and run search
        worker = SearchWorker(self.song_select_importer,
                              self.search_combobox.currentText())
        worker.show_info.connect(self.on_search_show_info)
        worker.found_song.connect(self.on_search_found_song)
        worker.finished.connect(self.on_search_finished)
        run_thread(worker, 'songselect')

    def on_stop_button_clicked(self):
        """
        Stop the search when the stop button is clicked.
        """
        self.song_select_importer.stop()

    def on_search_show_info(self, title, message):
        """
        Show an informational message from the search thread
        :param title:
        :param message:
        """
        QtWidgets.QMessageBox.information(self, title, message)

    def on_search_found_song(self, song):
        """
        Add a song to the list when one is found.
        :param song:
        """
        self.song_count += 1
        self.result_count_label.setText(
            translate('SongsPlugin.SongSelectForm',
                      'Found {count:d} song(s)').format(count=self.song_count))
        item_title = song['title'] + ' (' + ', '.join(song['authors']) + ')'
        song_item = QtWidgets.QListWidgetItem(item_title,
                                              self.search_results_widget)
        song_item.setData(QtCore.Qt.UserRole, song)

    def on_search_finished(self):
        """
        Slot which is called when the search is completed.
        """
        self.application.process_events()
        self.set_progress_visible(False)
        self.search_button.setEnabled(True)
        self.search_combobox.setEnabled(True)
        self.application.process_events()

    def on_search_results_widget_selection_changed(self):
        """
        Enable or disable the view button when the selection changes.
        """
        self.view_button.setEnabled(
            len(self.search_results_widget.selectedItems()) > 0)

    def on_view_button_clicked(self):
        """
        View a song from SongSelect.
        """
        self._view_song(self.search_results_widget.currentItem())

    def on_search_results_widget_double_clicked(self, current_item):
        """
        View a song from SongSelect

        :param current_item:
        """
        self._view_song(current_item)

    def on_back_button_clicked(self):
        """
        Go back to the search page.
        """
        self.stacked_widget.setCurrentIndex(1)
        self.search_combobox.setFocus()

    def on_import_button_clicked(self):
        """
        Import a song from SongSelect.
        """
        self.song_select_importer.save_song(self.song)
        self.song = None
        if QtWidgets.QMessageBox.question(
                self,
                translate('SongsPlugin.SongSelectForm', 'Song Imported'),
                translate(
                    'SongsPlugin.SongSelectForm',
                    'Your song has been imported, would you '
                    'like to import more songs?'),
                defaultButton=QtWidgets.QMessageBox.Yes
        ) == QtWidgets.QMessageBox.Yes:
            self.on_back_button_clicked()
        else:
            self.application.process_events()
            self.done(QtWidgets.QDialog.Accepted)

    def set_progress_visible(self, is_visible):
        """
        Show or hide the search progress, including the stop button.
        """
        self.search_progress_bar.setVisible(is_visible)
        self.stop_button.setVisible(is_visible)
コード例 #4
0
ファイル: songselectform.py プロジェクト: imkernel/openlp
class SongSelectForm(QtWidgets.QDialog, Ui_SongSelectDialog):
    """
    The :class:`SongSelectForm` class is the SongSelect dialog.
    """

    def __init__(self, parent=None, plugin=None, db_manager=None):
        QtWidgets.QDialog.__init__(self, parent, QtCore.Qt.WindowSystemMenuHint | QtCore.Qt.WindowTitleHint)
        self.plugin = plugin
        self.db_manager = db_manager
        self.setup_ui(self)

    def initialise(self):
        """
        Initialise the SongSelectForm
        """
        self.thread = None
        self.worker = None
        self.song_count = 0
        self.song = None
        self.set_progress_visible(False)
        self.song_select_importer = SongSelectImport(self.db_manager)
        self.save_password_checkbox.toggled.connect(self.on_save_password_checkbox_toggled)
        self.login_button.clicked.connect(self.on_login_button_clicked)
        self.search_button.clicked.connect(self.on_search_button_clicked)
        self.search_combobox.returnPressed.connect(self.on_search_button_clicked)
        self.stop_button.clicked.connect(self.on_stop_button_clicked)
        self.logout_button.clicked.connect(self.done)
        self.search_results_widget.itemDoubleClicked.connect(self.on_search_results_widget_double_clicked)
        self.search_results_widget.itemSelectionChanged.connect(self.on_search_results_widget_selection_changed)
        self.view_button.clicked.connect(self.on_view_button_clicked)
        self.back_button.clicked.connect(self.on_back_button_clicked)
        self.import_button.clicked.connect(self.on_import_button_clicked)

    def exec(self):
        """
        Execute the dialog. This method sets everything back to its initial
        values.
        """
        self.stacked_widget.setCurrentIndex(0)
        self.username_edit.setEnabled(True)
        self.password_edit.setEnabled(True)
        self.save_password_checkbox.setEnabled(True)
        self.search_combobox.clearEditText()
        self.search_combobox.clear()
        self.search_results_widget.clear()
        self.view_button.setEnabled(False)
        if Settings().contains(self.plugin.settings_section + '/songselect password'):
            self.username_edit.setText(Settings().value(self.plugin.settings_section + '/songselect username'))
            self.password_edit.setText(Settings().value(self.plugin.settings_section + '/songselect password'))
            self.save_password_checkbox.setChecked(True)
        if Settings().contains(self.plugin.settings_section + '/songselect searches'):
            self.search_combobox.addItems(
                Settings().value(self.plugin.settings_section + '/songselect searches').split('|'))
        self.username_edit.setFocus()
        return QtWidgets.QDialog.exec(self)

    def done(self, r):
        """
        Log out of SongSelect.

        :param r: The result of the dialog.
        """
        log.debug('Closing SongSelectForm')
        if self.stacked_widget.currentIndex() > 0:
            progress_dialog = QtWidgets.QProgressDialog(
                translate('SongsPlugin.SongSelectForm', 'Logging out...'), '', 0, 2, self)
            progress_dialog.setWindowModality(QtCore.Qt.WindowModal)
            progress_dialog.setCancelButton(None)
            progress_dialog.setValue(1)
            progress_dialog.show()
            progress_dialog.setFocus()
            self.application.process_events()
            sleep(0.5)
            self.application.process_events()
            self.song_select_importer.logout()
            self.application.process_events()
            progress_dialog.setValue(2)
        return QtWidgets.QDialog.done(self, r)

    def _update_login_progress(self):
        """
        Update the progress bar as the user logs in.
        """
        self.login_progress_bar.setValue(self.login_progress_bar.value() + 1)
        self.application.process_events()

    def _update_song_progress(self):
        """
        Update the progress bar as the song is being downloaded.
        """
        self.song_progress_bar.setValue(self.song_progress_bar.value() + 1)
        self.application.process_events()

    def _view_song(self, current_item):
        """
        Load a song into the song view.
        """
        if not current_item:
            return
        else:
            current_item = current_item.data(QtCore.Qt.UserRole)
        # Stop the current search, if it's running
        self.song_select_importer.stop()
        # Clear up the UI
        self.song_progress_bar.setVisible(True)
        self.import_button.setEnabled(False)
        self.back_button.setEnabled(False)
        self.title_edit.setText('')
        self.title_edit.setEnabled(False)
        self.copyright_edit.setText('')
        self.copyright_edit.setEnabled(False)
        self.ccli_edit.setText('')
        self.ccli_edit.setEnabled(False)
        self.author_list_widget.clear()
        self.author_list_widget.setEnabled(False)
        self.lyrics_table_widget.clear()
        self.lyrics_table_widget.setRowCount(0)
        self.lyrics_table_widget.setEnabled(False)
        self.stacked_widget.setCurrentIndex(2)
        song = {}
        for key, value in current_item.items():
            song[key] = value
        self.song_progress_bar.setValue(0)
        self.application.process_events()
        # Get the full song
        song = self.song_select_importer.get_song(song, self._update_song_progress)
        if not song:
            QtWidgets.QMessageBox.critical(
                self, translate('SongsPlugin.SongSelectForm', 'Incomplete song'),
                translate('SongsPlugin.SongSelectForm', 'This song is missing some information, like the lyrics, '
                                                        'and cannot be imported.'),
                QtWidgets.QMessageBox.StandardButtons(QtWidgets.QMessageBox.Ok), QtWidgets.QMessageBox.Ok)
            self.stacked_widget.setCurrentIndex(1)
            return
        # Update the UI
        self.title_edit.setText(song['title'])
        self.copyright_edit.setText(song['copyright'])
        self.ccli_edit.setText(song['ccli_number'])
        for author in song['authors']:
            QtWidgets.QListWidgetItem(author, self.author_list_widget)
        for counter, verse in enumerate(song['verses']):
            self.lyrics_table_widget.setRowCount(self.lyrics_table_widget.rowCount() + 1)
            item = QtWidgets.QTableWidgetItem(verse['lyrics'])
            item.setData(QtCore.Qt.UserRole, verse['label'])
            item.setFlags(item.flags() ^ QtCore.Qt.ItemIsEditable)
            self.lyrics_table_widget.setItem(counter, 0, item)
        self.lyrics_table_widget.setVerticalHeaderLabels([verse['label'] for verse in song['verses']])
        self.lyrics_table_widget.resizeRowsToContents()
        self.title_edit.setEnabled(True)
        self.copyright_edit.setEnabled(True)
        self.ccli_edit.setEnabled(True)
        self.author_list_widget.setEnabled(True)
        self.lyrics_table_widget.setEnabled(True)
        self.lyrics_table_widget.repaint()
        self.import_button.setEnabled(True)
        self.back_button.setEnabled(True)
        self.song_progress_bar.setVisible(False)
        self.song_progress_bar.setValue(0)
        self.song = song
        self.application.process_events()

    def on_save_password_checkbox_toggled(self, checked):
        """
        Show a warning dialog when the user toggles the save checkbox on or off.

        :param checked: If the combobox is checked or not
        """
        if checked and self.login_page.isVisible():
            answer = QtWidgets.QMessageBox.question(
                self, translate('SongsPlugin.SongSelectForm', 'Save Username and Password'),
                translate('SongsPlugin.SongSelectForm', 'WARNING: Saving your username and password is INSECURE, your '
                                                        'password is stored in PLAIN TEXT. Click Yes to save your '
                                                        'password or No to cancel this.'),
                QtWidgets.QMessageBox.StandardButtons(QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No),
                QtWidgets.QMessageBox.No)
            if answer == QtWidgets.QMessageBox.No:
                self.save_password_checkbox.setChecked(False)

    def on_login_button_clicked(self):
        """
        Log the user in to SongSelect.
        """
        self.username_edit.setEnabled(False)
        self.password_edit.setEnabled(False)
        self.save_password_checkbox.setEnabled(False)
        self.login_button.setEnabled(False)
        self.login_spacer.setVisible(False)
        self.login_progress_bar.setValue(0)
        self.login_progress_bar.setVisible(True)
        self.application.process_events()
        # Log the user in
        if not self.song_select_importer.login(
                self.username_edit.text(), self.password_edit.text(), self._update_login_progress):
            QtWidgets.QMessageBox.critical(
                self,
                translate('SongsPlugin.SongSelectForm', 'Error Logging In'),
                translate('SongsPlugin.SongSelectForm',
                          'There was a problem logging in, perhaps your username or password is incorrect?')
            )
        else:
            if self.save_password_checkbox.isChecked():
                Settings().setValue(self.plugin.settings_section + '/songselect username', self.username_edit.text())
                Settings().setValue(self.plugin.settings_section + '/songselect password', self.password_edit.text())
            else:
                Settings().remove(self.plugin.settings_section + '/songselect username')
                Settings().remove(self.plugin.settings_section + '/songselect password')
            self.stacked_widget.setCurrentIndex(1)
        self.login_progress_bar.setVisible(False)
        self.login_progress_bar.setValue(0)
        self.login_spacer.setVisible(True)
        self.login_button.setEnabled(True)
        self.username_edit.setEnabled(True)
        self.password_edit.setEnabled(True)
        self.save_password_checkbox.setEnabled(True)
        self.search_combobox.setFocus()
        self.application.process_events()

    def on_search_button_clicked(self):
        """
        Run a search on SongSelect.
        """
        # Set up UI components
        self.view_button.setEnabled(False)
        self.search_button.setEnabled(False)
        self.search_combobox.setEnabled(False)
        self.search_progress_bar.setMinimum(0)
        self.search_progress_bar.setMaximum(0)
        self.search_progress_bar.setValue(0)
        self.set_progress_visible(True)
        self.search_results_widget.clear()
        self.result_count_label.setText(translate('SongsPlugin.SongSelectForm',
                                                  'Found {count:d} song(s)').format(count=self.song_count))
        self.application.process_events()
        self.song_count = 0
        search_history = self.search_combobox.getItems()
        Settings().setValue(self.plugin.settings_section + '/songselect searches', '|'.join(search_history))
        # Create thread and run search
        self.thread = QtCore.QThread()
        self.worker = SearchWorker(self.song_select_importer, self.search_combobox.currentText())
        self.worker.moveToThread(self.thread)
        self.thread.started.connect(self.worker.start)
        self.worker.show_info.connect(self.on_search_show_info)
        self.worker.found_song.connect(self.on_search_found_song)
        self.worker.finished.connect(self.on_search_finished)
        self.worker.quit.connect(self.thread.quit)
        self.worker.quit.connect(self.worker.deleteLater)
        self.thread.finished.connect(self.thread.deleteLater)
        self.thread.start()

    def on_stop_button_clicked(self):
        """
        Stop the search when the stop button is clicked.
        """
        self.song_select_importer.stop()

    def on_search_show_info(self, title, message):
        """
        Show an informational message from the search thread
        :param title:
        :param message:
        """
        QtWidgets.QMessageBox.information(self, title, message)

    def on_search_found_song(self, song):
        """
        Add a song to the list when one is found.
        :param song:
        """
        self.song_count += 1
        self.result_count_label.setText(translate('SongsPlugin.SongSelectForm',
                                                  'Found {count:d} song(s)').format(count=self.song_count))
        item_title = song['title'] + ' (' + ', '.join(song['authors']) + ')'
        song_item = QtWidgets.QListWidgetItem(item_title, self.search_results_widget)
        song_item.setData(QtCore.Qt.UserRole, song)

    def on_search_finished(self):
        """
        Slot which is called when the search is completed.
        """
        self.application.process_events()
        self.set_progress_visible(False)
        self.search_button.setEnabled(True)
        self.search_combobox.setEnabled(True)
        self.application.process_events()

    def on_search_results_widget_selection_changed(self):
        """
        Enable or disable the view button when the selection changes.
        """
        self.view_button.setEnabled(len(self.search_results_widget.selectedItems()) > 0)

    def on_view_button_clicked(self):
        """
        View a song from SongSelect.
        """
        self._view_song(self.search_results_widget.currentItem())

    def on_search_results_widget_double_clicked(self, current_item):
        """
        View a song from SongSelect

        :param current_item:
        """
        self._view_song(current_item)

    def on_back_button_clicked(self):
        """
        Go back to the search page.
        """
        self.stacked_widget.setCurrentIndex(1)
        self.search_combobox.setFocus()

    def on_import_button_clicked(self):
        """
        Import a song from SongSelect.
        """
        self.song_select_importer.save_song(self.song)
        self.song = None
        if QtWidgets.QMessageBox.question(self, translate('SongsPlugin.SongSelectForm', 'Song Imported'),
                                          translate('SongsPlugin.SongSelectForm',
                                                    'Your song has been imported, would you '
                                                    'like to import more songs?'),
                                          QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No,
                                          QtWidgets.QMessageBox.Yes) == QtWidgets.QMessageBox.Yes:
            self.on_back_button_clicked()
        else:
            self.application.process_events()
            self.done(QtWidgets.QDialog.Accepted)

    def set_progress_visible(self, is_visible):
        """
        Show or hide the search progress, including the stop button.
        """
        self.search_progress_bar.setVisible(is_visible)
        self.stop_button.setVisible(is_visible)

    @property
    def application(self):
        """
        Adds the openlp to the class dynamically.
        Windows needs to access the application in a dynamic manner.
        """
        if is_win():
            return Registry().get('application')
        else:
            if not hasattr(self, '_application'):
                self._application = Registry().get('application')
            return self._application