openlp-core team mailing list archive
-
openlp-core team
-
Mailing list archive
-
Message #29507
[Merge] lp:~suutari-olli/openlp/combined-bible-quick-search into lp:openlp
Azaziah has proposed merging lp:~suutari-olli/openlp/combined-bible-quick-search into lp:openlp.
Requested reviews:
Tim Bentley (trb143)
Tomas Groth (tomasgroth)
For more details, see:
https://code.launchpad.net/~suutari-olli/openlp/combined-bible-quick-search/+merge/293495
This branch introduces following improvements to Quick Bible search:
- Combined Reference/Text search which first performs the Reference
search and then moves to Text search if nothing is found.
- Added Search while typing functionality for Quick Bible search
- Possibility to use “.” when shortening Book names in Reference search.
For an example Gen. 1 = Gen 1 = Genesis 1.
- New/Improved error messages
(E.g. added actual example verses to Reference error)
- 3 New settings for controlling Quick search behavior
This branch also prevents users from performing Text searches which are:
- Shorter than 3 characters long (not including spaces)
- Searches consisting from only spaces
These currently possible bad search quarries result in LONG search times
and program instability/crashing. It’s still possible to search 3 characters
separated by spaces, but that scenario is relatively rarer.
---------------------------------------------------------------------------------
In this re-proposal:
- (Improved the UI message for the new setting.)
- Added Search while typing functionality for Quick Bible search
- Added a setting for controlling this behavior
- Refactored some code
lp:~suutari-olli/openlp/combined-bible-quick-search (revision 2642)
[←[1;32mSUCCESS←[1;m] https://ci.openlp.io/job/Branch-01-Pull/1520/
[←[1;32mSUCCESS←[1;m] https://ci.openlp.io/job/Branch-02-Functional-Tests/1431/
[←[1;32mSUCCESS←[1;m] https://ci.openlp.io/job/Branch-03-Interface-Tests/1369/
[←[1;32mSUCCESS←[1;m] https://ci.openlp.io/job/Branch-04a-Windows_Functional_Tests/1165/
[←[1;32mSUCCESS←[1;m] https://ci.openlp.io/job/Branch-04b-Windows_Interface_Tests/756/
[←[1;32mSUCCESS←[1;m] https://ci.openlp.io/job/Branch-05a-Code_Analysis/823/
[←[1;32mSUCCESS←[1;m] https://ci.openlp.io/job/Branch-05b-Test_Coverage/691/
--
Your team OpenLP Core is subscribed to branch lp:openlp.
=== modified file 'openlp/core/common/uistrings.py'
--- openlp/core/common/uistrings.py 2016-04-16 19:51:35 +0000
+++ openlp/core/common/uistrings.py 2016-05-02 04:59:52 +0000
@@ -23,6 +23,7 @@
The :mod:`uistrings` module provides standard strings for OpenLP.
"""
import logging
+import itertools
from openlp.core.common import translate
@@ -152,3 +153,34 @@
self.Version = translate('OpenLP.Ui', 'Version')
self.View = translate('OpenLP.Ui', 'View')
self.ViewMode = translate('OpenLP.Ui', 'View Mode')
+ # Translations that are used in bibles\lib\mediaitem.py and bibles\lib\manager.py
+ self.BibleShortSearchTitle = translate('OpenLP.Ui', 'Search is Empty or too Short')
+ self.BibleShortSearch = translate('OpenLP.Ui', '<strong>The search you have entered is empty or shorter '
+ 'than 3 characters long.<br>Please try again with '
+ 'a longer search.</strong><br><br>You can separate different '
+ 'keywords by a space to search for all of your keywords and you '
+ 'can separate them by a comma to search for one of them.')
+ self.BibleNoBiblesTitle = translate('OpenLP.Ui', 'No Bibles Available')
+ self.BibleNoBibles = translate('OpenLP.Ui', '<strong>There are no Bibles currently installed.</strong><br><br>'
+ 'Please use the Import Wizard to install one or more Bibles.')
+ # Scripture reference error combined from small translation stings by using itertools.
+ book_chapter = translate('OpenLP.Ui', 'Book Chapter')
+ chapter = translate('OpenLP.Ui', 'Chapter')
+ verse = translate('OpenLP.Ui', 'Verse')
+ gap = ' | '
+ psalm = translate('OpenLP.Ui', 'Psalm')
+ may_shorten = translate('OpenLP.Ui', 'Book names may be shortened from full names, for an example Ps 23 = '
+ 'Psalm 23')
+ bible_scripture_items = \
+ [book_chapter, gap, psalm, ' 23<br>',
+ book_chapter, '%(range)s', chapter, gap, psalm, ' 23%(range)s24<br>',
+ book_chapter, '%(verse)s', verse, '%(range)s', verse, gap, psalm, ' 23%(verse)s1%(range)s2<br>',
+ book_chapter, '%(verse)s', verse, '%(range)s', verse, '%(list)s', verse, '%(range)s', verse, gap, psalm,
+ ' 23%(verse)s1%(range)s2%(list)s5%(range)s6<br>',
+ book_chapter, '%(verse)s', verse, '%(range)s', verse, '%(list)s', chapter, '%(verse)s', verse, '%(range)s',
+ verse, gap, psalm, ' 23%(verse)s1%(range)s2%(list)s24%(verse)s1%(range)s3<br>',
+ book_chapter, '%(verse)s', verse, '%(range)s', chapter, '%(verse)s', verse, gap, psalm,
+ ' 23%(verse)s1%(range)s24%(verse)s1<br><br>', may_shorten]
+ itertools.chain.from_iterable(itertools.repeat(strings, 1) if isinstance(strings, str)
+ else strings for strings in bible_scripture_items)
+ self.BibleScriptureError = ''.join(str(joined) for joined in bible_scripture_items)
=== modified file 'openlp/plugins/bibles/bibleplugin.py'
--- openlp/plugins/bibles/bibleplugin.py 2016-03-31 16:34:22 +0000
+++ openlp/plugins/bibles/bibleplugin.py 2016-05-02 04:59:52 +0000
@@ -41,7 +41,8 @@
'bibles/db password': '',
'bibles/db hostname': '',
'bibles/db database': '',
- 'bibles/last search type': BibleSearch.Reference,
+ 'bibles/last search type': BibleSearch.Combined,
+ 'bibles/reset to combined quick search': True,
'bibles/verse layout style': LayoutStyle.VersePerSlide,
'bibles/book name language': LanguageSelection.Bible,
'bibles/display brackets': DisplayStyle.NoBrackets,
@@ -59,7 +60,10 @@
'bibles/range separator': '',
'bibles/list separator': '',
'bibles/end separator': '',
- 'bibles/last directory import': ''
+ 'bibles/last directory import': '',
+ 'bibles/hide combined quick error': False,
+ 'bibles/is search while typing enabled': True,
+ 'bibles/hide web bible error if searching while typing': True
}
=== modified file 'openlp/plugins/bibles/lib/biblestab.py'
--- openlp/plugins/bibles/lib/biblestab.py 2015-12-31 22:46:06 +0000
+++ openlp/plugins/bibles/lib/biblestab.py 2016-05-02 04:59:52 +0000
@@ -128,6 +128,20 @@
self.language_selection_layout.addWidget(self.language_selection_label)
self.language_selection_layout.addWidget(self.language_selection_combo_box)
self.right_layout.addWidget(self.language_selection_group_box)
+ self.bible_quick_settings_group_box = QtWidgets.QGroupBox(self.right_column)
+ self.bible_quick_settings_group_box.setObjectName('bible_quick_settings_group_box')
+ self.right_layout.addWidget(self.bible_quick_settings_group_box)
+ self.search_settings_layout = QtWidgets.QFormLayout(self.bible_quick_settings_group_box)
+ self.search_settings_layout.setObjectName('search_settings_layout')
+ self.reset_to_combined_quick_search_check_box = QtWidgets.QCheckBox(self.bible_quick_settings_group_box)
+ self.reset_to_combined_quick_search_check_box.setObjectName('reset_to_combined_quick_search_check_box')
+ self.search_settings_layout.addRow(self.reset_to_combined_quick_search_check_box)
+ self.hide_combined_quick_error_check_box = QtWidgets.QCheckBox(self.bible_quick_settings_group_box)
+ self.hide_combined_quick_error_check_box.setObjectName('hide_combined_quick_error_check_box')
+ self.search_settings_layout.addRow(self.hide_combined_quick_error_check_box)
+ self.bible_search_while_typing_check_box = QtWidgets.QCheckBox(self.bible_quick_settings_group_box)
+ self.bible_search_while_typing_check_box.setObjectName('bible_search_while_typing_check_box')
+ self.search_settings_layout.addRow(self.bible_search_while_typing_check_box)
self.left_layout.addStretch()
self.right_layout.addStretch()
# Signals and slots
@@ -151,6 +165,12 @@
self.end_separator_line_edit.editingFinished.connect(self.on_end_separator_line_edit_finished)
Registry().register_function('theme_update_list', self.update_theme_list)
self.language_selection_combo_box.activated.connect(self.on_language_selection_combo_box_changed)
+ self.reset_to_combined_quick_search_check_box.stateChanged.connect(
+ self.on_reset_to_combined_quick_search_check_box_changed)
+ self.hide_combined_quick_error_check_box.stateChanged.connect(
+ self.on_hide_combined_quick_error_check_box_changed)
+ self.bible_search_while_typing_check_box.stateChanged.connect(
+ self.on_bible_search_while_typing_check_box_changed)
def retranslateUi(self):
self.verse_display_group_box.setTitle(translate('BiblesPlugin.BiblesTab', 'Verse Display'))
@@ -194,6 +214,15 @@
LanguageSelection.Application, translate('BiblesPlugin.BiblesTab', 'Application Language'))
self.language_selection_combo_box.setItemText(
LanguageSelection.English, translate('BiblesPlugin.BiblesTab', 'English'))
+ self.bible_quick_settings_group_box.setTitle(translate('BiblesPlugin.BiblesTab', 'Quick Search Settings'))
+ self.reset_to_combined_quick_search_check_box.setText(translate('BiblesPlugin.BiblesTab',
+ 'Reset search type to "Text or Scripture'
+ ' Reference" on startup'))
+ self.hide_combined_quick_error_check_box.setText(translate('BiblesPlugin.BiblesTab',
+ 'Don\'t show error if nothing is found in "Text or '
+ 'Scripture Reference"'))
+ self.bible_search_while_typing_check_box.setText(translate('BiblesPlugin.BiblesTab',
+ 'Search automatically when writing is stopped'))
def on_bible_theme_combo_box_changed(self):
self.bible_theme = self.bible_theme_combo_box.currentText()
@@ -302,6 +331,24 @@
self.end_separator_line_edit.setText(get_reference_separator('sep_e_default'))
self.end_separator_line_edit.setPalette(self.get_grey_text_palette(True))
+ def on_reset_to_combined_quick_search_check_box_changed(self, check_state):
+ """
+ Event handler for the 'hide_combined_quick_error' check box
+ """
+ self.reset_to_combined_quick_search = (check_state == QtCore.Qt.Checked)
+
+ def on_hide_combined_quick_error_check_box_changed(self, check_state):
+ """
+ Event handler for the 'hide_combined_quick_error' check box
+ """
+ self.hide_combined_quick_error = (check_state == QtCore.Qt.Checked)
+
+ def on_bible_search_while_typing_check_box_changed(self, check_state):
+ """
+ Event handler for the 'hide_combined_quick_error' check box
+ """
+ self.bible_search_while_typing = (check_state == QtCore.Qt.Checked)
+
def load(self):
settings = Settings()
settings.beginGroup(self.settings_section)
@@ -355,6 +402,12 @@
self.end_separator_check_box.setChecked(True)
self.language_selection = settings.value('book name language')
self.language_selection_combo_box.setCurrentIndex(self.language_selection)
+ self.reset_to_combined_quick_search = settings.value('reset to combined quick search')
+ self.reset_to_combined_quick_search_check_box.setChecked(self.reset_to_combined_quick_search)
+ self.hide_combined_quick_error = settings.value('hide combined quick error')
+ self.hide_combined_quick_error_check_box.setChecked(self.hide_combined_quick_error)
+ self.bible_search_while_typing = settings.value('is search while typing enabled')
+ self.bible_search_while_typing_check_box.setChecked(self.bible_search_while_typing)
settings.endGroup()
def save(self):
@@ -386,6 +439,9 @@
if self.language_selection != settings.value('book name language'):
settings.setValue('book name language', self.language_selection)
self.settings_form.register_post_process('bibles_load_list')
+ settings.setValue('reset to combined quick search', self.reset_to_combined_quick_search)
+ settings.setValue('hide combined quick error', self.hide_combined_quick_error)
+ settings.setValue('is search while typing enabled', self.bible_search_while_typing)
settings.endGroup()
if self.tab_visited:
self.settings_form.register_post_process('bibles_config_updated')
=== modified file 'openlp/plugins/bibles/lib/manager.py'
--- openlp/plugins/bibles/lib/manager.py 2016-04-12 20:05:58 +0000
+++ openlp/plugins/bibles/lib/manager.py 2016-05-02 04:59:52 +0000
@@ -23,7 +23,7 @@
import logging
import os
-from openlp.core.common import RegistryProperties, AppLocation, Settings, translate, delete_file
+from openlp.core.common import RegistryProperties, AppLocation, Settings, translate, delete_file, UiStrings
from openlp.plugins.bibles.lib import parse_reference, get_reference_separator, LanguageSelection
from openlp.plugins.bibles.lib.db import BibleDB, BibleMeta
from .csvbible import CSVBible
@@ -247,6 +247,7 @@
"""
Parses a scripture reference, fetches the verses from the Bible
specified, and returns a list of ``Verse`` objects.
+ This function is called in \bibles\lib\mediaitem.py by def on_quick_search_button
:param bible: Unicode. The Bible to use.
:param verse_text:
@@ -265,41 +266,20 @@
:param show_error:
"""
log.debug('BibleManager.get_verses("%s", "%s")', bible, verse_text)
+ # If no bibles are installed, message is given.
if not bible:
if show_error:
self.main_window.information_message(
- translate('BiblesPlugin.BibleManager', 'No Bibles Available'),
- translate('BiblesPlugin.BibleManager', 'There are no Bibles currently installed. Please use the '
- 'Import Wizard to install one or more Bibles.')
- )
+ ('%s' % UiStrings().BibleNoBiblesTitle),
+ ('%s' % UiStrings().BibleNoBibles))
return None
+ # Get the language for books.
language_selection = self.get_language_selection(bible)
ref_list = parse_reference(verse_text, self.db_cache[bible], language_selection, book_ref_id)
if ref_list:
return self.db_cache[bible].get_verses(ref_list, show_error)
+ # If nothing is found. Message is given if this is not combined search. (defined in mediaitem.py)
else:
- if show_error:
- reference_separators = {
- 'verse': get_reference_separator('sep_v_display'),
- 'range': get_reference_separator('sep_r_display'),
- 'list': get_reference_separator('sep_l_display')}
- self.main_window.information_message(
- translate('BiblesPlugin.BibleManager', 'Scripture Reference Error'),
- translate('BiblesPlugin.BibleManager', 'Your scripture reference is either not supported by '
- 'OpenLP or is invalid. Please make sure your reference '
- 'conforms to one of the following patterns or consult the manual:\n\n'
- 'Book Chapter\n'
- 'Book Chapter%(range)sChapter\n'
- 'Book Chapter%(verse)sVerse%(range)sVerse\n'
- 'Book Chapter%(verse)sVerse%(range)sVerse%(list)sVerse'
- '%(range)sVerse\n'
- 'Book Chapter%(verse)sVerse%(range)sVerse%(list)sChapter'
- '%(verse)sVerse%(range)sVerse\n'
- 'Book Chapter%(verse)sVerse%(range)sChapter%(verse)sVerse',
- 'Please pay attention to the appended "s" of the wildcards '
- 'and refrain from translating the words inside the names in the brackets.')
- % reference_separators
- )
return None
def get_language_selection(self, bible):
@@ -325,19 +305,18 @@
def verse_search(self, bible, second_bible, text):
"""
Does a verse search for the given bible and text.
+ This function is called in \bibles\lib\mediaitem.py by def on_quick_search_button.
:param bible: The bible to search in (unicode).
:param second_bible: The second bible (unicode). We do not search in this bible.
:param text: The text to search for (unicode).
"""
log.debug('BibleManager.verse_search("%s", "%s")', bible, text)
+ # If no bibles are installed, message is given.
if not bible:
self.main_window.information_message(
- translate('BiblesPlugin.BibleManager', 'No Bibles Available'),
- translate('BiblesPlugin.BibleManager',
- 'There are no Bibles currently installed. Please use the Import Wizard to install one or '
- 'more Bibles.')
- )
+ ('%s' % UiStrings().BibleNoBiblesTitle),
+ ('%s' % UiStrings().BibleNoBibles))
return None
# Check if the bible or second_bible is a web bible.
web_bible = self.db_cache[bible].get_object(BibleMeta, 'download_source')
@@ -345,20 +324,27 @@
if second_bible:
second_web_bible = self.db_cache[second_bible].get_object(BibleMeta, 'download_source')
if web_bible or second_web_bible:
- self.main_window.information_message(
- translate('BiblesPlugin.BibleManager', 'Web Bible cannot be used'),
- translate('BiblesPlugin.BibleManager', 'Text Search is not available with Web Bibles.')
- )
- return None
- if text:
+ # If either Bible is Web, cursor is reset to normal and message is given.
+ self.application.set_normal_cursor()
+ # If we are performing "Search while typing", do not show this error.
+ # Web Bible checking method is currently bound to this file, so it can't be properly moved to mediaitem.py
+ # Without making some changes to the stucture. (= self.db_cache[bible].get_object(BibleMeta,...)
+ if not Settings().value('bibles/hide web bible error if searching while typing'):
+ self.main_window.information_message(
+ translate('BiblesPlugin.BibleManager', 'Web Bible cannot be used'),
+ translate('BiblesPlugin.BibleManager', 'Text Search is not available with Web Bibles.\n'
+ 'Please use the Scripture Reference Search instead.\n\n'
+ 'This means that the currently used Bible or Second Bible\n'
+ 'is installed as Web Bible and may not be used.')
+ )
+ return None
+ # Shorter than 3 char searches break OpenLP with very long search times, thus they are blocked.
+ if len(text) - text.count(' ') < 3:
+ return None
+ # Fetch the results from db. If no results are found, return None, no message is given for this.
+ elif text:
return self.db_cache[bible].verse_search(text)
else:
- self.main_window.information_message(
- translate('BiblesPlugin.BibleManager', 'Scripture Reference Error'),
- translate('BiblesPlugin.BibleManager', 'You did not enter a search keyword.\nYou can separate '
- 'different keywords by a space to search for all of your keywords and you can separate '
- 'them by a comma to search for one of them.')
- )
return None
def save_meta_data(self, bible, version, copyright, permissions, book_name_language=None):
=== modified file 'openlp/plugins/bibles/lib/mediaitem.py'
--- openlp/plugins/bibles/lib/mediaitem.py 2016-04-10 20:24:07 +0000
+++ openlp/plugins/bibles/lib/mediaitem.py 2016-05-02 04:59:52 +0000
@@ -35,6 +35,8 @@
from openlp.plugins.bibles.lib import LayoutStyle, DisplayStyle, VerseReferenceList, get_reference_separator, \
LanguageSelection, BibleStrings
from openlp.plugins.bibles.lib.db import BiblesResourcesDB
+import re
+
log = logging.getLogger(__name__)
@@ -45,6 +47,7 @@
"""
Reference = 1
Text = 2
+ Combined = 3
class BibleMediaItem(MediaManagerItem):
@@ -250,6 +253,7 @@
# Other stuff
self.quick_search_edit.returnPressed.connect(self.on_quick_search_button)
self.search_tab_bar.currentChanged.connect(self.on_search_tab_bar_current_changed)
+ self.quick_search_edit.textChanged.connect(self.on_search_text_edit_changed)
def on_focus(self):
if self.quickTab.isVisible():
@@ -309,6 +313,9 @@
self.plugin.manager.media = self
self.load_bibles()
self.quick_search_edit.set_search_types([
+ (BibleSearch.Combined, ':/bibles/bibles_search_combined.png',
+ translate('BiblesPlugin.MediaItem', 'Text or Reference'),
+ translate('BiblesPlugin.MediaItem', 'Text or Reference...')),
(BibleSearch.Reference, ':/bibles/bibles_search_reference.png',
translate('BiblesPlugin.MediaItem', 'Scripture Reference'),
translate('BiblesPlugin.MediaItem', 'Search Scripture Reference...')),
@@ -423,16 +430,21 @@
def update_auto_completer(self):
"""
This updates the bible book completion list for the search field. The completion depends on the bible. It is
- only updated when we are doing a reference search, otherwise the auto completion list is removed.
+ only updated when we are doing reference or combined search, in text search the completion list is removed.
"""
log.debug('update_auto_completer')
- # Save the current search type to the configuration.
- Settings().setValue('%s/last search type' % self.settings_section, self.quick_search_edit.current_search_type())
+ # Save the current search type to the configuration. If setting for automatically resetting the search type to
+ # Combined is enabled, use that otherwise use the currently selected search type.
+ if Settings().value(self.settings_section + '/reset to combined quick search'):
+ Settings().setValue('%s/last search type' % self.settings_section, BibleSearch.Combined)
+ else:
+ Settings().setValue('%s/last search type' % self.settings_section,
+ self.quick_search_edit.current_search_type())
# Save the current bible to the configuration.
Settings().setValue(self.settings_section + '/quick bible', self.quickVersionComboBox.currentText())
books = []
- # We have to do a 'Reference Search'.
- if self.quick_search_edit.current_search_type() == BibleSearch.Reference:
+ # We have to do a 'Reference Search' (Or as part of Combined Search).
+ if self.quick_search_edit.current_search_type() is not BibleSearch.Text:
bibles = self.plugin.manager.get_bibles()
bible = self.quickVersionComboBox.currentText()
if bible:
@@ -648,52 +660,144 @@
self.check_search_result()
self.application.set_normal_cursor()
+ def on_quick_reference_search(self):
+ # We are doing a 'Reference Search'.
+ # Set Bibles to use the text input from Quick search field.
+ bible = self.quickVersionComboBox.currentText()
+ second_bible = self.quickSecondComboBox.currentText()
+ # Get input from field and replace '. ' with ''
+ # This will check if field has any '.' and removes them. Eg. Gen. 1 = Gen 1 = Genesis 1
+ text = self.quick_search_edit.text()
+ text = text.replace('. ', ' ')
+ # This is triggered on reference search, use the search from manager.py
+ if self.quick_search_edit.current_search_type() == BibleSearch.Reference:
+ self.search_results = self.plugin.manager.get_verses(bible, text)
+ elif self.quick_search_edit.current_search_type() == BibleSearch.Combined:
+ # In Combined Reference search no error is given if no results are found. (This would result in duplicate)
+ self.search_results = self.plugin.manager.get_verses(bible, text)
+ if second_bible and self.search_results:
+ self.second_search_results = \
+ self.plugin.manager.get_verses(second_bible, text, self.search_results[0].book.book_reference_id)
+
+ def on_quick_text_search(self):
+ # We are doing a 'Text Search'.
+ # Set Bibles to use the text input from Quick search field.
+ bible = self.quickVersionComboBox.currentText()
+ second_bible = self.quickSecondComboBox.currentText()
+ text = self.quick_search_edit.text()
+ # This changes the curson to "Loading animation"
+ self.application.set_busy_cursor()
+ # Get Bibles list
+ bibles = self.plugin.manager.get_bibles()
+ # Add results to "search_results"
+ self.search_results = self.plugin.manager.verse_search(bible, second_bible, text)
+ if second_bible and self.search_results:
+ # Set up variables,
+ # new_search_results is needed to make sure 2nd bible contains all verses. (And counting them on error)
+ text = []
+ new_search_results = []
+ count = 0
+ passage_not_found = False
+ # Search second bible for results of search_results to make sure everythigns there.
+ # Count all the unfound passages.
+ for verse in self.search_results:
+ db_book = bibles[second_bible].get_book_by_book_ref_id(verse.book.book_reference_id)
+ if not db_book:
+ log.debug('Passage "%s %d:%d" not found in Second Bible' %
+ (verse.book.name, verse.chapter, verse.verse))
+ passage_not_found = True
+ count += 1
+ continue
+ new_search_results.append(verse)
+ text.append((verse.book.book_reference_id, verse.chapter, verse.verse, verse.verse))
+ if passage_not_found:
+ # This is for the 2nd Bible.
+ self.main_window.information_message(
+ translate('BiblesPlugin.MediaItem', 'Information'),
+ translate('BiblesPlugin.MediaItem', 'The second Bible does not contain all the verses '
+ 'that are in the main Bible. Only verses found in both Bibles '
+ 'will be shown. %d verses have not been included '
+ 'in the results.') % count)
+ # Join the searches so only verses that are found on both Bibles are shown.
+ self.search_results = new_search_results
+ self.second_search_results = bibles[second_bible].get_verses(text)
+
def on_quick_search_button(self):
"""
- Does a quick search and saves the search results. Quick search can either be "Reference Search" or
- "Text Search".
+ This triggers the proper Quick search based on which search type is used.
+ "Eg. "Reference Search", "Text Search" or "Combined search".
"""
log.debug('Quick Search Button clicked')
+ # If we are performing "Search while typing", this setting is set to True, here it's reset to "False"
+ Settings().setValue('bibles/hide web bible error if searching while typing', False)
+ # Disable the button while processing, get text from Quick search field.
self.quickSearchButton.setEnabled(False)
self.application.process_events()
+ # These need to be defined here too so the search results can be displayed.
bible = self.quickVersionComboBox.currentText()
second_bible = self.quickSecondComboBox.currentText()
text = self.quick_search_edit.text()
if self.quick_search_edit.current_search_type() == BibleSearch.Reference:
- # We are doing a 'Reference Search'.
- self.search_results = self.plugin.manager.get_verses(bible, text)
- if second_bible and self.search_results:
- self.second_search_results = \
- self.plugin.manager.get_verses(second_bible, text, self.search_results[0].book.book_reference_id)
- else:
- # We are doing a 'Text Search'.
- self.application.set_busy_cursor()
- bibles = self.plugin.manager.get_bibles()
- self.search_results = self.plugin.manager.verse_search(bible, second_bible, text)
- if second_bible and self.search_results:
- text = []
- new_search_results = []
- count = 0
- passage_not_found = False
- for verse in self.search_results:
- db_book = bibles[second_bible].get_book_by_book_ref_id(verse.book.book_reference_id)
- if not db_book:
- log.debug('Passage "%s %d:%d" not found in Second Bible' %
- (verse.book.name, verse.chapter, verse.verse))
- passage_not_found = True
- count += 1
- continue
- new_search_results.append(verse)
- text.append((verse.book.book_reference_id, verse.chapter, verse.verse, verse.verse))
- if passage_not_found:
- QtWidgets.QMessageBox.information(
- self, translate('BiblesPlugin.MediaItem', 'Information'),
- translate('BiblesPlugin.MediaItem', 'The second Bible does not contain all the verses '
- 'that are in the main Bible. Only verses found in both Bibles will be shown. %d '
- 'verses have not been included in the results.') % count,
- QtWidgets.QMessageBox.StandardButtons(QtWidgets.QMessageBox.Ok))
- self.search_results = new_search_results
- self.second_search_results = bibles[second_bible].get_verses(text)
+ # We are doing a 'Reference Search'. (Get script from def on_quick_reference_search)
+ self.on_quick_reference_search()
+ # if nothing is found, message is given.
+ # Get reference separators from settings.
+ if not self.search_results:
+ reference_separators = {
+ 'verse': get_reference_separator('sep_v_display'),
+ 'range': get_reference_separator('sep_r_display'),
+ 'list': get_reference_separator('sep_l_display')}
+ self.main_window.information_message(
+ translate('BiblesPlugin.BibleManager', 'Scripture Reference Error'),
+ translate('BiblesPlugin.BibleManager', '<strong>OpenLP couldn’t find anything '
+ 'with your search.<br><br>'
+ 'Please make sure that your reference follows '
+ 'one of these patterns:</strong><br><br>%s'
+ % UiStrings().BibleScriptureError % reference_separators))
+ elif self.quick_search_edit.current_search_type() == BibleSearch.Text:
+ # We are doing a 'Text Search'. (Get script from def on_quick_text_search)
+ self.on_quick_text_search()
+ if not self.search_results and len(text) - text.count(' ') < 3 and bible:
+ self.main_window.information_message(
+ ('%s' % UiStrings().BibleShortSearchTitle),
+ ('%s' % UiStrings().BibleShortSearch))
+ elif self.quick_search_edit.current_search_type() == BibleSearch.Combined:
+ # We are doing a 'Combined search'. Starting with reference search.
+ self.on_quick_reference_search()
+ # If results are found, search will be finalized.
+ # This check needs to be here in order to avoid duplicate errors.
+ # If keyword is shorter than 3 (not including spaces), message is given. It's actually possible to find
+ # verses with less than 3 chars (Eg. G1 = Genesis 1) thus this error is not shown if any results are found.
+ # if no Bibles are installed, this message is not shown - "No bibles" message is shown instead. (and bible)
+ if not self.search_results and len(text) - text.count(' ') < 3 and bible:
+ self.main_window.information_message(
+ ('%s' % UiStrings().BibleShortSearchTitle),
+ ('%s' % UiStrings().BibleShortSearch))
+ if not self.search_results and len(text) - text.count(' ') > 2 and bible:
+ # Text search starts here if no reference was found and keyword is longer than 2.
+ # > 2 check is required in order to avoid duplicate error messages for short keywords.
+ self.on_quick_text_search()
+ # If no Text or Reference is found, message is given, unless a setting for not showing it is enabled.
+ if not self.search_results and not \
+ Settings().value(self.settings_section + '/hide combined quick error'):
+ self.application.set_normal_cursor()
+ # Reference separators need to be defined both, in here and on reference search,
+ # error won't work if they are left out from one.
+ reference_separators = {
+ 'verse': get_reference_separator('sep_v_display'),
+ 'range': get_reference_separator('sep_r_display'),
+ 'list': get_reference_separator('sep_l_display')}
+ self.main_window.information_message(translate('BiblesPlugin.BibleManager', 'Nothing found'),
+ translate('BiblesPlugin.BibleManager',
+ '<strong>OpenLP couldn’t find anything with your'
+ ' search.</strong><br><br>If you tried to search'
+ ' with Scripture Reference, please make<br> sure'
+ ' that your reference follows one of these'
+ ' patterns: <br><br>%s'
+ % UiStrings().BibleScriptureError %
+ reference_separators))
+ # Finalizing the search
+ # List is cleared if not locked, results are listed, button is set available, cursor is set to normal.
if not self.quickLockButton.isChecked():
self.list_view.clear()
if self.list_view.count() != 0 and self.search_results:
@@ -704,6 +808,32 @@
self.check_search_result()
self.application.set_normal_cursor()
+ def on_quick_search_search_as_type_text(self):
+ """
+ This function is called when "Search as you type" is enabled for Bibles.
+ It is basically the same thing as "on_quick_search_search" but all the error messages are removed.
+ For commented version, please visit def on_quick_search_button.
+ """
+ log.debug('Quick Search Button clicked')
+ bible = self.quickVersionComboBox.currentText()
+ second_bible = self.quickSecondComboBox.currentText()
+ if self.quick_search_edit.current_search_type() == BibleSearch.Reference:
+ self.on_quick_reference_search()
+ elif self.quick_search_edit.current_search_type() == BibleSearch.Text:
+ self.on_quick_text_search()
+ elif self.quick_search_edit.current_search_type() == BibleSearch.Combined:
+ self.on_quick_reference_search()
+ if not self.search_results:
+ self.on_quick_text_search()
+ if not self.quickLockButton.isChecked():
+ self.list_view.clear()
+ if self.list_view.count() != 0 and self.search_results:
+ self.__check_second_bible(bible, second_bible)
+ elif self.search_results:
+ self.display_results(bible, second_bible)
+ self.check_search_result()
+ self.application.set_normal_cursor()
+
def display_results(self, bible, second_bible=''):
"""
Displays the search results in the media manager. All data needed for further action is saved for/in each row.
@@ -715,6 +845,37 @@
self.search_results = {}
self.second_search_results = {}
+ def on_search_text_edit_changed(self):
+ """
+ If search automatically while typing is enabled, perform the search and list results when conditions are met.
+ """
+ text = self.quick_search_edit.text()
+ # If web bible is used, don't show the error while searching and typing.
+ # This would result in seeing the same message multiple times.
+ # This message is located in lib\manager.py, so the setting is required.
+ Settings().setValue('bibles/hide web bible error if searching while typing', True)
+ search_length = 1
+ if self.quick_search_edit.current_search_type() == BibleSearch.Combined:
+ search_length = 4
+ if self.quick_search_edit.current_search_type() == BibleSearch.Reference:
+ search_length = 3
+ elif self.quick_search_edit.current_search_type() == BibleSearch.Text:
+ search_length = 5
+ # Regex for finding space + any non whitemark character. (Prevents search from starting on 1 word searches)
+ space_and_any = re.compile(' \S')
+ # Turn this into a format that may be used in if statement.
+ count_space_any = space_and_any.findall(text)
+ # Start searching if this behaviour is not disabled in settings and conditions are met.
+ if Settings().value('bibles/is search while typing enabled'):
+ if len(text) > search_length and len(count_space_any) != 0:
+ # Start search if no chars are entered or deleted for 1.3 seconds
+ # Use the self.on_quick_search_search_as_type_text, this does not contain any error messages.
+ # This method may be a bit buggy sometimes and starts shorter than required searches due to the delay.
+ QtCore.QTimer().singleShot(1300, self.on_quick_search_search_as_type_text)
+ # If text length is less than 4 and results are not locked, it's still possible to search short references.
+ if not self.quickLockButton.isChecked() and len(text) < 4:
+ self.list_view.clear()
+
def build_display_results(self, bible, second_bible, search_results):
"""
Displays the search results in the media manager. All data needed for further action is saved for/in each row.
=== modified file 'resources/images/openlp-2.qrc'
--- resources/images/openlp-2.qrc 2016-04-18 05:35:21 +0000
+++ resources/images/openlp-2.qrc 2016-05-02 04:59:52 +0000
@@ -30,6 +30,7 @@
<file>image_new_group.png</file>
</qresource>
<qresource prefix="bibles">
+ <file>bibles_search_combined.png</file>
<file>bibles_search_text.png</file>
<file>bibles_search_reference.png</file>
<file>bibles_upgrade_alert.png</file>
=== modified file 'tests/functional/openlp_plugins/bibles/test_mediaitem.py'
--- tests/functional/openlp_plugins/bibles/test_mediaitem.py 2015-12-31 22:46:06 +0000
+++ tests/functional/openlp_plugins/bibles/test_mediaitem.py 2016-05-02 04:59:52 +0000
@@ -23,6 +23,8 @@
This module contains tests for the lib submodule of the Presentations plugin.
"""
from unittest import TestCase
+
+from openlp.core.common import Registry
from openlp.plugins.bibles.lib.mediaitem import BibleMediaItem
from tests.functional import MagicMock, patch
from tests.helpers.testmixin import TestMixin
@@ -41,6 +43,9 @@
patch('openlp.plugins.bibles.lib.mediaitem.BibleMediaItem.setup_item'):
self.media_item = BibleMediaItem(None, MagicMock())
self.setup_application()
+ self.mocked_main_window = MagicMock()
+ Registry.create()
+ Registry().register('main_window', self.mocked_main_window)
def display_results_no_results_test(self):
"""
@@ -109,3 +114,40 @@
mocked_list_view.selectAll.assert_called_once_with()
self.assertEqual(self.media_item.search_results, {})
self.assertEqual(self.media_item.second_search_results, {})
+
+ def on_quick_search_button_general_test(self):
+ """
+ Test that general things, which should be called on all Quick searches are called.
+ """
+
+ # GIVEN: self.application as self.app, all the required functions
+ Registry.create()
+ Registry().register('application', self.app)
+ self.media_item.quickSearchButton = MagicMock()
+ self.app.process_events = MagicMock()
+ self.media_item.quickVersionComboBox = MagicMock()
+ self.media_item.quickVersionComboBox.currentText = MagicMock()
+ self.media_item.quickSecondComboBox = MagicMock()
+ self.media_item.quickSecondComboBox.currentText = MagicMock()
+ self.media_item.quick_search_edit = MagicMock()
+ self.media_item.quick_search_edit.text = MagicMock()
+ self.media_item.quickLockButton = MagicMock()
+ self.media_item.list_view = MagicMock()
+ self.media_item.search_results = MagicMock()
+ self.media_item.display_results = MagicMock()
+ self.media_item.check_search_result = MagicMock()
+ self.app.set_normal_cursor = MagicMock()
+
+ # WHEN: on_quick_search_button is called
+ self.media_item.on_quick_search_button()
+
+ # THEN: Search should had been started and finalized properly
+ self.assertEqual(1, self.app.process_events.call_count, 'Normal cursor should had been called once')
+ self.assertEqual(1, self.media_item.quickVersionComboBox.currentText.call_count, 'Should had been called once')
+ self.assertEqual(1, self.media_item.quickSecondComboBox.currentText.call_count, 'Should had been called once')
+ self.assertEqual(1, self.media_item.quick_search_edit.text.call_count, 'Text edit Should had been called once')
+ self.assertEqual(1, self.media_item.quickLockButton.isChecked.call_count, 'Lock Should had been called once')
+ self.assertEqual(1, self.media_item.display_results.call_count, 'Display results Should had been called once')
+ self.assertEqual(2, self.media_item.quickSearchButton.setEnabled.call_count, 'Disable and Enable the button')
+ self.assertEqual(1, self.media_item.check_search_result.call_count, 'Check results Should had been called once')
+ self.assertEqual(1, self.app.set_normal_cursor.call_count, 'Normal cursor should had been called once')
Follow ups