Merge lp:~phill-ridout/openlp/saved_bible_verses into lp:openlp
- saved_bible_verses
- Merge into trunk
Status: | Superseded |
---|---|
Proposed branch: | lp:~phill-ridout/openlp/saved_bible_verses |
Merge into: | lp:openlp |
Diff against target: |
1076 lines (+324/-202) 12 files modified
openlp/core/ui/lib/listwidgetwithdnd.py (+10/-4) openlp/core/ui/lib/pathedit.py (+4/-4) openlp/plugins/bibles/lib/__init__.py (+3/-3) openlp/plugins/bibles/lib/db.py (+13/-0) openlp/plugins/bibles/lib/manager.py (+15/-43) openlp/plugins/bibles/lib/mediaitem.py (+178/-63) openlp/plugins/presentations/presentationplugin.py (+1/-1) openlp/plugins/songusage/forms/songusagedetaildialog.py (+1/-1) resources/images/openlp-2.qrc (+2/-4) tests/functional/openlp_core_ui_lib/test_listwidgetwithdnd.py (+33/-31) tests/functional/openlp_plugins/bibles/test_mediaitem.py (+63/-47) tests/interfaces/openlp_plugins/bibles/test_lib_parse_reference.py (+1/-1) |
To merge this branch: | bzr merge lp:~phill-ridout/openlp/saved_bible_verses |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Tim Bentley | Approve | ||
Review via email: mp+323727@code.launchpad.net |
This proposal supersedes a proposal from 2017-05-07.
This proposal has been superseded by a proposal from 2017-05-26.
Commit message
Description of the change
Adds more flexibility to 'locking' bible verses.
fixes #1625681 "If Bible search results are locked, duplicated results are listed"
lp:~phill-ridout/openlp/saved_bible_verses (revision 2744)
[SUCCESS] https:/
[SUCCESS] https:/
[SUCCESS] https:/
[SUCCESS] https:/
[SUCCESS] https:/
[SUCCESS] https:/
[FAILURE] https:/
Stopping after failure
Tim Bentley (trb143) wrote : Posted in a previous version of this proposal | # |
Tim Bentley (trb143) wrote : Posted in a previous version of this proposal | # |
Traceback (most recent call last):
File "/home/
self.
File "/home/
self.
File "/home/
self.
File "/home/
version = self.plugin.
AttributeError: 'NoneType' object has no attribute 'name'
downloaded and started to no bibles loaded !!!!!!!
Loaded a new bible and started to type. Search as type fired and downloaded the chapter and gave me a message saying search as you type is not available!
Then a traceback
Traceback (most recent call last):
File "/home/
self.
File "/home/
self.
File "/home/
self.
File "/home/
self.
File "/home/
for count, verse in enumerate(
TypeError: 'NoneType' object is not iterable
On the select UI I only have 2 lines of search as the options is large and takes up most the the space.
Tim Bentley (trb143) wrote : Posted in a previous version of this proposal | # |
Traceback (most recent call last):
File "./openlp.py", line 44, in <module>
main()
File "/home/
sys.
File "/home/
self.
File "/home/
self.
File "/home/
self.
File "/home/
self.
File "/home/
new_item = Registry(
File "/home/
self.
File "/home/
if self.display_
AttributeError: 'SongMediaItem' object has no attribute 'display_
on start up!
Tim Bentley (trb143) : | # |
Unmerged revisions
Preview Diff
1 | === modified file 'openlp/core/ui/lib/listwidgetwithdnd.py' (properties changed: -x to +x) |
2 | --- openlp/core/ui/lib/listwidgetwithdnd.py 2017-02-18 07:23:15 +0000 |
3 | +++ openlp/core/ui/lib/listwidgetwithdnd.py 2017-05-26 13:44:43 +0000 |
4 | @@ -44,7 +44,6 @@ |
5 | self.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection) |
6 | self.setAlternatingRowColors(True) |
7 | self.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) |
8 | - self.locked = False |
9 | |
10 | def activateDnD(self): |
11 | """ |
12 | @@ -54,15 +53,13 @@ |
13 | self.setDragDropMode(QtWidgets.QAbstractItemView.DragDrop) |
14 | Registry().register_function(('%s_dnd' % self.mime_data_text), self.parent().load_file) |
15 | |
16 | - def clear(self, search_while_typing=False, override_lock=False): |
17 | + def clear(self, search_while_typing=False): |
18 | """ |
19 | Re-implement clear, so that we can customise feedback when using 'Search as you type' |
20 | |
21 | :param search_while_typing: True if we want to display the customised message |
22 | :return: None |
23 | """ |
24 | - if self.locked and not override_lock: |
25 | - return |
26 | if search_while_typing: |
27 | self.no_results_text = UiStrings().ShortResults |
28 | else: |
29 | @@ -128,6 +125,15 @@ |
30 | else: |
31 | event.ignore() |
32 | |
33 | + def allItems(self): |
34 | + """ |
35 | + An generator to list all the items in the widget |
36 | + |
37 | + :return: a generator |
38 | + """ |
39 | + for row in range(self.count()): |
40 | + yield self.item(row) |
41 | + |
42 | def paintEvent(self, event): |
43 | """ |
44 | Re-implement paintEvent so that we can add 'No Results' text when the listWidget is empty. |
45 | |
46 | === modified file 'openlp/core/ui/lib/pathedit.py' |
47 | --- openlp/core/ui/lib/pathedit.py 2017-05-22 18:22:43 +0000 |
48 | +++ openlp/core/ui/lib/pathedit.py 2017-05-26 13:44:43 +0000 |
49 | @@ -46,16 +46,16 @@ |
50 | |
51 | :param parent: The parent of the widget. This is just passed to the super method. |
52 | :type parent: QWidget or None |
53 | - |
54 | + |
55 | :param dialog_caption: Used to customise the caption in the QFileDialog. |
56 | :param dialog_caption: str |
57 | - |
58 | + |
59 | :param default_path: The default path. This is set as the path when the revert button is clicked |
60 | :type default_path: str |
61 | |
62 | :param show_revert: Used to determin if the 'revert button' should be visible. |
63 | :type show_revert: bool |
64 | - |
65 | + |
66 | :return: None |
67 | :rtype: None |
68 | """ |
69 | @@ -72,7 +72,7 @@ |
70 | Set up the widget |
71 | :param show_revert: Show or hide the revert button |
72 | :type show_revert: bool |
73 | - |
74 | + |
75 | :return: None |
76 | :rtype: None |
77 | """ |
78 | |
79 | === modified file 'openlp/plugins/bibles/lib/__init__.py' |
80 | --- openlp/plugins/bibles/lib/__init__.py 2017-02-18 07:23:15 +0000 |
81 | +++ openlp/plugins/bibles/lib/__init__.py 2017-05-26 13:44:43 +0000 |
82 | @@ -341,10 +341,10 @@ |
83 | if not book_ref_id: |
84 | book_ref_id = bible.get_book_ref_id_by_localised_name(book, language_selection) |
85 | elif not bible.get_book_by_book_ref_id(book_ref_id): |
86 | - return False |
87 | + return [] |
88 | # We have not found the book so do not continue |
89 | if not book_ref_id: |
90 | - return False |
91 | + return [] |
92 | ranges = match.group('ranges') |
93 | range_list = get_reference_match('range_separator').split(ranges) |
94 | ref_list = [] |
95 | @@ -403,7 +403,7 @@ |
96 | return ref_list |
97 | else: |
98 | log.debug('Invalid reference: {text}'.format(text=reference)) |
99 | - return None |
100 | + return [] |
101 | |
102 | |
103 | class SearchResults(object): |
104 | |
105 | === modified file 'openlp/plugins/bibles/lib/db.py' |
106 | --- openlp/plugins/bibles/lib/db.py 2017-02-18 07:23:15 +0000 |
107 | +++ openlp/plugins/bibles/lib/db.py 2017-05-26 13:44:43 +0000 |
108 | @@ -158,6 +158,7 @@ |
109 | self.get_name() |
110 | if 'path' in kwargs: |
111 | self.path = kwargs['path'] |
112 | + self._is_web_bible = None |
113 | |
114 | def get_name(self): |
115 | """ |
116 | @@ -426,6 +427,18 @@ |
117 | return 0 |
118 | return count |
119 | |
120 | + @property |
121 | + def is_web_bible(self): |
122 | + """ |
123 | + A read only property indicating if the bible is a 'web bible' |
124 | + |
125 | + :return: If the bible is a web bible. |
126 | + :rtype: bool |
127 | + """ |
128 | + if self._is_web_bible is None: |
129 | + self._is_web_bible = bool(self.get_object(BibleMeta, 'download_source')) |
130 | + return self._is_web_bible |
131 | + |
132 | def dump_bible(self): |
133 | """ |
134 | Utility debugging method to dump the contents of a bible. |
135 | |
136 | === modified file 'openlp/plugins/bibles/lib/manager.py' |
137 | --- openlp/plugins/bibles/lib/manager.py 2017-03-15 19:51:10 +0000 |
138 | +++ openlp/plugins/bibles/lib/manager.py 2017-05-26 13:44:43 +0000 |
139 | @@ -142,8 +142,8 @@ |
140 | log.debug('Bible Name: "{name}"'.format(name=name)) |
141 | self.db_cache[name] = bible |
142 | # Look to see if lazy load bible exists and get create getter. |
143 | - source = self.db_cache[name].get_object(BibleMeta, 'download_source') |
144 | - if source: |
145 | + if self.db_cache[name].is_web_bible: |
146 | + source = self.db_cache[name].get_object(BibleMeta, 'download_source') |
147 | download_name = self.db_cache[name].get_object(BibleMeta, 'download_name').value |
148 | meta_proxy = self.db_cache[name].get_object(BibleMeta, 'proxy_server') |
149 | web_bible = HTTPBible(self.parent, path=self.path, file=filename, download_source=source.value, |
150 | @@ -278,7 +278,7 @@ |
151 | :param show_error: |
152 | """ |
153 | if not bible or not ref_list: |
154 | - return None |
155 | + return [] |
156 | return self.db_cache[bible].get_verses(ref_list, show_error) |
157 | |
158 | def get_language_selection(self, bible): |
159 | @@ -305,11 +305,17 @@ |
160 | """ |
161 | Does a verse search for the given bible and text. |
162 | |
163 | - :param bible: The bible to search in (unicode). |
164 | - :param second_bible: The second bible (unicode). We do not search in this bible. |
165 | - :param text: The text to search for (unicode). |
166 | + :param bible: The bible to search |
167 | + :type bible: str |
168 | + :param text: The text to search for |
169 | + :type text: str |
170 | + |
171 | + :return: The search results if valid, or None if the search is invalid. |
172 | + :rtype: None, list |
173 | """ |
174 | log.debug('BibleManager.verse_search("{bible}", "{text}")'.format(bible=bible, text=text)) |
175 | + if not text: |
176 | + return None |
177 | # If no bibles are installed, message is given. |
178 | if not bible: |
179 | self.main_window.information_message( |
180 | @@ -317,8 +323,7 @@ |
181 | UiStrings().BibleNoBibles) |
182 | return None |
183 | # Check if the bible or second_bible is a web bible. |
184 | - web_bible = self.db_cache[bible].get_object(BibleMeta, 'download_source') |
185 | - if web_bible: |
186 | + if self.db_cache[bible].is_web_bible: |
187 | # If either Bible is Web, cursor is reset to normal and message is given. |
188 | self.application.set_normal_cursor() |
189 | self.main_window.information_message( |
190 | @@ -328,41 +333,8 @@ |
191 | 'This means that the currently selected Bible is a Web Bible.') |
192 | ) |
193 | return None |
194 | - # Shorter than 3 char searches break OpenLP with very long search times, thus they are blocked. |
195 | - if len(text) - text.count(' ') < 3: |
196 | - return None |
197 | - # Fetch the results from db. If no results are found, return None, no message is given for this. |
198 | - elif text: |
199 | - return self.db_cache[bible].verse_search(text) |
200 | - else: |
201 | - return None |
202 | - |
203 | - def verse_search_while_typing(self, bible, second_bible, text): |
204 | - """ |
205 | - Does a verse search for the given bible and text. |
206 | - This is used during "Search while typing" |
207 | - It's the same thing as the normal text search, but it does not show the web Bible error. |
208 | - (It would result in the error popping every time a char is entered or removed) |
209 | - It also does not have a minimum text len, this is set in mediaitem.py |
210 | - |
211 | - :param bible: The bible to search in (unicode). |
212 | - :param second_bible: The second bible (unicode). We do not search in this bible. |
213 | - :param text: The text to search for (unicode). |
214 | - """ |
215 | - # If no bibles are installed, message is given. |
216 | - if not bible: |
217 | - return None |
218 | - # Check if the bible or second_bible is a web bible. |
219 | - web_bible = self.db_cache[bible].get_object(BibleMeta, 'download_source') |
220 | - second_web_bible = '' |
221 | - if second_bible: |
222 | - second_web_bible = self.db_cache[second_bible].get_object(BibleMeta, 'download_source') |
223 | - if web_bible or second_web_bible: |
224 | - # If either Bible is Web, cursor is reset to normal and search ends w/o any message. |
225 | - self.application.set_normal_cursor() |
226 | - return None |
227 | - # Fetch the results from db. If no results are found, return None, no message is given for this. |
228 | - elif text: |
229 | + # Fetch the results from db. If no results are found, return None, no message is given for this. |
230 | + if text: |
231 | return self.db_cache[bible].verse_search(text) |
232 | else: |
233 | return None |
234 | |
235 | === modified file 'openlp/plugins/bibles/lib/mediaitem.py' (properties changed: -x to +x) |
236 | --- openlp/plugins/bibles/lib/mediaitem.py 2017-02-18 20:22:47 +0000 |
237 | +++ openlp/plugins/bibles/lib/mediaitem.py 2017-05-26 13:44:43 +0000 |
238 | @@ -22,6 +22,7 @@ |
239 | |
240 | import logging |
241 | import re |
242 | +from enum import IntEnum, unique |
243 | |
244 | from PyQt5 import QtCore, QtWidgets |
245 | |
246 | @@ -48,15 +49,45 @@ |
247 | 'list': get_reference_separator('sep_l_display')} |
248 | |
249 | |
250 | -class BibleSearch(object): |
251 | +@unique |
252 | +class BibleSearch(IntEnum): |
253 | """ |
254 | - Enumeration class for the different search methods for the "Search" tab. |
255 | + Enumeration class for the different search types for the "Search" tab. |
256 | """ |
257 | Reference = 1 |
258 | Text = 2 |
259 | Combined = 3 |
260 | |
261 | |
262 | +@unique |
263 | +class ResultsTab(IntEnum): |
264 | + """ |
265 | + Enumeration class for the different tabs for the results list. |
266 | + """ |
267 | + Saved = 0 |
268 | + Search = 1 |
269 | + |
270 | + |
271 | +@unique |
272 | +class SearchStatus(IntEnum): |
273 | + """ |
274 | + Enumeration class for the different search methods. |
275 | + """ |
276 | + SearchButton = 0 |
277 | + SearchAsYouType = 1 |
278 | + NotEnoughText = 2 |
279 | + |
280 | + |
281 | +@unique |
282 | +class SearchTabs(IntEnum): |
283 | + """ |
284 | + Enumeration class for the tabs on the media item. |
285 | + """ |
286 | + Search = 0 |
287 | + Select = 1 |
288 | + Options = 2 |
289 | + |
290 | + |
291 | class BibleMediaItem(MediaManagerItem): |
292 | """ |
293 | This is the custom media manager item for Bibles. |
294 | @@ -73,11 +104,13 @@ |
295 | :param kwargs: Keyword arguments to pass to the super method. (dict) |
296 | """ |
297 | self.clear_icon = build_icon(':/bibles/bibles_search_clear.png') |
298 | - self.lock_icon = build_icon(':/bibles/bibles_search_lock.png') |
299 | - self.unlock_icon = build_icon(':/bibles/bibles_search_unlock.png') |
300 | + self.save_results_icon = build_icon(':/bibles/bibles_save_results.png') |
301 | self.sort_icon = build_icon(':/bibles/bibles_book_sort.png') |
302 | self.bible = None |
303 | self.second_bible = None |
304 | + self.saved_results = [] |
305 | + self.current_results = [] |
306 | + self.search_status = SearchStatus.SearchButton |
307 | # TODO: Make more central and clean up after! |
308 | self.search_timer = QtCore.QTimer() |
309 | self.search_timer.setInterval(200) |
310 | @@ -162,8 +195,10 @@ |
311 | self.select_tab.setVisible(False) |
312 | self.page_layout.addWidget(self.select_tab) |
313 | # General Search Opions |
314 | - self.options_widget = QtWidgets.QGroupBox(translate('BiblesPlugin.MediaItem', 'Options'), self) |
315 | - self.general_bible_layout = QtWidgets.QFormLayout(self.options_widget) |
316 | + self.options_tab = QtWidgets.QWidget() |
317 | + self.options_tab.setSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum) |
318 | + self.search_tab_bar.addTab(translate('BiblesPlugin.MediaItem', 'Options')) |
319 | + self.general_bible_layout = QtWidgets.QFormLayout(self.options_tab) |
320 | self.version_combo_box = create_horizontal_adjusting_combo_box(self, 'version_combo_box') |
321 | self.general_bible_layout.addRow('{version}:'.format(version=UiStrings().Version), self.version_combo_box) |
322 | self.second_combo_box = create_horizontal_adjusting_combo_box(self, 'second_combo_box') |
323 | @@ -171,20 +206,28 @@ |
324 | self.style_combo_box = create_horizontal_adjusting_combo_box(self, 'style_combo_box') |
325 | self.style_combo_box.addItems(['', '', '']) |
326 | self.general_bible_layout.addRow(UiStrings().LayoutStyle, self.style_combo_box) |
327 | - self.search_button_layout = QtWidgets.QHBoxLayout() |
328 | + self.options_tab.setVisible(False) |
329 | + self.page_layout.addWidget(self.options_tab) |
330 | + # This widget is the easier way to reset the spacing of search_button_layout. (Because page_layout has had its |
331 | + # spacing set to 0) |
332 | + self.search_button_widget = QtWidgets.QWidget() |
333 | + self.search_button_layout = QtWidgets.QHBoxLayout(self.search_button_widget) |
334 | self.search_button_layout.addStretch() |
335 | # Note: If we use QPushButton instead of the QToolButton, the icon will be larger than the Lock icon. |
336 | - self.clear_button = QtWidgets.QToolButton(self) |
337 | + self.clear_button = QtWidgets.QPushButton() |
338 | self.clear_button.setIcon(self.clear_icon) |
339 | - self.lock_button = QtWidgets.QToolButton(self) |
340 | - self.lock_button.setIcon(self.unlock_icon) |
341 | - self.lock_button.setCheckable(True) |
342 | + self.save_results_button = QtWidgets.QPushButton() |
343 | + self.save_results_button.setIcon(self.save_results_icon) |
344 | self.search_button_layout.addWidget(self.clear_button) |
345 | - self.search_button_layout.addWidget(self.lock_button) |
346 | + self.search_button_layout.addWidget(self.save_results_button) |
347 | self.search_button = QtWidgets.QPushButton(self) |
348 | self.search_button_layout.addWidget(self.search_button) |
349 | - self.general_bible_layout.addRow(self.search_button_layout) |
350 | - self.page_layout.addWidget(self.options_widget) |
351 | + self.page_layout.addWidget(self.search_button_widget) |
352 | + self.results_view_tab = QtWidgets.QTabBar(self) |
353 | + self.results_view_tab.addTab('') |
354 | + self.results_view_tab.addTab('') |
355 | + self.results_view_tab.setCurrentIndex(ResultsTab.Search) |
356 | + self.page_layout.addWidget(self.results_view_tab) |
357 | |
358 | def setupUi(self): |
359 | super().setupUi() |
360 | @@ -211,12 +254,15 @@ |
361 | # Buttons |
362 | self.book_order_button.toggled.connect(self.on_book_order_button_toggled) |
363 | self.clear_button.clicked.connect(self.on_clear_button_clicked) |
364 | - self.lock_button.toggled.connect(self.on_lock_button_toggled) |
365 | + self.save_results_button.clicked.connect(self.on_save_results_button_clicked) |
366 | self.search_button.clicked.connect(self.on_search_button_clicked) |
367 | # Other stuff |
368 | self.search_edit.returnPressed.connect(self.on_search_button_clicked) |
369 | self.search_tab_bar.currentChanged.connect(self.on_search_tab_bar_current_changed) |
370 | + self.results_view_tab.currentChanged.connect(self.on_results_view_tab_current_changed) |
371 | self.search_edit.textChanged.connect(self.on_search_edit_text_changed) |
372 | + self.on_results_view_tab_total_update(ResultsTab.Saved) |
373 | + self.on_results_view_tab_total_update(ResultsTab.Search) |
374 | |
375 | def retranslateUi(self): |
376 | log.debug('retranslateUi') |
377 | @@ -225,9 +271,9 @@ |
378 | self.style_combo_box.setItemText(LayoutStyle.VersePerSlide, UiStrings().VersePerSlide) |
379 | self.style_combo_box.setItemText(LayoutStyle.VersePerLine, UiStrings().VersePerLine) |
380 | self.style_combo_box.setItemText(LayoutStyle.Continuous, UiStrings().Continuous) |
381 | - self.clear_button.setToolTip(translate('BiblesPlugin.MediaItem', 'Clear the search results.')) |
382 | - self.lock_button.setToolTip( |
383 | - translate('BiblesPlugin.MediaItem', 'Toggle to keep or clear the previous results.')) |
384 | + self.clear_button.setToolTip(translate('BiblesPlugin.MediaItem', 'Clear the results on the current tab.')) |
385 | + self.save_results_button.setToolTip( |
386 | + translate('BiblesPlugin.MediaItem', 'Add the search results to the saved list.')) |
387 | self.search_button.setText(UiStrings().Search) |
388 | |
389 | def on_focus(self): |
390 | @@ -241,8 +287,10 @@ |
391 | if self.search_tab.isVisible(): |
392 | self.search_edit.setFocus() |
393 | self.search_edit.selectAll() |
394 | - else: |
395 | + if self.select_tab.isVisible(): |
396 | self.select_book_combo_box.setFocus() |
397 | + if self.options_tab.isVisible(): |
398 | + self.version_combo_box.setFocus() |
399 | |
400 | def config_update(self): |
401 | """ |
402 | @@ -415,14 +463,48 @@ |
403 | """ |
404 | Show the selected tab and set focus to it |
405 | |
406 | - :param index: The tab selected (int) |
407 | + :param index: The tab selected |
408 | + :type index: int |
409 | :return: None |
410 | """ |
411 | - search_tab = index == 0 |
412 | - self.search_tab.setVisible(search_tab) |
413 | - self.select_tab.setVisible(not search_tab) |
414 | + if index == SearchTabs.Search or index == SearchTabs.Select: |
415 | + self.search_button.setEnabled(True) |
416 | + else: |
417 | + self.search_button.setEnabled(False) |
418 | + self.search_tab.setVisible(index == SearchTabs.Search) |
419 | + self.select_tab.setVisible(index == SearchTabs.Select) |
420 | + self.options_tab.setVisible(index == SearchTabs.Options) |
421 | self.on_focus() |
422 | |
423 | + def on_results_view_tab_current_changed(self, index): |
424 | + """ |
425 | + Update list_widget with the contents of the selected list |
426 | + |
427 | + :param index: The index of the tab that has been changed to. (int) |
428 | + :return: None |
429 | + """ |
430 | + if index == ResultsTab.Saved: |
431 | + self.add_built_results_to_list_widget(self.saved_results) |
432 | + elif index == ResultsTab.Search: |
433 | + self.add_built_results_to_list_widget(self.current_results) |
434 | + |
435 | + def on_results_view_tab_total_update(self, index): |
436 | + """ |
437 | + Update the result total count on the tab with the given index. |
438 | + |
439 | + :param index: Index of the tab to update (int) |
440 | + :return: None |
441 | + """ |
442 | + string = '' |
443 | + count = 0 |
444 | + if index == ResultsTab.Saved: |
445 | + string = translate('BiblesPlugin.MediaItem', 'Saved ({result_count})') |
446 | + count = len(self.saved_results) |
447 | + elif index == ResultsTab.Search: |
448 | + string = translate('BiblesPlugin.MediaItem', 'Results ({result_count})') |
449 | + count = len(self.current_results) |
450 | + self.results_view_tab.setTabText(index, string.format(result_count=count)) |
451 | + |
452 | def on_book_order_button_toggled(self, checked): |
453 | """ |
454 | Change the sort order of the book names |
455 | @@ -442,22 +524,25 @@ |
456 | |
457 | :return: None |
458 | """ |
459 | - self.list_view.clear() |
460 | - self.search_edit.clear() |
461 | - self.on_focus() |
462 | + current_index = self.results_view_tab.currentIndex() |
463 | + for item in self.list_view.selectedItems(): |
464 | + self.list_view.takeItem(self.list_view.row(item)) |
465 | + results = [item.data(QtCore.Qt.UserRole) for item in self.list_view.allItems()] |
466 | + if current_index == ResultsTab.Saved: |
467 | + self.saved_results = results |
468 | + elif current_index == ResultsTab.Search: |
469 | + self.current_results = results |
470 | + self.on_results_view_tab_total_update(current_index) |
471 | |
472 | - def on_lock_button_toggled(self, checked): |
473 | + def on_save_results_button_clicked(self): |
474 | """ |
475 | - Toggle the lock button, if Search tab is used, set focus to search field. |
476 | + Add the selected verses to the saved_results list. |
477 | |
478 | - :param checked: The state of the toggle button. (bool) |
479 | :return: None |
480 | """ |
481 | - self.list_view.locked = checked |
482 | - if checked: |
483 | - self.sender().setIcon(self.lock_icon) |
484 | - else: |
485 | - self.sender().setIcon(self.unlock_icon) |
486 | + for verse in self.list_view.selectedItems(): |
487 | + self.saved_results.append(verse.data(QtCore.Qt.UserRole)) |
488 | + self.on_results_view_tab_total_update(ResultsTab.Saved) |
489 | |
490 | def on_style_combo_box_index_changed(self, index): |
491 | """ |
492 | @@ -490,16 +575,17 @@ |
493 | :return: None |
494 | """ |
495 | new_selection = self.second_combo_box.currentData() |
496 | - if self.list_view.count(): |
497 | + if self.saved_results: |
498 | # Exclusive or (^) the new and previous selections to detect if the user has switched between single and |
499 | # dual bible mode |
500 | if (new_selection is None) ^ (self.second_bible is None): |
501 | if critical_error_message_box( |
502 | message=translate('BiblesPlugin.MediaItem', |
503 | 'OpenLP cannot combine single and dual Bible verse search results. ' |
504 | - 'Do you want to clear your search results and start a new search?'), |
505 | + 'Do you want to clear your saved results?'), |
506 | parent=self, question=True) == QtWidgets.QMessageBox.Yes: |
507 | - self.list_view.clear(override_lock=True) |
508 | + self.saved_results = [] |
509 | + self.on_results_view_tab_total_update(ResultsTab.Saved) |
510 | else: |
511 | self.second_combo_box.setCurrentIndex(self.second_combo_box.findData(self.second_bible)) |
512 | return |
513 | @@ -525,7 +611,8 @@ |
514 | log.warning('Not enough chapters in %s', book_ref_id) |
515 | critical_error_message_box(message=translate('BiblesPlugin.MediaItem', 'Bible not fully loaded.')) |
516 | else: |
517 | - self.search_button.setEnabled(True) |
518 | + if self.select_tab.isVisible(): |
519 | + self.search_button.setEnabled(True) |
520 | self.adjust_combo_box(1, self.chapter_count, self.from_chapter) |
521 | self.adjust_combo_box(1, self.chapter_count, self.to_chapter) |
522 | self.adjust_combo_box(1, verse_count, self.from_verse) |
523 | @@ -602,6 +689,8 @@ |
524 | |
525 | :return: None |
526 | """ |
527 | + self.search_timer.stop() |
528 | + self.search_status = SearchStatus.SearchButton |
529 | if not self.bible: |
530 | self.main_window.information_message(UiStrings().BibleNoBiblesTitle, UiStrings().BibleNoBibles) |
531 | return |
532 | @@ -613,6 +702,7 @@ |
533 | elif self.select_tab.isVisible(): |
534 | self.select_search() |
535 | self.search_button.setEnabled(True) |
536 | + self.results_view_tab.setCurrentIndex(ResultsTab.Search) |
537 | self.application.set_normal_cursor() |
538 | |
539 | def select_search(self): |
540 | @@ -636,18 +726,21 @@ |
541 | |
542 | :return: None |
543 | """ |
544 | + self.search_results = [] |
545 | verse_refs = self.plugin.manager.parse_ref(self.bible.name, search_text) |
546 | self.search_results = self.plugin.manager.get_verses(self.bible.name, verse_refs, True) |
547 | if self.second_bible and self.search_results: |
548 | self.search_results = self.plugin.manager.get_verses(self.second_bible.name, verse_refs, True) |
549 | self.display_results() |
550 | |
551 | - def on_text_search(self, text, search_while_type=False): |
552 | + def on_text_search(self, text): |
553 | """ |
554 | We are doing a 'Text Search'. |
555 | This search is called on def text_search by 'Search' Text and Combined Searches. |
556 | """ |
557 | self.search_results = self.plugin.manager.verse_search(self.bible.name, text) |
558 | + if self.search_results is None: |
559 | + return |
560 | if self.second_bible and self.search_results: |
561 | filtered_search_results = [] |
562 | not_found_count = 0 |
563 | @@ -663,7 +756,7 @@ |
564 | verse=verse.verse, bible_name=self.second_bible.name)) |
565 | not_found_count += 1 |
566 | self.search_results = filtered_search_results |
567 | - if not_found_count != 0 and not search_while_type: |
568 | + if not_found_count != 0 and self.search_status == SearchStatus.SearchButton: |
569 | self.main_window.information_message( |
570 | translate('BiblesPlugin.MediaItem', 'Verses not found'), |
571 | translate('BiblesPlugin.MediaItem', |
572 | @@ -673,22 +766,23 @@ |
573 | ).format(second_name=self.second_bible.name, name=self.bible.name, count=not_found_count)) |
574 | self.display_results() |
575 | |
576 | - def text_search(self, search_while_type=False): |
577 | + def text_search(self): |
578 | """ |
579 | This triggers the proper 'Search' search based on which search type is used. |
580 | "Eg. "Reference Search", "Text Search" or "Combined search". |
581 | """ |
582 | + self.search_results = [] |
583 | log.debug('text_search called') |
584 | text = self.search_edit.text() |
585 | if text == '': |
586 | - self.list_view.clear() |
587 | + self.display_results() |
588 | return |
589 | - self.list_view.clear(search_while_typing=search_while_type) |
590 | + self.on_results_view_tab_total_update(ResultsTab.Search) |
591 | if self.search_edit.current_search_type() == BibleSearch.Reference: |
592 | if get_reference_match('full').match(text): |
593 | # Valid reference found. Do reference search. |
594 | self.text_reference_search(text) |
595 | - elif not search_while_type: |
596 | + elif self.search_status == SearchStatus.SearchButton: |
597 | self.main_window.information_message( |
598 | translate('BiblesPlugin.BibleManager', 'Scripture Reference Error'), |
599 | translate('BiblesPlugin.BibleManager', |
600 | @@ -700,10 +794,12 @@ |
601 | self.text_reference_search(text) |
602 | else: |
603 | # It can only be a 'Combined' search without a valid reference, or a 'Text' search |
604 | - if search_while_type: |
605 | - if len(text) > 8 and VALID_TEXT_SEARCH.search(text): |
606 | - self.on_text_search(text, True) |
607 | - elif VALID_TEXT_SEARCH.search(text): |
608 | + if self.search_status == SearchStatus.SearchAsYouType: |
609 | + if len(text) <= 8: |
610 | + self.search_status = SearchStatus.NotEnoughText |
611 | + self.display_results() |
612 | + return |
613 | + if VALID_TEXT_SEARCH.search(text): |
614 | self.on_text_search(text) |
615 | |
616 | def on_search_edit_text_changed(self): |
617 | @@ -713,9 +809,12 @@ |
618 | |
619 | :return: None |
620 | """ |
621 | - if Settings().value('bibles/is search while typing enabled'): |
622 | - if not self.search_timer.isActive(): |
623 | - self.search_timer.start() |
624 | + if not Settings().value('bibles/is search while typing enabled') or \ |
625 | + not self.bible or self.bible.is_web_bible or \ |
626 | + (self.second_bible and self.bible.is_web_bible): |
627 | + return |
628 | + if not self.search_timer.isActive(): |
629 | + self.search_timer.start() |
630 | |
631 | def on_search_timer_timeout(self): |
632 | """ |
633 | @@ -724,7 +823,9 @@ |
634 | |
635 | :return: None |
636 | """ |
637 | - self.text_search(True) |
638 | + self.search_status = SearchStatus.SearchAsYouType |
639 | + self.text_search() |
640 | + self.results_view_tab.setCurrentIndex(ResultsTab.Search) |
641 | |
642 | def display_results(self): |
643 | """ |
644 | @@ -732,14 +833,16 @@ |
645 | |
646 | :return: None |
647 | """ |
648 | - self.list_view.clear() |
649 | - if self.search_results: |
650 | - items = self.build_display_results(self.bible, self.second_bible, self.search_results) |
651 | - for item in items: |
652 | - self.list_view.addItem(item) |
653 | - self.list_view.selectAll() |
654 | + self.current_results = self.build_display_results(self.bible, self.second_bible, self.search_results) |
655 | self.search_results = [] |
656 | - self.second_search_results = [] |
657 | + self.add_built_results_to_list_widget(self.current_results) |
658 | + |
659 | + def add_built_results_to_list_widget(self, results): |
660 | + self.list_view.clear(self.search_status == SearchStatus.NotEnoughText) |
661 | + for item in self.build_list_widget_items(results): |
662 | + self.list_view.addItem(item) |
663 | + self.list_view.selectAll() |
664 | + self.on_results_view_tab_total_update(ResultsTab.Search) |
665 | |
666 | def build_display_results(self, bible, second_bible, search_results): |
667 | """ |
668 | @@ -789,10 +892,17 @@ |
669 | bible_text = '{book} {chapter:d}{sep}{verse:d} ({version}, {second_version})' |
670 | else: |
671 | bible_text = '{book} {chapter:d}{sep}{verse:d} ({version})' |
672 | - bible_verse = QtWidgets.QListWidgetItem(bible_text.format(sep=verse_separator, **data)) |
673 | + data['item_title'] = bible_text.format(sep=verse_separator, **data) |
674 | + items.append(data) |
675 | + return items |
676 | + |
677 | + def build_list_widget_items(self, items): |
678 | + list_widget_items = [] |
679 | + for data in items: |
680 | + bible_verse = QtWidgets.QListWidgetItem(data['item_title']) |
681 | bible_verse.setData(QtCore.Qt.UserRole, data) |
682 | - items.append(bible_verse) |
683 | - return items |
684 | + list_widget_items.append(bible_verse) |
685 | + return list_widget_items |
686 | |
687 | def generate_slide_data(self, service_item, item=None, xml_version=False, remote=False, |
688 | context=ServiceItemContext.Service): |
689 | @@ -897,6 +1007,8 @@ |
690 | """ |
691 | Search for some Bible verses (by reference). |
692 | """ |
693 | + if self.bible is None: |
694 | + return [] |
695 | reference = self.plugin.manager.parse_ref(self.bible.name, string) |
696 | search_results = self.plugin.manager.get_verses(self.bible.name, reference, showError) |
697 | if search_results: |
698 | @@ -908,6 +1020,9 @@ |
699 | """ |
700 | Create a media item from an item id. |
701 | """ |
702 | + if self.bible is None: |
703 | + return [] |
704 | reference = self.plugin.manager.parse_ref(self.bible.name, item_id) |
705 | search_results = self.plugin.manager.get_verses(self.bible.name, reference, False) |
706 | - return self.build_display_results(self.bible, None, search_results) |
707 | + items = self.build_display_results(self.bible, None, search_results) |
708 | + return self.build_list_widget_items(items) |
709 | |
710 | === modified file 'openlp/plugins/presentations/presentationplugin.py' |
711 | --- openlp/plugins/presentations/presentationplugin.py 2017-05-22 18:27:40 +0000 |
712 | +++ openlp/plugins/presentations/presentationplugin.py 2017-05-26 13:44:43 +0000 |
713 | @@ -1,4 +1,4 @@ |
714 | - # -*- coding: utf-8 -*- |
715 | +# -*- coding: utf-8 -*- |
716 | # vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 |
717 | |
718 | ############################################################################### |
719 | |
720 | === modified file 'openlp/plugins/songusage/forms/songusagedetaildialog.py' |
721 | --- openlp/plugins/songusage/forms/songusagedetaildialog.py 2017-05-22 18:22:43 +0000 |
722 | +++ openlp/plugins/songusage/forms/songusagedetaildialog.py 2017-05-26 13:44:43 +0000 |
723 | @@ -69,7 +69,7 @@ |
724 | self.file_horizontal_layout.setSpacing(8) |
725 | self.file_horizontal_layout.setContentsMargins(8, 8, 8, 8) |
726 | self.file_horizontal_layout.setObjectName('file_horizontal_layout') |
727 | - self.report_path_edit = PathEdit(self.file_group_box, path_type = PathType.Directories, show_revert=False) |
728 | + self.report_path_edit = PathEdit(self.file_group_box, path_type=PathType.Directories, show_revert=False) |
729 | self.file_horizontal_layout.addWidget(self.report_path_edit) |
730 | self.vertical_layout.addWidget(self.file_group_box) |
731 | self.button_box = create_button_box(song_usage_detail_dialog, 'button_box', ['cancel', 'ok']) |
732 | |
733 | === added file 'resources/images/bibles_save_results.png' |
734 | Binary files resources/images/bibles_save_results.png 1970-01-01 00:00:00 +0000 and resources/images/bibles_save_results.png 2017-05-26 13:44:43 +0000 differ |
735 | === removed file 'resources/images/bibles_search_lock.png' |
736 | Binary files resources/images/bibles_search_lock.png 2011-05-11 23:59:37 +0000 and resources/images/bibles_search_lock.png 1970-01-01 00:00:00 +0000 differ |
737 | === removed file 'resources/images/bibles_search_unlock.png' |
738 | Binary files resources/images/bibles_search_unlock.png 2011-05-11 23:59:37 +0000 and resources/images/bibles_search_unlock.png 1970-01-01 00:00:00 +0000 differ |
739 | === removed file 'resources/images/network_ssl.png' |
740 | Binary files resources/images/network_ssl.png 2014-04-14 18:09:47 +0000 and resources/images/network_ssl.png 1970-01-01 00:00:00 +0000 differ |
741 | === modified file 'resources/images/openlp-2.qrc' |
742 | --- resources/images/openlp-2.qrc 2016-12-18 14:11:31 +0000 |
743 | +++ resources/images/openlp-2.qrc 2017-05-26 13:44:43 +0000 |
744 | @@ -34,8 +34,7 @@ |
745 | <file>bibles_search_text.png</file> |
746 | <file>bibles_search_reference.png</file> |
747 | <file>bibles_search_clear.png</file> |
748 | - <file>bibles_search_unlock.png</file> |
749 | - <file>bibles_search_lock.png</file> |
750 | + <file>bibles_save_results.png</file> |
751 | </qresource> |
752 | <qresource prefix="plugins"> |
753 | <file>plugin_alerts.png</file> |
754 | @@ -144,7 +143,6 @@ |
755 | </qresource> |
756 | <qresource prefix="remote"> |
757 | <file>network_server.png</file> |
758 | - <file>network_ssl.png</file> |
759 | <file>network_auth.png</file> |
760 | </qresource> |
761 | <qresource prefix="songusage"> |
762 | @@ -188,4 +186,4 @@ |
763 | <file>android_app_qr.png</file> |
764 | <file>ios_app_qr.png</file> |
765 | </qresource> |
766 | -</RCC> |
767 | \ No newline at end of file |
768 | +</RCC> |
769 | |
770 | === modified file 'tests/functional/openlp_core_ui_lib/test_listwidgetwithdnd.py' (properties changed: -x to +x) |
771 | --- tests/functional/openlp_core_ui_lib/test_listwidgetwithdnd.py 2017-02-18 07:23:15 +0000 |
772 | +++ tests/functional/openlp_core_ui_lib/test_listwidgetwithdnd.py 2017-05-26 13:44:43 +0000 |
773 | @@ -23,6 +23,7 @@ |
774 | This module contains tests for the openlp.core.lib.listwidgetwithdnd module |
775 | """ |
776 | from unittest import TestCase |
777 | +from types import GeneratorType |
778 | |
779 | from openlp.core.common.uistrings import UiStrings |
780 | from openlp.core.ui.lib.listwidgetwithdnd import ListWidgetWithDnD |
781 | @@ -33,37 +34,6 @@ |
782 | """ |
783 | Test the :class:`~openlp.core.lib.listwidgetwithdnd.ListWidgetWithDnD` class |
784 | """ |
785 | - def test_clear_locked(self): |
786 | - """ |
787 | - Test the clear method the list is 'locked' |
788 | - """ |
789 | - with patch('openlp.core.ui.lib.listwidgetwithdnd.QtWidgets.QListWidget.clear') as mocked_clear_super_method: |
790 | - # GIVEN: An instance of ListWidgetWithDnD |
791 | - widget = ListWidgetWithDnD() |
792 | - |
793 | - # WHEN: The list is 'locked' and clear has been called |
794 | - widget.locked = True |
795 | - widget.clear() |
796 | - |
797 | - # THEN: The super method should not have been called (i.e. The list not cleared) |
798 | - self.assertFalse(mocked_clear_super_method.called) |
799 | - |
800 | - def test_clear_overide_locked(self): |
801 | - """ |
802 | - Test the clear method the list is 'locked', but clear is called with 'override_lock' set to True |
803 | - """ |
804 | - with patch('openlp.core.ui.lib.listwidgetwithdnd.QtWidgets.QListWidget.clear') as mocked_clear_super_method: |
805 | - # GIVEN: An instance of ListWidgetWithDnD |
806 | - widget = ListWidgetWithDnD() |
807 | - |
808 | - # WHEN: The list is 'locked' and clear has been called with override_lock se to True |
809 | - widget.locked = True |
810 | - widget.clear(override_lock=True) |
811 | - |
812 | - # THEN: The super method should have been called (i.e. The list is cleared regardless whether it is locked |
813 | - # or not) |
814 | - mocked_clear_super_method.assert_called_once_with() |
815 | - |
816 | def test_clear(self): |
817 | """ |
818 | Test the clear method when called without any arguments. |
819 | @@ -90,6 +60,38 @@ |
820 | # THEN: The results text should be the 'short results' text. |
821 | self.assertEqual(widget.no_results_text, UiStrings().ShortResults) |
822 | |
823 | + def test_all_items_no_list_items(self): |
824 | + """ |
825 | + Test allItems when there are no items in the list widget |
826 | + """ |
827 | + # GIVEN: An instance of ListWidgetWithDnD |
828 | + widget = ListWidgetWithDnD() |
829 | + with patch.object(widget, 'count', return_value=0), \ |
830 | + patch.object(widget, 'item', side_effect=lambda x: [][x]): |
831 | + |
832 | + # WHEN: Calling allItems |
833 | + result = widget.allItems() |
834 | + |
835 | + # THEN: An instance of a Generator object should be returned. The generator should not yeild any results |
836 | + self.assertIsInstance(result, GeneratorType) |
837 | + self.assertEqual(list(result), []) |
838 | + |
839 | + def test_all_items_list_items(self): |
840 | + """ |
841 | + Test allItems when the list widget contains some items. |
842 | + """ |
843 | + # GIVEN: An instance of ListWidgetWithDnD |
844 | + widget = ListWidgetWithDnD() |
845 | + with patch.object(widget, 'count', return_value=2), \ |
846 | + patch.object(widget, 'item', side_effect=lambda x: [5, 3][x]): |
847 | + |
848 | + # WHEN: Calling allItems |
849 | + result = widget.allItems() |
850 | + |
851 | + # THEN: An instance of a Generator object should be returned. The generator should not yeild any results |
852 | + self.assertIsInstance(result, GeneratorType) |
853 | + self.assertEqual(list(result), [5, 3]) |
854 | + |
855 | def test_paint_event(self): |
856 | """ |
857 | Test the paintEvent method when the list is not empty |
858 | |
859 | === modified file 'tests/functional/openlp_plugins/bibles/test_mediaitem.py' (properties changed: -x to +x) |
860 | --- tests/functional/openlp_plugins/bibles/test_mediaitem.py 2017-02-18 07:23:15 +0000 |
861 | +++ tests/functional/openlp_plugins/bibles/test_mediaitem.py 2017-05-26 13:44:43 +0000 |
862 | @@ -31,7 +31,8 @@ |
863 | |
864 | from openlp.core.common import Registry |
865 | from openlp.core.lib import MediaManagerItem |
866 | -from openlp.plugins.bibles.lib.mediaitem import BibleMediaItem, BibleSearch, get_reference_separators, VALID_TEXT_SEARCH |
867 | +from openlp.plugins.bibles.lib.mediaitem import BibleMediaItem, BibleSearch, ResultsTab, SearchStatus, \ |
868 | + get_reference_separators, VALID_TEXT_SEARCH |
869 | |
870 | |
871 | class TestBibleMediaItemModulefunctions(TestCase): |
872 | @@ -143,6 +144,7 @@ |
873 | self.media_item = BibleMediaItem(None, self.mocked_plugin) |
874 | |
875 | self.media_item.settings_section = 'bibles' |
876 | + self.media_item.results_view_tab = MagicMock() |
877 | |
878 | self.mocked_book_1 = MagicMock(**{'get_name.return_value': 'Book 1', 'book_reference_id': 1}) |
879 | self.mocked_book_2 = MagicMock(**{'get_name.return_value': 'Book 2', 'book_reference_id': 2}) |
880 | @@ -658,56 +660,65 @@ |
881 | # THEN: The select_book_combo_box model sort should have been reset |
882 | self.media_item.select_book_combo_box.model().sort.assert_called_once_with(-1) |
883 | |
884 | - def test_on_clear_button_clicked(self): |
885 | - """ |
886 | - Test on_clear_button_clicked |
887 | + def test_on_clear_button_clicked_saved_tab(self): |
888 | + """ |
889 | + Test on_clear_button_clicked when the saved tab is selected |
890 | + """ |
891 | + # GIVEN: An instance of :class:`MediaManagerItem` and mocked out saved_tab and select_tab and a mocked out |
892 | + # list_view and search_edit |
893 | + self.media_item.list_view = MagicMock() |
894 | + self.media_item.search_edit = MagicMock() |
895 | + self.media_item.results_view_tab = MagicMock(**{'currentIndex.return_value': ResultsTab.Saved}) |
896 | + self.media_item.saved_results = ['Some', 'Results'] |
897 | + with patch.object(self.media_item, 'on_focus'): |
898 | + |
899 | + # WHEN: Calling on_clear_button_clicked |
900 | + self.media_item.on_clear_button_clicked() |
901 | + |
902 | + # THEN: The list_view should be cleared |
903 | + self.assertEqual(self.media_item.saved_results, []) |
904 | + self.media_item.list_view.clear.assert_called_once_with() |
905 | + |
906 | + def test_on_clear_button_clicked_search_tab(self): |
907 | + """ |
908 | + Test on_clear_button_clicked when the search tab is selected |
909 | """ |
910 | # GIVEN: An instance of :class:`MediaManagerItem` and mocked out search_tab and select_tab and a mocked out |
911 | # list_view and search_edit |
912 | self.media_item.list_view = MagicMock() |
913 | self.media_item.search_edit = MagicMock() |
914 | + self.media_item.results_view_tab = MagicMock(**{'currentIndex.return_value': ResultsTab.Search}) |
915 | + self.media_item.current_results = ['Some', 'Results'] |
916 | with patch.object(self.media_item, 'on_focus'): |
917 | |
918 | # WHEN: Calling on_clear_button_clicked |
919 | self.media_item.on_clear_button_clicked() |
920 | |
921 | # THEN: The list_view and the search_edit should be cleared |
922 | + self.assertEqual(self.media_item.current_results, []) |
923 | self.media_item.list_view.clear.assert_called_once_with() |
924 | self.media_item.search_edit.clear.assert_called_once_with() |
925 | |
926 | - def test_on_lock_button_toggled_search_tab_lock_icon(self): |
927 | - """ |
928 | - Test that "on_lock_button_toggled" toggles the lock properly. |
929 | - """ |
930 | - # GIVEN: An instance of :class:`MediaManagerItem` a mocked sender and list_view |
931 | - self.media_item.list_view = MagicMock() |
932 | - self.media_item.lock_icon = 'lock icon' |
933 | - mocked_sender_instance = MagicMock() |
934 | - with patch.object(self.media_item, 'sender', return_value=mocked_sender_instance): |
935 | - |
936 | - # WHEN: When the lock_button is checked |
937 | - self.media_item.on_lock_button_toggled(True) |
938 | - |
939 | - # THEN: list_view should be 'locked' and the lock icon set |
940 | - self.assertTrue(self.media_item.list_view.locked) |
941 | - mocked_sender_instance.setIcon.assert_called_once_with('lock icon') |
942 | - |
943 | - def test_on_lock_button_toggled_unlock_icon(self): |
944 | - """ |
945 | - Test that "on_lock_button_toggled" toggles the lock properly. |
946 | - """ |
947 | - # GIVEN: An instance of :class:`MediaManagerItem` a mocked sender and list_view |
948 | - self.media_item.list_view = MagicMock() |
949 | - self.media_item.unlock_icon = 'unlock icon' |
950 | - mocked_sender_instance = MagicMock() |
951 | - with patch.object(self.media_item, 'sender', return_value=mocked_sender_instance): |
952 | - |
953 | - # WHEN: When the lock_button is unchecked |
954 | - self.media_item.on_lock_button_toggled(False) |
955 | - |
956 | - # THEN: list_view should be 'unlocked' and the unlock icon set |
957 | - self.assertFalse(self.media_item.list_view.locked) |
958 | - mocked_sender_instance.setIcon.assert_called_once_with('unlock icon') |
959 | + def test_on_save_results_button_clicked(self): |
960 | + """ |
961 | + Test that "on_save_results_button_clicked" saves the results. |
962 | + """ |
963 | + # GIVEN: An instance of :class:`MediaManagerItem` and a mocked list_view |
964 | + result_1 = MagicMock(**{'data.return_value': 'R1'}) |
965 | + result_2 = MagicMock(**{'data.return_value': 'R2'}) |
966 | + result_3 = MagicMock(**{'data.return_value': 'R3'}) |
967 | + self.media_item.list_view = MagicMock(**{'selectedItems.return_value': [result_1, result_2, result_3]}) |
968 | + |
969 | + with patch.object(self.media_item, 'on_results_view_tab_total_update') as \ |
970 | + mocked_on_results_view_tab_total_update: |
971 | + |
972 | + # WHEN: When the save_results_button is clicked |
973 | + self.media_item.on_save_results_button_clicked() |
974 | + |
975 | + # THEN: The selected results in the list_view should be added to the 'saved_results' list. And the saved_tab |
976 | + # total should be updated. |
977 | + self.assertEqual(self.media_item.saved_results, ['R1', 'R2', 'R3']) |
978 | + mocked_on_results_view_tab_total_update.assert_called_once_with(ResultsTab.Saved) |
979 | |
980 | def test_on_style_combo_box_changed(self): |
981 | """ |
982 | @@ -815,7 +826,9 @@ |
983 | self.media_item.list_view = MagicMock(**{'count.return_value': 5}) |
984 | self.media_item.style_combo_box = MagicMock() |
985 | self.media_item.select_book_combo_box = MagicMock() |
986 | + self.media_item.search_results = ['list', 'of', 'results'] |
987 | with patch.object(self.media_item, 'initialise_advanced_bible') as mocked_initialise_advanced_bible, \ |
988 | + patch.object(self.media_item, 'display_results'), \ |
989 | patch('openlp.plugins.bibles.lib.mediaitem.critical_error_message_box', |
990 | return_value=QtWidgets.QMessageBox.Yes) as mocked_critical_error_message_box: |
991 | |
992 | @@ -825,9 +838,8 @@ |
993 | self.media_item.second_combo_box = MagicMock(**{'currentData.return_value': self.mocked_bible_1}) |
994 | self.media_item.on_second_combo_box_index_changed(5) |
995 | |
996 | - # THEN: The list_view should be cleared and the selected bible should be set as the current bible |
997 | + # THEN: The selected bible should be set as the current bible |
998 | self.assertTrue(mocked_critical_error_message_box.called) |
999 | - self.media_item.list_view.clear.assert_called_once_with(override_lock=True) |
1000 | self.media_item.style_combo_box.setEnabled.assert_called_once_with(False) |
1001 | self.assertTrue(mocked_initialise_advanced_bible.called) |
1002 | self.assertEqual(self.media_item.second_bible, self.mocked_bible_1) |
1003 | @@ -841,7 +853,9 @@ |
1004 | self.media_item.list_view = MagicMock(**{'count.return_value': 5}) |
1005 | self.media_item.style_combo_box = MagicMock() |
1006 | self.media_item.select_book_combo_box = MagicMock() |
1007 | + self.media_item.search_results = ['list', 'of', 'results'] |
1008 | with patch.object(self.media_item, 'initialise_advanced_bible') as mocked_initialise_advanced_bible, \ |
1009 | + patch.object(self.media_item, 'display_results'), \ |
1010 | patch('openlp.plugins.bibles.lib.mediaitem.critical_error_message_box', |
1011 | return_value=QtWidgets.QMessageBox.Yes) as mocked_critical_error_message_box: |
1012 | # WHEN: The previously is a bible new selection is None and the user selects yes |
1013 | @@ -850,9 +864,8 @@ |
1014 | self.media_item.second_combo_box = MagicMock(**{'currentData.return_value': None}) |
1015 | self.media_item.on_second_combo_box_index_changed(0) |
1016 | |
1017 | - # THEN: The list_view should be cleared and the selected bible should be set as the current bible |
1018 | + # THEN: The selected bible should be set as the current bible |
1019 | self.assertTrue(mocked_critical_error_message_box.called) |
1020 | - self.media_item.list_view.clear.assert_called_once_with(override_lock=True) |
1021 | self.media_item.style_combo_box.setEnabled.assert_called_once_with(True) |
1022 | self.assertFalse(mocked_initialise_advanced_bible.called) |
1023 | self.assertEqual(self.media_item.second_bible, None) |
1024 | @@ -1388,8 +1401,9 @@ |
1025 | # WHEN: Calling on_search_timer_timeout |
1026 | self.media_item.on_search_timer_timeout() |
1027 | |
1028 | - # THEN: The text_search method should have been called with True |
1029 | - mocked_text_search.assert_called_once_with(True) |
1030 | + # THEN: The search_status should be set to SearchAsYouType and text_search should have been called |
1031 | + self.assertEqual(self.media_item.search_status, SearchStatus().SearchAsYouType) |
1032 | + mocked_text_search.assert_called_once_with() |
1033 | |
1034 | def test_display_results_no_results(self): |
1035 | """ |
1036 | @@ -1407,7 +1421,6 @@ |
1037 | self.media_item.display_results() |
1038 | |
1039 | # THEN: No items should be added to the list |
1040 | - self.media_item.list_view.clear.assert_called_once_with() |
1041 | self.assertFalse(self.media_item.list_view.addItem.called) |
1042 | |
1043 | def test_display_results_results(self): |
1044 | @@ -1415,7 +1428,10 @@ |
1045 | Test the display_results method when there are items to display |
1046 | """ |
1047 | # GIVEN: An instance of BibleMediaItem and a mocked build_display_results which returns a list of results |
1048 | - with patch.object(self.media_item, 'build_display_results', return_value=['list', 'items']): |
1049 | + with patch.object(self.media_item, 'build_display_results', return_value=[ |
1050 | + {'item_title': 'Title 1'}, {'item_title': 'Title 2'}]), \ |
1051 | + patch.object(self.media_item, 'add_built_results_to_list_widget') as \ |
1052 | + mocked_add_built_results_to_list_widget: |
1053 | self.media_item.search_results = ['results'] |
1054 | self.media_item.list_view = MagicMock() |
1055 | |
1056 | @@ -1423,5 +1439,5 @@ |
1057 | self.media_item.display_results() |
1058 | |
1059 | # THEN: addItem should have been with the display items |
1060 | - self.media_item.list_view.clear.assert_called_once_with() |
1061 | - self.assertEqual(self.media_item.list_view.addItem.call_args_list, [call('list'), call('items')]) |
1062 | + mocked_add_built_results_to_list_widget.assert_called_once_with( |
1063 | + [{'item_title': 'Title 1'}, {'item_title': 'Title 2'}]) |
1064 | |
1065 | === modified file 'tests/interfaces/openlp_plugins/bibles/test_lib_parse_reference.py' |
1066 | --- tests/interfaces/openlp_plugins/bibles/test_lib_parse_reference.py 2017-04-24 05:17:55 +0000 |
1067 | +++ tests/interfaces/openlp_plugins/bibles/test_lib_parse_reference.py 2017-05-26 13:44:43 +0000 |
1068 | @@ -108,7 +108,7 @@ |
1069 | # WHEN asking to parse the bible reference |
1070 | results = parse_reference('Raoul 1', self.manager.db_cache['tests'], MagicMock()) |
1071 | # THEN a verse array should be returned |
1072 | - self.assertEqual(False, results, "The bible Search should return False") |
1073 | + self.assertEqual([], results, "The bible Search should return an empty list") |
1074 | |
1075 | def test_parse_reference_five(self): |
1076 | """ |
Please can you remove the resource file as this makes it difficult to see the changes.
The resource file can be merged in the next request,