Status: | Merged |
---|---|
Merged at revision: | 2758 |
Proposed branch: | lp:~phill-ridout/openlp/pathlib2 |
Merge into: | lp:openlp |
Diff against target: |
1632 lines (+684/-251) 22 files modified
openlp/core/common/path.py (+61/-0) openlp/core/lib/__init__.py (+35/-1) openlp/core/lib/filedialog.py (+0/-58) openlp/core/lib/mediamanageritem.py (+11/-8) openlp/core/ui/advancedtab.py (+7/-6) openlp/core/ui/generaltab.py (+7/-4) openlp/core/ui/lib/filedialog.py (+113/-0) openlp/core/ui/lib/pathedit.py (+36/-20) openlp/core/ui/themeform.py (+7/-6) openlp/core/ui/thememanager.py (+12/-9) openlp/core/ui/themewizard.py (+2/-0) openlp/plugins/bibles/lib/importers/http.py (+2/-2) openlp/plugins/presentations/lib/presentationtab.py (+9/-7) openlp/plugins/songs/forms/editsongform.py (+8/-4) openlp/plugins/songs/forms/songimportform.py (+6/-4) openlp/plugins/songusage/forms/songusagedetailform.py (+5/-3) tests/functional/openlp_core_common/test_path.py (+88/-0) tests/functional/openlp_core_lib/test_file_dialog.py (+5/-56) tests/functional/openlp_core_lib/test_lib.py (+35/-4) tests/functional/openlp_core_ui/test_themeform.py (+2/-1) tests/functional/openlp_core_ui_lib/test_filedialog.py (+188/-0) tests/functional/openlp_core_ui_lib/test_pathedit.py (+45/-58) |
To merge this branch: | bzr merge lp:~phill-ridout/openlp/pathlib2 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Tim Bentley | Approve | ||
Raoul Snyman | Approve | ||
Review via email: mp+328825@code.launchpad.net |
This proposal supersedes a proposal from 2017-08-09.
Commit message
Description of the change
Definitely ready for merging, unless, of course you guys find some more issues!
Part 2
Changed the pathedit widget over to using pathlib
Added a 'patched' file dialog
Added a few utility methods
lp:~phill-ridout/openlp/pathlib2 (revision 2763)
[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 | # |
Raoul Snyman (raoul-snyman) wrote : Posted in a previous version of this proposal | # |
We already have a custom file dialog, why not extend that one rather than write a completely new class?
Raoul Snyman (raoul-snyman) wrote : Posted in a previous version of this proposal | # |
I've added some inline comments for some things I think need to be fixed up. Mostly small stuff.
Raoul Snyman (raoul-snyman) wrote : | # |
Looks OK to me.
Tim Bentley (trb143) : | # |
Preview Diff
1 | === added file 'openlp/core/common/path.py' |
2 | --- openlp/core/common/path.py 1970-01-01 00:00:00 +0000 |
3 | +++ openlp/core/common/path.py 2017-08-10 06:57:18 +0000 |
4 | @@ -0,0 +1,61 @@ |
5 | +# -*- coding: utf-8 -*- |
6 | +# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 |
7 | + |
8 | +############################################################################### |
9 | +# OpenLP - Open Source Lyrics Projection # |
10 | +# --------------------------------------------------------------------------- # |
11 | +# Copyright (c) 2008-2017 OpenLP Developers # |
12 | +# --------------------------------------------------------------------------- # |
13 | +# This program is free software; you can redistribute it and/or modify it # |
14 | +# under the terms of the GNU General Public License as published by the Free # |
15 | +# Software Foundation; version 2 of the License. # |
16 | +# # |
17 | +# This program is distributed in the hope that it will be useful, but WITHOUT # |
18 | +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or # |
19 | +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for # |
20 | +# more details. # |
21 | +# # |
22 | +# You should have received a copy of the GNU General Public License along # |
23 | +# with this program; if not, write to the Free Software Foundation, Inc., 59 # |
24 | +# Temple Place, Suite 330, Boston, MA 02111-1307 USA # |
25 | +############################################################################### |
26 | + |
27 | +from pathlib import Path |
28 | + |
29 | + |
30 | +def path_to_str(path): |
31 | + """ |
32 | + A utility function to convert a Path object or NoneType to a string equivalent. |
33 | + |
34 | + :param path: The value to convert to a string |
35 | + :type: pathlib.Path or None |
36 | + |
37 | + :return: An empty string if :param:`path` is None, else a string representation of the :param:`path` |
38 | + :rtype: str |
39 | + """ |
40 | + if not isinstance(path, Path) and path is not None: |
41 | + raise TypeError('parameter \'path\' must be of type Path or NoneType') |
42 | + if path is None: |
43 | + return '' |
44 | + else: |
45 | + return str(path) |
46 | + |
47 | + |
48 | +def str_to_path(string): |
49 | + """ |
50 | + A utility function to convert a str object to a Path or NoneType. |
51 | + |
52 | + This function is of particular use because initating a Path object with an empty string causes the Path object to |
53 | + point to the current working directory. |
54 | + |
55 | + :param string: The string to convert |
56 | + :type string: str |
57 | + |
58 | + :return: None if :param:`string` is empty, or a Path object representation of :param:`string` |
59 | + :rtype: pathlib.Path or None |
60 | + """ |
61 | + if not isinstance(string, str): |
62 | + raise TypeError('parameter \'string\' must be of type str') |
63 | + if string == '': |
64 | + return None |
65 | + return Path(string) |
66 | |
67 | === modified file 'openlp/core/lib/__init__.py' |
68 | --- openlp/core/lib/__init__.py 2017-08-06 07:23:26 +0000 |
69 | +++ openlp/core/lib/__init__.py 2017-08-10 06:57:18 +0000 |
70 | @@ -608,8 +608,42 @@ |
71 | return list_to_string |
72 | |
73 | |
74 | +def replace_params(args, kwargs, params): |
75 | + """ |
76 | + Apply a transformation function to the specified args or kwargs |
77 | + |
78 | + :param args: Positional arguments |
79 | + :type args: (,) |
80 | + |
81 | + :param kwargs: Key Word arguments |
82 | + :type kwargs: dict |
83 | + |
84 | + :param params: A tuple of tuples with the position and the key word to replace. |
85 | + :type params: ((int, str, path_to_str),) |
86 | + |
87 | + :return: The modified positional and keyword arguments |
88 | + :rtype: (tuple, dict) |
89 | + |
90 | + |
91 | + Usage: |
92 | + Take a method with the following signature, and assume we which to apply the str function to arg2: |
93 | + def method(arg1=None, arg2=None, arg3=None) |
94 | + |
95 | + As arg2 can be specified postitionally as the second argument (1 with a zero index) or as a keyword, the we |
96 | + would call this function as follows: |
97 | + |
98 | + replace_params(args, kwargs, ((1, 'arg2', str),)) |
99 | + """ |
100 | + args = list(args) |
101 | + for position, key_word, transform in params: |
102 | + if len(args) > position: |
103 | + args[position] = transform(args[position]) |
104 | + elif key_word in kwargs: |
105 | + kwargs[key_word] = transform(kwargs[key_word]) |
106 | + return tuple(args), kwargs |
107 | + |
108 | + |
109 | from .exceptions import ValidationError |
110 | -from .filedialog import FileDialog |
111 | from .screen import ScreenList |
112 | from .formattingtags import FormattingTags |
113 | from .plugin import PluginStatus, StringContent, Plugin |
114 | |
115 | === removed file 'openlp/core/lib/filedialog.py' |
116 | --- openlp/core/lib/filedialog.py 2016-12-31 11:01:36 +0000 |
117 | +++ openlp/core/lib/filedialog.py 1970-01-01 00:00:00 +0000 |
118 | @@ -1,58 +0,0 @@ |
119 | -# -*- coding: utf-8 -*- |
120 | -# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 |
121 | - |
122 | -############################################################################### |
123 | -# OpenLP - Open Source Lyrics Projection # |
124 | -# --------------------------------------------------------------------------- # |
125 | -# Copyright (c) 2008-2017 OpenLP Developers # |
126 | -# --------------------------------------------------------------------------- # |
127 | -# This program is free software; you can redistribute it and/or modify it # |
128 | -# under the terms of the GNU General Public License as published by the Free # |
129 | -# Software Foundation; version 2 of the License. # |
130 | -# # |
131 | -# This program is distributed in the hope that it will be useful, but WITHOUT # |
132 | -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or # |
133 | -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for # |
134 | -# more details. # |
135 | -# # |
136 | -# You should have received a copy of the GNU General Public License along # |
137 | -# with this program; if not, write to the Free Software Foundation, Inc., 59 # |
138 | -# Temple Place, Suite 330, Boston, MA 02111-1307 USA # |
139 | -############################################################################### |
140 | -""" |
141 | -Provide a work around for a bug in QFileDialog <https://bugs.launchpad.net/openlp/+bug/1209515> |
142 | -""" |
143 | -import logging |
144 | -import os |
145 | -from urllib import parse |
146 | - |
147 | -from PyQt5 import QtWidgets |
148 | - |
149 | -from openlp.core.common import UiStrings |
150 | - |
151 | -log = logging.getLogger(__name__) |
152 | - |
153 | - |
154 | -class FileDialog(QtWidgets.QFileDialog): |
155 | - """ |
156 | - Subclass QFileDialog to work round a bug |
157 | - """ |
158 | - @staticmethod |
159 | - def getOpenFileNames(parent, *args, **kwargs): |
160 | - """ |
161 | - Reimplement getOpenFileNames to fix the way it returns some file names that url encoded when selecting multiple |
162 | - files |
163 | - """ |
164 | - files, filter_used = QtWidgets.QFileDialog.getOpenFileNames(parent, *args, **kwargs) |
165 | - file_list = [] |
166 | - for file in files: |
167 | - if not os.path.exists(file): |
168 | - log.info('File not found. Attempting to unquote.') |
169 | - file = parse.unquote(file) |
170 | - if not os.path.exists(file): |
171 | - log.error('File {text} not found.'.format(text=file)) |
172 | - QtWidgets.QMessageBox.information(parent, UiStrings().FileNotFound, |
173 | - UiStrings().FileNotFoundMessage.format(name=file)) |
174 | - continue |
175 | - file_list.append(file) |
176 | - return file_list |
177 | |
178 | === modified file 'openlp/core/lib/mediamanageritem.py' |
179 | --- openlp/core/lib/mediamanageritem.py 2017-02-18 07:23:15 +0000 |
180 | +++ openlp/core/lib/mediamanageritem.py 2017-08-10 06:57:18 +0000 |
181 | @@ -26,12 +26,14 @@ |
182 | import os |
183 | import re |
184 | |
185 | -from PyQt5 import QtCore, QtGui, QtWidgets |
186 | +from PyQt5 import QtCore, QtWidgets |
187 | |
188 | from openlp.core.common import Registry, RegistryProperties, Settings, UiStrings, translate |
189 | -from openlp.core.lib import FileDialog, ServiceItem, StringContent, ServiceItemContext |
190 | +from openlp.core.common.path import path_to_str, str_to_path |
191 | +from openlp.core.lib import ServiceItem, StringContent, ServiceItemContext |
192 | from openlp.core.lib.searchedit import SearchEdit |
193 | from openlp.core.lib.ui import create_widget_action, critical_error_message_box |
194 | +from openlp.core.ui.lib.filedialog import FileDialog |
195 | from openlp.core.ui.lib.listwidgetwithdnd import ListWidgetWithDnD |
196 | from openlp.core.ui.lib.toolbar import OpenLPToolbar |
197 | |
198 | @@ -309,13 +311,14 @@ |
199 | """ |
200 | Add a file to the list widget to make it available for showing |
201 | """ |
202 | - files = FileDialog.getOpenFileNames(self, self.on_new_prompt, |
203 | - Settings().value(self.settings_section + '/last directory'), |
204 | - self.on_new_file_masks) |
205 | - log.info('New files(s) {files}'.format(files=files)) |
206 | - if files: |
207 | + file_paths, selected_filter = FileDialog.getOpenFileNames( |
208 | + self, self.on_new_prompt, |
209 | + str_to_path(Settings().value(self.settings_section + '/last directory')), |
210 | + self.on_new_file_masks) |
211 | + log.info('New files(s) {file_paths}'.format(file_paths=file_paths)) |
212 | + if file_paths: |
213 | self.application.set_busy_cursor() |
214 | - self.validate_and_load(files) |
215 | + self.validate_and_load([path_to_str(path) for path in file_paths]) |
216 | self.application.set_normal_cursor() |
217 | |
218 | def load_file(self, data): |
219 | |
220 | === modified file 'openlp/core/ui/advancedtab.py' |
221 | --- openlp/core/ui/advancedtab.py 2017-08-01 20:59:41 +0000 |
222 | +++ openlp/core/ui/advancedtab.py 2017-08-10 06:57:18 +0000 |
223 | @@ -30,6 +30,7 @@ |
224 | |
225 | from openlp.core.common import AppLocation, Settings, SlideLimits, UiStrings, translate |
226 | from openlp.core.common.languagemanager import format_time |
227 | +from openlp.core.common.path import path_to_str |
228 | from openlp.core.lib import SettingsTab, build_icon |
229 | from openlp.core.ui.lib import PathEdit, PathType |
230 | |
231 | @@ -156,7 +157,7 @@ |
232 | self.data_directory_new_label = QtWidgets.QLabel(self.data_directory_group_box) |
233 | self.data_directory_new_label.setObjectName('data_directory_current_label') |
234 | self.data_directory_path_edit = PathEdit(self.data_directory_group_box, path_type=PathType.Directories, |
235 | - default_path=str(AppLocation.get_directory(AppLocation.DataDir))) |
236 | + default_path=AppLocation.get_directory(AppLocation.DataDir)) |
237 | self.data_directory_layout.addRow(self.data_directory_new_label, self.data_directory_path_edit) |
238 | self.new_data_directory_has_files_label = QtWidgets.QLabel(self.data_directory_group_box) |
239 | self.new_data_directory_has_files_label.setObjectName('new_data_directory_has_files_label') |
240 | @@ -373,7 +374,7 @@ |
241 | self.new_data_directory_has_files_label.hide() |
242 | self.data_directory_cancel_button.hide() |
243 | # Since data location can be changed, make sure the path is present. |
244 | - self.data_directory_path_edit.path = str(AppLocation.get_data_path()) |
245 | + self.data_directory_path_edit.path = AppLocation.get_data_path() |
246 | # Don't allow data directory move if running portable. |
247 | if settings.value('advanced/is portable'): |
248 | self.data_directory_group_box.hide() |
249 | @@ -497,12 +498,12 @@ |
250 | 'closed.').format(path=new_data_path), |
251 | defaultButton=QtWidgets.QMessageBox.No) |
252 | if answer != QtWidgets.QMessageBox.Yes: |
253 | - self.data_directory_path_edit.path = str(AppLocation.get_data_path()) |
254 | + self.data_directory_path_edit.path = AppLocation.get_data_path() |
255 | return |
256 | # Check if data already exists here. |
257 | - self.check_data_overwrite(new_data_path) |
258 | + self.check_data_overwrite(path_to_str(new_data_path)) |
259 | # Save the new location. |
260 | - self.main_window.set_new_data_path(new_data_path) |
261 | + self.main_window.set_new_data_path(path_to_str(new_data_path)) |
262 | self.data_directory_cancel_button.show() |
263 | |
264 | def on_data_directory_copy_check_box_toggled(self): |
265 | @@ -550,7 +551,7 @@ |
266 | """ |
267 | Cancel the data directory location change |
268 | """ |
269 | - self.data_directory_path_edit.path = str(AppLocation.get_data_path()) |
270 | + self.data_directory_path_edit.path = AppLocation.get_data_path() |
271 | self.data_directory_copy_check_box.setChecked(False) |
272 | self.main_window.set_new_data_path(None) |
273 | self.main_window.set_copy_data(False) |
274 | |
275 | === modified file 'openlp/core/ui/generaltab.py' |
276 | --- openlp/core/ui/generaltab.py 2017-05-22 19:56:54 +0000 |
277 | +++ openlp/core/ui/generaltab.py 2017-08-10 06:57:18 +0000 |
278 | @@ -23,10 +23,12 @@ |
279 | The general tab of the configuration dialog. |
280 | """ |
281 | import logging |
282 | +from pathlib import Path |
283 | |
284 | from PyQt5 import QtCore, QtGui, QtWidgets |
285 | |
286 | from openlp.core.common import Registry, Settings, UiStrings, translate, get_images_filter |
287 | +from openlp.core.common.path import path_to_str, str_to_path |
288 | from openlp.core.lib import SettingsTab, ScreenList |
289 | from openlp.core.ui.lib import ColorButton, PathEdit |
290 | |
291 | @@ -172,7 +174,8 @@ |
292 | self.logo_layout.setObjectName('logo_layout') |
293 | self.logo_file_label = QtWidgets.QLabel(self.logo_group_box) |
294 | self.logo_file_label.setObjectName('logo_file_label') |
295 | - self.logo_file_path_edit = PathEdit(self.logo_group_box, default_path=':/graphics/openlp-splash-screen.png') |
296 | + self.logo_file_path_edit = PathEdit(self.logo_group_box, |
297 | + default_path=Path(':/graphics/openlp-splash-screen.png')) |
298 | self.logo_layout.addRow(self.logo_file_label, self.logo_file_path_edit) |
299 | self.logo_color_label = QtWidgets.QLabel(self.logo_group_box) |
300 | self.logo_color_label.setObjectName('logo_color_label') |
301 | @@ -266,7 +269,7 @@ |
302 | self.audio_group_box.setTitle(translate('OpenLP.GeneralTab', 'Background Audio')) |
303 | self.start_paused_check_box.setText(translate('OpenLP.GeneralTab', 'Start background audio paused')) |
304 | self.repeat_list_check_box.setText(translate('OpenLP.GeneralTab', 'Repeat track list')) |
305 | - self.logo_file_path_edit.dialog_caption = dialog_caption = translate('OpenLP.AdvancedTab', 'Select Logo File') |
306 | + self.logo_file_path_edit.dialog_caption = translate('OpenLP.AdvancedTab', 'Select Logo File') |
307 | self.logo_file_path_edit.filters = '{text};;{names} (*)'.format( |
308 | text=get_images_filter(), names=UiStrings().AllFiles) |
309 | |
310 | @@ -291,7 +294,7 @@ |
311 | self.auto_open_check_box.setChecked(settings.value('auto open')) |
312 | self.show_splash_check_box.setChecked(settings.value('show splash')) |
313 | self.logo_background_color = settings.value('logo background color') |
314 | - self.logo_file_path_edit.path = settings.value('logo file') |
315 | + self.logo_file_path_edit.path = str_to_path(settings.value('logo file')) |
316 | self.logo_hide_on_startup_check_box.setChecked(settings.value('logo hide on startup')) |
317 | self.logo_color_button.color = self.logo_background_color |
318 | self.check_for_updates_check_box.setChecked(settings.value('update check')) |
319 | @@ -325,7 +328,7 @@ |
320 | settings.setValue('auto open', self.auto_open_check_box.isChecked()) |
321 | settings.setValue('show splash', self.show_splash_check_box.isChecked()) |
322 | settings.setValue('logo background color', self.logo_background_color) |
323 | - settings.setValue('logo file', self.logo_file_path_edit.path) |
324 | + settings.setValue('logo file', path_to_str(self.logo_file_path_edit.path)) |
325 | settings.setValue('logo hide on startup', self.logo_hide_on_startup_check_box.isChecked()) |
326 | settings.setValue('update check', self.check_for_updates_check_box.isChecked()) |
327 | settings.setValue('save prompt', self.save_check_service_check_box.isChecked()) |
328 | |
329 | === added file 'openlp/core/ui/lib/filedialog.py' |
330 | --- openlp/core/ui/lib/filedialog.py 1970-01-01 00:00:00 +0000 |
331 | +++ openlp/core/ui/lib/filedialog.py 2017-08-10 06:57:18 +0000 |
332 | @@ -0,0 +1,113 @@ |
333 | +# -*- coding: utf-8 -*- |
334 | +# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 |
335 | + |
336 | +############################################################################### |
337 | +# OpenLP - Open Source Lyrics Projection # |
338 | +# --------------------------------------------------------------------------- # |
339 | +# Copyright (c) 2008-2017 OpenLP Developers # |
340 | +# --------------------------------------------------------------------------- # |
341 | +# This program is free software; you can redistribute it and/or modify it # |
342 | +# under the terms of the GNU General Public License as published by the Free # |
343 | +# Software Foundation; version 2 of the License. # |
344 | +# # |
345 | +# This program is distributed in the hope that it will be useful, but WITHOUT # |
346 | +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or # |
347 | +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for # |
348 | +# more details. # |
349 | +# # |
350 | +# You should have received a copy of the GNU General Public License along # |
351 | +# with this program; if not, write to the Free Software Foundation, Inc., 59 # |
352 | +# Temple Place, Suite 330, Boston, MA 02111-1307 USA # |
353 | +############################################################################### |
354 | +""" Patch the QFileDialog so it accepts and returns Path objects""" |
355 | +from pathlib import Path |
356 | + |
357 | +from PyQt5 import QtWidgets |
358 | + |
359 | +from openlp.core.common.path import path_to_str, str_to_path |
360 | +from openlp.core.lib import replace_params |
361 | + |
362 | + |
363 | +class FileDialog(QtWidgets.QFileDialog): |
364 | + @classmethod |
365 | + def getExistingDirectory(cls, *args, **kwargs): |
366 | + """ |
367 | + Wraps `getExistingDirectory` so that it can be called with, and return Path objects |
368 | + |
369 | + :type parent: QtWidgets.QWidget or None |
370 | + :type caption: str |
371 | + :type directory: pathlib.Path |
372 | + :type options: QtWidgets.QFileDialog.Options |
373 | + :rtype: tuple[Path, str] |
374 | + """ |
375 | + args, kwargs = replace_params(args, kwargs, ((2, 'directory', path_to_str),)) |
376 | + |
377 | + return_value = super().getExistingDirectory(*args, **kwargs) |
378 | + |
379 | + # getExistingDirectory returns a str that represents the path. The string is empty if the user cancels the |
380 | + # dialog. |
381 | + return str_to_path(return_value) |
382 | + |
383 | + @classmethod |
384 | + def getOpenFileName(cls, *args, **kwargs): |
385 | + """ |
386 | + Wraps `getOpenFileName` so that it can be called with, and return Path objects |
387 | + |
388 | + :type parent: QtWidgets.QWidget or None |
389 | + :type caption: str |
390 | + :type directory: pathlib.Path |
391 | + :type filter: str |
392 | + :type initialFilter: str |
393 | + :type options: QtWidgets.QFileDialog.Options |
394 | + :rtype: tuple[Path, str] |
395 | + """ |
396 | + args, kwargs = replace_params(args, kwargs, ((2, 'directory', path_to_str),)) |
397 | + |
398 | + file_name, selected_filter = super().getOpenFileName(*args, **kwargs) |
399 | + |
400 | + # getOpenFileName returns a tuple. The first item is a str that represents the path. The string is empty if |
401 | + # the user cancels the dialog. |
402 | + return str_to_path(file_name), selected_filter |
403 | + |
404 | + @classmethod |
405 | + def getOpenFileNames(cls, *args, **kwargs): |
406 | + """ |
407 | + Wraps `getOpenFileNames` so that it can be called with, and return Path objects |
408 | + |
409 | + :type parent: QtWidgets.QWidget or None |
410 | + :type caption: str |
411 | + :type directory: pathlib.Path |
412 | + :type filter: str |
413 | + :type initialFilter: str |
414 | + :type options: QtWidgets.QFileDialog.Options |
415 | + :rtype: tuple[list[Path], str] |
416 | + """ |
417 | + args, kwargs = replace_params(args, kwargs, ((2, 'directory', path_to_str),)) |
418 | + |
419 | + file_names, selected_filter = super().getOpenFileNames(*args, **kwargs) |
420 | + |
421 | + # getSaveFileName returns a tuple. The first item is a list of str's that represents the path. The list is |
422 | + # empty if the user cancels the dialog. |
423 | + paths = [str_to_path(path) for path in file_names] |
424 | + return paths, selected_filter |
425 | + |
426 | + @classmethod |
427 | + def getSaveFileName(cls, *args, **kwargs): |
428 | + """ |
429 | + Wraps `getSaveFileName` so that it can be called with, and return Path objects |
430 | + |
431 | + :type parent: QtWidgets.QWidget or None |
432 | + :type caption: str |
433 | + :type directory: pathlib.Path |
434 | + :type filter: str |
435 | + :type initialFilter: str |
436 | + :type options: QtWidgets.QFileDialog.Options |
437 | + :rtype: tuple[Path or None, str] |
438 | + """ |
439 | + args, kwargs = replace_params(args, kwargs, ((2, 'directory', path_to_str),)) |
440 | + |
441 | + file_name, selected_filter = super().getSaveFileName(*args, **kwargs) |
442 | + |
443 | + # getSaveFileName returns a tuple. The first item represents the path as a str. The string is empty if the user |
444 | + # cancels the dialog. |
445 | + return str_to_path(file_name), selected_filter |
446 | |
447 | === modified file 'openlp/core/ui/lib/pathedit.py' (properties changed: +x to -x) |
448 | --- openlp/core/ui/lib/pathedit.py 2017-07-04 23:13:51 +0000 |
449 | +++ openlp/core/ui/lib/pathedit.py 2017-08-10 06:57:18 +0000 |
450 | @@ -20,12 +20,14 @@ |
451 | # Temple Place, Suite 330, Boston, MA 02111-1307 USA # |
452 | ############################################################################### |
453 | from enum import Enum |
454 | -import os.path |
455 | +from pathlib import Path |
456 | |
457 | from PyQt5 import QtCore, QtWidgets |
458 | |
459 | from openlp.core.common import UiStrings, translate |
460 | +from openlp.core.common.path import path_to_str, str_to_path |
461 | from openlp.core.lib import build_icon |
462 | +from openlp.core.ui.lib.filedialog import FileDialog |
463 | |
464 | |
465 | class PathType(Enum): |
466 | @@ -38,11 +40,11 @@ |
467 | The :class:`~openlp.core.ui.lib.pathedit.PathEdit` class subclasses QWidget to create a custom widget for use when |
468 | a file or directory needs to be selected. |
469 | """ |
470 | - pathChanged = QtCore.pyqtSignal(str) |
471 | + pathChanged = QtCore.pyqtSignal(Path) |
472 | |
473 | def __init__(self, parent=None, path_type=PathType.Files, default_path=None, dialog_caption=None, show_revert=True): |
474 | """ |
475 | - Initalise the PathEdit widget |
476 | + Initialise the PathEdit widget |
477 | |
478 | :param parent: The parent of the widget. This is just passed to the super method. |
479 | :type parent: QWidget or None |
480 | @@ -51,9 +53,9 @@ |
481 | :type dialog_caption: str |
482 | |
483 | :param default_path: The default path. This is set as the path when the revert button is clicked |
484 | - :type default_path: str |
485 | + :type default_path: pathlib.Path |
486 | |
487 | - :param show_revert: Used to determin if the 'revert button' should be visible. |
488 | + :param show_revert: Used to determine if the 'revert button' should be visible. |
489 | :type show_revert: bool |
490 | |
491 | :return: None |
492 | @@ -79,7 +81,6 @@ |
493 | widget_layout = QtWidgets.QHBoxLayout() |
494 | widget_layout.setContentsMargins(0, 0, 0, 0) |
495 | self.line_edit = QtWidgets.QLineEdit(self) |
496 | - self.line_edit.setText(self._path) |
497 | widget_layout.addWidget(self.line_edit) |
498 | self.browse_button = QtWidgets.QToolButton(self) |
499 | self.browse_button.setIcon(build_icon(':/general/general_open.png')) |
500 | @@ -101,7 +102,7 @@ |
501 | A property getter method to return the selected path. |
502 | |
503 | :return: The selected path |
504 | - :rtype: str |
505 | + :rtype: pathlib.Path |
506 | """ |
507 | return self._path |
508 | |
509 | @@ -111,11 +112,15 @@ |
510 | A Property setter method to set the selected path |
511 | |
512 | :param path: The path to set the widget to |
513 | - :type path: str |
514 | + :type path: pathlib.Path |
515 | + |
516 | + :return: None |
517 | + :rtype: None |
518 | """ |
519 | self._path = path |
520 | - self.line_edit.setText(path) |
521 | - self.line_edit.setToolTip(path) |
522 | + text = path_to_str(path) |
523 | + self.line_edit.setText(text) |
524 | + self.line_edit.setToolTip(text) |
525 | |
526 | @property |
527 | def path_type(self): |
528 | @@ -124,7 +129,7 @@ |
529 | selecting a file or directory. |
530 | |
531 | :return: The type selected |
532 | - :rtype: Enum of PathEdit |
533 | + :rtype: PathType |
534 | """ |
535 | return self._path_type |
536 | |
537 | @@ -133,8 +138,11 @@ |
538 | """ |
539 | A Property setter method to set the path type |
540 | |
541 | - :param path: The type of path to select |
542 | - :type path: Enum of PathEdit |
543 | + :param path_type: The type of path to select |
544 | + :type path_type: PathType |
545 | + |
546 | + :return: None |
547 | + :rtype: None |
548 | """ |
549 | self._path_type = path_type |
550 | self.update_button_tool_tips() |
551 | @@ -142,7 +150,9 @@ |
552 | def update_button_tool_tips(self): |
553 | """ |
554 | Called to update the tooltips on the buttons. This is changing path types, and when the widget is initalised |
555 | + |
556 | :return: None |
557 | + :rtype: None |
558 | """ |
559 | if self._path_type == PathType.Directories: |
560 | self.browse_button.setToolTip(translate('OpenLP.PathEdit', 'Browse for directory.')) |
561 | @@ -156,21 +166,21 @@ |
562 | A handler to handle a click on the browse button. |
563 | |
564 | Show the QFileDialog and process the input from the user |
565 | + |
566 | :return: None |
567 | + :rtype: None |
568 | """ |
569 | caption = self.dialog_caption |
570 | - path = '' |
571 | + path = None |
572 | if self._path_type == PathType.Directories: |
573 | if not caption: |
574 | caption = translate('OpenLP.PathEdit', 'Select Directory') |
575 | - path = QtWidgets.QFileDialog.getExistingDirectory(self, caption, |
576 | - self._path, QtWidgets.QFileDialog.ShowDirsOnly) |
577 | + path = FileDialog.getExistingDirectory(self, caption, self._path, FileDialog.ShowDirsOnly) |
578 | elif self._path_type == PathType.Files: |
579 | if not caption: |
580 | caption = self.dialog_caption = translate('OpenLP.PathEdit', 'Select File') |
581 | - path, filter_used = QtWidgets.QFileDialog.getOpenFileName(self, caption, self._path, self.filters) |
582 | + path, filter_used = FileDialog.getOpenFileName(self, caption, self._path, self.filters) |
583 | if path: |
584 | - path = os.path.normpath(path) |
585 | self.on_new_path(path) |
586 | |
587 | def on_revert_button_clicked(self): |
588 | @@ -178,16 +188,21 @@ |
589 | A handler to handle a click on the revert button. |
590 | |
591 | Set the new path to the value of the default_path instance variable. |
592 | + |
593 | :return: None |
594 | + :rtype: None |
595 | """ |
596 | self.on_new_path(self.default_path) |
597 | |
598 | def on_line_edit_editing_finished(self): |
599 | """ |
600 | A handler to handle when the line edit has finished being edited. |
601 | + |
602 | :return: None |
603 | + :rtype: None |
604 | """ |
605 | - self.on_new_path(self.line_edit.text()) |
606 | + path = str_to_path(self.line_edit.text()) |
607 | + self.on_new_path(path) |
608 | |
609 | def on_new_path(self, path): |
610 | """ |
611 | @@ -196,9 +211,10 @@ |
612 | Emits the pathChanged Signal |
613 | |
614 | :param path: The new path |
615 | - :type path: str |
616 | + :type path: pathlib.Path |
617 | |
618 | :return: None |
619 | + :rtype: None |
620 | """ |
621 | if self._path != path: |
622 | self.path = path |
623 | |
624 | === modified file 'openlp/core/ui/themeform.py' |
625 | --- openlp/core/ui/themeform.py 2017-05-30 18:50:39 +0000 |
626 | +++ openlp/core/ui/themeform.py 2017-08-10 06:57:18 +0000 |
627 | @@ -28,6 +28,7 @@ |
628 | from PyQt5 import QtCore, QtGui, QtWidgets |
629 | |
630 | from openlp.core.common import Registry, RegistryProperties, UiStrings, translate, get_images_filter, is_not_image_file |
631 | +from openlp.core.common.path import path_to_str, str_to_path |
632 | from openlp.core.lib.theme import BackgroundType, BackgroundGradientType |
633 | from openlp.core.lib.ui import critical_error_message_box |
634 | from openlp.core.ui import ThemeLayoutForm |
635 | @@ -316,11 +317,11 @@ |
636 | self.setField('background_type', 1) |
637 | elif self.theme.background_type == BackgroundType.to_string(BackgroundType.Image): |
638 | self.image_color_button.color = self.theme.background_border_color |
639 | - self.image_path_edit.path = self.theme.background_filename |
640 | + self.image_path_edit.path = str_to_path(self.theme.background_filename) |
641 | self.setField('background_type', 2) |
642 | elif self.theme.background_type == BackgroundType.to_string(BackgroundType.Video): |
643 | self.video_color_button.color = self.theme.background_border_color |
644 | - self.video_path_edit.path = self.theme.background_filename |
645 | + self.video_path_edit.path = str_to_path(self.theme.background_filename) |
646 | self.setField('background_type', 4) |
647 | elif self.theme.background_type == BackgroundType.to_string(BackgroundType.Transparent): |
648 | self.setField('background_type', 3) |
649 | @@ -448,18 +449,18 @@ |
650 | """ |
651 | self.theme.background_end_color = color |
652 | |
653 | - def on_image_path_edit_path_changed(self, filename): |
654 | + def on_image_path_edit_path_changed(self, file_path): |
655 | """ |
656 | Background Image button pushed. |
657 | """ |
658 | - self.theme.background_filename = filename |
659 | + self.theme.background_filename = path_to_str(file_path) |
660 | self.set_background_page_values() |
661 | |
662 | - def on_video_path_edit_path_changed(self, filename): |
663 | + def on_video_path_edit_path_changed(self, file_path): |
664 | """ |
665 | Background video button pushed. |
666 | """ |
667 | - self.theme.background_filename = filename |
668 | + self.theme.background_filename = path_to_str(file_path) |
669 | self.set_background_page_values() |
670 | |
671 | def on_main_color_changed(self, color): |
672 | |
673 | === modified file 'openlp/core/ui/thememanager.py' |
674 | --- openlp/core/ui/thememanager.py 2017-08-03 04:21:19 +0000 |
675 | +++ openlp/core/ui/thememanager.py 2017-08-10 06:57:18 +0000 |
676 | @@ -22,7 +22,6 @@ |
677 | """ |
678 | The Theme Manager manages adding, deleteing and modifying of themes. |
679 | """ |
680 | -import json |
681 | import os |
682 | import zipfile |
683 | import shutil |
684 | @@ -32,12 +31,14 @@ |
685 | |
686 | from openlp.core.common import Registry, RegistryProperties, AppLocation, Settings, OpenLPMixin, RegistryMixin, \ |
687 | UiStrings, check_directory_exists, translate, is_win, get_filesystem_encoding, delete_file |
688 | -from openlp.core.lib import FileDialog, ImageSource, ValidationError, get_text_file_string, build_icon, \ |
689 | +from openlp.core.common.path import path_to_str, str_to_path |
690 | +from openlp.core.lib import ImageSource, ValidationError, get_text_file_string, build_icon, \ |
691 | check_item_selected, create_thumb, validate_thumb |
692 | from openlp.core.lib.theme import Theme, BackgroundType |
693 | from openlp.core.lib.ui import critical_error_message_box, create_widget_action |
694 | from openlp.core.ui import FileRenameForm, ThemeForm |
695 | from openlp.core.ui.lib import OpenLPToolbar |
696 | +from openlp.core.ui.lib.filedialog import FileDialog |
697 | from openlp.core.common.languagemanager import get_locale_key |
698 | |
699 | |
700 | @@ -424,15 +425,17 @@ |
701 | those files. This process will only load version 2 themes. |
702 | :param field: |
703 | """ |
704 | - files = FileDialog.getOpenFileNames(self, |
705 | - translate('OpenLP.ThemeManager', 'Select Theme Import File'), |
706 | - Settings().value(self.settings_section + '/last directory import'), |
707 | - translate('OpenLP.ThemeManager', 'OpenLP Themes (*.otz)')) |
708 | - self.log_info('New Themes {name}'.format(name=str(files))) |
709 | - if not files: |
710 | + file_paths, selected_filter = FileDialog.getOpenFileNames( |
711 | + self, |
712 | + translate('OpenLP.ThemeManager', 'Select Theme Import File'), |
713 | + str_to_path(Settings().value(self.settings_section + '/last directory import')), |
714 | + translate('OpenLP.ThemeManager', 'OpenLP Themes (*.otz)')) |
715 | + self.log_info('New Themes {file_paths}'.format(file_paths=file_paths)) |
716 | + if not file_paths: |
717 | return |
718 | self.application.set_busy_cursor() |
719 | - for file_name in files: |
720 | + for file_path in file_paths: |
721 | + file_name = path_to_str(file_path) |
722 | Settings().setValue(self.settings_section + '/last directory import', str(file_name)) |
723 | self.unzip_theme(file_name, self.path) |
724 | self.load_themes() |
725 | |
726 | === modified file 'openlp/core/ui/themewizard.py' |
727 | --- openlp/core/ui/themewizard.py 2017-05-22 18:22:43 +0000 |
728 | +++ openlp/core/ui/themewizard.py 2017-08-10 06:57:18 +0000 |
729 | @@ -22,6 +22,8 @@ |
730 | """ |
731 | The Create/Edit theme wizard |
732 | """ |
733 | +from pathlib import Path |
734 | + |
735 | from PyQt5 import QtCore, QtGui, QtWidgets |
736 | |
737 | from openlp.core.common import UiStrings, translate, is_macosx |
738 | |
739 | === modified file 'openlp/plugins/bibles/lib/importers/http.py' |
740 | --- openlp/plugins/bibles/lib/importers/http.py 2017-08-02 06:10:26 +0000 |
741 | +++ openlp/plugins/bibles/lib/importers/http.py 2017-08-10 06:57:18 +0000 |
742 | @@ -255,7 +255,7 @@ |
743 | chapter=chapter, |
744 | version=version) |
745 | soup = get_soup_for_bible_ref( |
746 | - 'http://biblegateway.com/passage/?{url}'.format(url=url_params), |
747 | + 'http://www.biblegateway.com/passage/?{url}'.format(url=url_params), |
748 | pre_parse_regex=r'<meta name.*?/>', pre_parse_substitute='') |
749 | if not soup: |
750 | return None |
751 | @@ -284,7 +284,7 @@ |
752 | """ |
753 | log.debug('BGExtract.get_books_from_http("{version}")'.format(version=version)) |
754 | url_params = urllib.parse.urlencode({'action': 'getVersionInfo', 'vid': '{version}'.format(version=version)}) |
755 | - reference_url = 'http://biblegateway.com/versions/?{url}#books'.format(url=url_params) |
756 | + reference_url = 'http://www.biblegateway.com/versions/?{url}#books'.format(url=url_params) |
757 | page = get_web_page(reference_url) |
758 | if not page: |
759 | send_error_message('download') |
760 | |
761 | === modified file 'openlp/plugins/presentations/lib/presentationtab.py' |
762 | --- openlp/plugins/presentations/lib/presentationtab.py 2017-05-22 18:27:40 +0000 |
763 | +++ openlp/plugins/presentations/lib/presentationtab.py 2017-08-10 06:57:18 +0000 |
764 | @@ -20,10 +20,11 @@ |
765 | # Temple Place, Suite 330, Boston, MA 02111-1307 USA # |
766 | ############################################################################### |
767 | |
768 | -from PyQt5 import QtGui, QtWidgets |
769 | +from PyQt5 import QtWidgets |
770 | |
771 | from openlp.core.common import Settings, UiStrings, translate |
772 | -from openlp.core.lib import SettingsTab, build_icon |
773 | +from openlp.core.common.path import path_to_str, str_to_path |
774 | +from openlp.core.lib import SettingsTab |
775 | from openlp.core.lib.ui import critical_error_message_box |
776 | from openlp.core.ui.lib import PathEdit |
777 | from openlp.plugins.presentations.lib.pdfcontroller import PdfController |
778 | @@ -156,7 +157,7 @@ |
779 | self.program_path_edit.setEnabled(enable_pdf_program) |
780 | pdf_program = Settings().value(self.settings_section + '/pdf_program') |
781 | if pdf_program: |
782 | - self.program_path_edit.path = pdf_program |
783 | + self.program_path_edit.path = str_to_path(pdf_program) |
784 | |
785 | def save(self): |
786 | """ |
787 | @@ -192,7 +193,7 @@ |
788 | Settings().setValue(setting_key, self.ppt_window_check_box.checkState()) |
789 | changed = True |
790 | # Save pdf-settings |
791 | - pdf_program = self.program_path_edit.path |
792 | + pdf_program = path_to_str(self.program_path_edit.path) |
793 | enable_pdf_program = self.pdf_program_check_box.checkState() |
794 | # If the given program is blank disable using the program |
795 | if pdf_program == '': |
796 | @@ -219,12 +220,13 @@ |
797 | checkbox.setEnabled(controller.is_available()) |
798 | self.set_controller_text(checkbox, controller) |
799 | |
800 | - def on_program_path_edit_path_changed(self, filename): |
801 | + def on_program_path_edit_path_changed(self, new_path): |
802 | """ |
803 | Select the mudraw or ghostscript binary that should be used. |
804 | """ |
805 | - if filename: |
806 | - if not PdfController.process_check_binary(filename): |
807 | + new_path = path_to_str(new_path) |
808 | + if new_path: |
809 | + if not PdfController.process_check_binary(new_path): |
810 | critical_error_message_box(UiStrings().Error, |
811 | translate('PresentationPlugin.PresentationTab', |
812 | 'The program is not ghostscript or mudraw which is required.')) |
813 | |
814 | === modified file 'openlp/plugins/songs/forms/editsongform.py' |
815 | --- openlp/plugins/songs/forms/editsongform.py 2017-08-01 20:59:41 +0000 |
816 | +++ openlp/plugins/songs/forms/editsongform.py 2017-08-10 06:57:18 +0000 |
817 | @@ -28,12 +28,15 @@ |
818 | import re |
819 | import os |
820 | import shutil |
821 | +from pathlib import Path |
822 | |
823 | from PyQt5 import QtCore, QtWidgets |
824 | |
825 | from openlp.core.common import Registry, RegistryProperties, AppLocation, UiStrings, check_directory_exists, translate |
826 | -from openlp.core.lib import FileDialog, PluginStatus, MediaType, create_separated_list |
827 | +from openlp.core.common.path import path_to_str |
828 | +from openlp.core.lib import PluginStatus, MediaType, create_separated_list |
829 | from openlp.core.lib.ui import set_case_insensitive_completer, critical_error_message_box, find_and_set_in_combo_box |
830 | +from openlp.core.ui.lib.filedialog import FileDialog |
831 | from openlp.core.common.languagemanager import get_natural_key |
832 | from openlp.plugins.songs.lib import VerseType, clean_song |
833 | from openlp.plugins.songs.lib.db import Book, Song, Author, AuthorType, Topic, MediaFile, SongBookEntry |
834 | @@ -925,9 +928,10 @@ |
835 | Loads file(s) from the filesystem. |
836 | """ |
837 | filters = '{text} (*)'.format(text=UiStrings().AllFiles) |
838 | - file_names = FileDialog.getOpenFileNames(self, translate('SongsPlugin.EditSongForm', 'Open File(s)'), '', |
839 | - filters) |
840 | - for filename in file_names: |
841 | + file_paths, selected_filter = FileDialog.getOpenFileNames( |
842 | + self, translate('SongsPlugin.EditSongForm', 'Open File(s)'), Path(), filters) |
843 | + for file_path in file_paths: |
844 | + filename = path_to_str(file_path) |
845 | item = QtWidgets.QListWidgetItem(os.path.split(str(filename))[1]) |
846 | item.setData(QtCore.Qt.UserRole, filename) |
847 | self.audio_list_widget.addItem(item) |
848 | |
849 | === modified file 'openlp/plugins/songs/forms/songimportform.py' |
850 | --- openlp/plugins/songs/forms/songimportform.py 2017-05-30 18:42:35 +0000 |
851 | +++ openlp/plugins/songs/forms/songimportform.py 2017-08-10 06:57:18 +0000 |
852 | @@ -29,8 +29,9 @@ |
853 | from PyQt5 import QtCore, QtWidgets |
854 | |
855 | from openlp.core.common import RegistryProperties, Settings, UiStrings, translate |
856 | -from openlp.core.lib import FileDialog |
857 | +from openlp.core.common.path import path_to_str, str_to_path |
858 | from openlp.core.lib.ui import critical_error_message_box |
859 | +from openlp.core.ui.lib.filedialog import FileDialog |
860 | from openlp.core.ui.lib.wizard import OpenLPWizard, WizardStrings |
861 | from openlp.plugins.songs.lib.importer import SongFormat, SongFormatSelect |
862 | |
863 | @@ -237,10 +238,11 @@ |
864 | if filters: |
865 | filters += ';;' |
866 | filters += '{text} (*)'.format(text=UiStrings().AllFiles) |
867 | - file_names = FileDialog.getOpenFileNames( |
868 | + file_paths, selected_filter = FileDialog.getOpenFileNames( |
869 | self, title, |
870 | - Settings().value(self.plugin.settings_section + '/last directory import'), filters) |
871 | - if file_names: |
872 | + str_to_path(Settings().value(self.plugin.settings_section + '/last directory import')), filters) |
873 | + if file_paths: |
874 | + file_names = [path_to_str(file_path) for file_path in file_paths] |
875 | listbox.addItems(file_names) |
876 | Settings().setValue(self.plugin.settings_section + '/last directory import', |
877 | os.path.split(str(file_names[0]))[0]) |
878 | |
879 | === modified file 'openlp/plugins/songusage/forms/songusagedetailform.py' |
880 | --- openlp/plugins/songusage/forms/songusagedetailform.py 2017-06-04 12:14:23 +0000 |
881 | +++ openlp/plugins/songusage/forms/songusagedetailform.py 2017-08-10 06:57:18 +0000 |
882 | @@ -27,6 +27,7 @@ |
883 | from sqlalchemy.sql import and_ |
884 | |
885 | from openlp.core.common import RegistryProperties, Settings, check_directory_exists, translate |
886 | +from openlp.core.common.path import path_to_str, str_to_path |
887 | from openlp.core.lib.ui import critical_error_message_box |
888 | from openlp.plugins.songusage.lib.db import SongUsageItem |
889 | from .songusagedetaildialog import Ui_SongUsageDetailDialog |
890 | @@ -55,20 +56,21 @@ |
891 | """ |
892 | self.from_date_calendar.setSelectedDate(Settings().value(self.plugin.settings_section + '/from date')) |
893 | self.to_date_calendar.setSelectedDate(Settings().value(self.plugin.settings_section + '/to date')) |
894 | - self.report_path_edit.path = Settings().value(self.plugin.settings_section + '/last directory export') |
895 | + self.report_path_edit.path = str_to_path( |
896 | + Settings().value(self.plugin.settings_section + '/last directory export')) |
897 | |
898 | def on_report_path_edit_path_changed(self, file_path): |
899 | """ |
900 | Triggered when the Directory selection button is clicked |
901 | """ |
902 | - Settings().setValue(self.plugin.settings_section + '/last directory export', file_path) |
903 | + Settings().setValue(self.plugin.settings_section + '/last directory export', path_to_str(file_path)) |
904 | |
905 | def accept(self): |
906 | """ |
907 | Ok was triggered so lets save the data and run the report |
908 | """ |
909 | log.debug('accept') |
910 | - path = self.report_path_edit.path |
911 | + path = path_to_str(self.report_path_edit.path) |
912 | if not path: |
913 | self.main_window.error_message( |
914 | translate('SongUsagePlugin.SongUsageDetailForm', 'Output Path Not Selected'), |
915 | |
916 | === added file 'tests/functional/openlp_core_common/test_path.py' |
917 | --- tests/functional/openlp_core_common/test_path.py 1970-01-01 00:00:00 +0000 |
918 | +++ tests/functional/openlp_core_common/test_path.py 2017-08-10 06:57:18 +0000 |
919 | @@ -0,0 +1,88 @@ |
920 | +# -*- coding: utf-8 -*- |
921 | +# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 |
922 | + |
923 | +############################################################################### |
924 | +# OpenLP - Open Source Lyrics Projection # |
925 | +# --------------------------------------------------------------------------- # |
926 | +# Copyright (c) 2008-2017 OpenLP Developers # |
927 | +# --------------------------------------------------------------------------- # |
928 | +# This program is free software; you can redistribute it and/or modify it # |
929 | +# under the terms of the GNU General Public License as published by the Free # |
930 | +# Software Foundation; version 2 of the License. # |
931 | +# # |
932 | +# This program is distributed in the hope that it will be useful, but WITHOUT # |
933 | +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or # |
934 | +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for # |
935 | +# more details. # |
936 | +# # |
937 | +# You should have received a copy of the GNU General Public License along # |
938 | +# with this program; if not, write to the Free Software Foundation, Inc., 59 # |
939 | +# Temple Place, Suite 330, Boston, MA 02111-1307 USA # |
940 | +############################################################################### |
941 | +""" |
942 | +Package to test the openlp.core.common.path package. |
943 | +""" |
944 | +import os |
945 | +from pathlib import Path |
946 | +from unittest import TestCase |
947 | + |
948 | +from openlp.core.common.path import path_to_str, str_to_path |
949 | + |
950 | + |
951 | +class TestPath(TestCase): |
952 | + """ |
953 | + Tests for the :mod:`openlp.core.common.path` module |
954 | + """ |
955 | + |
956 | + def test_path_to_str_type_error(self): |
957 | + """ |
958 | + Test that `path_to_str` raises a type error when called with an invalid type |
959 | + """ |
960 | + # GIVEN: The `path_to_str` function |
961 | + # WHEN: Calling `path_to_str` with an invalid Type |
962 | + # THEN: A TypeError should have been raised |
963 | + with self.assertRaises(TypeError): |
964 | + path_to_str(str()) |
965 | + |
966 | + def test_path_to_str_none(self): |
967 | + """ |
968 | + Test that `path_to_str` correctly converts the path parameter when passed with None |
969 | + """ |
970 | + # GIVEN: The `path_to_str` function |
971 | + # WHEN: Calling the `path_to_str` function with None |
972 | + result = path_to_str(None) |
973 | + |
974 | + # THEN: `path_to_str` should return an empty string |
975 | + self.assertEqual(result, '') |
976 | + |
977 | + def test_path_to_str_path_object(self): |
978 | + """ |
979 | + Test that `path_to_str` correctly converts the path parameter when passed a Path object |
980 | + """ |
981 | + # GIVEN: The `path_to_str` function |
982 | + # WHEN: Calling the `path_to_str` function with a Path object |
983 | + result = path_to_str(Path('test/path')) |
984 | + |
985 | + # THEN: `path_to_str` should return a string representation of the Path object |
986 | + self.assertEqual(result, os.path.join('test', 'path')) |
987 | + |
988 | + def test_str_to_path_type_error(self): |
989 | + """ |
990 | + Test that `str_to_path` raises a type error when called with an invalid type |
991 | + """ |
992 | + # GIVEN: The `str_to_path` function |
993 | + # WHEN: Calling `str_to_path` with an invalid Type |
994 | + # THEN: A TypeError should have been raised |
995 | + with self.assertRaises(TypeError): |
996 | + str_to_path(Path()) |
997 | + |
998 | + def test_str_to_path_empty_str(self): |
999 | + """ |
1000 | + Test that `str_to_path` correctly converts the string parameter when passed with and empty string |
1001 | + """ |
1002 | + # GIVEN: The `str_to_path` function |
1003 | + # WHEN: Calling the `str_to_path` function with None |
1004 | + result = str_to_path('') |
1005 | + |
1006 | + # THEN: `path_to_str` should return None |
1007 | + self.assertEqual(result, None) |
1008 | |
1009 | === modified file 'tests/functional/openlp_core_lib/test_file_dialog.py' |
1010 | --- tests/functional/openlp_core_lib/test_file_dialog.py 2017-04-24 05:17:55 +0000 |
1011 | +++ tests/functional/openlp_core_lib/test_file_dialog.py 2017-08-10 06:57:18 +0000 |
1012 | @@ -20,12 +20,10 @@ |
1013 | # Temple Place, Suite 330, Boston, MA 02111-1307 USA # |
1014 | ############################################################################### |
1015 | """ |
1016 | -Package to test the openlp.core.lib.filedialog package. |
1017 | +Package to test the openlp.core.ui.lib.filedialog package. |
1018 | """ |
1019 | from unittest import TestCase |
1020 | -from unittest.mock import MagicMock, call, patch |
1021 | - |
1022 | -from openlp.core.lib.filedialog import FileDialog |
1023 | +from unittest.mock import MagicMock, patch |
1024 | |
1025 | |
1026 | class TestFileDialog(TestCase): |
1027 | @@ -33,9 +31,9 @@ |
1028 | Test the functions in the :mod:`filedialog` module. |
1029 | """ |
1030 | def setUp(self): |
1031 | - self.os_patcher = patch('openlp.core.lib.filedialog.os') |
1032 | - self.qt_gui_patcher = patch('openlp.core.lib.filedialog.QtWidgets') |
1033 | - self.ui_strings_patcher = patch('openlp.core.lib.filedialog.UiStrings') |
1034 | + self.os_patcher = patch('openlp.core.ui.lib.filedialog.os') |
1035 | + self.qt_gui_patcher = patch('openlp.core.ui.lib.filedialog.QtWidgets') |
1036 | + self.ui_strings_patcher = patch('openlp.core.ui.lib.filedialog.UiStrings') |
1037 | self.mocked_os = self.os_patcher.start() |
1038 | self.mocked_qt_gui = self.qt_gui_patcher.start() |
1039 | self.mocked_ui_strings = self.ui_strings_patcher.start() |
1040 | @@ -45,52 +43,3 @@ |
1041 | self.os_patcher.stop() |
1042 | self.qt_gui_patcher.stop() |
1043 | self.ui_strings_patcher.stop() |
1044 | - |
1045 | - def test_get_open_file_names_canceled(self): |
1046 | - """ |
1047 | - Test that FileDialog.getOpenFileNames() returns and empty QStringList when QFileDialog is canceled |
1048 | - (returns an empty QStringList) |
1049 | - """ |
1050 | - self.mocked_os.reset_mock() |
1051 | - |
1052 | - # GIVEN: An empty QStringList as a return value from QFileDialog.getOpenFileNames |
1053 | - self.mocked_qt_gui.QFileDialog.getOpenFileNames.return_value = ([], []) |
1054 | - |
1055 | - # WHEN: FileDialog.getOpenFileNames is called |
1056 | - result = FileDialog.getOpenFileNames(self.mocked_parent) |
1057 | - |
1058 | - # THEN: The returned value should be an empty QStringList and os.path.exists should not have been called |
1059 | - assert not self.mocked_os.path.exists.called |
1060 | - self.assertEqual(result, [], |
1061 | - 'FileDialog.getOpenFileNames should return and empty list when QFileDialog.getOpenFileNames ' |
1062 | - 'is canceled') |
1063 | - |
1064 | - def test_returned_file_list(self): |
1065 | - """ |
1066 | - Test that FileDialog.getOpenFileNames handles a list of files properly when QFileList.getOpenFileNames |
1067 | - returns a good file name, a url encoded file name and a non-existing file |
1068 | - """ |
1069 | - self.mocked_os.rest_mock() |
1070 | - self.mocked_qt_gui.reset_mock() |
1071 | - |
1072 | - # GIVEN: A List of known values as a return value from QFileDialog.getOpenFileNames and a list of valid file |
1073 | - # names. |
1074 | - self.mocked_qt_gui.QFileDialog.getOpenFileNames.return_value = ([ |
1075 | - '/Valid File', '/url%20encoded%20file%20%231', '/non-existing'], []) |
1076 | - self.mocked_os.path.exists.side_effect = lambda file_name: file_name in [ |
1077 | - '/Valid File', '/url encoded file #1'] |
1078 | - self.mocked_ui_strings().FileNotFound = 'File Not Found' |
1079 | - self.mocked_ui_strings().FileNotFoundMessage = 'File {name} not found.\nPlease try selecting it individually.' |
1080 | - |
1081 | - # WHEN: FileDialog.getOpenFileNames is called |
1082 | - result = FileDialog.getOpenFileNames(self.mocked_parent) |
1083 | - |
1084 | - # THEN: os.path.exists should have been called with known args. QmessageBox.information should have been |
1085 | - # called. The returned result should correlate with the input. |
1086 | - call_list = [call('/Valid File'), call('/url%20encoded%20file%20%231'), call('/url encoded file #1'), |
1087 | - call('/non-existing'), call('/non-existing')] |
1088 | - self.mocked_os.path.exists.assert_has_calls(call_list) |
1089 | - self.mocked_qt_gui.QMessageBox.information.assert_called_with( |
1090 | - self.mocked_parent, 'File Not Found', |
1091 | - 'File /non-existing not found.\nPlease try selecting it individually.') |
1092 | - self.assertEqual(result, ['/Valid File', '/url encoded file #1'], 'The returned file list is incorrect') |
1093 | |
1094 | === modified file 'tests/functional/openlp_core_lib/test_lib.py' |
1095 | --- tests/functional/openlp_core_lib/test_lib.py 2017-05-17 20:06:45 +0000 |
1096 | +++ tests/functional/openlp_core_lib/test_lib.py 2017-08-10 06:57:18 +0000 |
1097 | @@ -29,10 +29,9 @@ |
1098 | |
1099 | from PyQt5 import QtCore, QtGui |
1100 | |
1101 | -from openlp.core.lib import FormattingTags, expand_chords_for_printing |
1102 | -from openlp.core.lib import build_icon, check_item_selected, clean_tags, create_thumb, create_separated_list, \ |
1103 | - expand_tags, get_text_file_string, image_to_byte, resize_image, str_to_bool, validate_thumb, expand_chords, \ |
1104 | - compare_chord_lyric, find_formatting_tags |
1105 | +from openlp.core.lib import FormattingTags, build_icon, check_item_selected, clean_tags, compare_chord_lyric, \ |
1106 | + create_separated_list, create_thumb, expand_chords, expand_chords_for_printing, expand_tags, find_formatting_tags, \ |
1107 | + get_text_file_string, image_to_byte, replace_params, resize_image, str_to_bool, validate_thumb |
1108 | |
1109 | TEST_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', 'resources')) |
1110 | |
1111 | @@ -652,6 +651,38 @@ |
1112 | mocked_os.stat.assert_any_call(thumb_path) |
1113 | assert result is False, 'The result should be False' |
1114 | |
1115 | + def test_replace_params_no_params(self): |
1116 | + """ |
1117 | + Test replace_params when called with and empty tuple instead of parameters to replace |
1118 | + """ |
1119 | + # GIVEN: Some test data |
1120 | + test_args = (1, 2) |
1121 | + test_kwargs = {'arg3': 3, 'arg4': 4} |
1122 | + test_params = tuple() |
1123 | + |
1124 | + # WHEN: Calling replace_params |
1125 | + result_args, result_kwargs = replace_params(test_args, test_kwargs, test_params) |
1126 | + |
1127 | + # THEN: The positional and keyword args should not have changed |
1128 | + self.assertEqual(test_args, result_args) |
1129 | + self.assertEqual(test_kwargs, result_kwargs) |
1130 | + |
1131 | + def test_replace_params_params(self): |
1132 | + """ |
1133 | + Test replace_params when given a positional and a keyword argument to change |
1134 | + """ |
1135 | + # GIVEN: Some test data |
1136 | + test_args = (1, 2) |
1137 | + test_kwargs = {'arg3': 3, 'arg4': 4} |
1138 | + test_params = ((1, 'arg2', str), (2, 'arg3', str)) |
1139 | + |
1140 | + # WHEN: Calling replace_params |
1141 | + result_args, result_kwargs = replace_params(test_args, test_kwargs, test_params) |
1142 | + |
1143 | + # THEN: The positional and keyword args should have have changed |
1144 | + self.assertEqual(result_args, (1, '2')) |
1145 | + self.assertEqual(result_kwargs, {'arg3': '3', 'arg4': 4}) |
1146 | + |
1147 | def test_resize_thumb(self): |
1148 | """ |
1149 | Test the resize_thumb() function |
1150 | |
1151 | === modified file 'tests/functional/openlp_core_ui/test_themeform.py' |
1152 | --- tests/functional/openlp_core_ui/test_themeform.py 2017-05-14 07:15:29 +0000 |
1153 | +++ tests/functional/openlp_core_ui/test_themeform.py 2017-08-10 06:57:18 +0000 |
1154 | @@ -22,6 +22,7 @@ |
1155 | """ |
1156 | Package to test the openlp.core.ui.themeform package. |
1157 | """ |
1158 | +from pathlib import Path |
1159 | from unittest import TestCase |
1160 | from unittest.mock import MagicMock, patch |
1161 | |
1162 | @@ -45,7 +46,7 @@ |
1163 | self.instance.theme = MagicMock() |
1164 | |
1165 | # WHEN: `on_image_path_edit_path_changed` is clicked |
1166 | - self.instance.on_image_path_edit_path_changed('/new/pat.h') |
1167 | + self.instance.on_image_path_edit_path_changed(Path('/', 'new', 'pat.h')) |
1168 | |
1169 | # THEN: The theme background file should be set and `set_background_page_values` should have been called |
1170 | self.assertEqual(self.instance.theme.background_filename, '/new/pat.h') |
1171 | |
1172 | === renamed file 'tests/functional/openlp_core_ui_lib/test_color_button.py' => 'tests/functional/openlp_core_ui_lib/test_colorbutton.py' |
1173 | === added file 'tests/functional/openlp_core_ui_lib/test_filedialog.py' |
1174 | --- tests/functional/openlp_core_ui_lib/test_filedialog.py 1970-01-01 00:00:00 +0000 |
1175 | +++ tests/functional/openlp_core_ui_lib/test_filedialog.py 2017-08-10 06:57:18 +0000 |
1176 | @@ -0,0 +1,188 @@ |
1177 | +import os |
1178 | +from unittest import TestCase |
1179 | +from unittest.mock import patch |
1180 | +from pathlib import Path |
1181 | + |
1182 | +from PyQt5 import QtWidgets |
1183 | + |
1184 | +from openlp.core.ui.lib.filedialog import FileDialog |
1185 | + |
1186 | + |
1187 | +class TestFileDialogPatches(TestCase): |
1188 | + """ |
1189 | + Tests for the :mod:`openlp.core.ui.lib.filedialogpatches` module |
1190 | + """ |
1191 | + |
1192 | + def test_file_dialog(self): |
1193 | + """ |
1194 | + Test that the :class:`FileDialog` instantiates correctly |
1195 | + """ |
1196 | + # GIVEN: The FileDialog class |
1197 | + # WHEN: Creating an instance |
1198 | + instance = FileDialog() |
1199 | + |
1200 | + # THEN: The instance should be an instance of QFileDialog |
1201 | + self.assertIsInstance(instance, QtWidgets.QFileDialog) |
1202 | + |
1203 | + def test_get_existing_directory_user_abort(self): |
1204 | + """ |
1205 | + Test that `getExistingDirectory` handles the case when the user cancels the dialog |
1206 | + """ |
1207 | + # GIVEN: FileDialog with a mocked QDialog.getExistingDirectory method |
1208 | + # WHEN: Calling FileDialog.getExistingDirectory and the user cancels the dialog returns a empty string |
1209 | + with patch('PyQt5.QtWidgets.QFileDialog.getExistingDirectory', return_value=''): |
1210 | + result = FileDialog.getExistingDirectory() |
1211 | + |
1212 | + # THEN: The result should be None |
1213 | + self.assertEqual(result, None) |
1214 | + |
1215 | + def test_get_existing_directory_user_accepts(self): |
1216 | + """ |
1217 | + Test that `getExistingDirectory` handles the case when the user accepts the dialog |
1218 | + """ |
1219 | + # GIVEN: FileDialog with a mocked QDialog.getExistingDirectory method |
1220 | + # WHEN: Calling FileDialog.getExistingDirectory, the user chooses a file and accepts the dialog (it returns a |
1221 | + # string pointing to the directory) |
1222 | + with patch('PyQt5.QtWidgets.QFileDialog.getExistingDirectory', return_value=os.path.join('test', 'dir')): |
1223 | + result = FileDialog.getExistingDirectory() |
1224 | + |
1225 | + # THEN: getExistingDirectory() should return a Path object pointing to the chosen file |
1226 | + self.assertEqual(result, Path('test', 'dir')) |
1227 | + |
1228 | + def test_get_existing_directory_param_order(self): |
1229 | + """ |
1230 | + Test that `getExistingDirectory` passes the parameters to `QFileDialog.getExistingDirectory` in the correct |
1231 | + order |
1232 | + """ |
1233 | + # GIVEN: FileDialog |
1234 | + with patch('openlp.core.ui.lib.filedialog.QtWidgets.QFileDialog.getExistingDirectory', return_value='') \ |
1235 | + as mocked_get_existing_directory: |
1236 | + |
1237 | + # WHEN: Calling the getExistingDirectory method with all parameters set |
1238 | + FileDialog.getExistingDirectory('Parent', 'Caption', Path('test', 'dir'), 'Options') |
1239 | + |
1240 | + # THEN: The `QFileDialog.getExistingDirectory` should have been called with the parameters in the correct |
1241 | + # order |
1242 | + mocked_get_existing_directory.assert_called_once_with('Parent', 'Caption', os.path.join('test', 'dir'), |
1243 | + 'Options') |
1244 | + |
1245 | + def test_get_open_file_name_user_abort(self): |
1246 | + """ |
1247 | + Test that `getOpenFileName` handles the case when the user cancels the dialog |
1248 | + """ |
1249 | + # GIVEN: FileDialog with a mocked QDialog.getOpenFileName method |
1250 | + # WHEN: Calling FileDialog.getOpenFileName and the user cancels the dialog (it returns a tuple with the first |
1251 | + # value set as an empty string) |
1252 | + with patch('PyQt5.QtWidgets.QFileDialog.getOpenFileName', return_value=('', '')): |
1253 | + result = FileDialog.getOpenFileName() |
1254 | + |
1255 | + # THEN: First value should be None |
1256 | + self.assertEqual(result[0], None) |
1257 | + |
1258 | + def test_get_open_file_name_user_accepts(self): |
1259 | + """ |
1260 | + Test that `getOpenFileName` handles the case when the user accepts the dialog |
1261 | + """ |
1262 | + # GIVEN: FileDialog with a mocked QDialog.getOpenFileName method |
1263 | + # WHEN: Calling FileDialog.getOpenFileName, the user chooses a file and accepts the dialog (it returns a |
1264 | + # tuple with the first value set as an string pointing to the file) |
1265 | + with patch('PyQt5.QtWidgets.QFileDialog.getOpenFileName', |
1266 | + return_value=(os.path.join('test', 'chosen.file'), '')): |
1267 | + result = FileDialog.getOpenFileName() |
1268 | + |
1269 | + # THEN: getOpenFileName() should return a tuple with the first value set to a Path object pointing to the |
1270 | + # chosen file |
1271 | + self.assertEqual(result[0], Path('test', 'chosen.file')) |
1272 | + |
1273 | + def test_get_open_file_name_selected_filter(self): |
1274 | + """ |
1275 | + Test that `getOpenFileName` does not modify the selectedFilter as returned by `QFileDialog.getOpenFileName` |
1276 | + """ |
1277 | + # GIVEN: FileDialog with a mocked QDialog.get_save_file_name method |
1278 | + # WHEN: Calling FileDialog.getOpenFileName, and `QFileDialog.getOpenFileName` returns a known `selectedFilter` |
1279 | + with patch('PyQt5.QtWidgets.QFileDialog.getOpenFileName', return_value=('', 'selected filter')): |
1280 | + result = FileDialog.getOpenFileName() |
1281 | + |
1282 | + # THEN: getOpenFileName() should return a tuple with the second value set to a the selected filter |
1283 | + self.assertEqual(result[1], 'selected filter') |
1284 | + |
1285 | + def test_get_open_file_names_user_abort(self): |
1286 | + """ |
1287 | + Test that `getOpenFileNames` handles the case when the user cancels the dialog |
1288 | + """ |
1289 | + # GIVEN: FileDialog with a mocked QDialog.getOpenFileNames method |
1290 | + # WHEN: Calling FileDialog.getOpenFileNames and the user cancels the dialog (it returns a tuple with the first |
1291 | + # value set as an empty list) |
1292 | + with patch('PyQt5.QtWidgets.QFileDialog.getOpenFileNames', return_value=([], '')): |
1293 | + result = FileDialog.getOpenFileNames() |
1294 | + |
1295 | + # THEN: First value should be an empty list |
1296 | + self.assertEqual(result[0], []) |
1297 | + |
1298 | + def test_get_open_file_names_user_accepts(self): |
1299 | + """ |
1300 | + Test that `getOpenFileNames` handles the case when the user accepts the dialog |
1301 | + """ |
1302 | + # GIVEN: FileDialog with a mocked QDialog.getOpenFileNames method |
1303 | + # WHEN: Calling FileDialog.getOpenFileNames, the user chooses some files and accepts the dialog (it returns a |
1304 | + # tuple with the first value set as a list of strings pointing to the file) |
1305 | + with patch('PyQt5.QtWidgets.QFileDialog.getOpenFileNames', |
1306 | + return_value=([os.path.join('test', 'chosen.file1'), os.path.join('test', 'chosen.file2')], '')): |
1307 | + result = FileDialog.getOpenFileNames() |
1308 | + |
1309 | + # THEN: getOpenFileNames() should return a tuple with the first value set to a list of Path objects pointing |
1310 | + # to the chosen file |
1311 | + self.assertEqual(result[0], [Path('test', 'chosen.file1'), Path('test', 'chosen.file2')]) |
1312 | + |
1313 | + def test_get_open_file_names_selected_filter(self): |
1314 | + """ |
1315 | + Test that `getOpenFileNames` does not modify the selectedFilter as returned by `QFileDialog.getOpenFileNames` |
1316 | + """ |
1317 | + # GIVEN: FileDialog with a mocked QDialog.getOpenFileNames method |
1318 | + # WHEN: Calling FileDialog.getOpenFileNames, and `QFileDialog.getOpenFileNames` returns a known |
1319 | + # `selectedFilter` |
1320 | + with patch('PyQt5.QtWidgets.QFileDialog.getOpenFileNames', return_value=([], 'selected filter')): |
1321 | + result = FileDialog.getOpenFileNames() |
1322 | + |
1323 | + # THEN: getOpenFileNames() should return a tuple with the second value set to a the selected filter |
1324 | + self.assertEqual(result[1], 'selected filter') |
1325 | + |
1326 | + def test_get_save_file_name_user_abort(self): |
1327 | + """ |
1328 | + Test that `getSaveFileName` handles the case when the user cancels the dialog |
1329 | + """ |
1330 | + # GIVEN: FileDialog with a mocked QDialog.get_save_file_name method |
1331 | + # WHEN: Calling FileDialog.getSaveFileName and the user cancels the dialog (it returns a tuple with the first |
1332 | + # value set as an empty string) |
1333 | + with patch('PyQt5.QtWidgets.QFileDialog.getSaveFileName', return_value=('', '')): |
1334 | + result = FileDialog.getSaveFileName() |
1335 | + |
1336 | + # THEN: First value should be None |
1337 | + self.assertEqual(result[0], None) |
1338 | + |
1339 | + def test_get_save_file_name_user_accepts(self): |
1340 | + """ |
1341 | + Test that `getSaveFileName` handles the case when the user accepts the dialog |
1342 | + """ |
1343 | + # GIVEN: FileDialog with a mocked QDialog.getSaveFileName method |
1344 | + # WHEN: Calling FileDialog.getSaveFileName, the user chooses a file and accepts the dialog (it returns a |
1345 | + # tuple with the first value set as an string pointing to the file) |
1346 | + with patch('PyQt5.QtWidgets.QFileDialog.getSaveFileName', |
1347 | + return_value=(os.path.join('test', 'chosen.file'), '')): |
1348 | + result = FileDialog.getSaveFileName() |
1349 | + |
1350 | + # THEN: getSaveFileName() should return a tuple with the first value set to a Path object pointing to the |
1351 | + # chosen file |
1352 | + self.assertEqual(result[0], Path('test', 'chosen.file')) |
1353 | + |
1354 | + def test_get_save_file_name_selected_filter(self): |
1355 | + """ |
1356 | + Test that `getSaveFileName` does not modify the selectedFilter as returned by `QFileDialog.getSaveFileName` |
1357 | + """ |
1358 | + # GIVEN: FileDialog with a mocked QDialog.get_save_file_name method |
1359 | + # WHEN: Calling FileDialog.getSaveFileName, and `QFileDialog.getSaveFileName` returns a known `selectedFilter` |
1360 | + with patch('PyQt5.QtWidgets.QFileDialog.getSaveFileName', return_value=('', 'selected filter')): |
1361 | + result = FileDialog.getSaveFileName() |
1362 | + |
1363 | + # THEN: getSaveFileName() should return a tuple with the second value set to a the selected filter |
1364 | + self.assertEqual(result[1], 'selected filter') |
1365 | |
1366 | === renamed file 'tests/functional/openlp_core_ui_lib/test_path_edit.py' => 'tests/functional/openlp_core_ui_lib/test_pathedit.py' |
1367 | --- tests/functional/openlp_core_ui_lib/test_path_edit.py 2017-05-13 07:35:39 +0000 |
1368 | +++ tests/functional/openlp_core_ui_lib/test_pathedit.py 2017-08-10 06:57:18 +0000 |
1369 | @@ -22,12 +22,13 @@ |
1370 | """ |
1371 | This module contains tests for the openlp.core.ui.lib.pathedit module |
1372 | """ |
1373 | +import os |
1374 | +from pathlib import Path |
1375 | from unittest import TestCase |
1376 | - |
1377 | -from PyQt5 import QtWidgets |
1378 | +from unittest.mock import MagicMock, PropertyMock, patch |
1379 | |
1380 | from openlp.core.ui.lib import PathEdit, PathType |
1381 | -from unittest.mock import MagicMock, PropertyMock, patch |
1382 | +from openlp.core.ui.lib.filedialog import FileDialog |
1383 | |
1384 | |
1385 | class TestPathEdit(TestCase): |
1386 | @@ -43,11 +44,11 @@ |
1387 | Test the `path` property getter. |
1388 | """ |
1389 | # GIVEN: An instance of PathEdit with the `_path` instance variable set |
1390 | - self.widget._path = 'getter/test/pat.h' |
1391 | + self.widget._path = Path('getter', 'test', 'pat.h') |
1392 | |
1393 | # WHEN: Reading the `path` property |
1394 | # THEN: The value that we set should be returned |
1395 | - self.assertEqual(self.widget.path, 'getter/test/pat.h') |
1396 | + self.assertEqual(self.widget.path, Path('getter', 'test', 'pat.h')) |
1397 | |
1398 | def test_path_setter(self): |
1399 | """ |
1400 | @@ -57,13 +58,13 @@ |
1401 | self.widget.line_edit = MagicMock() |
1402 | |
1403 | # WHEN: Writing to the `path` property |
1404 | - self.widget.path = 'setter/test/pat.h' |
1405 | + self.widget.path = Path('setter', 'test', 'pat.h') |
1406 | |
1407 | # THEN: The `_path` instance variable should be set with the test data. The `line_edit` text and tooltip |
1408 | # should have also been set. |
1409 | - self.assertEqual(self.widget._path, 'setter/test/pat.h') |
1410 | - self.widget.line_edit.setToolTip.assert_called_once_with('setter/test/pat.h') |
1411 | - self.widget.line_edit.setText.assert_called_once_with('setter/test/pat.h') |
1412 | + self.assertEqual(self.widget._path, Path('setter', 'test', 'pat.h')) |
1413 | + self.widget.line_edit.setToolTip.assert_called_once_with(os.path.join('setter', 'test', 'pat.h')) |
1414 | + self.widget.line_edit.setText.assert_called_once_with(os.path.join('setter', 'test', 'pat.h')) |
1415 | |
1416 | def test_path_type_getter(self): |
1417 | """ |
1418 | @@ -125,22 +126,20 @@ |
1419 | """ |
1420 | # GIVEN: An instance of PathEdit with the `path_type` set to `Directories` and a mocked |
1421 | # QFileDialog.getExistingDirectory |
1422 | - with patch('openlp.core.ui.lib.pathedit.QtWidgets.QFileDialog.getExistingDirectory', return_value='') as \ |
1423 | + with patch('openlp.core.ui.lib.pathedit.FileDialog.getExistingDirectory', return_value=None) as \ |
1424 | mocked_get_existing_directory, \ |
1425 | - patch('openlp.core.ui.lib.pathedit.QtWidgets.QFileDialog.getOpenFileName') as \ |
1426 | - mocked_get_open_file_name, \ |
1427 | - patch('openlp.core.ui.lib.pathedit.os.path.normpath') as mocked_normpath: |
1428 | + patch('openlp.core.ui.lib.pathedit.FileDialog.getOpenFileName') as mocked_get_open_file_name: |
1429 | self.widget._path_type = PathType.Directories |
1430 | - self.widget._path = 'test/path/' |
1431 | + self.widget._path = Path('test', 'path') |
1432 | |
1433 | # WHEN: Calling on_browse_button_clicked |
1434 | self.widget.on_browse_button_clicked() |
1435 | |
1436 | # THEN: The FileDialog.getExistingDirectory should have been called with the default caption |
1437 | - mocked_get_existing_directory.assert_called_once_with(self.widget, 'Select Directory', 'test/path/', |
1438 | - QtWidgets.QFileDialog.ShowDirsOnly) |
1439 | + mocked_get_existing_directory.assert_called_once_with(self.widget, 'Select Directory', |
1440 | + Path('test', 'path'), |
1441 | + FileDialog.ShowDirsOnly) |
1442 | self.assertFalse(mocked_get_open_file_name.called) |
1443 | - self.assertFalse(mocked_normpath.called) |
1444 | |
1445 | def test_on_browse_button_clicked_directory_custom_caption(self): |
1446 | """ |
1447 | @@ -149,45 +148,40 @@ |
1448 | """ |
1449 | # GIVEN: An instance of PathEdit with the `path_type` set to `Directories` and a mocked |
1450 | # QFileDialog.getExistingDirectory with `default_caption` set. |
1451 | - with patch('openlp.core.ui.lib.pathedit.QtWidgets.QFileDialog.getExistingDirectory', return_value='') as \ |
1452 | + with patch('openlp.core.ui.lib.pathedit.FileDialog.getExistingDirectory', return_value=None) as \ |
1453 | mocked_get_existing_directory, \ |
1454 | - patch('openlp.core.ui.lib.pathedit.QtWidgets.QFileDialog.getOpenFileName') as \ |
1455 | - mocked_get_open_file_name, \ |
1456 | - patch('openlp.core.ui.lib.pathedit.os.path.normpath') as mocked_normpath: |
1457 | + patch('openlp.core.ui.lib.pathedit.FileDialog.getOpenFileName') as mocked_get_open_file_name: |
1458 | self.widget._path_type = PathType.Directories |
1459 | - self.widget._path = 'test/path/' |
1460 | + self.widget._path = Path('test', 'path') |
1461 | self.widget.dialog_caption = 'Directory Caption' |
1462 | |
1463 | # WHEN: Calling on_browse_button_clicked |
1464 | self.widget.on_browse_button_clicked() |
1465 | |
1466 | # THEN: The FileDialog.getExistingDirectory should have been called with the custom caption |
1467 | - mocked_get_existing_directory.assert_called_once_with(self.widget, 'Directory Caption', 'test/path/', |
1468 | - QtWidgets.QFileDialog.ShowDirsOnly) |
1469 | + mocked_get_existing_directory.assert_called_once_with(self.widget, 'Directory Caption', |
1470 | + Path('test', 'path'), |
1471 | + FileDialog.ShowDirsOnly) |
1472 | self.assertFalse(mocked_get_open_file_name.called) |
1473 | - self.assertFalse(mocked_normpath.called) |
1474 | |
1475 | def test_on_browse_button_clicked_file(self): |
1476 | """ |
1477 | Test the `browse_button` `clicked` handler on_browse_button_clicked when the `path_type` is set to Files. |
1478 | """ |
1479 | # GIVEN: An instance of PathEdit with the `path_type` set to `Files` and a mocked QFileDialog.getOpenFileName |
1480 | - with patch('openlp.core.ui.lib.pathedit.QtWidgets.QFileDialog.getExistingDirectory') as \ |
1481 | - mocked_get_existing_directory, \ |
1482 | - patch('openlp.core.ui.lib.pathedit.QtWidgets.QFileDialog.getOpenFileName', return_value=('', '')) as \ |
1483 | - mocked_get_open_file_name, \ |
1484 | - patch('openlp.core.ui.lib.pathedit.os.path.normpath') as mocked_normpath: |
1485 | + with patch('openlp.core.ui.lib.pathedit.FileDialog.getExistingDirectory') as mocked_get_existing_directory, \ |
1486 | + patch('openlp.core.ui.lib.pathedit.FileDialog.getOpenFileName', return_value=(None, '')) as \ |
1487 | + mocked_get_open_file_name: |
1488 | self.widget._path_type = PathType.Files |
1489 | - self.widget._path = 'test/pat.h' |
1490 | + self.widget._path = Path('test', 'pat.h') |
1491 | |
1492 | # WHEN: Calling on_browse_button_clicked |
1493 | self.widget.on_browse_button_clicked() |
1494 | |
1495 | # THEN: The FileDialog.getOpenFileName should have been called with the default caption |
1496 | - mocked_get_open_file_name.assert_called_once_with(self.widget, 'Select File', 'test/pat.h', |
1497 | + mocked_get_open_file_name.assert_called_once_with(self.widget, 'Select File', Path('test', 'pat.h'), |
1498 | self.widget.filters) |
1499 | self.assertFalse(mocked_get_existing_directory.called) |
1500 | - self.assertFalse(mocked_normpath.called) |
1501 | |
1502 | def test_on_browse_button_clicked_file_custom_caption(self): |
1503 | """ |
1504 | @@ -196,23 +190,20 @@ |
1505 | """ |
1506 | # GIVEN: An instance of PathEdit with the `path_type` set to `Files` and a mocked QFileDialog.getOpenFileName |
1507 | # with `default_caption` set. |
1508 | - with patch('openlp.core.ui.lib.pathedit.QtWidgets.QFileDialog.getExistingDirectory') as \ |
1509 | - mocked_get_existing_directory, \ |
1510 | - patch('openlp.core.ui.lib.pathedit.QtWidgets.QFileDialog.getOpenFileName', return_value=('', '')) as \ |
1511 | - mocked_get_open_file_name, \ |
1512 | - patch('openlp.core.ui.lib.pathedit.os.path.normpath') as mocked_normpath: |
1513 | + with patch('openlp.core.ui.lib.pathedit.FileDialog.getExistingDirectory') as mocked_get_existing_directory, \ |
1514 | + patch('openlp.core.ui.lib.pathedit.FileDialog.getOpenFileName', return_value=(None, '')) as \ |
1515 | + mocked_get_open_file_name: |
1516 | self.widget._path_type = PathType.Files |
1517 | - self.widget._path = 'test/pat.h' |
1518 | + self.widget._path = Path('test', 'pat.h') |
1519 | self.widget.dialog_caption = 'File Caption' |
1520 | |
1521 | # WHEN: Calling on_browse_button_clicked |
1522 | self.widget.on_browse_button_clicked() |
1523 | |
1524 | # THEN: The FileDialog.getOpenFileName should have been called with the custom caption |
1525 | - mocked_get_open_file_name.assert_called_once_with(self.widget, 'File Caption', 'test/pat.h', |
1526 | + mocked_get_open_file_name.assert_called_once_with(self.widget, 'File Caption', Path('test', 'pat.h'), |
1527 | self.widget.filters) |
1528 | self.assertFalse(mocked_get_existing_directory.called) |
1529 | - self.assertFalse(mocked_normpath.called) |
1530 | |
1531 | def test_on_browse_button_clicked_user_cancels(self): |
1532 | """ |
1533 | @@ -221,16 +212,14 @@ |
1534 | """ |
1535 | # GIVEN: An instance of PathEdit with a mocked QFileDialog.getOpenFileName which returns an empty str for the |
1536 | # file path. |
1537 | - with patch('openlp.core.ui.lib.pathedit.QtWidgets.QFileDialog.getOpenFileName', return_value=('', '')) as \ |
1538 | - mocked_get_open_file_name, \ |
1539 | - patch('openlp.core.ui.lib.pathedit.os.path.normpath') as mocked_normpath: |
1540 | + with patch('openlp.core.ui.lib.pathedit.FileDialog.getOpenFileName', return_value=(None, '')) as \ |
1541 | + mocked_get_open_file_name: |
1542 | |
1543 | # WHEN: Calling on_browse_button_clicked |
1544 | self.widget.on_browse_button_clicked() |
1545 | |
1546 | # THEN: normpath should not have been called |
1547 | self.assertTrue(mocked_get_open_file_name.called) |
1548 | - self.assertFalse(mocked_normpath.called) |
1549 | |
1550 | def test_on_browse_button_clicked_user_accepts(self): |
1551 | """ |
1552 | @@ -239,9 +228,8 @@ |
1553 | """ |
1554 | # GIVEN: An instance of PathEdit with a mocked QFileDialog.getOpenFileName which returns a str for the file |
1555 | # path. |
1556 | - with patch('openlp.core.ui.lib.pathedit.QtWidgets.QFileDialog.getOpenFileName', |
1557 | - return_value=('/test/pat.h', '')) as mocked_get_open_file_name, \ |
1558 | - patch('openlp.core.ui.lib.pathedit.os.path.normpath') as mocked_normpath, \ |
1559 | + with patch('openlp.core.ui.lib.pathedit.FileDialog.getOpenFileName', |
1560 | + return_value=(Path('test', 'pat.h'), '')) as mocked_get_open_file_name, \ |
1561 | patch.object(self.widget, 'on_new_path'): |
1562 | |
1563 | # WHEN: Calling on_browse_button_clicked |
1564 | @@ -249,7 +237,6 @@ |
1565 | |
1566 | # THEN: normpath and `on_new_path` should have been called |
1567 | self.assertTrue(mocked_get_open_file_name.called) |
1568 | - mocked_normpath.assert_called_once_with('/test/pat.h') |
1569 | self.assertTrue(self.widget.on_new_path.called) |
1570 | |
1571 | def test_on_revert_button_clicked(self): |
1572 | @@ -258,13 +245,13 @@ |
1573 | """ |
1574 | # GIVEN: An instance of PathEdit with a mocked `on_new_path`, and the `default_path` set. |
1575 | with patch.object(self.widget, 'on_new_path') as mocked_on_new_path: |
1576 | - self.widget.default_path = '/default/pat.h' |
1577 | + self.widget.default_path = Path('default', 'pat.h') |
1578 | |
1579 | # WHEN: Calling `on_revert_button_clicked` |
1580 | self.widget.on_revert_button_clicked() |
1581 | |
1582 | # THEN: on_new_path should have been called with the default path |
1583 | - mocked_on_new_path.assert_called_once_with('/default/pat.h') |
1584 | + mocked_on_new_path.assert_called_once_with(Path('default', 'pat.h')) |
1585 | |
1586 | def test_on_line_edit_editing_finished(self): |
1587 | """ |
1588 | @@ -272,13 +259,13 @@ |
1589 | """ |
1590 | # GIVEN: An instance of PathEdit with a mocked `line_edit` and `on_new_path`. |
1591 | with patch.object(self.widget, 'on_new_path') as mocked_on_new_path: |
1592 | - self.widget.line_edit = MagicMock(**{'text.return_value': '/test/pat.h'}) |
1593 | + self.widget.line_edit = MagicMock(**{'text.return_value': 'test/pat.h'}) |
1594 | |
1595 | # WHEN: Calling `on_line_edit_editing_finished` |
1596 | self.widget.on_line_edit_editing_finished() |
1597 | |
1598 | # THEN: on_new_path should have been called with the path enetered in `line_edit` |
1599 | - mocked_on_new_path.assert_called_once_with('/test/pat.h') |
1600 | + mocked_on_new_path.assert_called_once_with(Path('test', 'pat.h')) |
1601 | |
1602 | def test_on_new_path_no_change(self): |
1603 | """ |
1604 | @@ -286,11 +273,11 @@ |
1605 | """ |
1606 | # GIVEN: An instance of PathEdit with a test path and mocked `pathChanged` signal |
1607 | with patch('openlp.core.ui.lib.pathedit.PathEdit.path', new_callable=PropertyMock): |
1608 | - self.widget._path = '/old/test/pat.h' |
1609 | + self.widget._path = Path('/old', 'test', 'pat.h') |
1610 | self.widget.pathChanged = MagicMock() |
1611 | |
1612 | # WHEN: Calling `on_new_path` with the same path as the existing path |
1613 | - self.widget.on_new_path('/old/test/pat.h') |
1614 | + self.widget.on_new_path(Path('/old', 'test', 'pat.h')) |
1615 | |
1616 | # THEN: The `pathChanged` signal should not be emitted |
1617 | self.assertFalse(self.widget.pathChanged.emit.called) |
1618 | @@ -301,11 +288,11 @@ |
1619 | """ |
1620 | # GIVEN: An instance of PathEdit with a test path and mocked `pathChanged` signal |
1621 | with patch('openlp.core.ui.lib.pathedit.PathEdit.path', new_callable=PropertyMock): |
1622 | - self.widget._path = '/old/test/pat.h' |
1623 | + self.widget._path = Path('/old', 'test', 'pat.h') |
1624 | self.widget.pathChanged = MagicMock() |
1625 | |
1626 | # WHEN: Calling `on_new_path` with the a new path |
1627 | - self.widget.on_new_path('/new/test/pat.h') |
1628 | + self.widget.on_new_path(Path('/new', 'test', 'pat.h')) |
1629 | |
1630 | # THEN: The `pathChanged` signal should be emitted |
1631 | - self.widget.pathChanged.emit.assert_called_once_with('/new/test/pat.h') |
1632 | + self.widget.pathChanged.emit.assert_called_once_with(Path('/new', 'test', 'pat.h')) |
Trunk is fine
Advanced Tab tim/Projects/ OpenLP/ openlp/ pathlib2/ openlp/ core/ui/ mainwindow. py", line 810, in on_settings_ configure_ iem_clicked settings_ form.exec( ) tim/Projects/ OpenLP/ openlp/ pathlib2/ openlp/ core/ui/ settingsform. py", line 70, in exec insert_ tab(self. general_ tab) tim/Projects/ OpenLP/ openlp/ pathlib2/ openlp/ core/ui/ settingsform. py", line 89, in insert_tab debug(' Inserting {text} tab'.format( text=tab_ widget. tab_title) )
Traceback (most recent call last):
File "/home/
self.
File "/home/
self.
File "/home/
log.
AttributeError: 'NoneType' object has no attribute 'tab_title'
Theme Edit with image tim/Projects/ OpenLP/ openlp/ pathlib2/ openlp/ core/ui/ thememanager. py", line 326, in on_edit_theme theme_form. exec(True) tim/Projects/ OpenLP/ openlp/ pathlib2/ openlp/ core/ui/ themeform. py", line 279, in exec set_defaults( ) tim/Projects/ OpenLP/ openlp/ pathlib2/ openlp/ core/ui/ themeform. py", line 109, in set_defaults set_background_ page_values( ) tim/Projects/ OpenLP/ openlp/ pathlib2/ openlp/ core/ui/ themeform. py", line 322, in set_background_ page_values image_path_ edit.path = path_to_ str(self. theme.backgroun d_filename) tim/Projects/ OpenLP/ openlp/ pathlib2/ openlp/ core/common/ path.py" , line 37, in path_to_str 'parameter \'path\' must be of type Path or NoneType')
Traceback (most recent call last):
File "/home/
self.
File "/home/
self.
File "/home/
self.
File "/home/
self.
File "/home/
raise TypeError(
TypeError: parameter 'path' must be of type Path or NoneType